From 43a62bc912f362c263fb5608f1ac80fd871a7053 Mon Sep 17 00:00:00 2001 From: Neha Bhat <84361901+ndbhat@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:35:06 -0400 Subject: [PATCH] Check for RequestInProgressException when creating CA resource (#46) Description of changes: 1. Check for RequestInProgressException when creating CA resources 2. Release artifacts for release v0.0.11 By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. --- apis/v1alpha1/ack-generate-metadata.yaml | 6 +- apis/v1alpha1/certificate_authority.go | 4 +- apis/v1alpha1/generator.yaml | 33 ++++- config/controller/kustomization.yaml | 2 +- ...rvices.k8s.aws_certificateauthorities.yaml | 1 + generator.yaml | 33 ++++- helm/Chart.yaml | 4 +- ...rvices.k8s.aws_certificateauthorities.yaml | 1 + helm/templates/NOTES.txt | 2 +- helm/values.yaml | 2 +- pkg/resource/certificate/sdk.go | 7 +- .../certificate_authority/manager_factory.go | 2 +- pkg/resource/certificate_authority/sdk.go | 74 +++++----- .../certificate_authority_activation/sdk.go | 27 +++- .../sdk_create_post_build_request.go.tpl | 3 - .../sdk_create_post_set_output.go.tpl | 7 - .../sdk_read_one_post_set_output.go.tpl | 10 +- test/e2e/tests/test_ca.py | 5 +- test/e2e/tests/test_ca_activation.py | 27 ++-- test/e2e/tests/test_certificate.py | 129 +++++++++++++++++- 20 files changed, 294 insertions(+), 85 deletions(-) delete mode 100644 templates/hooks/certificate_authority/sdk_create_post_build_request.go.tpl delete mode 100644 templates/hooks/certificate_authority/sdk_create_post_set_output.go.tpl diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index 5e667b2..bcb9e52 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2024-04-19T16:18:58Z" + build_date: "2024-04-23T13:51:34Z" build_hash: af8006cb7248f0177e2a14b709c69c3a99a016ad go_version: go1.22.0 version: v0.33.0 -api_directory_checksum: 804becfbeaa4f4dee31721a37049a05b7c904de7 +api_directory_checksum: 14ee2b48df20458271bcddc87436760812fd6ccd api_version: v1alpha1 aws_sdk_go_version: v1.49.6 generator_config_info: - file_checksum: 86a608ca2c8bf34d5bb49a64b4eaa7a055f0b4f7 + file_checksum: 5e732471cd7372bbdab8104a11bf3ac2c0dd6511 original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/certificate_authority.go b/apis/v1alpha1/certificate_authority.go index b811f7b..d25fce8 100644 --- a/apis/v1alpha1/certificate_authority.go +++ b/apis/v1alpha1/certificate_authority.go @@ -82,7 +82,9 @@ type CertificateAuthoritySpec struct { // Key-value pairs that will be attached to the new private CA. You can associate // up to 50 tags with a private CA. For information using tags with IAM to manage // permissions, see Controlling Access Using IAM Tags (https://docs.aws.amazon.com/IAM/latest/UserGuide/access_iam-tags.html). - Tags []*Tag `json:"tags,omitempty"` + Tags []*Tag `json:"tags,omitempty"` + // The type of the certificate authority. + // +kubebuilder:validation:Required Type *string `json:"type,omitempty"` // Specifies whether the CA issues general-purpose certificates that typically // require a revocation mechanism, or short-lived certificates that may optionally diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index adc40f4..3b00ab1 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -5,7 +5,6 @@ ignore: - Permission field_paths: - CreateCertificateAuthorityInput.IdempotencyToken - - CreateCertificateAuthorityInput.CertificateAuthorityType - IssueCertificateInput.IdempotencyToken - IssueCertificateInput.Csr - ImportCertificateAuthorityCertificateInput.Certificate @@ -28,13 +27,16 @@ model_name: acm-pca prefix_config: {} resources: CertificateAuthority: + reconcile: + requeue_on_success_seconds: 30 + renames: + operations: + CreateCertificateAuthority: + input_fields: + CertificateAuthorityType: Type hooks: delta_pre_compare: code: customSetDefaults(a, b) - sdk_create_post_build_request: - template_path: hooks/certificate_authority/sdk_create_post_build_request.go.tpl - sdk_create_post_set_output: - template_path: hooks/certificate_authority/sdk_create_post_set_output.go.tpl sdk_update_pre_build_request: template_path: hooks/certificate_authority/sdk_update_pre_build_request.go.tpl sdk_read_one_post_set_output: @@ -56,7 +58,6 @@ resources: CertificateAuthorityConfiguration: is_immutable: true Type: - type: string is_immutable: true go_tag: json:"type,omitempty" KeyStorageSecurityStandard: @@ -130,6 +131,11 @@ resources: - MissingParameter - ValidationError - ValidationException + - InvalidArgsException + - InvalidArnException + - InvalidStateException + - LimitExceededException + - MalformedCSRException fields: CertificateAuthorityARN: references: @@ -157,6 +163,21 @@ resources: tags: ignore: true CertificateAuthorityActivation: + exceptions: + terminal_codes: + - InvalidAction + - InvalidParameterCombination + - InvalidParameterValue + - InvalidQueryParameter + - MissingParameter + - ValidationError + - ValidationException + - CertificateMismatchException + - InvalidArnException + - InvalidRequestException + - InvalidStateException + - MalformedCertificateException + - RequestFailedException hooks: delta_pre_compare: code: customSetDefaults(a, b) diff --git a/config/controller/kustomization.yaml b/config/controller/kustomization.yaml index 186d150..e84ae33 100644 --- a/config/controller/kustomization.yaml +++ b/config/controller/kustomization.yaml @@ -6,4 +6,4 @@ kind: Kustomization images: - name: controller newName: public.ecr.aws/aws-controllers-k8s/acmpca-controller - newTag: 0.0.10 + newTag: 0.0.11 diff --git a/config/crd/bases/acmpca.services.k8s.aws_certificateauthorities.yaml b/config/crd/bases/acmpca.services.k8s.aws_certificateauthorities.yaml index 3597e79..6c16af2 100644 --- a/config/crd/bases/acmpca.services.k8s.aws_certificateauthorities.yaml +++ b/config/crd/bases/acmpca.services.k8s.aws_certificateauthorities.yaml @@ -429,6 +429,7 @@ spec: type: object type: array type: + description: The type of the certificate authority. type: string usageMode: description: |- diff --git a/generator.yaml b/generator.yaml index adc40f4..3b00ab1 100644 --- a/generator.yaml +++ b/generator.yaml @@ -5,7 +5,6 @@ ignore: - Permission field_paths: - CreateCertificateAuthorityInput.IdempotencyToken - - CreateCertificateAuthorityInput.CertificateAuthorityType - IssueCertificateInput.IdempotencyToken - IssueCertificateInput.Csr - ImportCertificateAuthorityCertificateInput.Certificate @@ -28,13 +27,16 @@ model_name: acm-pca prefix_config: {} resources: CertificateAuthority: + reconcile: + requeue_on_success_seconds: 30 + renames: + operations: + CreateCertificateAuthority: + input_fields: + CertificateAuthorityType: Type hooks: delta_pre_compare: code: customSetDefaults(a, b) - sdk_create_post_build_request: - template_path: hooks/certificate_authority/sdk_create_post_build_request.go.tpl - sdk_create_post_set_output: - template_path: hooks/certificate_authority/sdk_create_post_set_output.go.tpl sdk_update_pre_build_request: template_path: hooks/certificate_authority/sdk_update_pre_build_request.go.tpl sdk_read_one_post_set_output: @@ -56,7 +58,6 @@ resources: CertificateAuthorityConfiguration: is_immutable: true Type: - type: string is_immutable: true go_tag: json:"type,omitempty" KeyStorageSecurityStandard: @@ -130,6 +131,11 @@ resources: - MissingParameter - ValidationError - ValidationException + - InvalidArgsException + - InvalidArnException + - InvalidStateException + - LimitExceededException + - MalformedCSRException fields: CertificateAuthorityARN: references: @@ -157,6 +163,21 @@ resources: tags: ignore: true CertificateAuthorityActivation: + exceptions: + terminal_codes: + - InvalidAction + - InvalidParameterCombination + - InvalidParameterValue + - InvalidQueryParameter + - MissingParameter + - ValidationError + - ValidationException + - CertificateMismatchException + - InvalidArnException + - InvalidRequestException + - InvalidStateException + - MalformedCertificateException + - RequestFailedException hooks: delta_pre_compare: code: customSetDefaults(a, b) diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 10510f5..64b147e 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v1 name: acmpca-chart description: A Helm chart for the ACK service controller for AWS Private Certificate Authority (PCA) -version: 0.0.10 -appVersion: 0.0.10 +version: 0.0.11 +appVersion: 0.0.11 home: https://github.com/aws-controllers-k8s/acmpca-controller icon: https://raw.githubusercontent.com/aws/eks-charts/master/docs/logo/aws.png sources: diff --git a/helm/crds/acmpca.services.k8s.aws_certificateauthorities.yaml b/helm/crds/acmpca.services.k8s.aws_certificateauthorities.yaml index ac7fba6..581298a 100644 --- a/helm/crds/acmpca.services.k8s.aws_certificateauthorities.yaml +++ b/helm/crds/acmpca.services.k8s.aws_certificateauthorities.yaml @@ -429,6 +429,7 @@ spec: type: object type: array type: + description: The type of the certificate authority. type: string usageMode: description: |- diff --git a/helm/templates/NOTES.txt b/helm/templates/NOTES.txt index 613a078..bbffce6 100644 --- a/helm/templates/NOTES.txt +++ b/helm/templates/NOTES.txt @@ -1,5 +1,5 @@ {{ .Chart.Name }} has been installed. -This chart deploys "public.ecr.aws/aws-controllers-k8s/acmpca-controller:0.0.10". +This chart deploys "public.ecr.aws/aws-controllers-k8s/acmpca-controller:0.0.11". Check its status by running: kubectl --namespace {{ .Release.Namespace }} get pods -l "app.kubernetes.io/instance={{ .Release.Name }}" diff --git a/helm/values.yaml b/helm/values.yaml index f9fd3c2..2877fa2 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -4,7 +4,7 @@ image: repository: public.ecr.aws/aws-controllers-k8s/acmpca-controller - tag: 0.0.10 + tag: 0.0.11 pullPolicy: IfNotPresent pullSecrets: [] diff --git a/pkg/resource/certificate/sdk.go b/pkg/resource/certificate/sdk.go index 725858a..45fbe59 100644 --- a/pkg/resource/certificate/sdk.go +++ b/pkg/resource/certificate/sdk.go @@ -613,7 +613,12 @@ func (rm *resourceManager) terminalAWSError(err error) bool { "InvalidQueryParameter", "MissingParameter", "ValidationError", - "ValidationException": + "ValidationException", + "InvalidArgsException", + "InvalidArnException", + "InvalidStateException", + "LimitExceededException", + "MalformedCSRException": return true default: return false diff --git a/pkg/resource/certificate_authority/manager_factory.go b/pkg/resource/certificate_authority/manager_factory.go index 2558ff3..5bf1520 100644 --- a/pkg/resource/certificate_authority/manager_factory.go +++ b/pkg/resource/certificate_authority/manager_factory.go @@ -82,7 +82,7 @@ func (f *resourceManagerFactory) IsAdoptable() bool { // RequeueOnSuccessSeconds returns true if the resource should be requeued after specified seconds // Default is false which means resource will not be requeued after success. func (f *resourceManagerFactory) RequeueOnSuccessSeconds() int { - return 0 + return 30 } func newResourceManagerFactory() *resourceManagerFactory { diff --git a/pkg/resource/certificate_authority/sdk.go b/pkg/resource/certificate_authority/sdk.go index c41711d..c68a5fc 100644 --- a/pkg/resource/certificate_authority/sdk.go +++ b/pkg/resource/certificate_authority/sdk.go @@ -446,6 +446,14 @@ func (rm *resourceManager) sdkFind( ko.Spec.RevocationConfiguration = revocationConfiguration } + + ko.Status.CertificateSigningRequest, err = rm.getCertificateAuthorityCsr(ctx, *resourceARN) + if err != nil && strings.HasPrefix(err.Error(), "RequestInProgressException") { + return nil, ackrequeue.NeededAfter(err, ackrequeue.DefaultRequeueAfterDuration) + } + if err != nil { + return nil, err + } return &resource{ko}, nil } @@ -489,9 +497,6 @@ func (rm *resourceManager) sdkCreate( if err != nil { return nil, err } - if desired.ko.Spec.Type != nil { - input.SetCertificateAuthorityType(*desired.ko.Spec.Type) - } var resp *svcsdk.CreateCertificateAuthorityOutput _ = resp @@ -513,13 +518,6 @@ func (rm *resourceManager) sdkCreate( } rm.setStatusDefaults(ko) - if ko.Status.ACKResourceMetadata != nil && ko.Status.ACKResourceMetadata.ARN != nil { - resourceARN := (*string)(ko.Status.ACKResourceMetadata.ARN) - ko.Status.CertificateSigningRequest, err = rm.getCertificateAuthorityCsr(ctx, *resourceARN) - if err != nil { - return nil, err - } - } return &resource{ko}, nil } @@ -753,55 +751,58 @@ func (rm *resourceManager) newCreateRequestPayload( } res.SetCertificateAuthorityConfiguration(f0) } + if r.ko.Spec.Type != nil { + res.SetCertificateAuthorityType(*r.ko.Spec.Type) + } if r.ko.Spec.KeyStorageSecurityStandard != nil { res.SetKeyStorageSecurityStandard(*r.ko.Spec.KeyStorageSecurityStandard) } if r.ko.Spec.RevocationConfiguration != nil { - f2 := &svcsdk.RevocationConfiguration{} + f3 := &svcsdk.RevocationConfiguration{} if r.ko.Spec.RevocationConfiguration.CRLConfiguration != nil { - f2f0 := &svcsdk.CrlConfiguration{} + f3f0 := &svcsdk.CrlConfiguration{} if r.ko.Spec.RevocationConfiguration.CRLConfiguration.CustomCNAME != nil { - f2f0.SetCustomCname(*r.ko.Spec.RevocationConfiguration.CRLConfiguration.CustomCNAME) + f3f0.SetCustomCname(*r.ko.Spec.RevocationConfiguration.CRLConfiguration.CustomCNAME) } if r.ko.Spec.RevocationConfiguration.CRLConfiguration.Enabled != nil { - f2f0.SetEnabled(*r.ko.Spec.RevocationConfiguration.CRLConfiguration.Enabled) + f3f0.SetEnabled(*r.ko.Spec.RevocationConfiguration.CRLConfiguration.Enabled) } if r.ko.Spec.RevocationConfiguration.CRLConfiguration.ExpirationInDays != nil { - f2f0.SetExpirationInDays(*r.ko.Spec.RevocationConfiguration.CRLConfiguration.ExpirationInDays) + f3f0.SetExpirationInDays(*r.ko.Spec.RevocationConfiguration.CRLConfiguration.ExpirationInDays) } if r.ko.Spec.RevocationConfiguration.CRLConfiguration.S3BucketName != nil { - f2f0.SetS3BucketName(*r.ko.Spec.RevocationConfiguration.CRLConfiguration.S3BucketName) + f3f0.SetS3BucketName(*r.ko.Spec.RevocationConfiguration.CRLConfiguration.S3BucketName) } if r.ko.Spec.RevocationConfiguration.CRLConfiguration.S3ObjectACL != nil { - f2f0.SetS3ObjectAcl(*r.ko.Spec.RevocationConfiguration.CRLConfiguration.S3ObjectACL) + f3f0.SetS3ObjectAcl(*r.ko.Spec.RevocationConfiguration.CRLConfiguration.S3ObjectACL) } - f2.SetCrlConfiguration(f2f0) + f3.SetCrlConfiguration(f3f0) } if r.ko.Spec.RevocationConfiguration.OCSPConfiguration != nil { - f2f1 := &svcsdk.OcspConfiguration{} + f3f1 := &svcsdk.OcspConfiguration{} if r.ko.Spec.RevocationConfiguration.OCSPConfiguration.Enabled != nil { - f2f1.SetEnabled(*r.ko.Spec.RevocationConfiguration.OCSPConfiguration.Enabled) + f3f1.SetEnabled(*r.ko.Spec.RevocationConfiguration.OCSPConfiguration.Enabled) } if r.ko.Spec.RevocationConfiguration.OCSPConfiguration.OCSPCustomCNAME != nil { - f2f1.SetOcspCustomCname(*r.ko.Spec.RevocationConfiguration.OCSPConfiguration.OCSPCustomCNAME) + f3f1.SetOcspCustomCname(*r.ko.Spec.RevocationConfiguration.OCSPConfiguration.OCSPCustomCNAME) } - f2.SetOcspConfiguration(f2f1) + f3.SetOcspConfiguration(f3f1) } - res.SetRevocationConfiguration(f2) + res.SetRevocationConfiguration(f3) } if r.ko.Spec.Tags != nil { - f3 := []*svcsdk.Tag{} - for _, f3iter := range r.ko.Spec.Tags { - f3elem := &svcsdk.Tag{} - if f3iter.Key != nil { - f3elem.SetKey(*f3iter.Key) + f4 := []*svcsdk.Tag{} + for _, f4iter := range r.ko.Spec.Tags { + f4elem := &svcsdk.Tag{} + if f4iter.Key != nil { + f4elem.SetKey(*f4iter.Key) } - if f3iter.Value != nil { - f3elem.SetValue(*f3iter.Value) + if f4iter.Value != nil { + f4elem.SetValue(*f4iter.Value) } - f3 = append(f3, f3elem) + f4 = append(f4, f4elem) } - res.SetTags(f3) + res.SetTags(f4) } if r.ko.Spec.UsageMode != nil { res.SetUsageMode(*r.ko.Spec.UsageMode) @@ -1027,8 +1028,13 @@ func (rm *resourceManager) updateConditions( recoverableCondition.Message = nil } } - // Required to avoid the "declared but not used" error in the default case - _ = syncCondition + if syncCondition == nil && onSuccess { + syncCondition = &ackv1alpha1.Condition{ + Type: ackv1alpha1.ConditionTypeResourceSynced, + Status: corev1.ConditionTrue, + } + ko.Status.Conditions = append(ko.Status.Conditions, syncCondition) + } if terminalCondition != nil || recoverableCondition != nil || syncCondition != nil { return &resource{ko}, true // updated } diff --git a/pkg/resource/certificate_authority_activation/sdk.go b/pkg/resource/certificate_authority_activation/sdk.go index afff134..349d149 100644 --- a/pkg/resource/certificate_authority_activation/sdk.go +++ b/pkg/resource/certificate_authority_activation/sdk.go @@ -271,8 +271,31 @@ func (rm *resourceManager) updateConditions( // and if the exception indicates that it is a Terminal exception // 'Terminal' exception are specified in generator configuration func (rm *resourceManager) terminalAWSError(err error) bool { - // No terminal_errors specified for this resource in generator config - return false + if err == nil { + return false + } + awsErr, ok := ackerr.AWSError(err) + if !ok { + return false + } + switch awsErr.Code() { + case "InvalidAction", + "InvalidParameterCombination", + "InvalidParameterValue", + "InvalidQueryParameter", + "MissingParameter", + "ValidationError", + "ValidationException", + "CertificateMismatchException", + "InvalidArnException", + "InvalidRequestException", + "InvalidStateException", + "MalformedCertificateException", + "RequestFailedException": + return true + default: + return false + } } // getImmutableFieldChanges returns list of immutable fields from the diff --git a/templates/hooks/certificate_authority/sdk_create_post_build_request.go.tpl b/templates/hooks/certificate_authority/sdk_create_post_build_request.go.tpl deleted file mode 100644 index 356266d..0000000 --- a/templates/hooks/certificate_authority/sdk_create_post_build_request.go.tpl +++ /dev/null @@ -1,3 +0,0 @@ - if desired.ko.Spec.Type != nil { - input.SetCertificateAuthorityType(*desired.ko.Spec.Type) - } \ No newline at end of file diff --git a/templates/hooks/certificate_authority/sdk_create_post_set_output.go.tpl b/templates/hooks/certificate_authority/sdk_create_post_set_output.go.tpl deleted file mode 100644 index e46a617..0000000 --- a/templates/hooks/certificate_authority/sdk_create_post_set_output.go.tpl +++ /dev/null @@ -1,7 +0,0 @@ - if ko.Status.ACKResourceMetadata != nil && ko.Status.ACKResourceMetadata.ARN != nil { - resourceARN := (*string)(ko.Status.ACKResourceMetadata.ARN) - ko.Status.CertificateSigningRequest, err = rm.getCertificateAuthorityCsr(ctx, *resourceARN) - if err != nil { - return nil, err - } - } \ No newline at end of file diff --git a/templates/hooks/certificate_authority/sdk_read_one_post_set_output.go.tpl b/templates/hooks/certificate_authority/sdk_read_one_post_set_output.go.tpl index 638dcac..906ac20 100644 --- a/templates/hooks/certificate_authority/sdk_read_one_post_set_output.go.tpl +++ b/templates/hooks/certificate_authority/sdk_read_one_post_set_output.go.tpl @@ -26,4 +26,12 @@ ko.Spec.RevocationConfiguration = revocationConfiguration - } \ No newline at end of file + } + + ko.Status.CertificateSigningRequest, err = rm.getCertificateAuthorityCsr(ctx, *resourceARN) + if err != nil && strings.HasPrefix(err.Error(), "RequestInProgressException") { + return nil, ackrequeue.NeededAfter(err, ackrequeue.DefaultRequeueAfterDuration) + } + if err != nil { + return nil, err + } \ No newline at end of file diff --git a/test/e2e/tests/test_ca.py b/test/e2e/tests/test_ca.py index 42f2567..9a201e1 100644 --- a/test/e2e/tests/test_ca.py +++ b/test/e2e/tests/test_ca.py @@ -32,7 +32,7 @@ RESOURCE_PLURAL = "certificateauthorities" -CREATE_WAIT_AFTER_SECONDS = 60 +CREATE_WAIT_AFTER_SECONDS = 30 UPDATE_WAIT_AFTER_SECONDS = 10 DELETE_WAIT_AFTER_SECONDS = 10 @@ -122,6 +122,9 @@ def test_ca_crud(self, acmpca_client, simple_certificate_authority): actual=observed_tags, ) + ca_cr = k8s.patch_custom_resource(ca_ref, {}) + logging.info(ca_cr) + # Check CA Status fields assert 'status' in ca_cr assert 'certificateSigningRequest' in ca_cr['status'] diff --git a/test/e2e/tests/test_ca_activation.py b/test/e2e/tests/test_ca_activation.py index b228eaf..73abc42 100644 --- a/test/e2e/tests/test_ca_activation.py +++ b/test/e2e/tests/test_ca_activation.py @@ -80,7 +80,7 @@ def simple_certificate_authority(): k8s.create_custom_resource(ca_ref, ca_resource_data) ca_cr = k8s.wait_resource_consumed_by_controller(ca_ref) - time.sleep(CREATE_WAIT_AFTER_SECONDS) + time.sleep(30) assert ca_cr is not None assert k8s.get_resource_exists(ca_ref) @@ -89,7 +89,7 @@ def simple_certificate_authority(): ca_resource_arn = k8s.get_resource_arn(ca_cr) assert ca_resource_arn is not None - yield (ca_cr, ca_name) + yield (ca_cr,ca_ref, ca_name) #Delete CA k8s resource _, deleted = k8s.delete_custom_resource(ca_ref) @@ -144,7 +144,7 @@ def subordinate_certificate_authority(acmpca_client, simple_ca_activation): @pytest.fixture(scope="module") def simple_root_certificate(acmpca_client, create_secret, simple_certificate_authority): - (ca_cr, ca_name) = simple_certificate_authority + (ca_cr, ca_ref, ca_name) = simple_certificate_authority ca_arn = ca_cr['status']['ackResourceMetadata']['arn'] cert_name = random_suffix_name("certificate", 30) @@ -186,7 +186,7 @@ def simple_root_certificate(acmpca_client, create_secret, simple_certificate_aut resource_arn = k8s.get_resource_arn(cr) assert resource_arn is not None - yield (ca_name, ca_arn, secret, resource_arn) + yield (ca_cr, ca_ref, ca_name, ca_arn, secret, resource_arn) #Delete Certificate k8s resource _, deleted = k8s.delete_custom_resource(ref) @@ -243,7 +243,7 @@ def subordinate_ca_certificate(create_secret, subordinate_certificate_authority) @pytest.fixture(scope="module") def simple_ca_activation(simple_root_certificate, create_certificate_chain_secret, acmpca_client): - (ca_name, ca_arn, secret, cert_arn) = simple_root_certificate + (ca_cr, ca_ref, ca_name, ca_arn, secret, cert_arn) = simple_root_certificate certificate_chain_secret = create_certificate_chain_secret @@ -364,7 +364,7 @@ def subordinate_ca_activation(subordinate_ca_certificate, create_certificate_cha @pytest.fixture(scope="module") def simple_ca_activation_with_ref(simple_root_certificate, create_certificate_chain_secret, acmpca_client): - (ca_name, ca_arn, secret, cert_arn) = simple_root_certificate + (ca_cr, ca_ref, ca_name, ca_arn, secret, cert_arn) = simple_root_certificate certificate_chain_secret = create_certificate_chain_secret @@ -424,7 +424,7 @@ def simple_ca_activation_with_ref(simple_root_certificate, create_certificate_ch @pytest.fixture(scope="module") def simple_ca_activation_status_disabled(simple_root_certificate, create_certificate_chain_secret, acmpca_client): - (ca_name, ca_arn, secret, cert_arn) = simple_root_certificate + (ca_cr, ca_ref, ca_name, ca_arn, secret, cert_arn) = simple_root_certificate certificate_chain_secret = create_certificate_chain_secret @@ -471,9 +471,8 @@ def simple_ca_activation_status_disabled(simple_root_certificate, create_certifi class TestCertificateAuthorityActivation: def test_activation_crud(self, acmpca_client, simple_root_certificate, create_certificate_chain_secret): - time.sleep(30) - - (ca_name, ca_arn, secret, cert_arn) = simple_root_certificate + + (ca_cr, ca_ref, ca_name, ca_arn, secret, cert_arn) = simple_root_certificate activation_name = random_suffix_name("certificate-authority-activation", 50) certificate_chain_secret = create_certificate_chain_secret @@ -512,6 +511,12 @@ def test_activation_crud(self, acmpca_client, simple_root_certificate, create_ce acmpca_validator = ACMPCAValidator(acmpca_client) acmpca_validator.assert_certificate_authority(ca_arn, "ACTIVE") + ca_cr = k8s.patch_custom_resource(ca_ref, {}) + logging.info(ca_cr) + + assert 'status' in ca_cr['status'] + assert ca_cr['status']['status'] == "ACTIVE" + cert = acmpca_validator.get_certificate(ca_arn=ca_arn, cert_arn=cert_arn) # Check certificate chain is in secret @@ -570,7 +575,6 @@ def test_activation_crud(self, acmpca_client, simple_root_certificate, create_ce acmpca_validator.assert_certificate_authority(ca_arn, "DISABLED") def test_subordinate_ca_activation(self, acmpca_client, subordinate_ca_activation): - time.sleep(30) (sub_ca_arn, sub_ca_cert_arn, root_ca_arn, root_ca_cert_arn, complete_certificate_chain_secret, sub_ca_cert_secret) = subordinate_ca_activation @@ -600,7 +604,6 @@ def test_subordinate_ca_activation(self, acmpca_client, subordinate_ca_activatio assert base64.b64decode(api_response[complete_certificate_chain_secret.key]).decode("ascii") == complete_certificate_chain def test_disabled_activation(self, acmpca_client, simple_ca_activation_status_disabled): - time.sleep(10) (ca_arn, certificate_chain_secret, cert_arn) = simple_ca_activation_status_disabled diff --git a/test/e2e/tests/test_certificate.py b/test/e2e/tests/test_certificate.py index d90c0fc..eebfd08 100644 --- a/test/e2e/tests/test_certificate.py +++ b/test/e2e/tests/test_certificate.py @@ -30,7 +30,6 @@ RESOURCE_PLURAL = "certificates" -CREATE_CA_WAIT_AFTER_SECONDS = 60 CREATE_WAIT_AFTER_SECONDS = 10 UPDATE_WAIT_AFTER_SECONDS = 10 DELETE_WAIT_AFTER_SECONDS = 10 @@ -71,7 +70,7 @@ def simple_certificate_authority(): k8s.create_custom_resource(ca_ref, ca_resource_data) ca_cr = k8s.wait_resource_consumed_by_controller(ca_ref) - time.sleep(CREATE_CA_WAIT_AFTER_SECONDS) + time.sleep(30) assert ca_cr is not None assert k8s.get_resource_exists(ca_ref) @@ -187,6 +186,104 @@ def simple_root_certificate_with_ref(acmpca_client, create_secret, simple_certif _, deleted = k8s.delete_custom_resource(ref) assert deleted is True +@pytest.fixture(scope="module") +def simple_root_certificate_without_secret_key(acmpca_client, create_secret, simple_certificate_authority): + (ca_cr, ca_name) = simple_certificate_authority + ca_arn = ca_cr['status']['ackResourceMetadata']['arn'] + + cert_name = random_suffix_name("certificate", 30) + + secret = create_secret + + replacements = {} + replacements["NAME"] = cert_name + replacements["CA_NAME"] = ca_name + replacements["CERTIFICATE_SEC_NS"] = secret.ns + replacements["CERTIFICATE_SEC_NAME"] = secret.name + replacements["TEMPLATE_ARN"] = "arn:aws:acm-pca:::template/RootCACertificate/V1" + + # Load Certificate CR + resource_data = load_acmpca_resource( + "certificate_without_secret_key", + additional_replacements=replacements, + ) + + # Create k8s resource + ref = k8s.create_reference( + CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, + cert_name, namespace="default", + ) + k8s.create_custom_resource(ref, resource_data) + cr = k8s.wait_resource_consumed_by_controller(ref) + + time.sleep(CREATE_WAIT_AFTER_SECONDS) + + # Check CA status is PENDING_CERTIFICATE + acmpca_validator = ACMPCAValidator(acmpca_client) + acmpca_validator.assert_certificate_authority(ca_arn, "PENDING_CERTIFICATE") + + assert cr is not None + assert k8s.get_resource_exists(ref) + logging.info(cr) + + resource_arn = k8s.get_resource_arn(cr) + assert resource_arn is not None + + yield (ca_arn, resource_arn, secret) + + #Delete Certificate k8s resource + _, deleted = k8s.delete_custom_resource(ref) + assert deleted is True + +@pytest.fixture(scope="module") +def simple_root_certificate_without_secret_namespace(acmpca_client, create_secret, simple_certificate_authority): + (ca_cr, ca_name) = simple_certificate_authority + ca_arn = ca_cr['status']['ackResourceMetadata']['arn'] + + cert_name = random_suffix_name("certificate", 30) + + secret = create_secret + + replacements = {} + replacements["NAME"] = cert_name + replacements["CA_NAME"] = ca_name + replacements["CERTIFICATE_SEC_NAME"] = secret.name + replacements["CERTIFICATE_SEC_KEY"] = secret.key + replacements["TEMPLATE_ARN"] = "arn:aws:acm-pca:::template/RootCACertificate/V1" + + # Load Certificate CR + resource_data = load_acmpca_resource( + "certificate_without_secret_namespace", + additional_replacements=replacements, + ) + + # Create k8s resource + ref = k8s.create_reference( + CRD_GROUP, CRD_VERSION, RESOURCE_PLURAL, + cert_name, namespace="default", + ) + k8s.create_custom_resource(ref, resource_data) + cr = k8s.wait_resource_consumed_by_controller(ref) + + time.sleep(CREATE_WAIT_AFTER_SECONDS) + + # Check CA status is PENDING_CERTIFICATE + acmpca_validator = ACMPCAValidator(acmpca_client) + acmpca_validator.assert_certificate_authority(ca_arn, "PENDING_CERTIFICATE") + + assert cr is not None + assert k8s.get_resource_exists(ref) + logging.info(cr) + + resource_arn = k8s.get_resource_arn(cr) + assert resource_arn is not None + + yield (ca_arn, resource_arn, secret) + + #Delete Certificate k8s resource + _, deleted = k8s.delete_custom_resource(ref) + assert deleted is True + @service_marker class TestCertificate: @@ -218,5 +315,33 @@ def test_create_delete_with_ref(self, acmpca_client, simple_root_certificate_wit acmpca_validator = ACMPCAValidator(acmpca_client) cert = acmpca_validator.get_certificate(ca_arn=ca_arn, cert_arn=cert_arn) + assert 'certificate' in api_response + assert base64.b64decode(api_response['certificate']).decode("ascii") == cert + + def test_create_delete_without_secret_key(self, acmpca_client, simple_root_certificate_without_secret_key): + + (ca_arn, cert_arn, secret) = simple_root_certificate_without_secret_key + + # Check certificate is in secret + _api_client = _get_k8s_api_client() + api_response = client.CoreV1Api(_api_client).read_namespaced_secret(secret.name, secret.ns).data + + acmpca_validator = ACMPCAValidator(acmpca_client) + cert = acmpca_validator.get_certificate(ca_arn=ca_arn, cert_arn=cert_arn) + + assert 'certificate' in api_response + assert base64.b64decode(api_response['certificate']).decode("ascii") == cert + + def test_create_delete_without_secret_namespace(self, acmpca_client, simple_root_certificate_without_secret_namespace): + + (ca_arn, cert_arn, secret) = simple_root_certificate_without_secret_namespace + + # Check certificate is in secret + _api_client = _get_k8s_api_client() + api_response = client.CoreV1Api(_api_client).read_namespaced_secret(secret.name, secret.ns).data + + acmpca_validator = ACMPCAValidator(acmpca_client) + cert = acmpca_validator.get_certificate(ca_arn=ca_arn, cert_arn=cert_arn) + assert 'certificate' in api_response assert base64.b64decode(api_response['certificate']).decode("ascii") == cert \ No newline at end of file