From 84457a12a8f22ff1cb5a01d68ae3b80e7728b71d Mon Sep 17 00:00:00 2001 From: AWS Controllers for Kubernetes Bot <82905295+ack-bot@users.noreply.github.com> Date: Tue, 2 May 2023 07:42:53 -0600 Subject: [PATCH] Update to ACK runtime `v0.26.0`, code-generator `v0.26.0` (#77) ### Update to ACK runtime `v0.26.0`, code-generator `v0.26.0` ---------- * ACK code-generator `v0.26.0` [release notes](https://github.com/aws-controllers-k8s/code-generator/releases/tag/v0.26.0) * ACK runtime `v0.26.0` [release notes](https://github.com/aws-controllers-k8s/runtime/releases/tag/v0.26.0) ---------- NOTE: This PR increments the release version of service controller from `v1.2.0` to `v1.2.1` Once this PR is merged, release `v1.2.1` will be automatically created for `iam-controller` **Please close this PR, if you do not want the new patch release for `iam-controller`** ---------- #### stdout for `make build-controller`: ``` building ack-generate ... ok. ==== building iam-controller ==== Copying common custom resource definitions into iam Building Kubernetes API objects for iam Generating deepcopy code for iam Generating custom resource definitions for iam Building service controller for iam Generating RBAC manifests for iam Running gofmt against generated code for iam Updating additional GitHub repository maintenance files ==== building iam-controller release artifacts ==== Building release artifacts for iam-v1.2.1 Generating common custom resource definitions Generating custom resource definitions for iam Generating RBAC manifests for iam ``` ---------- 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 | 8 +- cmd/controller/main.go | 9 +- config/controller/kustomization.yaml | 2 +- go.mod | 2 +- go.sum | 4 +- helm/Chart.yaml | 4 +- helm/templates/NOTES.txt | 2 +- helm/values.yaml | 2 +- pkg/resource/group/references.go | 87 +++++++------ .../open_id_connect_provider/references.go | 32 ++--- pkg/resource/policy/references.go | 32 ++--- pkg/resource/role/references.go | 115 ++++++++++-------- pkg/resource/user/references.go | 115 ++++++++++-------- 13 files changed, 235 insertions(+), 179 deletions(-) diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index babd77e..69b424a 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,8 +1,8 @@ ack_generate_info: - build_date: "2023-04-03T14:40:28Z" - build_hash: a6ae2078e57187b2daf47978bc07bd67072d2cba - go_version: go1.19.6 - version: v0.25.0-1-ga6ae207 + build_date: "2023-05-01T22:15:40Z" + build_hash: 6657565bb742e5cd4cd340d01d5e4786b5fbabc0 + go_version: go1.19 + version: v0.26.0 api_directory_checksum: 26341f700d12dfcd4033cf4203492fa381daa7b0 api_version: v1alpha1 aws_sdk_go_version: v1.44.93 diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 5212976..f8cfc53 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -27,6 +27,7 @@ import ( svcsdk "github.com/aws/aws-sdk-go/service/iam" flag "github.com/spf13/pflag" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrlrt "sigs.k8s.io/controller-runtime" ctrlrtmetrics "sigs.k8s.io/controller-runtime/pkg/metrics" @@ -64,7 +65,13 @@ func main() { flag.Parse() ackCfg.SetupLogger() - if err := ackCfg.Validate(); err != nil { + managerFactories := svcresource.GetManagerFactories() + resourceGVKs := make([]schema.GroupVersionKind, 0, len(managerFactories)) + for _, mf := range managerFactories { + resourceGVKs = append(resourceGVKs, mf.ResourceDescriptor().GroupVersionKind()) + } + + if err := ackCfg.Validate(ackcfg.WithGVKs(resourceGVKs)); err != nil { setupLog.Error( err, "Unable to create controller manager", "aws.service", awsServiceAlias, diff --git a/config/controller/kustomization.yaml b/config/controller/kustomization.yaml index 0701ce2..2bad4e1 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/iam-controller - newTag: v1.2.0 + newTag: v1.2.1 diff --git a/go.mod b/go.mod index 7b752e8..8f7fdd3 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/aws-controllers-k8s/iam-controller go 1.19 require ( - 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.93 github.com/go-logr/logr v1.2.3 github.com/samber/lo v1.37.0 diff --git a/go.sum b/go.sum index fca2277..221d1d2 100644 --- a/go.sum +++ b/go.sum @@ -38,8 +38,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -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.93 h1:hAgd9fuaptBatSft27/5eBMdcA8+cIMqo96/tZ6rKl8= github.com/aws/aws-sdk-go v1.44.93/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 02294a4..7380cd0 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -1,8 +1,8 @@ apiVersion: v1 name: iam-chart description: A Helm chart for the ACK service controller for AWS Identity & Access Management (IAM) -version: v1.2.0 -appVersion: v1.2.0 +version: v1.2.1 +appVersion: v1.2.1 home: https://github.com/aws-controllers-k8s/iam-controller icon: https://raw.githubusercontent.com/aws/eks-charts/master/docs/logo/aws.png sources: diff --git a/helm/templates/NOTES.txt b/helm/templates/NOTES.txt index 255469f..e33bba8 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/iam-controller:v1.2.0". +This chart deploys "public.ecr.aws/aws-controllers-k8s/iam-controller:v1.2.1". 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 9180ac6..adad701 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -4,7 +4,7 @@ image: repository: public.ecr.aws/aws-controllers-k8s/iam-controller - tag: v1.2.0 + tag: v1.2.1 pullPolicy: IfNotPresent pullSecrets: [] diff --git a/pkg/resource/group/references.go b/pkg/resource/group/references.go index 8fce2c6..2ad22ee 100644 --- a/pkg/resource/group/references.go +++ b/pkg/resource/group/references.go @@ -24,84 +24,91 @@ 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/iam-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 len(ko.Spec.PolicyRefs) > 0 { + ko.Spec.Policies = 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 = resolveReferenceForPolicies(ctx, apiReader, namespace, ko) + if fieldHasReferences, err := rm.resolveReferenceForPolicies(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.Group) error { - if ko.Spec.PolicyRefs != nil && ko.Spec.Policies != nil { + + if len(ko.Spec.PolicyRefs) > 0 && len(ko.Spec.Policies) > 0 { return ackerr.ResourceReferenceAndIDNotSupportedFor("Policies", "PolicyRefs") } return nil } -// hasNonNilReferences returns true if resource contains a reference to another -// resource -func hasNonNilReferences(ko *svcapitypes.Group) bool { - return false || (ko.Spec.PolicyRefs != nil) -} - // resolveReferenceForPolicies reads the resource referenced // from PolicyRefs field and sets the Policies -// from referenced resource -func resolveReferenceForPolicies( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForPolicies( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.Group, -) error { - if len(ko.Spec.PolicyRefs) > 0 { - resolved0 := []*string{} - for _, iter0 := range ko.Spec.PolicyRefs { - arr := iter0.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: PolicyRefs") +) (hasReferences bool, err error) { + for _, f0iter := range ko.Spec.PolicyRefs { + if f0iter != nil && f0iter.From != nil { + hasReferences = true + arr := f0iter.From + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: PolicyRefs") } obj := &svcapitypes.Policy{} if err := getReferencedResourceState_Policy(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err } - resolved0 = append(resolved0, (*string)(obj.Status.ACKResourceMetadata.ARN)) + if ko.Spec.Policies == nil { + ko.Spec.Policies = make([]*string, 0, 1) + } + ko.Spec.Policies = append(ko.Spec.Policies, (*string)(obj.Status.ACKResourceMetadata.ARN)) } - ko.Spec.Policies = resolved0 } - return nil + return hasReferences, nil } // getReferencedResourceState_Policy looks up whether a referenced resource diff --git a/pkg/resource/open_id_connect_provider/references.go b/pkg/resource/open_id_connect_provider/references.go index b6e0e32..3f957c9 100644 --- a/pkg/resource/open_id_connect_provider/references.go +++ b/pkg/resource/open_id_connect_provider/references.go @@ -24,19 +24,29 @@ import ( svcapitypes "github.com/aws-controllers-k8s/iam-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.OpenIDConnectProvider) error { return nil } - -// hasNonNilReferences returns true if resource contains a reference to another -// resource -func hasNonNilReferences(ko *svcapitypes.OpenIDConnectProvider) bool { - return false -} diff --git a/pkg/resource/policy/references.go b/pkg/resource/policy/references.go index 1a53a4e..6ecb0dc 100644 --- a/pkg/resource/policy/references.go +++ b/pkg/resource/policy/references.go @@ -24,19 +24,29 @@ import ( svcapitypes "github.com/aws-controllers-k8s/iam-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.Policy) error { return nil } - -// hasNonNilReferences returns true if resource contains a reference to another -// resource -func hasNonNilReferences(ko *svcapitypes.Policy) bool { - return false -} diff --git a/pkg/resource/role/references.go b/pkg/resource/role/references.go index 60691fa..48a270c 100644 --- a/pkg/resource/role/references.go +++ b/pkg/resource/role/references.go @@ -24,86 +24,100 @@ 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/iam-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.PermissionsBoundaryRef != nil { + ko.Spec.PermissionsBoundary = nil + } + + if len(ko.Spec.PolicyRefs) > 0 { + ko.Spec.Policies = 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 = resolveReferenceForPermissionsBoundary(ctx, apiReader, namespace, ko) - } - if err == nil { - err = resolveReferenceForPolicies(ctx, apiReader, namespace, ko) + if fieldHasReferences, err := rm.resolveReferenceForPermissionsBoundary(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.resolveReferenceForPolicies(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) - } - return &resource{ko}, err + + return &resource{ko}, resourceHasReferences, err } // validateReferenceFields validates the reference field and corresponding // identifier field. func validateReferenceFields(ko *svcapitypes.Role) error { + if ko.Spec.PermissionsBoundaryRef != nil && ko.Spec.PermissionsBoundary != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("PermissionsBoundary", "PermissionsBoundaryRef") } - if ko.Spec.PolicyRefs != nil && ko.Spec.Policies != nil { + + if len(ko.Spec.PolicyRefs) > 0 && len(ko.Spec.Policies) > 0 { return ackerr.ResourceReferenceAndIDNotSupportedFor("Policies", "PolicyRefs") } return nil } -// hasNonNilReferences returns true if resource contains a reference to another -// resource -func hasNonNilReferences(ko *svcapitypes.Role) bool { - return false || (ko.Spec.PermissionsBoundaryRef != nil) || (ko.Spec.PolicyRefs != nil) -} - // resolveReferenceForPermissionsBoundary reads the resource referenced // from PermissionsBoundaryRef field and sets the PermissionsBoundary -// from referenced resource -func resolveReferenceForPermissionsBoundary( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForPermissionsBoundary( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.Role, -) error { +) (hasReferences bool, err error) { if ko.Spec.PermissionsBoundaryRef != nil && ko.Spec.PermissionsBoundaryRef.From != nil { + hasReferences = true arr := ko.Spec.PermissionsBoundaryRef.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: PermissionsBoundaryRef") + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: PermissionsBoundaryRef") } obj := &svcapitypes.Policy{} if err := getReferencedResourceState_Policy(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err } ko.Spec.PermissionsBoundary = (*string)(obj.Status.ACKResourceMetadata.ARN) } - return nil + return hasReferences, nil } // getReferencedResourceState_Policy looks up whether a referenced resource @@ -159,28 +173,31 @@ func getReferencedResourceState_Policy( // resolveReferenceForPolicies reads the resource referenced // from PolicyRefs field and sets the Policies -// from referenced resource -func resolveReferenceForPolicies( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForPolicies( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.Role, -) error { - if len(ko.Spec.PolicyRefs) > 0 { - resolved0 := []*string{} - for _, iter0 := range ko.Spec.PolicyRefs { - arr := iter0.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: PolicyRefs") +) (hasReferences bool, err error) { + for _, f0iter := range ko.Spec.PolicyRefs { + if f0iter != nil && f0iter.From != nil { + hasReferences = true + arr := f0iter.From + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: PolicyRefs") } obj := &svcapitypes.Policy{} if err := getReferencedResourceState_Policy(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err + } + if ko.Spec.Policies == nil { + ko.Spec.Policies = make([]*string, 0, 1) } - resolved0 = append(resolved0, (*string)(obj.Status.ACKResourceMetadata.ARN)) + ko.Spec.Policies = append(ko.Spec.Policies, (*string)(obj.Status.ACKResourceMetadata.ARN)) } - ko.Spec.Policies = resolved0 } - return nil + return hasReferences, nil } diff --git a/pkg/resource/user/references.go b/pkg/resource/user/references.go index b284fdf..43169cf 100644 --- a/pkg/resource/user/references.go +++ b/pkg/resource/user/references.go @@ -24,86 +24,100 @@ 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/iam-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.PermissionsBoundaryRef != nil { + ko.Spec.PermissionsBoundary = nil + } + + if len(ko.Spec.PolicyRefs) > 0 { + ko.Spec.Policies = 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 = resolveReferenceForPermissionsBoundary(ctx, apiReader, namespace, ko) - } - if err == nil { - err = resolveReferenceForPolicies(ctx, apiReader, namespace, ko) + if fieldHasReferences, err := rm.resolveReferenceForPermissionsBoundary(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.resolveReferenceForPolicies(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) - } - return &resource{ko}, err + + return &resource{ko}, resourceHasReferences, err } // validateReferenceFields validates the reference field and corresponding // identifier field. func validateReferenceFields(ko *svcapitypes.User) error { + if ko.Spec.PermissionsBoundaryRef != nil && ko.Spec.PermissionsBoundary != nil { return ackerr.ResourceReferenceAndIDNotSupportedFor("PermissionsBoundary", "PermissionsBoundaryRef") } - if ko.Spec.PolicyRefs != nil && ko.Spec.Policies != nil { + + if len(ko.Spec.PolicyRefs) > 0 && len(ko.Spec.Policies) > 0 { return ackerr.ResourceReferenceAndIDNotSupportedFor("Policies", "PolicyRefs") } return nil } -// hasNonNilReferences returns true if resource contains a reference to another -// resource -func hasNonNilReferences(ko *svcapitypes.User) bool { - return false || (ko.Spec.PermissionsBoundaryRef != nil) || (ko.Spec.PolicyRefs != nil) -} - // resolveReferenceForPermissionsBoundary reads the resource referenced // from PermissionsBoundaryRef field and sets the PermissionsBoundary -// from referenced resource -func resolveReferenceForPermissionsBoundary( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForPermissionsBoundary( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.User, -) error { +) (hasReferences bool, err error) { if ko.Spec.PermissionsBoundaryRef != nil && ko.Spec.PermissionsBoundaryRef.From != nil { + hasReferences = true arr := ko.Spec.PermissionsBoundaryRef.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: PermissionsBoundaryRef") + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: PermissionsBoundaryRef") } obj := &svcapitypes.Policy{} if err := getReferencedResourceState_Policy(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err } ko.Spec.PermissionsBoundary = (*string)(obj.Status.ACKResourceMetadata.ARN) } - return nil + return hasReferences, nil } // getReferencedResourceState_Policy looks up whether a referenced resource @@ -159,28 +173,31 @@ func getReferencedResourceState_Policy( // resolveReferenceForPolicies reads the resource referenced // from PolicyRefs field and sets the Policies -// from referenced resource -func resolveReferenceForPolicies( +// from referenced resource. Returns a boolean indicating whether a reference +// contains references, or an error +func (rm *resourceManager) resolveReferenceForPolicies( ctx context.Context, apiReader client.Reader, namespace string, ko *svcapitypes.User, -) error { - if len(ko.Spec.PolicyRefs) > 0 { - resolved0 := []*string{} - for _, iter0 := range ko.Spec.PolicyRefs { - arr := iter0.From - if arr == nil || arr.Name == nil || *arr.Name == "" { - return fmt.Errorf("provided resource reference is nil or empty: PolicyRefs") +) (hasReferences bool, err error) { + for _, f0iter := range ko.Spec.PolicyRefs { + if f0iter != nil && f0iter.From != nil { + hasReferences = true + arr := f0iter.From + if arr.Name == nil || *arr.Name == "" { + return hasReferences, fmt.Errorf("provided resource reference is nil or empty: PolicyRefs") } obj := &svcapitypes.Policy{} if err := getReferencedResourceState_Policy(ctx, apiReader, obj, *arr.Name, namespace); err != nil { - return err + return hasReferences, err + } + if ko.Spec.Policies == nil { + ko.Spec.Policies = make([]*string, 0, 1) } - resolved0 = append(resolved0, (*string)(obj.Status.ACKResourceMetadata.ARN)) + ko.Spec.Policies = append(ko.Spec.Policies, (*string)(obj.Status.ACKResourceMetadata.ARN)) } - ko.Spec.Policies = resolved0 } - return nil + return hasReferences, nil }