From 7f8641dd94a9ab7cd67b93cfc2d551c3a616db2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thierry=20Sall=C3=A9?= Date: Wed, 12 Jan 2022 15:39:29 +0100 Subject: [PATCH] [additionalOutputFormats] Update comments and add more tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Thierry Sallé --- deploy/crds/crd-certificates.yaml | 6 +- .../apis/certmanager/types_certificate.go | 12 +- .../certmanager/v1alpha2/types_certificate.go | 10 +- .../certmanager/v1alpha3/types_certificate.go | 10 +- .../certmanager/v1beta1/types_certificate.go | 10 +- pkg/apis/certmanager/v1/types_certificate.go | 24 +- .../internal/secretsmanager/secret.go | 14 +- .../internal/secretsmanager/secret_test.go | 294 +++++++++++++++++- .../validation/certificates/certificates.go | 7 +- test/e2e/suite/issuers/ca/certificate.go | 2 +- test/unit/gen/certificate.go | 2 +- 11 files changed, 336 insertions(+), 55 deletions(-) diff --git a/deploy/crds/crd-certificates.yaml b/deploy/crds/crd-certificates.yaml index 4fbc5a36b..d57717f11 100644 --- a/deploy/crds/crd-certificates.yaml +++ b/deploy/crds/crd-certificates.yaml @@ -68,16 +68,16 @@ spec: - secretName properties: additionalOutputFormats: - description: AdditionalOutputFormats allows for requests for additional formats in which the private key should be written to the secret + description: AdditionalOutputFormats allows for requests of additional output formats of the private key and the certificate to be written to the secret. This is an Alpha Feature and should be enabled with --feature-gates option. type: array items: - description: AdditionalOutputFormat wraps an additional key output format type + description: AdditionalOutputFormat wraps an additional output format type type: object required: - type properties: type: - description: OutputFormatType specifies which additional key formats should be added to Kubernetes secrets. Allowed values are `DER` or `CombinedPEM`. When Type is set to `DER` an additional entry `key.der` will be created in the secret containing the binary format of the key. When Type is set to `CombinedPEM` an additional entry `tls-combined.pem` will be created in the secret containing the PEM formatted certificate chain and key (tls.key + tls.crt concatenated) + description: OutputFormatType specifies which additional output formats should be added to Kubernetes secrets. Allowed values are `DER` or `CombinedPEM`. When Type is set to `DER` an additional entry `key.der` will be created in the secret containing the binary format of the key. When Type is set to `CombinedPEM` an additional entry `tls-combined.pem` will be created in the secret containing the PEM formatted certificate chain and key (tls.key + tls.crt concatenated) type: string enum: - DER diff --git a/internal/apis/certmanager/types_certificate.go b/internal/apis/certmanager/types_certificate.go index 21ca106a8..795ff8bf3 100644 --- a/internal/apis/certmanager/types_certificate.go +++ b/internal/apis/certmanager/types_certificate.go @@ -166,7 +166,9 @@ type CertificateSpec struct { // revisions will not be garbage collected. Default value is `nil`. RevisionHistoryLimit *int32 - // AdditionalOutputFormats allows for requests for additional formats in which the private key should be written to the secret + // AdditionalOutputFormats allows for requests of additional output formats + // of the private key and the certificate to be written to the secret. + // This is an Alpha Feature and should be enabled with --feature-gates option. AdditionalOutputFormats []AdditionalOutputFormat } @@ -208,7 +210,7 @@ type CertificatePrivateKey struct { Size int } -// OutputFormatType specifies which additional key formats should be added to Kubernetes secrets. +// OutputFormatType specifies which additional output formats should be added to Kubernetes secrets. // Allowed values are `DER` or `CombinedPEM`. // When Type is set to `DER` an additional entry `key.der` will be created in the secret // containing the binary format of the key. @@ -217,16 +219,16 @@ type CertificatePrivateKey struct { type OutputFormatType string const ( - // AdditionalKeyOutputFormatDER requests that the DER binary format of the key + // AdditionalOutputFormatDER requests that the DER binary format of the key // is stored in the `key.der` key of a secret - AdditionalKeyOutputFormatDER OutputFormatType = "DER" + AdditionalOutputFormatDER OutputFormatType = "DER" // AdditionalOutputFormatCombinedPEM requests that an entry containing both the PEM tls.crt and tls.key // values concatenated is stored in the `tls-combined.pem` key of a secret AdditionalOutputFormatCombinedPEM OutputFormatType = "CombinedPEM" ) -// AdditionalOutputFormat wraps an additional key output format type +// AdditionalOutputFormat wraps an additional output format type type AdditionalOutputFormat struct { Type OutputFormatType } diff --git a/internal/apis/certmanager/v1alpha2/types_certificate.go b/internal/apis/certmanager/v1alpha2/types_certificate.go index 6dc701e6d..98c02aab6 100644 --- a/internal/apis/certmanager/v1alpha2/types_certificate.go +++ b/internal/apis/certmanager/v1alpha2/types_certificate.go @@ -209,7 +209,9 @@ type CertificateSpec struct { // +optional RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty"` // Validated by the validating webhook. - // AdditionalOutputFormats allows for requests for additional formats in which the private key should be written to the secret + // AdditionalOutputFormats allows for requests of additional output formats + // of the private key and the certificate to be written to the secret. + // This is an Alpha Feature and should be enabled with --feature-gates option. // +optional AdditionalOutputFormats []AdditionalOutputFormat `json:"additionalOutputFormats,omitempty"` } @@ -447,7 +449,7 @@ type CertificateSecretTemplate struct { Labels map[string]string `json:"labels,omitempty"` } -// OutputFormatType specifies which additional key formats should be added to Kubernetes secrets. +// OutputFormatType specifies which additional output formats should be added to Kubernetes secrets. // Allowed values are `DER` or `CombinedPEM`. // When Type is set to `DER` an additional entry `key.der` will be created in the secret // containing the binary format of the key. @@ -457,9 +459,9 @@ type CertificateSecretTemplate struct { type OutputFormatType string const ( - // AdditionalKeyOutputFormatDER requests that the DER binary format of the key + // AdditionalOutputFormatDER requests that the DER binary format of the key // is stored in the `key.der` key of a secret - AdditionalKeyOutputFormatDER OutputFormatType = "DER" + AdditionalOutputFormatDER OutputFormatType = "DER" // AdditionalOutputFormatCombinedPEM requests that an entry containing both the PEM tls.crt and tls.key // values concatenated is stored in the `tls-combined.pem` key of a secret diff --git a/internal/apis/certmanager/v1alpha3/types_certificate.go b/internal/apis/certmanager/v1alpha3/types_certificate.go index d763a9abc..6da185f6c 100644 --- a/internal/apis/certmanager/v1alpha3/types_certificate.go +++ b/internal/apis/certmanager/v1alpha3/types_certificate.go @@ -207,7 +207,9 @@ type CertificateSpec struct { // +optional RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty"` // Validated by the validating webhook. - // AdditionalOutputFormats allows for requests for additional formats in which the private key should be written to the secret + // AdditionalOutputFormats allows for requests of additional output formats + // of the private key and the certificate to be written to the secret. + // This is an Alpha Feature and should be enabled with --feature-gates option. // +optional AdditionalOutputFormats []AdditionalOutputFormat `json:"additionalOutputFormats,omitempty"` } @@ -454,7 +456,7 @@ type CertificateSecretTemplate struct { Labels map[string]string `json:"labels,omitempty"` } -// OutputFormatType specifies which additional key formats should be added to Kubernetes secrets. +// OutputFormatType specifies which additional output formats should be added to Kubernetes secrets. // Allowed values are `DER` or `CombinedPEM`. // When Type is set to `DER` an additional entry `key.der` will be created in the secret // containing the binary format of the key. @@ -464,9 +466,9 @@ type CertificateSecretTemplate struct { type OutputFormatType string const ( - // AdditionalKeyOutputFormatDER requests that the DER binary format of the key + // AdditionalOutputFormatDER requests that the DER binary format of the key // is stored in the `key.der` key of a secret - AdditionalKeyOutputFormatDER OutputFormatType = "DER" + AdditionalOutputFormatDER OutputFormatType = "DER" // AdditionalOutputFormatCombinedPEM requests that an entry containing both the PEM tls.crt and tls.key // values concatenated is stored in the `tls-combined.pem` key of a secret diff --git a/internal/apis/certmanager/v1beta1/types_certificate.go b/internal/apis/certmanager/v1beta1/types_certificate.go index 25c23ba72..19c319131 100644 --- a/internal/apis/certmanager/v1beta1/types_certificate.go +++ b/internal/apis/certmanager/v1beta1/types_certificate.go @@ -184,7 +184,9 @@ type CertificateSpec struct { // +optional RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty"` // Validated by the validating webhook. - // AdditionalOutputFormats allows for requests for additional formats in which the private key should be written to the secret + // AdditionalOutputFormats allows for requests of additional output formats + // of the private key and the certificate to be written to the secret. + // This is an Alpha Feature and should be enabled with --feature-gates option. // +optional AdditionalOutputFormats []AdditionalOutputFormat `json:"additionalOutputFormats,omitempty"` } @@ -452,7 +454,7 @@ type CertificateSecretTemplate struct { Labels map[string]string `json:"labels,omitempty"` } -// OutputFormatType specifies which additional key formats should be added to Kubernetes secrets. +// OutputFormatType specifies which additional output formats should be added to Kubernetes secrets. // Allowed values are `DER` or `CombinedPEM`. // When Type is set to `DER` an additional entry `key.der` will be created in the secret // containing the binary format of the key. @@ -462,9 +464,9 @@ type CertificateSecretTemplate struct { type OutputFormatType string const ( - // AdditionalKeyOutputFormatDER requests that the DER binary format of the key + // AdditionalOutputFormatDER requests that the DER binary format of the key // is stored in the `key.der` key of a secret - AdditionalKeyOutputFormatDER OutputFormatType = "DER" + AdditionalOutputFormatDER OutputFormatType = "DER" // AdditionalOutputFormatCombinedPEM requests that an entry containing both the PEM tls.crt and tls.key // values concatenated is stored in the `tls-combined.pem` key of a secret diff --git a/pkg/apis/certmanager/v1/types_certificate.go b/pkg/apis/certmanager/v1/types_certificate.go index fdc3f6eb1..cbf8ba003 100644 --- a/pkg/apis/certmanager/v1/types_certificate.go +++ b/pkg/apis/certmanager/v1/types_certificate.go @@ -188,7 +188,9 @@ type CertificateSpec struct { // +optional RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty"` // Validated by the validating webhook. - // AdditionalOutputFormats allows for requests for additional formats in which the private key should be written to the secret + // AdditionalOutputFormats allows for requests of additional output formats + // of the private key and the certificate to be written to the secret. + // This is an Alpha Feature and should be enabled with --feature-gates option. // +optional AdditionalOutputFormats []AdditionalOutputFormat `json:"additionalOutputFormats,omitempty"` } @@ -253,7 +255,7 @@ var ( RotationPolicyAlways PrivateKeyRotationPolicy = "Always" ) -// OutputFormatType specifies which additional key formats should be added to Kubernetes secrets. +// OutputFormatType specifies which additional output formats should be added to Kubernetes secrets. // Allowed values are `DER` or `CombinedPEM`. // When Type is set to `DER` an additional entry `key.der` will be created in the secret // containing the binary format of the key. @@ -263,24 +265,24 @@ var ( type OutputFormatType string const ( - // AdditionalKeyOutputFormatDER requests that the DER binary format of the key - // is stored in the `key.der` key of a secret - AdditionalKeyOutputFormatDER OutputFormatType = "DER" + // AdditionalOutputFormatDER requests that the DER binary format of the key + // is stored in the `key.der` key of the certificate's secret. + AdditionalOutputFormatDER OutputFormatType = "DER" - // AdditionalKeyOutputFormatDERKey is the name of the data entry in the Secret resource - // used to store the DER formatted private key - AdditionalKeyOutputFormatDERKey string = "key.der" + // AdditionalOutputFormatDERKey is the name of the data entry in the Secret resource + // used to store the DER formatted private key. + AdditionalOutputFormatDERKey string = "key.der" // AdditionalOutputFormatCombinedPEM requests that an entry containing both the PEM tls.crt and tls.key - // values concatenated is stored in the `tls-combined.pem` key of a secret + // values concatenated is stored in the `tls-combined.pem` key of the certificate's secret. AdditionalOutputFormatCombinedPEM OutputFormatType = "CombinedPEM" // AdditionalOutputFormatPEMKey is the name of the data entry in the Secret resource - // used to store the combined PEM certificate + key + // used to store the combined PEM certificate + key. AdditionalOutputFormatPEMKey string = "tls-combined.pem" ) -// AdditionalOutputFormat wraps an additional key output format type +// AdditionalOutputFormat wraps an additional output format type type AdditionalOutputFormat struct { Type OutputFormatType `json:"type"` } diff --git a/pkg/controller/certificates/internal/secretsmanager/secret.go b/pkg/controller/certificates/internal/secretsmanager/secret.go index c82225c43..11b9e5f9d 100644 --- a/pkg/controller/certificates/internal/secretsmanager/secret.go +++ b/pkg/controller/certificates/internal/secretsmanager/secret.go @@ -125,7 +125,7 @@ func (s *SecretsManager) UpdateData(ctx context.Context, crt *cmapi.Certificate, func updateSecretWithAdditionalOutputFormats(crt *cmapi.Certificate, secret *corev1.Secret, data SecretData) error { if crt.Spec.AdditionalOutputFormats == nil { - delete(secret.Data, cmapi.AdditionalKeyOutputFormatDERKey) + delete(secret.Data, cmapi.AdditionalOutputFormatDERKey) delete(secret.Data, cmapi.AdditionalOutputFormatPEMKey) return nil } @@ -135,21 +135,21 @@ func updateSecretWithAdditionalOutputFormats(crt *cmapi.Certificate, secret *cor for _, f := range crt.Spec.AdditionalOutputFormats { switch f.Type { - case cmapi.AdditionalKeyOutputFormatDER: + case cmapi.AdditionalOutputFormatDER: additionalOutputFormatDER = true case cmapi.AdditionalOutputFormatCombinedPEM: additionalOutputFormatPEM = true default: - return fmt.Errorf("Unknown additional output format %s", f.Type) + return fmt.Errorf("unknown additional output format %s", f.Type) } } if additionalOutputFormatDER { // Store binary format of the private key block, _ := pem.Decode(data.PrivateKey) - secret.Data[cmapi.AdditionalKeyOutputFormatDERKey] = block.Bytes + secret.Data[cmapi.AdditionalOutputFormatDERKey] = block.Bytes } else { - delete(secret.Data, cmapi.AdditionalKeyOutputFormatDERKey) + delete(secret.Data, cmapi.AdditionalOutputFormatDERKey) } if additionalOutputFormatPEM { @@ -248,7 +248,9 @@ func (s *SecretsManager) setValues(crt *cmapi.Certificate, secret *corev1.Secret // Add additional output formats if utilfeature.DefaultFeatureGate.Enabled(feature.AdditionalCertificateOutputFormats) { - updateSecretWithAdditionalOutputFormats(crt, secret, data) + if err := updateSecretWithAdditionalOutputFormats(crt, secret, data); err != nil { + return fmt.Errorf("error during additional output format update: %w", err) + } } } secret.Data[corev1.TLSPrivateKeyKey] = data.PrivateKey diff --git a/pkg/controller/certificates/internal/secretsmanager/secret_test.go b/pkg/controller/certificates/internal/secretsmanager/secret_test.go index 5e78e74ef..62ee9ae96 100644 --- a/pkg/controller/certificates/internal/secretsmanager/secret_test.go +++ b/pkg/controller/certificates/internal/secretsmanager/secret_test.go @@ -77,11 +77,17 @@ func TestSecretsManager(t *testing.T) { ) // enable feature gate additional private key for this test defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultMutableFeatureGate, feature.AdditionalCertificateOutputFormats, true)() + baseCertWithAdditionalOutputFormatDER := gen.CertificateFrom(baseCertBundle.Certificate, + gen.SetCertificateAdditionalOutputFormats(cmapi.AdditionalOutputFormat{Type: "DER"}), + ) + baseCertWithAdditionalOutputFormatCombinedPEM := gen.CertificateFrom(baseCertBundle.Certificate, + gen.SetCertificateAdditionalOutputFormats(cmapi.AdditionalOutputFormat{Type: "CombinedPEM"}), + ) baseCertWithAdditionalOutputFormats := gen.CertificateFrom(baseCertBundle.Certificate, - gen.SetCertificateAdditionalOutputFormats([]cmapi.AdditionalOutputFormat{ - {Type: "DER"}, - {Type: "CombinedPEM"}, - }), + gen.SetCertificateAdditionalOutputFormats( + cmapi.AdditionalOutputFormat{Type: "DER"}, + cmapi.AdditionalOutputFormat{Type: "CombinedPEM"}, + ), ) block, _ := pem.Decode(baseCertBundle.PrivateKeyBytes) tlsDerContent := block.Bytes @@ -415,7 +421,87 @@ func TestSecretsManager(t *testing.T) { expectedErr: false, }, - "if secret does not exist, create new Secret with additional key formats and enabled feature": { + "if secret does not exist, create new Secret with additional output format DER": { + certificate: baseCertWithAdditionalOutputFormatDER, + SecretData: SecretData{Certificate: baseCertBundle.CertBytes, CA: []byte("test-ca"), PrivateKey: baseCertBundle.PrivateKeyBytes}, + builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{}, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + corev1.SchemeGroupVersion.WithResource("secrets"), + gen.DefaultTestNamespace, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: gen.DefaultTestNamespace, + Name: "output", + Annotations: map[string]string{ + cmapi.CertificateNameKey: "test", + cmapi.IssuerGroupAnnotationKey: "foo.io", + cmapi.IssuerKindAnnotationKey: "Issuer", + cmapi.IssuerNameAnnotationKey: "ca-issuer", + + cmapi.CommonNameAnnotationKey: baseCertBundle.Cert.Subject.CommonName, + cmapi.AltNamesAnnotationKey: strings.Join(baseCertBundle.Cert.DNSNames, ","), + cmapi.IPSANAnnotationKey: strings.Join(utilpki.IPAddressesToString(baseCertBundle.Cert.IPAddresses), ","), + cmapi.URISANAnnotationKey: strings.Join(utilpki.URLsToString(baseCertBundle.Cert.URIs), ","), + }, + Labels: map[string]string{}, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: baseCertBundle.CertBytes, + corev1.TLSPrivateKeyKey: baseCertBundle.PrivateKeyBytes, + cmmeta.TLSCAKey: []byte("test-ca"), + cmapi.AdditionalOutputFormatDERKey: tlsDerContent, + }, + Type: corev1.SecretTypeTLS, + }, + )), + }, + }, + expectedErr: false, + }, + + "if secret does not exist, create new Secret with additional output format CombinedPEM": { + certificate: baseCertWithAdditionalOutputFormatCombinedPEM, + SecretData: SecretData{Certificate: baseCertBundle.CertBytes, CA: []byte("test-ca"), PrivateKey: baseCertBundle.PrivateKeyBytes}, + builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{}, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewCreateAction( + corev1.SchemeGroupVersion.WithResource("secrets"), + gen.DefaultTestNamespace, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: gen.DefaultTestNamespace, + Name: "output", + Annotations: map[string]string{ + cmapi.CertificateNameKey: "test", + cmapi.IssuerGroupAnnotationKey: "foo.io", + cmapi.IssuerKindAnnotationKey: "Issuer", + cmapi.IssuerNameAnnotationKey: "ca-issuer", + + cmapi.CommonNameAnnotationKey: baseCertBundle.Cert.Subject.CommonName, + cmapi.AltNamesAnnotationKey: strings.Join(baseCertBundle.Cert.DNSNames, ","), + cmapi.IPSANAnnotationKey: strings.Join(utilpki.IPAddressesToString(baseCertBundle.Cert.IPAddresses), ","), + cmapi.URISANAnnotationKey: strings.Join(utilpki.URLsToString(baseCertBundle.Cert.URIs), ","), + }, + Labels: map[string]string{}, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: baseCertBundle.CertBytes, + corev1.TLSPrivateKeyKey: baseCertBundle.PrivateKeyBytes, + cmmeta.TLSCAKey: []byte("test-ca"), + cmapi.AdditionalOutputFormatPEMKey: []byte(strings.Join([]string{string(baseCertBundle.PrivateKeyBytes), string(baseCertBundle.CertBytes)}, "\n")), + }, + Type: corev1.SecretTypeTLS, + }, + )), + }, + }, + expectedErr: false, + }, + + "if secret does not exist, create new Secret with additional output format DER and CombinedPEM": { certificate: baseCertWithAdditionalOutputFormats, SecretData: SecretData{Certificate: baseCertBundle.CertBytes, CA: []byte("test-ca"), PrivateKey: baseCertBundle.PrivateKeyBytes}, builder: &testpkg.Builder{ @@ -442,11 +528,199 @@ func TestSecretsManager(t *testing.T) { Labels: map[string]string{}, }, Data: map[string][]byte{ - corev1.TLSCertKey: baseCertBundle.CertBytes, - corev1.TLSPrivateKeyKey: baseCertBundle.PrivateKeyBytes, - cmmeta.TLSCAKey: []byte("test-ca"), - cmapi.AdditionalKeyOutputFormatDERKey: tlsDerContent, - cmapi.AdditionalOutputFormatPEMKey: []byte(strings.Join([]string{string(baseCertBundle.PrivateKeyBytes), string(baseCertBundle.CertBytes)}, "\n")), + corev1.TLSCertKey: baseCertBundle.CertBytes, + corev1.TLSPrivateKeyKey: baseCertBundle.PrivateKeyBytes, + cmmeta.TLSCAKey: []byte("test-ca"), + cmapi.AdditionalOutputFormatDERKey: tlsDerContent, + cmapi.AdditionalOutputFormatPEMKey: []byte(strings.Join([]string{string(baseCertBundle.PrivateKeyBytes), string(baseCertBundle.CertBytes)}, "\n")), + }, + Type: corev1.SecretTypeTLS, + }, + )), + }, + }, + expectedErr: false, + }, + + "if secret exists, with tls-combined.pem and key.der but no additional formats specified": { + certificate: baseCertBundle.Certificate, + certificateOptions: controllerpkg.CertificateOptions{ + EnableOwnerRef: false, + }, + SecretData: SecretData{Certificate: baseCertBundle.CertBytes, CA: []byte("test-ca"), PrivateKey: []byte("test-key")}, + builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: gen.DefaultTestNamespace, + Name: "output", + Annotations: map[string]string{ + "my-custom": "annotation", + }, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: []byte("foo"), + corev1.TLSPrivateKeyKey: []byte("foo"), + cmmeta.TLSCAKey: []byte("foo"), + cmapi.AdditionalOutputFormatDERKey: []byte("foo"), + cmapi.AdditionalOutputFormatPEMKey: []byte("foo"), + }, + Type: corev1.SecretTypeTLS, + }, + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewUpdateAction( + corev1.SchemeGroupVersion.WithResource("secrets"), + gen.DefaultTestNamespace, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: gen.DefaultTestNamespace, + Name: "output", + Annotations: map[string]string{ + "my-custom": "annotation", + + cmapi.CertificateNameKey: "test", + cmapi.IssuerGroupAnnotationKey: "foo.io", + cmapi.IssuerKindAnnotationKey: "Issuer", + cmapi.IssuerNameAnnotationKey: "ca-issuer", + + cmapi.CommonNameAnnotationKey: baseCertBundle.Cert.Subject.CommonName, + cmapi.AltNamesAnnotationKey: strings.Join(baseCertBundle.Cert.DNSNames, ","), + cmapi.IPSANAnnotationKey: strings.Join(utilpki.IPAddressesToString(baseCertBundle.Cert.IPAddresses), ","), + cmapi.URISANAnnotationKey: strings.Join(utilpki.URLsToString(baseCertBundle.Cert.URIs), ","), + }, + Labels: map[string]string{}, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: baseCertBundle.CertBytes, + corev1.TLSPrivateKeyKey: []byte("test-key"), + cmmeta.TLSCAKey: []byte("test-ca"), + }, + Type: corev1.SecretTypeTLS, + }, + )), + }, + }, + expectedErr: false, + }, + + "if secret exists, with tls-combined.pem and key.der but only DER Format specified": { + certificate: baseCertWithAdditionalOutputFormatDER, + certificateOptions: controllerpkg.CertificateOptions{ + EnableOwnerRef: false, + }, + SecretData: SecretData{Certificate: baseCertBundle.CertBytes, CA: []byte("test-ca"), PrivateKey: baseCertBundle.PrivateKeyBytes}, + builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: gen.DefaultTestNamespace, + Name: "output", + Annotations: map[string]string{ + "my-custom": "annotation", + }, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: []byte("foo"), + corev1.TLSPrivateKeyKey: []byte("foo"), + cmmeta.TLSCAKey: []byte("foo"), + cmapi.AdditionalOutputFormatDERKey: []byte("foo"), + cmapi.AdditionalOutputFormatPEMKey: []byte("foo"), + }, + Type: corev1.SecretTypeTLS, + }, + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewUpdateAction( + corev1.SchemeGroupVersion.WithResource("secrets"), + gen.DefaultTestNamespace, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: gen.DefaultTestNamespace, + Name: "output", + Annotations: map[string]string{ + "my-custom": "annotation", + + cmapi.CertificateNameKey: "test", + cmapi.IssuerGroupAnnotationKey: "foo.io", + cmapi.IssuerKindAnnotationKey: "Issuer", + cmapi.IssuerNameAnnotationKey: "ca-issuer", + + cmapi.CommonNameAnnotationKey: baseCertBundle.Cert.Subject.CommonName, + cmapi.AltNamesAnnotationKey: strings.Join(baseCertBundle.Cert.DNSNames, ","), + cmapi.IPSANAnnotationKey: strings.Join(utilpki.IPAddressesToString(baseCertBundle.Cert.IPAddresses), ","), + cmapi.URISANAnnotationKey: strings.Join(utilpki.URLsToString(baseCertBundle.Cert.URIs), ","), + }, + Labels: map[string]string{}, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: baseCertBundle.CertBytes, + corev1.TLSPrivateKeyKey: baseCertBundle.PrivateKeyBytes, + cmmeta.TLSCAKey: []byte("test-ca"), + cmapi.AdditionalOutputFormatDERKey: tlsDerContent, + }, + Type: corev1.SecretTypeTLS, + }, + )), + }, + }, + expectedErr: false, + }, + + "if secret exists, with tls-combined.pem and key.der but only Combined PEM Format specified": { + certificate: baseCertWithAdditionalOutputFormatCombinedPEM, + certificateOptions: controllerpkg.CertificateOptions{ + EnableOwnerRef: false, + }, + SecretData: SecretData{Certificate: baseCertBundle.CertBytes, CA: []byte("test-ca"), PrivateKey: baseCertBundle.PrivateKeyBytes}, + builder: &testpkg.Builder{ + KubeObjects: []runtime.Object{ + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: gen.DefaultTestNamespace, + Name: "output", + Annotations: map[string]string{ + "my-custom": "annotation", + }, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: []byte("foo"), + corev1.TLSPrivateKeyKey: []byte("foo"), + cmmeta.TLSCAKey: []byte("foo"), + cmapi.AdditionalOutputFormatDERKey: []byte("foo"), + cmapi.AdditionalOutputFormatPEMKey: []byte("foo"), + }, + Type: corev1.SecretTypeTLS, + }, + }, + ExpectedActions: []testpkg.Action{ + testpkg.NewAction(coretesting.NewUpdateAction( + corev1.SchemeGroupVersion.WithResource("secrets"), + gen.DefaultTestNamespace, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: gen.DefaultTestNamespace, + Name: "output", + Annotations: map[string]string{ + "my-custom": "annotation", + + cmapi.CertificateNameKey: "test", + cmapi.IssuerGroupAnnotationKey: "foo.io", + cmapi.IssuerKindAnnotationKey: "Issuer", + cmapi.IssuerNameAnnotationKey: "ca-issuer", + + cmapi.CommonNameAnnotationKey: baseCertBundle.Cert.Subject.CommonName, + cmapi.AltNamesAnnotationKey: strings.Join(baseCertBundle.Cert.DNSNames, ","), + cmapi.IPSANAnnotationKey: strings.Join(utilpki.IPAddressesToString(baseCertBundle.Cert.IPAddresses), ","), + cmapi.URISANAnnotationKey: strings.Join(utilpki.URLsToString(baseCertBundle.Cert.URIs), ","), + }, + Labels: map[string]string{}, + }, + Data: map[string][]byte{ + corev1.TLSCertKey: baseCertBundle.CertBytes, + corev1.TLSPrivateKeyKey: baseCertBundle.PrivateKeyBytes, + cmmeta.TLSCAKey: []byte("test-ca"), + cmapi.AdditionalOutputFormatPEMKey: []byte(strings.Join([]string{string(baseCertBundle.PrivateKeyBytes), string(baseCertBundle.CertBytes)}, "\n")), }, Type: corev1.SecretTypeTLS, }, diff --git a/test/e2e/framework/helper/validation/certificates/certificates.go b/test/e2e/framework/helper/validation/certificates/certificates.go index 826e29768..2c50ea7d9 100644 --- a/test/e2e/framework/helper/validation/certificates/certificates.go +++ b/test/e2e/framework/helper/validation/certificates/certificates.go @@ -39,20 +39,15 @@ type ValidationFunc func(certificate *cmapi.Certificate, secret *corev1.Secret) // ExpectValidKeysInSecret checks that the secret contains valid keys func ExpectValidKeysInSecret(_ *cmapi.Certificate, secret *corev1.Secret) error { - validKeys := [5]string{corev1.TLSPrivateKeyKey, corev1.TLSCertKey, cmmeta.TLSCAKey, cmapi.AdditionalKeyOutputFormatDERKey, cmapi.AdditionalOutputFormatPEMKey} + validKeys := [5]string{corev1.TLSPrivateKeyKey, corev1.TLSCertKey, cmmeta.TLSCAKey, cmapi.AdditionalOutputFormatDERKey, cmapi.AdditionalOutputFormatPEMKey} nbValidKeys := 0 for k := range secret.Data { - found := false for _, k2 := range validKeys { if k == k2 { - found = true nbValidKeys++ break } } - if !found { - return fmt.Errorf("Expected secret key %s to be a valid key (%v)", k, validKeys) - } } if nbValidKeys < 2 { return fmt.Errorf("Expected at least 2 valid keys in certificate secret, but there was %d", nbValidKeys) diff --git a/test/e2e/suite/issuers/ca/certificate.go b/test/e2e/suite/issuers/ca/certificate.go index 4a097b845..c47d3defe 100644 --- a/test/e2e/suite/issuers/ca/certificate.go +++ b/test/e2e/suite/issuers/ca/certificate.go @@ -125,7 +125,7 @@ var _ = framework.CertManagerDescribe("CA Certificate", func() { Expect(err).NotTo(HaveOccurred()) }) - It("should be able to create a certificate with additional private key formats", func() { + It("should be able to create a certificate with additional output formats", func() { certClient := f.CertManagerClientSet.CertmanagerV1().Certificates(f.Namespace.Name) cert := util.NewCertManagerBasicCertificate(certificateName, certificateSecretName, issuerName, v1.IssuerKind, nil, nil) diff --git a/test/unit/gen/certificate.go b/test/unit/gen/certificate.go index 5edb0d061..d42a8822f 100644 --- a/test/unit/gen/certificate.go +++ b/test/unit/gen/certificate.go @@ -262,7 +262,7 @@ func SetCertificateRevisionHistoryLimit(limit int32) CertificateModifier { } } -func SetCertificateAdditionalOutputFormats(additionalOutputFormats []v1.AdditionalOutputFormat) CertificateModifier { +func SetCertificateAdditionalOutputFormats(additionalOutputFormats ...v1.AdditionalOutputFormat) CertificateModifier { return func(crt *v1.Certificate) { crt.Spec.AdditionalOutputFormats = additionalOutputFormats }