diff --git a/pkg/certificate/encode.go b/pkg/certificate/encode.go index 4dea318b4e..cb93d6f0c2 100644 --- a/pkg/certificate/encode.go +++ b/pkg/certificate/encode.go @@ -48,7 +48,7 @@ func DecodePEMCertificate(certPEM []byte) (*x509.Certificate, error) { var block *pemEnc.Block block, certPEM = pemEnc.Decode(certPEM) if block == nil { - return nil, errNoCertificateInPEM + return nil, ErrNoCertificateInPEM } if block.Type != TypeCertificate || len(block.Headers) != 0 { continue @@ -62,7 +62,7 @@ func DecodePEMCertificate(certPEM []byte) (*x509.Certificate, error) { return cert, nil } - return nil, errNoCertificateInPEM + return nil, ErrNoCertificateInPEM } // DecodePEMPrivateKey converts a certificate from PEM to x509 encoding @@ -84,7 +84,7 @@ func DecodePEMPrivateKey(keyPEM []byte) (*rsa.PrivateKey, error) { return caKeyInterface.(*rsa.PrivateKey), nil } - return nil, errNoCertificateInPEM + return nil, ErrNoCertificateInPEM } // EncodeCertReqDERtoPEM encodes the certificate request provided in DER format diff --git a/pkg/certificate/errors.go b/pkg/certificate/errors.go index d0c88b27dd..e5cd62817a 100644 --- a/pkg/certificate/errors.go +++ b/pkg/certificate/errors.go @@ -7,5 +7,7 @@ import ( var errEncodeKey = errors.New("encode key") var errEncodeCert = errors.New("encode cert") var errMarshalPrivateKey = errors.New("marshal private key") -var errNoCertificateInPEM = errors.New("no certificate in PEM") var errNoPrivateKeyInPEM = errors.New("no private Key in PEM") + +// ErrNoCertificateInPEM is the errror for no certificate in PEM +var ErrNoCertificateInPEM = errors.New("no certificate in PEM") diff --git a/pkg/certificate/providers/certmanager/certificate_manager.go b/pkg/certificate/providers/certmanager/certificate_manager.go index 23f1e7a1b6..57c41f7b1b 100644 --- a/pkg/certificate/providers/certmanager/certificate_manager.go +++ b/pkg/certificate/providers/certmanager/certificate_manager.go @@ -52,7 +52,7 @@ func (cm *CertManager) GetCertificate(cn certificate.CommonName) (certificate.Ce if cert := cm.getFromCache(cn); cert != nil { return cert, nil } - return nil, fmt.Errorf("failed to find certificate with CN=%s", cn) + return nil, errCertNotFound } func (cm *CertManager) deleteFromCache(cn certificate.CommonName) { diff --git a/pkg/certificate/providers/certmanager/certificate_manager_test.go b/pkg/certificate/providers/certmanager/certificate_manager_test.go index b899aaba2c..bb9d9b1be5 100644 --- a/pkg/certificate/providers/certmanager/certificate_manager_test.go +++ b/pkg/certificate/providers/certmanager/certificate_manager_test.go @@ -3,8 +3,11 @@ package certmanager import ( "crypto/rand" "crypto/x509" + "testing" "time" + tassert "github.com/stretchr/testify/assert" + . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -15,29 +18,31 @@ import ( cmfakeapi "github.com/jetstack/cert-manager/pkg/client/clientset/versioned/typed/certmanager/v1/fake" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/testing" + test "k8s.io/client-go/testing" "github.com/openservicemesh/osm/pkg/certificate" "github.com/openservicemesh/osm/pkg/configurator" "github.com/openservicemesh/osm/pkg/tests" ) +var ( + mockCtrl = gomock.NewController(GinkgoT()) + mockConfigurator = configurator.NewMockConfigurator(mockCtrl) + cn = certificate.CommonName("bookbuyer.azure.mesh") + crNotReady = &cmapi.CertificateRequest{ + ObjectMeta: metav1.ObjectMeta{ + Name: "osm-123", + Namespace: "osm-system", + }, + } +) + var _ = Describe("Test cert-manager Certificate Manager", func() { defer GinkgoRecover() - var ( - mockCtrl *gomock.Controller - mockConfigurator *configurator.MockConfigurator - ) - - mockCtrl = gomock.NewController(GinkgoT()) - mockConfigurator = configurator.NewMockConfigurator(mockCtrl) - Context("Test Getting a certificate from the cache", func() { validity := 1 * time.Hour - cn := certificate.CommonName("bookbuyer.azure.mesh") - rootCertPEM, err := tests.GetPEMCert() if err != nil { GinkgoT().Fatalf("Error loading sample test certificate: %s", err.Error()) @@ -75,12 +80,6 @@ var _ = Describe("Test cert-manager Certificate Manager", func() { GinkgoT().Fatalf("Error loading ca %s: %s", rootCertPEM, err.Error()) } - crNotReady := &cmapi.CertificateRequest{ - ObjectMeta: metav1.ObjectMeta{ - Name: "osm-123", - Namespace: "osm-system", - }, - } crReady := crNotReady.DeepCopy() crReady.Status = cmapi.CertificateRequestStatus{ Certificate: signedCertPEM, @@ -94,7 +93,7 @@ var _ = Describe("Test cert-manager Certificate Manager", func() { } fakeClient := cmfakeclient.NewSimpleClientset() - fakeClient.CertmanagerV1().(*cmfakeapi.FakeCertmanagerV1).Fake.PrependReactor("*", "*", func(action testing.Action) (bool, runtime.Object, error) { + fakeClient.CertmanagerV1().(*cmfakeapi.FakeCertmanagerV1).Fake.PrependReactor("*", "*", func(action test.Action) (bool, runtime.Object, error) { switch action.GetVerb() { case "create": return true, crNotReady, nil @@ -120,5 +119,135 @@ var _ = Describe("Test cert-manager Certificate Manager", func() { Expect(getCertificateError).ToNot(HaveOccurred()) Expect(cachedCert).To(Equal(cert)) }) + + It("should rotate the certificate", func() { + mockConfigurator.EXPECT().GetServiceCertValidityPeriod().Return(validity).AnyTimes() + + cert, err := cm.RotateCertificate(cn) + Expect(err).Should(BeNil()) + cachedCert, err := cm.GetCertificate(cn) + Expect(cachedCert).To(Equal(cert)) + Expect(err).Should(BeNil()) + }) }) }) + +func TestReleaseCertificate(t *testing.T) { + cert := &Certificate{ + commonName: cn, + expiration: time.Now().Add(1 * time.Hour), + } + manager := &CertManager{cache: map[certificate.CommonName]certificate.Certificater{cn: cert}} + + testCases := []struct { + name string + commonName certificate.CommonName + }{ + { + name: "release existing certificate", + commonName: cn, + }, + { + name: "release non-existing certificate", + commonName: cn, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + assert := tassert.New(t) + + manager.ReleaseCertificate(tc.commonName) + _, err := manager.GetCertificate(tc.commonName) + assert.ErrorIs(err, errCertNotFound) + }) + } +} + +func TestGetRootCertificate(t *testing.T) { + assert := tassert.New(t) + + manager := &CertManager{ + ca: &Certificate{ + commonName: cn, + expiration: time.Now().Add(1 * time.Hour), + }, + } + cert, err := manager.GetRootCertificate() + + assert.Nil(err) + assert.Equal(manager.ca, cert) +} + +func TestCertificaterFromCertificateRequest(t *testing.T) { + assert := tassert.New(t) + fakeClient := cmfakeclient.NewSimpleClientset() + + rootCertPEM, err := tests.GetPEMCert() + assert.Nil(err) + + rootCert, err := certificate.DecodePEMCertificate(rootCertPEM) + assert.Nil(err) + + rootKeyPEM, err := tests.GetPEMPrivateKey() + assert.Nil(err) + + rootKey, err := certificate.DecodePEMPrivateKey(rootKeyPEM) + assert.Nil(err) + + rootCertificator, err := NewRootCertificateFromPEM(rootCertPEM) + assert.Nil(err) + + cm, err := NewCertManager(rootCertificator, fakeClient, "osm-system", cmmeta.ObjectReference{Name: "osm-ca"}, mockConfigurator) + assert.Nil(err) + + signedCertDER, err := x509.CreateCertificate(rand.Reader, rootCert, rootCert, rootKey.Public(), rootKey) + assert.Nil(err) + + signedCertPEM, err := certificate.EncodeCertDERtoPEM(signedCertDER) + assert.Nil(err) + + crReady := crNotReady.DeepCopy() + crReady.Status = cmapi.CertificateRequestStatus{ + Certificate: signedCertPEM, + CA: signedCertPEM, + Conditions: []cmapi.CertificateRequestCondition{ + { + Type: cmapi.CertificateRequestConditionReady, + Status: cmmeta.ConditionTrue, + }, + }, + } + emptyArr := []byte{} + testCases := []struct { + name string + cr cmapi.CertificateRequest + expectedCertIsNil bool + expectedError error + }{ + { + name: "Could not decode PEM Cert", + cr: *crNotReady, + expectedCertIsNil: true, + expectedError: certificate.ErrNoCertificateInPEM, + }, + { + name: "default", + cr: *crReady, + expectedCertIsNil: false, + expectedError: nil, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + cert, err := cm.certificaterFromCertificateRequest(&tc.cr, emptyArr) + + assert.Equal(tc.expectedCertIsNil, cert == nil) + assert.Equal(tc.expectedError, err) + }) + } + // Tests if cmapi.CertificateRequest is nil + cert, err := cm.certificaterFromCertificateRequest(nil, emptyArr) + assert.Nil(cert) + assert.Nil(err) +} diff --git a/pkg/certificate/providers/certmanager/errors.go b/pkg/certificate/providers/certmanager/errors.go new file mode 100644 index 0000000000..39218bdb67 --- /dev/null +++ b/pkg/certificate/providers/certmanager/errors.go @@ -0,0 +1,7 @@ +package certmanager + +import ( + "errors" +) + +var errCertNotFound = errors.New("failed to find cert")