From 695e0fa5a0be2bfb81ef5e4679b411174c0e36d1 Mon Sep 17 00:00:00 2001 From: Yash Pimple Date: Wed, 24 Apr 2024 01:43:44 +0530 Subject: [PATCH 1/2] fix: failed checks Signed-off-by: Yash Pimple --- .../eventfilter/eventfilter_test.go | 101 ++++++++++++++++++ .../kubeutils/certificates_test.go | 77 +++++++++++++ keptn-cert-manager/kubeutils/secret_test.go | 41 +++++++ .../pkg/certificates/watcher_test.go | 77 ++++++------- 4 files changed, 258 insertions(+), 38 deletions(-) create mode 100644 keptn-cert-manager/kubeutils/certificates_test.go diff --git a/keptn-cert-manager/eventfilter/eventfilter_test.go b/keptn-cert-manager/eventfilter/eventfilter_test.go index ce2db7e23c..cc01baec0d 100644 --- a/keptn-cert-manager/eventfilter/eventfilter_test.go +++ b/keptn-cert-manager/eventfilter/eventfilter_test.go @@ -6,6 +6,8 @@ import ( "github.com/stretchr/testify/assert" v1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "sigs.k8s.io/controller-runtime/pkg/event" ) const ( @@ -76,3 +78,102 @@ func Test_matchesName(t *testing.T) { assert.False(t, matchesName(deployment, []string{"my-deployment"})) } + +func Test_matchesLabels(t *testing.T) { + deployment := &v1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "test", + "env": "dev", + }, + }, + } + + firstSelector := labels.SelectorFromSet(labels.Set{ + "app": "test", + "env": "dev", // different value for 'env' + }) + + assert.True(t, matchesLabels(deployment, firstSelector)) + + secondSelectors := labels.SelectorFromSet(labels.Set{ + "app": "test", + "env": "prod", + }) + + assert.False(t, matchesLabels(deployment, secondSelectors)) + + deploymentNoLabels := &v1.Deployment{} + + assert.False(t, matchesLabels(deploymentNoLabels, firstSelector)) +} + +func TestForLabelsAndNamespace(t *testing.T) { + deployment := &v1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: testName1, + Namespace: testNamespace1, + Labels: map[string]string{ + "app": "test", + "env": "dev", + }, + }, + } + + selector := labels.SelectorFromSet(labels.Set{ + "app": "test", + "env": "dev", + }) + + // when the deployments matched with labels and is present in the required namespace. + assert.True(t, ForLabelsAndNamespace(selector, testNamespace1).Generic(event.GenericEvent{Object: deployment})) + + // when the namespace doesn't match. + assert.False(t, ForLabelsAndNamespace(selector, "another-namespace").Generic(event.GenericEvent{Object: deployment})) + + // when the labels don't match. + deploymentNoLabels := &v1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: testName1, + Namespace: testNamespace1, + }, + } + assert.False(t, ForLabelsAndNamespace(selector, testNamespace1).Generic(event.GenericEvent{Object: deploymentNoLabels})) +} + +func TestForNamesAndNamespace(t *testing.T) { + deployment1 := &v1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-1", + Namespace: "test-namespace-1", + }, + } + + deployment2 := &v1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: "deployment-2", + Namespace: "test-namespace-2", + }, + } + + names := []string{"deployment-1", "deployment-2"} + namespace := "test-namespace-1" + + // Test deployments in the same namespace and with matching names + pred := ForNamesAndNamespace(names, namespace) + assert.True(t, pred.Generic(event.GenericEvent{Object: deployment1})) + assert.False(t, pred.Generic(event.GenericEvent{Object: deployment2})) + + // Test deployments in different namespace + namespace = "test-namespace-2" + pred = ForNamesAndNamespace(names, namespace) + assert.False(t, pred.Generic(event.GenericEvent{Object: deployment1})) + assert.True(t, pred.Generic(event.GenericEvent{Object: deployment2})) + + // Test deployments with mismatched names + names = []string{"deployment-3", "deployment-4"} + namespace = "test-namespace-1" + pred = ForNamesAndNamespace(names, namespace) + assert.False(t, pred.Generic(event.GenericEvent{Object: deployment1})) + assert.False(t, pred.Generic(event.GenericEvent{Object: deployment2})) +} diff --git a/keptn-cert-manager/kubeutils/certificates_test.go b/keptn-cert-manager/kubeutils/certificates_test.go new file mode 100644 index 0000000000..feef50388a --- /dev/null +++ b/keptn-cert-manager/kubeutils/certificates_test.go @@ -0,0 +1,77 @@ +package kubeutils + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "math/big" + "testing" + "time" +) + +func TestValidateCertificateExpiration(t *testing.T) { + certTemplate := &x509.Certificate{ + SerialNumber: big.NewInt(1), + NotBefore: time.Now(), + NotAfter: time.Now().Add(24 * time.Hour), + } + + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("Failed to generate private key: %v", err) + } + + certBytes, err := x509.CreateCertificate(rand.Reader, certTemplate, certTemplate, &privateKey.PublicKey, privateKey) + if err != nil { + t.Fatalf("Failed to create certificate: %v", err) + } + + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certBytes}) + + testCases := []struct { + name string + certData []byte + renewalThreshold time.Duration + now time.Time + expectedValid bool + expectedErrorNil bool + }{ + { + name: "Valid certificate", + certData: certPEM, + renewalThreshold: 2 * time.Hour, + now: time.Now().Add(21 * time.Hour), // Certificate is still valids + expectedValid: true, + expectedErrorNil: true, + }, + { + name: "Expired certificate", + certData: certPEM, + renewalThreshold: 2 * time.Hour, + now: time.Now().Add(25 * time.Hour), // Certificate has expired + expectedValid: false, + expectedErrorNil: true, + }, + { + name: "Invalid PEM data", + certData: []byte("invalid PEM data"), + renewalThreshold: 2 * time.Hour, + now: time.Now(), + expectedValid: false, + expectedErrorNil: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + valid, err := ValidateCertificateExpiration(tc.certData, tc.renewalThreshold, tc.now) + if valid != tc.expectedValid { + t.Errorf("Expected valid=%v, got %v", tc.expectedValid, valid) + } + if (err == nil) != tc.expectedErrorNil { + t.Errorf("Expected error nil=%v, got error=%v", tc.expectedErrorNil, err) + } + }) + } +} diff --git a/keptn-cert-manager/kubeutils/secret_test.go b/keptn-cert-manager/kubeutils/secret_test.go index bcc4d688c7..7f2b5d1658 100644 --- a/keptn-cert-manager/kubeutils/secret_test.go +++ b/keptn-cert-manager/kubeutils/secret_test.go @@ -23,6 +23,7 @@ func TestSecretQuery(t *testing.T) { t.Run(`Update secret when data has changed`, testUpdateSecretWhenDataChanged) t.Run(`Update secret when labels have changed`, testUpdateSecretWhenLabelsChanged) t.Run(`Create secret in target namespace`, testCreateSecretInTargetNamespace) + t.Run(`New Secret`, Secretwithmutiplekeys) } func testGetSecret(t *testing.T) { @@ -259,3 +260,43 @@ func createTestSecret(labels map[string]string, data map[string][]byte) *corev1. } return secret } + +func Secretwithmutiplekeys(t *testing.T) { + + testCases := []struct { + name string + namespace string + data map[string][]byte + }{ + { + name: "test-secret-1", + namespace: "test-namespace-1", + data: map[string][]byte{ + "key1": []byte("value1"), + "key2": []byte("value2"), + }, + }, + { + name: "test-secret-2", + namespace: "test-namespace-2", + data: map[string][]byte{ + "key1": []byte("value1"), + }, + }, + { + name: "test-secret-3", + namespace: "test-namespace-3", + data: map[string][]byte{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + secret := NewSecret(tc.name, tc.namespace, tc.data) + + assert.Equal(t, tc.name, secret.Name) + assert.Equal(t, tc.namespace, secret.Namespace) + assert.Equal(t, tc.data, secret.Data) + }) + } +} diff --git a/keptn-cert-manager/pkg/certificates/watcher_test.go b/keptn-cert-manager/pkg/certificates/watcher_test.go index 378c9b077c..b3f0315fa8 100644 --- a/keptn-cert-manager/pkg/certificates/watcher_test.go +++ b/keptn-cert-manager/pkg/certificates/watcher_test.go @@ -21,30 +21,6 @@ import ( ) const CACERT = `-----BEGIN CERTIFICATE----- -MIIBrzCCAVmgAwIBAgIUH/zWlPkTXVBcu2zOvUy/NV1hCKkwDQYJKoZIhvcNAQEL -BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM -GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA0MTgwOTEzMDdaFw0zNDA0 -MTYwOTEzMDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw -HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwXDANBgkqhkiG9w0BAQEF -AANLADBIAkEAyLJjXFVA0DzUVSJy+ANqe+tXki2MsWgm+cbYkpBMLJMKhhwnv6vW -Hxwsh5MZNwAmSoprINGb7i6Ub2OhjpVq0QIDAQABoyEwHzAdBgNVHQ4EFgQUtwGr -j5axZSNJo6o1mP7L09axxIIwDQYJKoZIhvcNAQELBQADQQDIJGtVIgsg0J3e5QRf -LZ21sKKY+xzeG5yy90ao8QMWX9CqCpZncprE1MJijkG7paCFq6Bh22g6xTZYYJ1m -yG/y ------END CERTIFICATE-----` - -const CAKEY = `-----BEGIN PRIVATE KEY----- -MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAyLJjXFVA0DzUVSJy -+ANqe+tXki2MsWgm+cbYkpBMLJMKhhwnv6vWHxwsh5MZNwAmSoprINGb7i6Ub2Oh -jpVq0QIDAQABAkANdxJ9hmbD0eD5GUeXZjtFtyN39kBjQraiuXmcU7wYnWJ9OyaB -jsKkWlv9vx1stbMSYzlSQepDRYVcKL6AgGexAiEA7EwLkpiWT41/IwIIoYVQNgMN -Q/n8ltO47ecFljF1G6UCIQDZbm1JXYF068xo0vglnKl9HK3I69cHA4hrVww0ZUha -vQIgIDy7s3NHxnCqcK89WDPk3omKDMUVNcqKx0ImW/hBXtUCIQCvrMgCCdmp9UaP -vz0dbomGe6ByARMYKKOVTpyezOJ75QIgNqihb0lQbzEceTo6S2bQakDH7dH4Eydd -hMfh5Ml1u3o= ------END PRIVATE KEY-----` - -const OUTDATED_CACERT = `-----BEGIN CERTIFICATE----- MIICPTCCAeKgAwIBAgIRAMIV/0UqFGHgKSYOWBdx/KcwCgYIKoZIzj0EAwIwczEL MAkGA1UEBhMCQVQxCzAJBgNVBAgTAktMMRMwEQYDVQQHEwpLbGFnZW5mdXJ0MQ4w DAYDVQQKEwVLZXB0bjEZMBcGA1UECxMQTGlmZWN5Y2xlVG9vbGtpdDEXMBUGA1UE @@ -60,7 +36,7 @@ ow49D22Gsrh7YM+vmTQCIQDU1L5IT0Zz+bdIyFSsDnEUXZDeydNv56DoSLh+358Y aw== -----END CERTIFICATE-----` -const OUTDATED_CAKEY = `-----BEGIN PRIVATE KEY----- +const CAKEY = `-----BEGIN PRIVATE KEY----- MHcCAQEEII5SAqBxINKatksyu2mTvLZZhfEOpNinYJDwlQjkfreboAoGCCqGSM49 AwEHoUQDQgAE/EA/glMl/ArP8/fZ1e7J9WLuSKdA95tJjAX+BEBRw3R0ICLoafFs jY5eVxTSC4PMde/dVGHcRfZ+I2zNx8poJg== @@ -110,18 +86,6 @@ var emptySecret = v1.Secret{ }, } -var outdatedSecret = v1.Secret{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "my-cert", - }, - Data: map[string][]byte{ - ServerCert: []byte(OUTDATED_CACERT), - ServerKey: []byte(OUTDATED_CAKEY), - }, -} - var goodSecret = v1.Secret{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ @@ -290,7 +254,7 @@ func TestCertificateWatcher_updateCertificatesFromSecret(t *testing.T) { }, { name: "outdated certificate found, nothing in dir", - apiReader: newFakeClient(&outdatedSecret), + apiReader: newFakeClient(&emptySecret), certificateDirectory: t.TempDir(), namespace: "default", certificateSecretName: "my-cert", @@ -360,3 +324,40 @@ func TestNewCertificateWatcher(t *testing.T) { func TestNewNoOpCertificateWatcher(t *testing.T) { require.EqualValues(t, NewNoOpCertificateWatcher(), &NoOpCertificateWatcher{}) } + +func TestCertificateWatcher_watchForCertificatesSecret(t *testing.T) { + mockReader := newFakeClient() + logger := testr.New(t) + + watcher := &CertificateWatcher{ + apiReader: mockReader, + fs: afero.NewOsFs(), + certificateDirectory: "", + namespace: "", + certificateSecretName: "", + ICertificateHandler: defaultCertificateHandler{}, + Log: logger, + } + + updateSignal := make(chan struct{}) + defer close(updateSignal) + + updateCalled := false + go func() { + time.Sleep(10 * time.Millisecond) + updateSignal <- struct{}{} + }() + + go watcher.watchForCertificatesSecret() + + select { + case <-time.After(20 * time.Millisecond): + t.Error("Expected update but did not receive it within the specified interval") + case <-updateSignal: + updateCalled = true + } + + if !updateCalled { + t.Error("updateCertificatesFromSecret method was not called as expected") + } +} From 071543786e67fc790dbeebfbe827115a447a18ac Mon Sep 17 00:00:00 2001 From: Yash Pimple Date: Wed, 24 Apr 2024 01:54:14 +0530 Subject: [PATCH 2/2] fix: failed unit test Signed-off-by: Yash Pimple --- .../pkg/certificates/watcher_test.go | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/keptn-cert-manager/pkg/certificates/watcher_test.go b/keptn-cert-manager/pkg/certificates/watcher_test.go index b3f0315fa8..e18f2929f8 100644 --- a/keptn-cert-manager/pkg/certificates/watcher_test.go +++ b/keptn-cert-manager/pkg/certificates/watcher_test.go @@ -21,6 +21,30 @@ import ( ) const CACERT = `-----BEGIN CERTIFICATE----- +MIIBrzCCAVmgAwIBAgIUH/zWlPkTXVBcu2zOvUy/NV1hCKkwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yNDA0MTgwOTEzMDdaFw0zNDA0 +MTYwOTEzMDdaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwXDANBgkqhkiG9w0BAQEF +AANLADBIAkEAyLJjXFVA0DzUVSJy+ANqe+tXki2MsWgm+cbYkpBMLJMKhhwnv6vW +Hxwsh5MZNwAmSoprINGb7i6Ub2OhjpVq0QIDAQABoyEwHzAdBgNVHQ4EFgQUtwGr +j5axZSNJo6o1mP7L09axxIIwDQYJKoZIhvcNAQELBQADQQDIJGtVIgsg0J3e5QRf +LZ21sKKY+xzeG5yy90ao8QMWX9CqCpZncprE1MJijkG7paCFq6Bh22g6xTZYYJ1m +yG/y +-----END CERTIFICATE-----` + +const CAKEY = `-----BEGIN PRIVATE KEY----- +MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAyLJjXFVA0DzUVSJy ++ANqe+tXki2MsWgm+cbYkpBMLJMKhhwnv6vWHxwsh5MZNwAmSoprINGb7i6Ub2Oh +jpVq0QIDAQABAkANdxJ9hmbD0eD5GUeXZjtFtyN39kBjQraiuXmcU7wYnWJ9OyaB +jsKkWlv9vx1stbMSYzlSQepDRYVcKL6AgGexAiEA7EwLkpiWT41/IwIIoYVQNgMN +Q/n8ltO47ecFljF1G6UCIQDZbm1JXYF068xo0vglnKl9HK3I69cHA4hrVww0ZUha +vQIgIDy7s3NHxnCqcK89WDPk3omKDMUVNcqKx0ImW/hBXtUCIQCvrMgCCdmp9UaP +vz0dbomGe6ByARMYKKOVTpyezOJ75QIgNqihb0lQbzEceTo6S2bQakDH7dH4Eydd +hMfh5Ml1u3o= +-----END PRIVATE KEY-----` + +const OUTDATED_CACERT = `-----BEGIN CERTIFICATE----- MIICPTCCAeKgAwIBAgIRAMIV/0UqFGHgKSYOWBdx/KcwCgYIKoZIzj0EAwIwczEL MAkGA1UEBhMCQVQxCzAJBgNVBAgTAktMMRMwEQYDVQQHEwpLbGFnZW5mdXJ0MQ4w DAYDVQQKEwVLZXB0bjEZMBcGA1UECxMQTGlmZWN5Y2xlVG9vbGtpdDEXMBUGA1UE @@ -36,7 +60,7 @@ ow49D22Gsrh7YM+vmTQCIQDU1L5IT0Zz+bdIyFSsDnEUXZDeydNv56DoSLh+358Y aw== -----END CERTIFICATE-----` -const CAKEY = `-----BEGIN PRIVATE KEY----- +const OUTDATED_CAKEY = `-----BEGIN PRIVATE KEY----- MHcCAQEEII5SAqBxINKatksyu2mTvLZZhfEOpNinYJDwlQjkfreboAoGCCqGSM49 AwEHoUQDQgAE/EA/glMl/ArP8/fZ1e7J9WLuSKdA95tJjAX+BEBRw3R0ICLoafFs jY5eVxTSC4PMde/dVGHcRfZ+I2zNx8poJg== @@ -86,6 +110,18 @@ var emptySecret = v1.Secret{ }, } +var outdatedSecret = v1.Secret{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{ + Namespace: "default", + Name: "my-cert", + }, + Data: map[string][]byte{ + ServerCert: []byte(OUTDATED_CACERT), + ServerKey: []byte(OUTDATED_CAKEY), + }, +} + var goodSecret = v1.Secret{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{ @@ -254,7 +290,7 @@ func TestCertificateWatcher_updateCertificatesFromSecret(t *testing.T) { }, { name: "outdated certificate found, nothing in dir", - apiReader: newFakeClient(&emptySecret), + apiReader: newFakeClient(&outdatedSecret), certificateDirectory: t.TempDir(), namespace: "default", certificateSecretName: "my-cert",