From f85be7d76bf73d59dd4d70ffd07f1e34dfd1a2ef Mon Sep 17 00:00:00 2001 From: Remigiusz Janeczek Date: Fri, 16 Oct 2020 11:08:09 +0200 Subject: [OOM-K8S-CERT-EXTERNAL-PROVIDER] Provide certs to CMPv2 Issuer Format code Issue-ID: OOM-2559 Signed-off-by: Remigiusz Janeczek Change-Id: I88346b96657606b010aa8d7da0f8b86d1844f9d7 --- .../src/cmpv2api/cmpv2_groupversion_info.go | 1 - .../src/cmpv2api/cmpv2_groupversion_info_test.go | 2 +- .../src/cmpv2api/cmpv2_issuer_crd_deepcopy.go | 2 +- .../src/cmpv2api/cmpv2_issuer_crd_schema.go | 15 +-- .../certificate_request_controller.go | 10 +- .../certificate_request_controller_test.go | 6 +- .../src/cmpv2controller/cmpv2_issuer_controller.go | 54 ++++------ .../cmpv2_issuer_controller_test.go | 67 ++++++++---- .../cmpv2controller/cmpv2_issuer_status_updater.go | 2 + .../src/cmpv2controller/status_reason.go | 6 +- .../src/cmpv2provisioner/cmpv2_provisioner.go | 23 ++-- .../cmpv2provisioner/cmpv2_provisioner_factory.go | 55 ++++++++++ .../cmpv2_provisioner_factory_test.go | 120 +++++++++++++++++++++ certServiceK8sExternalProvider/src/exit_code.go | 6 +- .../src/exit_code_test.go | 1 + 15 files changed, 288 insertions(+), 82 deletions(-) create mode 100644 certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory.go create mode 100644 certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_test.go (limited to 'certServiceK8sExternalProvider/src') diff --git a/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_groupversion_info.go b/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_groupversion_info.go index 996cf21a..ec4d6835 100644 --- a/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_groupversion_info.go +++ b/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_groupversion_info.go @@ -42,4 +42,3 @@ var ( ) const CMPv2IssuerKind = "CMPv2Issuer" - diff --git a/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_groupversion_info_test.go b/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_groupversion_info_test.go index b95bded5..eae6a2c8 100644 --- a/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_groupversion_info_test.go +++ b/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_groupversion_info_test.go @@ -22,6 +22,7 @@ package cmpv2api import ( "testing" + "github.com/stretchr/testify/assert" ) @@ -33,4 +34,3 @@ func Test_shouldHaveRightGroupVersion(t *testing.T) { func Test_shouldRightIssuerKind(t *testing.T) { assert.Equal(t, "CMPv2Issuer", CMPv2IssuerKind) } - diff --git a/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_issuer_crd_deepcopy.go b/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_issuer_crd_deepcopy.go index 68e79ce1..83785ab9 100644 --- a/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_issuer_crd_deepcopy.go +++ b/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_issuer_crd_deepcopy.go @@ -125,7 +125,7 @@ func (inputIssuerList *CMPv2IssuerList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (inputIssuerSpec *CMPv2IssuerSpec) DeepCopyInto(outIssuerSpec *CMPv2IssuerSpec) { *outIssuerSpec = *inputIssuerSpec - outIssuerSpec.KeyRef = inputIssuerSpec.KeyRef + outIssuerSpec.CertSecretRef = inputIssuerSpec.CertSecretRef } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CMPv2IssuerSpec. diff --git a/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_issuer_crd_schema.go b/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_issuer_crd_schema.go index f2482657..f26dc876 100644 --- a/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_issuer_crd_schema.go +++ b/certServiceK8sExternalProvider/src/cmpv2api/cmpv2_issuer_crd_schema.go @@ -37,10 +37,10 @@ func init() { type CMPv2IssuerSpec struct { // URL is the base URL for the CertService certificates instance. URL string `json:"url"` - + // CaName is the name of the external CA server + CaName string `json:"caName"` // KeyRef is a reference to a Secret containing the provisioner - // password used to decrypt the provisioner private key. - KeyRef SecretKeySelector `json:"keyRef"` + CertSecretRef SecretKeySelector `json:"certSecretRef"` } // CMPv2IssuerStatus defines the observed state of CMPv2Issuer @@ -72,9 +72,12 @@ type SecretKeySelector struct { // The name of the secret in the pod's namespace to select from. Name string `json:"name"` - // The key of the secret to select from. Must be a valid secret key. - // +optional - Key string `json:"key,omitempty"` + // The key of the secret to select private key from. Must be a valid secret key. + KeyRef string `json:"keyRef,omitempty"` + // The key of the secret to select cert from. Must be a valid secret key. + CertRef string `json:"certRef,omitempty"` + // The key of the secret to select cacert from. Must be a valid secret key. + CacertRef string `json:"cacertRef,omitempty"` } // ConditionType represents a CMPv2Issuer condition type. diff --git a/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller.go b/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller.go index 38b5cdf3..54b4b103 100644 --- a/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller.go +++ b/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller.go @@ -28,8 +28,6 @@ package cmpv2controller import ( "context" "fmt" - "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" - provisioners "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner" "github.com/go-logr/logr" apiutil "github.com/jetstack/cert-manager/pkg/api/util" @@ -41,6 +39,9 @@ import ( "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" + provisioners "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner" ) // CertificateRequestController reconciles a CMPv2Issuer object. @@ -144,7 +145,6 @@ func (controller *CertificateRequestController) setStatus(ctx context.Context, c return controller.Client.Status().Update(ctx, certificateRequest) } - func isCMPv2IssuerReady(issuer cmpv2api.CMPv2Issuer) bool { condition := cmpv2api.CMPv2IssuerCondition{Type: cmpv2api.ConditionReady, Status: cmpv2api.ConditionTrue} return hasCondition(issuer, condition) @@ -183,12 +183,12 @@ func (controller *CertificateRequestController) handleErrorCMPv2IssuerIsNotReady return err } -func (controller *CertificateRequestController) handleErrorGettingCMPv2Issuer(ctx context.Context, log logr.Logger, err error, certificateRequest *cmapi.CertificateRequest, issuerNamespaceName types.NamespacedName, req ctrl.Request) { +func (controller *CertificateRequestController) handleErrorGettingCMPv2Issuer(ctx context.Context, log logr.Logger, err error, certificateRequest *cmapi.CertificateRequest, issuerNamespaceName types.NamespacedName, req ctrl.Request) { log.Error(err, "Failed to retrieve CMPv2Issuer resource", "namespace", req.Namespace, "name", certificateRequest.Spec.IssuerRef.Name) _ = controller.setStatus(ctx, certificateRequest, cmmeta.ConditionFalse, cmapi.CertificateRequestReasonPending, "Failed to retrieve CMPv2Issuer resource %s: %v", issuerNamespaceName, err) } -func (controller *CertificateRequestController) handleErrorFailedToSignCertificate(ctx context.Context, log logr.Logger, err error, certificateRequest *cmapi.CertificateRequest) { +func (controller *CertificateRequestController) handleErrorFailedToSignCertificate(ctx context.Context, log logr.Logger, err error, certificateRequest *cmapi.CertificateRequest) { log.Error(err, "Failed to sign certificate request") _ = controller.setStatus(ctx, certificateRequest, cmmeta.ConditionFalse, cmapi.CertificateRequestReasonFailed, "Failed to sign certificate request: %v", err) } diff --git a/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller_test.go b/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller_test.go index 7e55f36f..2c401cce 100644 --- a/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller_test.go +++ b/certServiceK8sExternalProvider/src/cmpv2controller/certificate_request_controller_test.go @@ -21,10 +21,10 @@ package cmpv2controller import ( - cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" "testing" - "github.com/stretchr/testify/assert" + cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" + "github.com/stretchr/testify/assert" ) const group = "certmanager.onap.org" @@ -43,7 +43,6 @@ func Test_shouldBeInvalidCMPv2CertificateRequest_whenKindIsCertificateRequest(t assert.False(t, isCMPv2CertificateRequest(request)) } - func Test_shouldBeValidCMPv2CertificateRequest_whenKindIsCMPvIssuer(t *testing.T) { request := new(cmapi.CertificateRequest) request.Spec.IssuerRef.Group = group @@ -51,4 +50,3 @@ func Test_shouldBeValidCMPv2CertificateRequest_whenKindIsCMPvIssuer(t *testing.T assert.True(t, isCMPv2CertificateRequest(request)) } - diff --git a/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller.go b/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller.go index f57f5677..1b4e5312 100644 --- a/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller.go +++ b/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller.go @@ -28,6 +28,7 @@ package cmpv2controller import ( "context" "fmt" + "github.com/go-logr/logr" core "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -35,10 +36,11 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" "k8s.io/utils/clock" - "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" - provisioners "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" + provisioners "onap.org/oom-certservice/k8s-external-provider/src/cmpv2provisioner" ) // CMPv2IssuerController reconciles a CMPv2Issuer object @@ -74,21 +76,18 @@ func (controller *CMPv2IssuerController) Reconcile(req ctrl.Request) (ctrl.Resul var secret core.Secret secretNamespaceName := types.NamespacedName{ Namespace: req.Namespace, - Name: issuer.Spec.KeyRef.Name, + Name: issuer.Spec.CertSecretRef.Name, } if err := controller.loadResource(ctx, secretNamespaceName, &secret); err != nil { handleErrorInvalidSecret(ctx, log, err, statusUpdater, secretNamespaceName) return ctrl.Result{}, err } - password, ok := secret.Data[issuer.Spec.KeyRef.Key] - if !ok { - err := handleErrorSecretNotFound(ctx, log, issuer, statusUpdater, secretNamespaceName, secret) - return ctrl.Result{}, err - } // 4. Create CMPv2 provisioner and store the instance for further use - provisioner, err := provisioners.New(issuer, password) + provisioner, err := provisioners.CreateProvisioner(issuer, secret) if err != nil { + log.Error(err, "failed to initialize provisioner") + statusUpdater.UpdateNoError(ctx, cmpv2api.ConditionFalse, "Error", "Failed to initialize provisioner: %v", err) handleErrorProvisionerInitialization(ctx, log, err, statusUpdater) return ctrl.Result{}, err } @@ -103,7 +102,6 @@ func (controller *CMPv2IssuerController) Reconcile(req ctrl.Request) (ctrl.Resul return ctrl.Result{}, nil } - func (controller *CMPv2IssuerController) SetupWithManager(manager ctrl.Manager) error { return ctrl.NewControllerManagedBy(manager). For(&cmpv2api.CMPv2Issuer{}). @@ -114,18 +112,22 @@ func (controller *CMPv2IssuerController) loadResource(ctx context.Context, key c return controller.Client.Get(ctx, key, obj) } - func validateCMPv2IssuerSpec(issuerSpec cmpv2api.CMPv2IssuerSpec, log logr.Logger) error { switch { - case issuerSpec.URL == "": - return fmt.Errorf("spec.url cannot be empty") - case issuerSpec.KeyRef.Name == "": - return fmt.Errorf("spec.keyRef.name cannot be empty") - case issuerSpec.KeyRef.Key == "": - return fmt.Errorf("spec.keyRef.key cannot be empty") - default: - log.Info("CMPv2Issuer validated. ") - return nil + case issuerSpec.URL == "": + return fmt.Errorf("spec.url cannot be empty") + case issuerSpec.CaName == "": + return fmt.Errorf("spec.caName cannot be empty") + case issuerSpec.CertSecretRef.Name == "": + return fmt.Errorf("spec.certSecretRef.name cannot be empty") + case issuerSpec.CertSecretRef.KeyRef == "": + return fmt.Errorf("spec.certSecretRef.keyRef cannot be empty") + case issuerSpec.CertSecretRef.CertRef == "": + return fmt.Errorf("spec.certSecretRef.certRef cannot be empty") + case issuerSpec.CertSecretRef.CacertRef == "": + return fmt.Errorf("spec.certSecretRef.cacertRef cannot be empty") + default: + return nil } } @@ -134,22 +136,19 @@ func updateCMPv2IssuerStatusToVerified(statusUpdater *CMPv2IssuerStatusUpdater, return statusUpdater.Update(ctx, cmpv2api.ConditionTrue, Verified, "CMPv2Issuer verified and ready to sign certificates") } - // Error handling func handleErrorUpdatingCMPv2IssuerStatus(log logr.Logger, err error) { log.Error(err, "Failed to update CMPv2Issuer status") } - func handleErrorLoadingCMPv2Issuer(log logr.Logger, err error) { log.Error(err, "Failed to retrieve CMPv2Issuer resource") } - func handleErrorProvisionerInitialization(ctx context.Context, log logr.Logger, err error, statusUpdater *CMPv2IssuerStatusUpdater) { log.Error(err, "Failed to initialize provisioner") - statusUpdater.UpdateNoError(ctx, cmpv2api.ConditionFalse, Error, "Failed initialize provisioner") + statusUpdater.UpdateNoError(ctx, cmpv2api.ConditionFalse, Error, "Failed to initialize provisioner: %v", err) } func handleErrorCMPv2IssuerValidation(ctx context.Context, log logr.Logger, err error, statusUpdater *CMPv2IssuerStatusUpdater) { @@ -157,13 +156,6 @@ func handleErrorCMPv2IssuerValidation(ctx context.Context, log logr.Logger, err statusUpdater.UpdateNoError(ctx, cmpv2api.ConditionFalse, ValidationFailed, "Failed to validate resource: %v", err) } -func handleErrorSecretNotFound(ctx context.Context, log logr.Logger, issuer *cmpv2api.CMPv2Issuer, statusUpdater *CMPv2IssuerStatusUpdater, secretNamespaceName types.NamespacedName, secret core.Secret) error { - err := fmt.Errorf("secret %s does not contain key %s", secret.Name, issuer.Spec.KeyRef.Key) - log.Error(err, "Failed to retrieve CMPv2Issuer provisioner secret", "namespace", secretNamespaceName.Namespace, "name", secretNamespaceName.Name) - statusUpdater.UpdateNoError(ctx, cmpv2api.ConditionFalse, NotFound, "Failed to retrieve provisioner secret: %v", err) - return err -} - func handleErrorInvalidSecret(ctx context.Context, log logr.Logger, err error, statusUpdater *CMPv2IssuerStatusUpdater, secretNamespaceName types.NamespacedName) { log.Error(err, "Failed to retrieve CMPv2Issuer provisioner secret", "namespace", secretNamespaceName.Namespace, "name", secretNamespaceName.Name) if apierrors.IsNotFound(err) { diff --git a/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller_test.go b/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller_test.go index 8409ea78..79c78ed5 100644 --- a/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller_test.go +++ b/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_controller_test.go @@ -21,13 +21,22 @@ package cmpv2controller import ( + "testing" + "github.com/go-logr/logr" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" - "testing" ) +func Test_shouldBeValidCMPv2IssuerSpec_whenAllFieldsAreSet(t *testing.T) { + spec := getValidCMPv2IssuerSpec() + + err := validateCMPv2IssuerSpec(spec, &MockLogger{}) + assert.Nil(t, err) +} + func Test_shouldBeInvalidCMPv2IssuerSpec_whenSpecIsEmpty(t *testing.T) { spec := cmpv2api.CMPv2IssuerSpec{} err := validateCMPv2IssuerSpec(spec, nil) @@ -35,32 +44,50 @@ func Test_shouldBeInvalidCMPv2IssuerSpec_whenSpecIsEmpty(t *testing.T) { } func Test_shouldBeInvalidCMPv2IssuerSpec_whenNotAllFieldsAreSet(t *testing.T) { - spec := cmpv2api.CMPv2IssuerSpec{} - spec.URL = "https://localhost" - spec.KeyRef = cmpv2api.SecretKeySelector{} - spec.KeyRef.Name = "secret-key" + setEmptyFieldFunctions := map[string]func(spec *cmpv2api.CMPv2IssuerSpec){ + "emptyUrl": func(spec *cmpv2api.CMPv2IssuerSpec) { spec.URL = "" }, + "empryCaName": func(spec *cmpv2api.CMPv2IssuerSpec) { spec.CaName = "" }, + "emptySecretName": func(spec *cmpv2api.CMPv2IssuerSpec) { spec.CertSecretRef.Name = "" }, + "emptySecretKeyRef": func(spec *cmpv2api.CMPv2IssuerSpec) { spec.CertSecretRef.KeyRef = "" }, + "emptySecretCertRef": func(spec *cmpv2api.CMPv2IssuerSpec) { spec.CertSecretRef.CertRef = "" }, + "emptySecretCaertRef": func(spec *cmpv2api.CMPv2IssuerSpec) { spec.CertSecretRef.CacertRef = "" }, + } - err := validateCMPv2IssuerSpec(spec, &MockLogger{}) - assert.NotNil(t, err) + for caseName, setEmptyFieldFunction := range setEmptyFieldFunctions { + t.Run(caseName, func(t *testing.T) { + test_shouldBeInvalidCMPv2IssuerSpec_whenFunctionApplied(t, setEmptyFieldFunction) + }) + } } -func Test_shouldBeValidCMPv2IssuerSpec_whenAllFieldsAreSet(t *testing.T) { - spec := cmpv2api.CMPv2IssuerSpec{} - spec.URL = "https://localhost" - spec.KeyRef = cmpv2api.SecretKeySelector{} - spec.KeyRef.Name = "secret-key" - spec.KeyRef.Key = "the-key" +func test_shouldBeInvalidCMPv2IssuerSpec_whenFunctionApplied(t *testing.T, transformSpec func(spec *cmpv2api.CMPv2IssuerSpec)) { + spec := getValidCMPv2IssuerSpec() + transformSpec(&spec) + err := validateCMPv2IssuerSpec(spec, nil) + assert.NotNil(t, err) +} - err := validateCMPv2IssuerSpec(spec, &MockLogger{}) - assert.Nil(t, err) +func getValidCMPv2IssuerSpec() cmpv2api.CMPv2IssuerSpec { + issuerSpec := cmpv2api.CMPv2IssuerSpec{ + URL: "https://oom-cert-service:8443/v1/certificate/", + CaName: "RA", + CertSecretRef: cmpv2api.SecretKeySelector{ + Name: "issuer-cert-secret", + KeyRef: "cmpv2Issuer-key.pem", + CertRef: "cmpv2Issuer-cert.pem", + CacertRef: "cacert.pem", + }, + } + return issuerSpec } type MockLogger struct { mock.Mock } -func (m *MockLogger) Info(msg string, keysAndValues ...interface{}) {} + +func (m *MockLogger) Info(msg string, keysAndValues ...interface{}) {} func (m *MockLogger) Error(err error, msg string, keysAndValues ...interface{}) {} -func (m *MockLogger) Enabled() bool { return false } -func (m *MockLogger) V(level int) logr.Logger { return m } -func (m *MockLogger) WithValues(keysAndValues ...interface{}) logr.Logger { return m } -func (m *MockLogger) WithName(name string) logr.Logger { return m } +func (m *MockLogger) Enabled() bool { return false } +func (m *MockLogger) V(level int) logr.Logger { return m } +func (m *MockLogger) WithValues(keysAndValues ...interface{}) logr.Logger { return m } +func (m *MockLogger) WithName(name string) logr.Logger { return m } diff --git a/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_status_updater.go b/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_status_updater.go index 017e36a4..f07101db 100644 --- a/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_status_updater.go +++ b/certServiceK8sExternalProvider/src/cmpv2controller/cmpv2_issuer_status_updater.go @@ -28,9 +28,11 @@ package cmpv2controller import ( "context" "fmt" + "github.com/go-logr/logr" core "k8s.io/api/core/v1" meta "k8s.io/apimachinery/pkg/apis/meta/v1" + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" ) diff --git a/certServiceK8sExternalProvider/src/cmpv2controller/status_reason.go b/certServiceK8sExternalProvider/src/cmpv2controller/status_reason.go index d41712d3..fc1772e9 100644 --- a/certServiceK8sExternalProvider/src/cmpv2controller/status_reason.go +++ b/certServiceK8sExternalProvider/src/cmpv2controller/status_reason.go @@ -21,8 +21,8 @@ package cmpv2controller const ( - NotFound = "NotFound" + NotFound = "NotFound" ValidationFailed = "ValidationFailed" - Error = "Error" - Verified = "Verified" + Error = "Error" + Verified = "Verified" ) diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner.go b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner.go index a51b8425..e48b527d 100644 --- a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner.go +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner.go @@ -32,30 +32,39 @@ import ( "encoding/base64" "encoding/pem" "fmt" + "sync" + certmanager "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1" "k8s.io/apimachinery/pkg/types" - "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" ctrl "sigs.k8s.io/controller-runtime" - "sync" + + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" ) var collection = new(sync.Map) type CertServiceCA struct { - name string - url string - key []byte + name string + url string + caName string + key []byte + cert []byte + cacert []byte } -func New(cmpv2Issuer *cmpv2api.CMPv2Issuer, key []byte) (*CertServiceCA, error) { +func New(cmpv2Issuer *cmpv2api.CMPv2Issuer, key []byte, cert []byte, cacert []byte) (*CertServiceCA, error) { ca := CertServiceCA{} ca.name = cmpv2Issuer.Name ca.url = cmpv2Issuer.Spec.URL + ca.caName = cmpv2Issuer.Spec.CaName ca.key = key + ca.cert = cert + ca.cacert = cacert log := ctrl.Log.WithName("cmpv2-provisioner") - log.Info("Configuring CA: ", "name", ca.name, "url", ca.url, "key", ca.key) + log.Info("Configuring CA: ", "name", ca.name, "url", ca.url, "caName", ca.caName, "key", ca.key, + "cert", ca.cert, "cacert", ca.cacert) return &ca, nil } diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory.go b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory.go new file mode 100644 index 00000000..4a3898e7 --- /dev/null +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory.go @@ -0,0 +1,55 @@ +/* + * ============LICENSE_START======================================================= + * oom-certservice-k8s-external-provider + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package cmpv2provisioner + +import ( + "fmt" + + v1 "k8s.io/api/core/v1" + + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" +) + +func CreateProvisioner(issuer *cmpv2api.CMPv2Issuer, secret v1.Secret) (*CertServiceCA, error) { + secretKeys := issuer.Spec.CertSecretRef + key, err := readValueFromSecret(secret, secretKeys.KeyRef) + if err != nil { + return nil, err + } + cert, err := readValueFromSecret(secret, secretKeys.CertRef) + if err != nil { + return nil, err + } + cacert, err := readValueFromSecret(secret, secretKeys.CacertRef) + if err != nil { + return nil, err + } + return New(issuer, key, cert, cacert) +} + +func readValueFromSecret(secret v1.Secret, secretKey string) ([]byte, error) { + value, ok := secret.Data[secretKey] + if !ok { + err := fmt.Errorf("secret %s does not contain key %s", secret.Name, secretKey) + return nil, err + } + return value, nil +} diff --git a/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_test.go b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_test.go new file mode 100644 index 00000000..6ef33098 --- /dev/null +++ b/certServiceK8sExternalProvider/src/cmpv2provisioner/cmpv2_provisioner_factory_test.go @@ -0,0 +1,120 @@ +/* + * ============LICENSE_START======================================================= + * oom-certservice-k8s-external-provider + * ================================================================================ + * Copyright (C) 2020 Nokia. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package cmpv2provisioner + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" + + "onap.org/oom-certservice/k8s-external-provider/src/cmpv2api" +) + +const ( + secretName = "issuer-cert-secret" + url = "https://oom-cert-service:8443/v1/certificate/" + caName = "RA" + keySecretKey = "cmpv2Issuer-key.pem" + certSecretKey = "cmpv2Issuer-cert.pem" + cacertSecretKey = "cacert.pem" +) + +var ( + keySecretValue = []byte("keyData") + certSecretValue = []byte("certData") + cacertSecretValue = []byte("cacertData") +) + +func Test_shouldCreateProvisioner(t *testing.T) { + issuer, secret := getValidIssuerAndSecret() + + provisioner, _ := CreateProvisioner(&issuer, secret) + + assert.NotNil(t, provisioner) + assert.Equal(t, url, provisioner.url) + assert.Equal(t, caName, provisioner.caName) + assert.Equal(t, keySecretValue, provisioner.key) + assert.Equal(t, certSecretValue, provisioner.cert) + assert.Equal(t, cacertSecretValue, provisioner.cacert) +} + +func Test_shouldReturnError_whenSecretMissingKeyRef(t *testing.T) { + issuer, secret := getValidIssuerAndSecret() + delete(secret.Data, keySecretKey) + + provisioner, err := CreateProvisioner(&issuer, secret) + + assert.Nil(t, provisioner) + if assert.Error(t, err) { + assert.Equal(t, fmt.Errorf("secret %s does not contain key %s", secretName, keySecretKey), err) + } +} + +func Test_shouldReturnError_whenSecretMissingCertRef(t *testing.T) { + issuer, secret := getValidIssuerAndSecret() + delete(secret.Data, certSecretKey) + + provisioner, err := CreateProvisioner(&issuer, secret) + + assert.Nil(t, provisioner) + if assert.Error(t, err) { + assert.Equal(t, fmt.Errorf("secret %s does not contain key %s", secretName, certSecretKey), err) + } +} + +func Test_shouldReturnError_whenSecretMissingCacertRef(t *testing.T) { + issuer, secret := getValidIssuerAndSecret() + delete(secret.Data, cacertSecretKey) + + provisioner, err := CreateProvisioner(&issuer, secret) + + assert.Nil(t, provisioner) + if assert.Error(t, err) { + assert.Equal(t, fmt.Errorf("secret %s does not contain key %s", secretName, cacertSecretKey), err) + } +} + +func getValidIssuerAndSecret() (cmpv2api.CMPv2Issuer, v1.Secret) { + issuer := cmpv2api.CMPv2Issuer{ + Spec: cmpv2api.CMPv2IssuerSpec{ + URL: url, + CaName: caName, + CertSecretRef: cmpv2api.SecretKeySelector{ + Name: secretName, + KeyRef: keySecretKey, + CertRef: certSecretKey, + CacertRef: cacertSecretKey, + }, + }, + } + secret := v1.Secret{ + + Data: map[string][]byte{ + keySecretKey: keySecretValue, + certSecretKey: certSecretValue, + cacertSecretKey: cacertSecretValue, + }, + } + secret.Name = secretName + return issuer, secret +} diff --git a/certServiceK8sExternalProvider/src/exit_code.go b/certServiceK8sExternalProvider/src/exit_code.go index 7435c64f..4fb984d3 100644 --- a/certServiceK8sExternalProvider/src/exit_code.go +++ b/certServiceK8sExternalProvider/src/exit_code.go @@ -1,13 +1,13 @@ package app type ExitCode struct { - Code int + Code int Message string } var ( - FAILED_TO_CREATE_CONTROLLER_MANAGER = ExitCode{1, "Unable to create k8s controller manager"} + FAILED_TO_CREATE_CONTROLLER_MANAGER = ExitCode{1, "Unable to create K8s controller manager"} FAILED_TO_REGISTER_CMPv2_ISSUER_CONTROLLER = ExitCode{2, "Unable to register CMPv2Issuer controller"} FAILED_TO_REGISTER_CERT_REQUEST_CONTROLLER = ExitCode{3, "Unable to register CertificateRequestController"} - EXCEPTION_WHILE_RUNNING_CONTROLLER_MANAGER = ExitCode{4, "An exception occurs while running k8s controller manager"} + EXCEPTION_WHILE_RUNNING_CONTROLLER_MANAGER = ExitCode{4, "An exception occurs while running K8s controller manager"} ) diff --git a/certServiceK8sExternalProvider/src/exit_code_test.go b/certServiceK8sExternalProvider/src/exit_code_test.go index 8a42909a..1492036b 100644 --- a/certServiceK8sExternalProvider/src/exit_code_test.go +++ b/certServiceK8sExternalProvider/src/exit_code_test.go @@ -22,6 +22,7 @@ package app import ( "testing" + "github.com/stretchr/testify/assert" ) -- cgit 1.2.3-korg