From 0b2cdf5a403cd977c91776548e73c3cd2e3132ce Mon Sep 17 00:00:00 2001 From: joshvanl Date: Mon, 25 Jul 2022 15:31:56 +0100 Subject: [PATCH] Adds e2e tests for CertificateRequest self signing controller; focussing on requests being re-synced when the target Secret is up Signed-off-by: joshvanl --- .../approval/approval.go | 0 .../approval/userinfo.go | 0 test/e2e/suite/certificaterequests/doc.go | 22 ++ .../certificaterequests/selfsigned/secret.go | 267 ++++++++++++++++++ test/e2e/suite/doc.go | 2 +- test/unit/crypto/crypto.go | 4 +- 6 files changed, 292 insertions(+), 3 deletions(-) rename test/e2e/suite/{ => certificaterequests}/approval/approval.go (100%) rename test/e2e/suite/{ => certificaterequests}/approval/userinfo.go (100%) create mode 100644 test/e2e/suite/certificaterequests/doc.go create mode 100644 test/e2e/suite/certificaterequests/selfsigned/secret.go diff --git a/test/e2e/suite/approval/approval.go b/test/e2e/suite/certificaterequests/approval/approval.go similarity index 100% rename from test/e2e/suite/approval/approval.go rename to test/e2e/suite/certificaterequests/approval/approval.go diff --git a/test/e2e/suite/approval/userinfo.go b/test/e2e/suite/certificaterequests/approval/userinfo.go similarity index 100% rename from test/e2e/suite/approval/userinfo.go rename to test/e2e/suite/certificaterequests/approval/userinfo.go diff --git a/test/e2e/suite/certificaterequests/doc.go b/test/e2e/suite/certificaterequests/doc.go new file mode 100644 index 000000000..05f74e32c --- /dev/null +++ b/test/e2e/suite/certificaterequests/doc.go @@ -0,0 +1,22 @@ +/* +Copyright 2020 The cert-manager Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package certificaterequests + +import ( + _ "github.com/cert-manager/cert-manager/test/e2e/suite/certificaterequests/approval" + _ "github.com/cert-manager/cert-manager/test/e2e/suite/certificaterequests/selfsigned" +) diff --git a/test/e2e/suite/certificaterequests/selfsigned/secret.go b/test/e2e/suite/certificaterequests/selfsigned/secret.go new file mode 100644 index 000000000..b0e941a87 --- /dev/null +++ b/test/e2e/suite/certificaterequests/selfsigned/secret.go @@ -0,0 +1,267 @@ +/* +Copyright 2020 The cert-manager Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package selfsigned + +import ( + "context" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/clock" + + apiutil "github.com/cert-manager/cert-manager/pkg/api/util" + cmapi "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1" + cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1" + "github.com/cert-manager/cert-manager/test/e2e/framework" + testcrypto "github.com/cert-manager/cert-manager/test/unit/crypto" +) + +// This test ensures that a self-signed certificaterequest will still be signed +// even if the private key Secret was created _after_ the CertificateRequest +// was created. +var _ = framework.CertManagerDescribe("CertificateRequests SelfSigned Secret", func() { + f := framework.NewDefaultFramework("certificaterequests-selfsigned-secret") + + var ( + request *cmapi.CertificateRequest + issuer cmapi.GenericIssuer + secret *corev1.Secret + bundle *testcrypto.CryptoBundle + ) + + JustBeforeEach(func() { + var err error + bundle, err = testcrypto.CreateCryptoBundle(&cmapi.Certificate{ + Spec: cmapi.CertificateSpec{ + CommonName: "selfsigned-test", + }, + }, clock.RealClock{}) + Expect(err).NotTo(HaveOccurred()) + }) + + JustAfterEach(func() { + Expect(f.CRClient.Delete(context.TODO(), request)).NotTo(HaveOccurred()) + Expect(f.CRClient.Delete(context.TODO(), issuer)).NotTo(HaveOccurred()) + Expect(f.CRClient.Delete(context.TODO(), secret)).NotTo(HaveOccurred()) + }) + + It("Issuer: the private key Secret is created after the request is created should still be signed", func() { + var err error + issuer, err = f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), &cmapi.Issuer{ + ObjectMeta: metav1.ObjectMeta{GenerateName: "selfsigned-", Namespace: f.Namespace.Name}, + Spec: cmapi.IssuerSpec{IssuerConfig: cmapi.IssuerConfig{SelfSigned: new(cmapi.SelfSignedIssuer)}}, + }, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Create(context.TODO(), &cmapi.CertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "selfsigned-", Namespace: f.Namespace.Name, + Annotations: map[string]string{"cert-manager.io/private-key-secret-name": "selfsigned-test"}, + }, + Spec: cmapi.CertificateRequestSpec{ + Request: bundle.CSRBytes, + IssuerRef: cmmeta.ObjectReference{Name: issuer.GetName(), Kind: "Issuer", Group: "cert-manager.io"}, + }, + }, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + By("waiting for request to be set to pending") + Eventually(func() bool { + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + return apiutil.CertificateRequestHasCondition(request, cmapi.CertificateRequestCondition{ + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionFalse, + Reason: cmapi.CertificateRequestReasonPending, + }) + }, "20s", "1s").Should(BeTrue(), "request was not set to pending in time: %#+v", request) + + By("creating Secret with private key should result in the request to be signed") + secret, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "selfsigned-test", Namespace: f.Namespace.Name}, + Data: map[string][]byte{ + "tls.key": bundle.PrivateKeyBytes, + }, + }, metav1.CreateOptions{}) + + Eventually(func() bool { + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + return apiutil.CertificateRequestHasCondition(request, cmapi.CertificateRequestCondition{ + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionTrue, + Reason: cmapi.CertificateRequestReasonIssued, + }) && len(request.Status.Certificate) > 0 + }, "20s", "1s").Should(BeTrue(), "request was not signed in time: %#+v", request) + }) + + It("Issuer: private key Secret is updated with a valid private key after the request is created should still be signed", func() { + var err error + By("creating Secret with missing private key") + secret, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "selfsigned-test", Namespace: f.Namespace.Name}, + Data: map[string][]byte{}, + }, metav1.CreateOptions{}) + + issuer, err = f.CertManagerClientSet.CertmanagerV1().Issuers(f.Namespace.Name).Create(context.TODO(), &cmapi.Issuer{ + ObjectMeta: metav1.ObjectMeta{GenerateName: "selfsigned-", Namespace: f.Namespace.Name}, + Spec: cmapi.IssuerSpec{IssuerConfig: cmapi.IssuerConfig{SelfSigned: new(cmapi.SelfSignedIssuer)}}, + }, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Create(context.TODO(), &cmapi.CertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "selfsigned-", Namespace: f.Namespace.Name, + Annotations: map[string]string{"cert-manager.io/private-key-secret-name": "selfsigned-test"}, + }, + Spec: cmapi.CertificateRequestSpec{ + Request: bundle.CSRBytes, + IssuerRef: cmmeta.ObjectReference{Name: issuer.GetName(), Kind: "Issuer", Group: "cert-manager.io"}, + }, + }, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + By("waiting for request to be set to pending") + Eventually(func() bool { + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + return apiutil.CertificateRequestHasCondition(request, cmapi.CertificateRequestCondition{ + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionFalse, + Reason: cmapi.CertificateRequestReasonPending, + }) + }, "20s", "1s").Should(BeTrue(), "request was not set to pending in time: %#+v", request) + + By("updating referenced private key Secret should get the request signed") + secret.Data = map[string][]byte{"tls.key": bundle.PrivateKeyBytes} + _, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Update(context.TODO(), secret, metav1.UpdateOptions{}) + Expect(err).NotTo(HaveOccurred()) + Eventually(func() bool { + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + return apiutil.CertificateRequestHasCondition(request, cmapi.CertificateRequestCondition{ + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionTrue, + Reason: cmapi.CertificateRequestReasonIssued, + }) && len(request.Status.Certificate) > 0 + }, "20s", "1s").Should(BeTrue(), "request was not signed in time: %#+v", request) + }) + + It("ClusterIssuer: the private key Secret is created after the request is created should still be signed", func() { + var err error + issuer, err = f.CertManagerClientSet.CertmanagerV1().ClusterIssuers().Create(context.TODO(), &cmapi.ClusterIssuer{ + ObjectMeta: metav1.ObjectMeta{GenerateName: "selfsigned-"}, + Spec: cmapi.IssuerSpec{IssuerConfig: cmapi.IssuerConfig{SelfSigned: new(cmapi.SelfSignedIssuer)}}, + }, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Create(context.TODO(), &cmapi.CertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "selfsigned-", Namespace: f.Namespace.Name, + Annotations: map[string]string{"cert-manager.io/private-key-secret-name": "selfsigned-test"}, + }, + Spec: cmapi.CertificateRequestSpec{ + Request: bundle.CSRBytes, + IssuerRef: cmmeta.ObjectReference{Name: issuer.GetName(), Kind: "ClusterIssuer", Group: "cert-manager.io"}, + }, + }, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + By("waiting for request to be set to pending") + Eventually(func() bool { + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + return apiutil.CertificateRequestHasCondition(request, cmapi.CertificateRequestCondition{ + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionFalse, + Reason: cmapi.CertificateRequestReasonPending, + }) + }, "20s", "1s").Should(BeTrue(), "request was not set to pending in time: %#+v", request) + + By("creating Secret with private key should result in the request to be signed") + secret, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "selfsigned-test", Namespace: f.Namespace.Name}, + Data: map[string][]byte{ + "tls.key": bundle.PrivateKeyBytes, + }, + }, metav1.CreateOptions{}) + + Eventually(func() bool { + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + return apiutil.CertificateRequestHasCondition(request, cmapi.CertificateRequestCondition{ + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionTrue, + Reason: cmapi.CertificateRequestReasonIssued, + }) && len(request.Status.Certificate) > 0 + }, "20s", "1s").Should(BeTrue(), "request was not signed in time: %#+v", request) + }) + + It("ClusterIssuer: private key Secret is updated with a valid private key after the request is created should still be signed", func() { + var err error + By("creating Secret with missing private key") + secret, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Create(context.TODO(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "selfsigned-test", Namespace: f.Namespace.Name}, + Data: map[string][]byte{}, + }, metav1.CreateOptions{}) + + issuer, err = f.CertManagerClientSet.CertmanagerV1().ClusterIssuers().Create(context.TODO(), &cmapi.ClusterIssuer{ + ObjectMeta: metav1.ObjectMeta{GenerateName: "selfsigned-"}, + Spec: cmapi.IssuerSpec{IssuerConfig: cmapi.IssuerConfig{SelfSigned: new(cmapi.SelfSignedIssuer)}}, + }, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Create(context.TODO(), &cmapi.CertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + GenerateName: "selfsigned-", Namespace: f.Namespace.Name, + Annotations: map[string]string{"cert-manager.io/private-key-secret-name": "selfsigned-test"}, + }, + Spec: cmapi.CertificateRequestSpec{ + Request: bundle.CSRBytes, + IssuerRef: cmmeta.ObjectReference{Name: issuer.GetName(), Kind: "ClusterIssuer", Group: "cert-manager.io"}, + }, + }, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + + By("waiting for request to be set to pending") + Eventually(func() bool { + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + return apiutil.CertificateRequestHasCondition(request, cmapi.CertificateRequestCondition{ + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionFalse, + Reason: cmapi.CertificateRequestReasonPending, + }) + }, "20s", "1s").Should(BeTrue(), "request was not set to pending in time: %#+v", request) + + By("updating referenced private key Secret should get the request signed") + secret.Data = map[string][]byte{"tls.key": bundle.PrivateKeyBytes} + _, err = f.KubeClientSet.CoreV1().Secrets(f.Namespace.Name).Update(context.TODO(), secret, metav1.UpdateOptions{}) + Expect(err).NotTo(HaveOccurred()) + Eventually(func() bool { + request, err = f.CertManagerClientSet.CertmanagerV1().CertificateRequests(f.Namespace.Name).Get(context.TODO(), request.Name, metav1.GetOptions{}) + Expect(err).NotTo(HaveOccurred()) + return apiutil.CertificateRequestHasCondition(request, cmapi.CertificateRequestCondition{ + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionTrue, + Reason: cmapi.CertificateRequestReasonIssued, + }) && len(request.Status.Certificate) > 0 + }, "20s", "1s").Should(BeTrue(), "request was not signed in time: %#+v", request) + }) +}) diff --git a/test/e2e/suite/doc.go b/test/e2e/suite/doc.go index cb6ec22db..591090483 100644 --- a/test/e2e/suite/doc.go +++ b/test/e2e/suite/doc.go @@ -17,7 +17,7 @@ limitations under the License. package suite import ( - _ "github.com/cert-manager/cert-manager/test/e2e/suite/approval" + _ "github.com/cert-manager/cert-manager/test/e2e/suite/certificaterequests" _ "github.com/cert-manager/cert-manager/test/e2e/suite/certificates" _ "github.com/cert-manager/cert-manager/test/e2e/suite/conformance" _ "github.com/cert-manager/cert-manager/test/e2e/suite/issuers" diff --git a/test/unit/crypto/crypto.go b/test/unit/crypto/crypto.go index 952b15a3a..be86d46c9 100644 --- a/test/unit/crypto/crypto.go +++ b/test/unit/crypto/crypto.go @@ -74,14 +74,14 @@ type CryptoBundle struct { // MustCreateCryptoBundle creates a CryptoBundle to be used with tests or fails. func MustCreateCryptoBundle(t *testing.T, crt *cmapi.Certificate, clock clock.Clock) CryptoBundle { - c, err := createCryptoBundle(crt, clock) + c, err := CreateCryptoBundle(crt, clock) if err != nil { t.Fatalf("error generating crypto bundle: %v", err) } return *c } -func createCryptoBundle(originalCert *cmapi.Certificate, clock clock.Clock) (*CryptoBundle, error) { +func CreateCryptoBundle(originalCert *cmapi.Certificate, clock clock.Clock) (*CryptoBundle, error) { crt := originalCert.DeepCopy() if crt.Spec.PrivateKey == nil { crt.Spec.PrivateKey = &cmapi.CertificatePrivateKey{}