diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index b12c48a..b79057c 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2023-04-04T18:27:42Z" - build_hash: a6ae2078e57187b2daf47978bc07bd67072d2cba + build_date: "2023-04-19T08:40:46Z" + build_hash: e69321ed2d9567bf2d439af970b7466086f0b4fa go_version: go1.19.4 - version: v0.25.0-1-ga6ae207 -api_directory_checksum: b14be96ab59442ec6c1665e0c1a14713da9e5f42 + version: v0.25.0-6-ge69321e +api_directory_checksum: 017e52b555b690a39a60e82a6f9574ab42b52973 api_version: v1alpha1 aws_sdk_go_version: v1.44.197 generator_config_info: - file_checksum: eb6753cd4cab8dea8412148ed883fca3928ec692 + file_checksum: 9da316c2b7ef89650333f38895f067642d5c5e34 original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index 8ef8ae6..5d72fcb 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -34,6 +34,7 @@ operations: operation_type: SET_ATTRIBUTES resources: Topic: + is_arn_primary_key: true update_operation: custom_method_name: customUpdate unpack_attributes_map: @@ -87,6 +88,7 @@ resources: compare: is_ignored: true PlatformApplication: + is_arn_primary_key: true unpack_attributes_map: set_attributes_single_attribute: false fields: @@ -133,6 +135,7 @@ resources: tags: ignore: true PlatformEndpoint: + is_arn_primary_key: true unpack_attributes_map: set_attributes_single_attribute: false fields: @@ -145,6 +148,7 @@ resources: tags: ignore: true Subscription: + is_arn_primary_key: true update_operation: custom_method_name: customUpdate unpack_attributes_map: diff --git a/generator.yaml b/generator.yaml index 8ef8ae6..5d72fcb 100644 --- a/generator.yaml +++ b/generator.yaml @@ -34,6 +34,7 @@ operations: operation_type: SET_ATTRIBUTES resources: Topic: + is_arn_primary_key: true update_operation: custom_method_name: customUpdate unpack_attributes_map: @@ -87,6 +88,7 @@ resources: compare: is_ignored: true PlatformApplication: + is_arn_primary_key: true unpack_attributes_map: set_attributes_single_attribute: false fields: @@ -133,6 +135,7 @@ resources: tags: ignore: true PlatformEndpoint: + is_arn_primary_key: true unpack_attributes_map: set_attributes_single_attribute: false fields: @@ -145,6 +148,7 @@ resources: tags: ignore: true Subscription: + is_arn_primary_key: true update_operation: custom_method_name: customUpdate unpack_attributes_map: diff --git a/go.mod b/go.mod index 4cca4dc..f1ff4c9 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/aws-controllers-k8s/iam-controller v1.1.1 github.com/aws-controllers-k8s/kms-controller v1.0.2 - github.com/aws-controllers-k8s/runtime v0.25.0 + github.com/aws-controllers-k8s/runtime v0.26.0 github.com/aws/aws-sdk-go v1.44.197 github.com/go-logr/logr v1.2.3 github.com/spf13/pflag v1.0.5 diff --git a/go.sum b/go.sum index ac478bb..56254bf 100644 --- a/go.sum +++ b/go.sum @@ -42,8 +42,8 @@ github.com/aws-controllers-k8s/iam-controller v1.1.1 h1:O6arh7DNlQF26MEKzgA2/kBE github.com/aws-controllers-k8s/iam-controller v1.1.1/go.mod h1:2+ARwRpazTq5MErjMz0MpXHhtAzRfNtY56Uj0gvu9vE= github.com/aws-controllers-k8s/kms-controller v1.0.2 h1:v8nh/oaX/U6spCwBDaWyem7XXpzoP/MnkJyEjNOZN9s= github.com/aws-controllers-k8s/kms-controller v1.0.2/go.mod h1:BeoijsyGjJ9G5VcDjpFdxBW0IxaeKXYX497XmUJiPSQ= -github.com/aws-controllers-k8s/runtime v0.25.0 h1:6SYa8qmbw+Yil5/LodF7LmIGxBhpjz4QEIvNjpeRuoc= -github.com/aws-controllers-k8s/runtime v0.25.0/go.mod h1:jizDzKikL09cueIuA9ZxoZ+4pfn5U7oKW5s/ZAqOA6E= +github.com/aws-controllers-k8s/runtime v0.26.0 h1:XKqygFzHSBtM74Ov9IroZbyCVeYei9Eskp4aKbJ2SFw= +github.com/aws-controllers-k8s/runtime v0.26.0/go.mod h1:jizDzKikL09cueIuA9ZxoZ+4pfn5U7oKW5s/ZAqOA6E= github.com/aws/aws-sdk-go v1.44.197 h1:pkg/NZsov9v/CawQWy+qWVzJMIZRQypCtYjUBXFomF8= github.com/aws/aws-sdk-go v1.44.197/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= diff --git a/pkg/resource/platform_application/references.go b/pkg/resource/platform_application/references.go index 26a0c35..b404363 100644 --- a/pkg/resource/platform_application/references.go +++ b/pkg/resource/platform_application/references.go @@ -25,7 +25,6 @@ import ( iamapitypes "github.com/aws-controllers-k8s/iam-controller/apis/v1alpha1" ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" - ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" @@ -38,97 +37,136 @@ import ( // +kubebuilder:rbac:groups=iam.services.k8s.aws,resources=roles,verbs=get;list // +kubebuilder:rbac:groups=iam.services.k8s.aws,resources=roles/status,verbs=get;list +// ClearResolvedReferences removes any reference values that were made +// concrete in the spec. It returns a copy of the input AWSResource which +// contains the original *Ref values, but none of their respective concrete +// values. +func (rm *resourceManager) ClearResolvedReferences(res acktypes.AWSResource) acktypes.AWSResource { + ko := rm.concreteResource(res).ko.DeepCopy() + + if ko.Spec.EventEndpointCreatedRef != nil { + ko.Spec.EventEndpointCreated = nil + } + + if ko.Spec.EventEndpointDeletedRef != nil { + ko.Spec.EventEndpointDeleted = nil + } + + if ko.Spec.EventEndpointUpdatedRef != nil { + ko.Spec.EventEndpointUpdated = nil + } + + if ko.Spec.FailureFeedbackRoleRef != nil { + ko.Spec.FailureFeedbackRoleARN = nil + } + + if ko.Spec.SuccessFeedbackRoleRef != nil { + ko.Spec.SuccessFeedbackRoleARN = nil + } + + return &resource{ko} +} + // ResolveReferences finds if there are any Reference field(s) present -// inside AWSResource passed in the parameter and attempts to resolve -// those reference field(s) into target field(s). -// It returns an AWSResource with resolved reference(s), and an error if the -// passed AWSResource's reference field(s) cannot be resolved. -// This method also adds/updates the ConditionTypeReferencesResolved for the -// AWSResource. +// inside AWSResource passed in the parameter and attempts to resolve those +// reference field(s) into their respective target field(s). It returns a +// copy of the input AWSResource with resolved reference(s), a boolean which +// is set to true if the resource contains any references (regardless of if +// they are resolved successfully) and an error if the passed AWSResource's +// reference field(s) could not be resolved. func (rm *resourceManager) ResolveReferences( ctx context.Context, apiReader client.Reader, res acktypes.AWSResource, -) (acktypes.AWSResource, error) { +) (acktypes.AWSResource, bool, error) { namespace := res.MetaObject().GetNamespace() - ko := rm.concreteResource(res).ko.DeepCopy() + ko := rm.concreteResource(res).ko + + resourceHasReferences := false err := validateReferenceFields(ko) - if err == nil { - err = resolveReferenceForEventEndpointCreated(ctx, apiReader, namespace, ko) - } - if err == nil { - err = resolveReferenceForEventEndpointDeleted(ctx, apiReader, namespace, ko) - } - if err == nil { - err = resolveReferenceForEventEndpointUpdated(ctx, apiReader, namespace, ko) + if fieldHasReferences, err := rm.resolveReferenceForEventEndpointCreated(ctx, apiReader, namespace, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences } - if err == nil { - err = resolveReferenceForFailureFeedbackRoleARN(ctx, apiReader, namespace, ko) + + if fieldHasReferences, err := rm.resolveReferenceForEventEndpointDeleted(ctx, apiReader, namespace, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences } - if err == nil { - err = resolveReferenceForSuccessFeedbackRoleARN(ctx, apiReader, namespace, ko) + + if fieldHasReferences, err := rm.resolveReferenceForEventEndpointUpdated(ctx, apiReader, namespace, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences } - // If there was an error while resolving any reference, reset all the - // resolved values so that they do not get persisted inside etcd - if err != nil { - ko = rm.concreteResource(res).ko.DeepCopy() + if fieldHasReferences, err := rm.resolveReferenceForFailureFeedbackRoleARN(ctx, apiReader, namespace, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences } - if hasNonNilReferences(ko) { - return ackcondition.WithReferencesResolvedCondition(&resource{ko}, err) + + if fieldHasReferences, err := rm.resolveReferenceForSuccessFeedbackRoleARN(ctx, apiReader, namespace, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences } - return &resource{ko}, err + + return &resource{ko}, resourceHasReferences, err } // validateReferenceFields validates the reference field and corresponding // identifier field. func validateReferenceFields(ko *svcapitypes.PlatformApplication) error { + if ko.Spec.EventEndpointCreatedRef != nil && ko.Spec.EventEndpointCreated != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("EventEndpointCreated", "EventEndpointCreatedRef") } + if ko.Spec.EventEndpointDeletedRef != nil && ko.Spec.EventEndpointDeleted != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("EventEndpointDeleted", "EventEndpointDeletedRef") } + if ko.Spec.EventEndpointUpdatedRef != nil && ko.Spec.EventEndpointUpdated != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("EventEndpointUpdated", "EventEndpointUpdatedRef") } + if ko.Spec.FailureFeedbackRoleRef != nil && ko.Spec.FailureFeedbackRoleARN != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("FailureFeedbackRoleARN", "FailureFeedbackRoleRef") } + if ko.Spec.SuccessFeedbackRoleRef != nil && ko.Spec.SuccessFeedbackRoleARN != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("SuccessFeedbackRoleARN", "SuccessFeedbackRoleRef") } return nil } -// hasNonNilReferences returns true if resource contains a reference to another -// resource -func hasNonNilReferences(ko *svcapitypes.PlatformApplication) bool { - return false || (ko.Spec.EventEndpointCreatedRef != nil) || (ko.Spec.EventEndpointDeletedRef != nil) || (ko.Spec.EventEndpointUpdatedRef != nil) || (ko.Spec.FailureFeedbackRoleRef != nil) || (ko.Spec.SuccessFeedbackRoleRef != nil) -} - // resolveReferenceForEventEndpointCreated reads the resource referenced // from EventEndpointCreatedRef field and sets the EventEndpointCreated -// from referenced resource -func resolveReferenceForEventEndpointCreated( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForEventEndpointCreated( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.PlatformApplication, -) error { +) (hasReferences bool, err error) { if ko.Spec.EventEndpointCreatedRef != nil && ko.Spec.EventEndpointCreatedRef.From != nil { + hasReferences = true arr := ko.Spec.EventEndpointCreatedRef.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: EventEndpointCreatedRef") + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: EventEndpointCreatedRef") } obj := &svcapitypes.Topic{} if err := getReferencedResourceState_Topic(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err } ko.Spec.EventEndpointCreated = (*string)(obj.Status.ACKResourceMetadata.ARN) } - return nil + return hasReferences, nil } // getReferencedResourceState_Topic looks up whether a referenced resource @@ -184,74 +222,80 @@ func getReferencedResourceState_Topic( // resolveReferenceForEventEndpointDeleted reads the resource referenced // from EventEndpointDeletedRef field and sets the EventEndpointDeleted -// from referenced resource -func resolveReferenceForEventEndpointDeleted( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForEventEndpointDeleted( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.PlatformApplication, -) error { +) (hasReferences bool, err error) { if ko.Spec.EventEndpointDeletedRef != nil && ko.Spec.EventEndpointDeletedRef.From != nil { + hasReferences = true arr := ko.Spec.EventEndpointDeletedRef.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: EventEndpointDeletedRef") + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: EventEndpointDeletedRef") } obj := &svcapitypes.Topic{} if err := getReferencedResourceState_Topic(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err } ko.Spec.EventEndpointDeleted = (*string)(obj.Status.ACKResourceMetadata.ARN) } - return nil + return hasReferences, nil } // resolveReferenceForEventEndpointUpdated reads the resource referenced // from EventEndpointUpdatedRef field and sets the EventEndpointUpdated -// from referenced resource -func resolveReferenceForEventEndpointUpdated( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForEventEndpointUpdated( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.PlatformApplication, -) error { +) (hasReferences bool, err error) { if ko.Spec.EventEndpointUpdatedRef != nil && ko.Spec.EventEndpointUpdatedRef.From != nil { + hasReferences = true arr := ko.Spec.EventEndpointUpdatedRef.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: EventEndpointUpdatedRef") + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: EventEndpointUpdatedRef") } obj := &svcapitypes.Topic{} if err := getReferencedResourceState_Topic(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err } ko.Spec.EventEndpointUpdated = (*string)(obj.Status.ACKResourceMetadata.ARN) } - return nil + return hasReferences, nil } // resolveReferenceForFailureFeedbackRoleARN reads the resource referenced // from FailureFeedbackRoleRef field and sets the FailureFeedbackRoleARN -// from referenced resource -func resolveReferenceForFailureFeedbackRoleARN( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForFailureFeedbackRoleARN( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.PlatformApplication, -) error { +) (hasReferences bool, err error) { if ko.Spec.FailureFeedbackRoleRef != nil && ko.Spec.FailureFeedbackRoleRef.From != nil { + hasReferences = true arr := ko.Spec.FailureFeedbackRoleRef.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: FailureFeedbackRoleRef") + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: FailureFeedbackRoleRef") } obj := &iamapitypes.Role{} if err := getReferencedResourceState_Role(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err } ko.Spec.FailureFeedbackRoleARN = (*string)(obj.Status.ACKResourceMetadata.ARN) } - return nil + return hasReferences, nil } // getReferencedResourceState_Role looks up whether a referenced resource @@ -307,24 +351,26 @@ func getReferencedResourceState_Role( // resolveReferenceForSuccessFeedbackRoleARN reads the resource referenced // from SuccessFeedbackRoleRef field and sets the SuccessFeedbackRoleARN -// from referenced resource -func resolveReferenceForSuccessFeedbackRoleARN( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForSuccessFeedbackRoleARN( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.PlatformApplication, -) error { +) (hasReferences bool, err error) { if ko.Spec.SuccessFeedbackRoleRef != nil && ko.Spec.SuccessFeedbackRoleRef.From != nil { + hasReferences = true arr := ko.Spec.SuccessFeedbackRoleRef.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: SuccessFeedbackRoleRef") + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: SuccessFeedbackRoleRef") } obj := &iamapitypes.Role{} if err := getReferencedResourceState_Role(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err } ko.Spec.SuccessFeedbackRoleARN = (*string)(obj.Status.ACKResourceMetadata.ARN) } - return nil + return hasReferences, nil } diff --git a/pkg/resource/platform_application/resource.go b/pkg/resource/platform_application/resource.go index 67c13fd..9937c70 100644 --- a/pkg/resource/platform_application/resource.go +++ b/pkg/resource/platform_application/resource.go @@ -85,6 +85,11 @@ func (r *resource) SetStatus(desired acktypes.AWSResource) { // SetIdentifiers sets the Spec or Status field that is referenced as the unique // resource identifier func (r *resource) SetIdentifiers(identifier *ackv1alpha1.AWSIdentifiers) error { + if r.ko.Status.ACKResourceMetadata == nil { + r.ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + r.ko.Status.ACKResourceMetadata.ARN = identifier.ARN + return nil } diff --git a/pkg/resource/platform_endpoint/references.go b/pkg/resource/platform_endpoint/references.go index 4c80b50..69ff3a0 100644 --- a/pkg/resource/platform_endpoint/references.go +++ b/pkg/resource/platform_endpoint/references.go @@ -24,19 +24,29 @@ import ( svcapitypes "github.com/aws-controllers-k8s/sns-controller/apis/v1alpha1" ) +// ClearResolvedReferences removes any reference values that were made +// concrete in the spec. It returns a copy of the input AWSResource which +// contains the original *Ref values, but none of their respective concrete +// values. +func (rm *resourceManager) ClearResolvedReferences(res acktypes.AWSResource) acktypes.AWSResource { + ko := rm.concreteResource(res).ko.DeepCopy() + + return &resource{ko} +} + // ResolveReferences finds if there are any Reference field(s) present -// inside AWSResource passed in the parameter and attempts to resolve -// those reference field(s) into target field(s). -// It returns an AWSResource with resolved reference(s), and an error if the -// passed AWSResource's reference field(s) cannot be resolved. -// This method also adds/updates the ConditionTypeReferencesResolved for the -// AWSResource. +// inside AWSResource passed in the parameter and attempts to resolve those +// reference field(s) into their respective target field(s). It returns a +// copy of the input AWSResource with resolved reference(s), a boolean which +// is set to true if the resource contains any references (regardless of if +// they are resolved successfully) and an error if the passed AWSResource's +// reference field(s) could not be resolved. func (rm *resourceManager) ResolveReferences( ctx context.Context, apiReader client.Reader, res acktypes.AWSResource, -) (acktypes.AWSResource, error) { - return res, nil +) (acktypes.AWSResource, bool, error) { + return res, false, nil } // validateReferenceFields validates the reference field and corresponding @@ -44,9 +54,3 @@ func (rm *resourceManager) ResolveReferences( func validateReferenceFields(ko *svcapitypes.PlatformEndpoint) error { return nil } - -// hasNonNilReferences returns true if resource contains a reference to another -// resource -func hasNonNilReferences(ko *svcapitypes.PlatformEndpoint) bool { - return false -} diff --git a/pkg/resource/platform_endpoint/resource.go b/pkg/resource/platform_endpoint/resource.go index 49cec42..8211a9c 100644 --- a/pkg/resource/platform_endpoint/resource.go +++ b/pkg/resource/platform_endpoint/resource.go @@ -85,6 +85,11 @@ func (r *resource) SetStatus(desired acktypes.AWSResource) { // SetIdentifiers sets the Spec or Status field that is referenced as the unique // resource identifier func (r *resource) SetIdentifiers(identifier *ackv1alpha1.AWSIdentifiers) error { + if r.ko.Status.ACKResourceMetadata == nil { + r.ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + r.ko.Status.ACKResourceMetadata.ARN = identifier.ARN + return nil } diff --git a/pkg/resource/subscription/references.go b/pkg/resource/subscription/references.go index fdb7f41..6737eaa 100644 --- a/pkg/resource/subscription/references.go +++ b/pkg/resource/subscription/references.go @@ -24,46 +24,56 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" - ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" svcapitypes "github.com/aws-controllers-k8s/sns-controller/apis/v1alpha1" ) +// ClearResolvedReferences removes any reference values that were made +// concrete in the spec. It returns a copy of the input AWSResource which +// contains the original *Ref values, but none of their respective concrete +// values. +func (rm *resourceManager) ClearResolvedReferences(res acktypes.AWSResource) acktypes.AWSResource { + ko := rm.concreteResource(res).ko.DeepCopy() + + if ko.Spec.TopicRef != nil { + ko.Spec.TopicARN = nil + } + + return &resource{ko} +} + // ResolveReferences finds if there are any Reference field(s) present -// inside AWSResource passed in the parameter and attempts to resolve -// those reference field(s) into target field(s). -// It returns an AWSResource with resolved reference(s), and an error if the -// passed AWSResource's reference field(s) cannot be resolved. -// This method also adds/updates the ConditionTypeReferencesResolved for the -// AWSResource. +// inside AWSResource passed in the parameter and attempts to resolve those +// reference field(s) into their respective target field(s). It returns a +// copy of the input AWSResource with resolved reference(s), a boolean which +// is set to true if the resource contains any references (regardless of if +// they are resolved successfully) and an error if the passed AWSResource's +// reference field(s) could not be resolved. func (rm *resourceManager) ResolveReferences( ctx context.Context, apiReader client.Reader, res acktypes.AWSResource, -) (acktypes.AWSResource, error) { +) (acktypes.AWSResource, bool, error) { namespace := res.MetaObject().GetNamespace() - ko := rm.concreteResource(res).ko.DeepCopy() + ko := rm.concreteResource(res).ko + + resourceHasReferences := false err := validateReferenceFields(ko) - if err == nil { - err = resolveReferenceForTopicARN(ctx, apiReader, namespace, ko) + if fieldHasReferences, err := rm.resolveReferenceForTopicARN(ctx, apiReader, namespace, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences } - // If there was an error while resolving any reference, reset all the - // resolved values so that they do not get persisted inside etcd - if err != nil { - ko = rm.concreteResource(res).ko.DeepCopy() - } - if hasNonNilReferences(ko) { - return ackcondition.WithReferencesResolvedCondition(&resource{ko}, err) - } - return &resource{ko}, err + return &resource{ko}, resourceHasReferences, err } // validateReferenceFields validates the reference field and corresponding // identifier field. func validateReferenceFields(ko *svcapitypes.Subscription) error { + if ko.Spec.TopicRef != nil && ko.Spec.TopicARN != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("TopicARN", "TopicRef") } @@ -73,34 +83,30 @@ func validateReferenceFields(ko *svcapitypes.Subscription) error { return nil } -// hasNonNilReferences returns true if resource contains a reference to another -// resource -func hasNonNilReferences(ko *svcapitypes.Subscription) bool { - return false || (ko.Spec.TopicRef != nil) -} - // resolveReferenceForTopicARN reads the resource referenced // from TopicRef field and sets the TopicARN -// from referenced resource -func resolveReferenceForTopicARN( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForTopicARN( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.Subscription, -) error { +) (hasReferences bool, err error) { if ko.Spec.TopicRef != nil && ko.Spec.TopicRef.From != nil { + hasReferences = true arr := ko.Spec.TopicRef.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: TopicRef") + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: TopicRef") } obj := &svcapitypes.Topic{} if err := getReferencedResourceState_Topic(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err } ko.Spec.TopicARN = (*string)(obj.Status.ACKResourceMetadata.ARN) } - return nil + return hasReferences, nil } // getReferencedResourceState_Topic looks up whether a referenced resource diff --git a/pkg/resource/subscription/resource.go b/pkg/resource/subscription/resource.go index 0d64590..f562486 100644 --- a/pkg/resource/subscription/resource.go +++ b/pkg/resource/subscription/resource.go @@ -85,6 +85,11 @@ func (r *resource) SetStatus(desired acktypes.AWSResource) { // SetIdentifiers sets the Spec or Status field that is referenced as the unique // resource identifier func (r *resource) SetIdentifiers(identifier *ackv1alpha1.AWSIdentifiers) error { + if r.ko.Status.ACKResourceMetadata == nil { + r.ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + r.ko.Status.ACKResourceMetadata.ARN = identifier.ARN + return nil } diff --git a/pkg/resource/topic/references.go b/pkg/resource/topic/references.go index 1a4ae90..0041435 100644 --- a/pkg/resource/topic/references.go +++ b/pkg/resource/topic/references.go @@ -26,7 +26,6 @@ import ( iamapitypes "github.com/aws-controllers-k8s/iam-controller/apis/v1alpha1" kmsapitypes "github.com/aws-controllers-k8s/kms-controller/apis/v1alpha1" ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" - ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" @@ -39,79 +38,94 @@ import ( // +kubebuilder:rbac:groups=iam.services.k8s.aws,resources=policies,verbs=get;list // +kubebuilder:rbac:groups=iam.services.k8s.aws,resources=policies/status,verbs=get;list +// ClearResolvedReferences removes any reference values that were made +// concrete in the spec. It returns a copy of the input AWSResource which +// contains the original *Ref values, but none of their respective concrete +// values. +func (rm *resourceManager) ClearResolvedReferences(res acktypes.AWSResource) acktypes.AWSResource { + ko := rm.concreteResource(res).ko.DeepCopy() + + if ko.Spec.KMSMasterKeyRef != nil { + ko.Spec.KMSMasterKeyID = nil + } + + if ko.Spec.PolicyRef != nil { + ko.Spec.Policy = nil + } + + return &resource{ko} +} + // ResolveReferences finds if there are any Reference field(s) present -// inside AWSResource passed in the parameter and attempts to resolve -// those reference field(s) into target field(s). -// It returns an AWSResource with resolved reference(s), and an error if the -// passed AWSResource's reference field(s) cannot be resolved. -// This method also adds/updates the ConditionTypeReferencesResolved for the -// AWSResource. +// inside AWSResource passed in the parameter and attempts to resolve those +// reference field(s) into their respective target field(s). It returns a +// copy of the input AWSResource with resolved reference(s), a boolean which +// is set to true if the resource contains any references (regardless of if +// they are resolved successfully) and an error if the passed AWSResource's +// reference field(s) could not be resolved. func (rm *resourceManager) ResolveReferences( ctx context.Context, apiReader client.Reader, res acktypes.AWSResource, -) (acktypes.AWSResource, error) { +) (acktypes.AWSResource, bool, error) { namespace := res.MetaObject().GetNamespace() - ko := rm.concreteResource(res).ko.DeepCopy() + ko := rm.concreteResource(res).ko + + resourceHasReferences := false err := validateReferenceFields(ko) - if err == nil { - err = resolveReferenceForKMSMasterKeyID(ctx, apiReader, namespace, ko) - } - if err == nil { - err = resolveReferenceForPolicy(ctx, apiReader, namespace, ko) + if fieldHasReferences, err := rm.resolveReferenceForKMSMasterKeyID(ctx, apiReader, namespace, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences } - // If there was an error while resolving any reference, reset all the - // resolved values so that they do not get persisted inside etcd - if err != nil { - ko = rm.concreteResource(res).ko.DeepCopy() - } - if hasNonNilReferences(ko) { - return ackcondition.WithReferencesResolvedCondition(&resource{ko}, err) + if fieldHasReferences, err := rm.resolveReferenceForPolicy(ctx, apiReader, namespace, ko); err != nil { + return &resource{ko}, (resourceHasReferences || fieldHasReferences), err + } else { + resourceHasReferences = resourceHasReferences || fieldHasReferences } - return &resource{ko}, err + + return &resource{ko}, resourceHasReferences, err } // validateReferenceFields validates the reference field and corresponding // identifier field. func validateReferenceFields(ko *svcapitypes.Topic) error { + if ko.Spec.KMSMasterKeyRef != nil && ko.Spec.KMSMasterKeyID != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("KMSMasterKeyID", "KMSMasterKeyRef") } + if ko.Spec.PolicyRef != nil && ko.Spec.Policy != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("Policy", "PolicyRef") } return nil } -// hasNonNilReferences returns true if resource contains a reference to another -// resource -func hasNonNilReferences(ko *svcapitypes.Topic) bool { - return false || (ko.Spec.KMSMasterKeyRef != nil) || (ko.Spec.PolicyRef != nil) -} - // resolveReferenceForKMSMasterKeyID reads the resource referenced // from KMSMasterKeyRef field and sets the KMSMasterKeyID -// from referenced resource -func resolveReferenceForKMSMasterKeyID( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForKMSMasterKeyID( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.Topic, -) error { +) (hasReferences bool, err error) { if ko.Spec.KMSMasterKeyRef != nil && ko.Spec.KMSMasterKeyRef.From != nil { + hasReferences = true arr := ko.Spec.KMSMasterKeyRef.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: KMSMasterKeyRef") + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: KMSMasterKeyRef") } obj := &kmsapitypes.Key{} if err := getReferencedResourceState_Key(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err } ko.Spec.KMSMasterKeyID = (*string)(obj.Status.KeyID) } - return nil + return hasReferences, nil } // getReferencedResourceState_Key looks up whether a referenced resource @@ -167,26 +181,28 @@ func getReferencedResourceState_Key( // resolveReferenceForPolicy reads the resource referenced // from PolicyRef field and sets the Policy -// from referenced resource -func resolveReferenceForPolicy( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForPolicy( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.Topic, -) error { +) (hasReferences bool, err error) { if ko.Spec.PolicyRef != nil && ko.Spec.PolicyRef.From != nil { + hasReferences = true arr := ko.Spec.PolicyRef.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: PolicyRef") + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: PolicyRef") } obj := &iamapitypes.Policy{} if err := getReferencedResourceState_Policy(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err } ko.Spec.Policy = (*string)(obj.Spec.PolicyDocument) } - return nil + return hasReferences, nil } // getReferencedResourceState_Policy looks up whether a referenced resource diff --git a/pkg/resource/topic/resource.go b/pkg/resource/topic/resource.go index 171abed..a5e316f 100644 --- a/pkg/resource/topic/resource.go +++ b/pkg/resource/topic/resource.go @@ -85,6 +85,11 @@ func (r *resource) SetStatus(desired acktypes.AWSResource) { // SetIdentifiers sets the Spec or Status field that is referenced as the unique // resource identifier func (r *resource) SetIdentifiers(identifier *ackv1alpha1.AWSIdentifiers) error { + if r.ko.Status.ACKResourceMetadata == nil { + r.ko.Status.ACKResourceMetadata = &ackv1alpha1.ResourceMetadata{} + } + r.ko.Status.ACKResourceMetadata.ARN = identifier.ARN + return nil }