diff --git a/pkg/featuregate/features.go b/pkg/featuregate/features.go index 56275af..4e1bc5a 100644 --- a/pkg/featuregate/features.go +++ b/pkg/featuregate/features.go @@ -16,11 +16,16 @@ // optionally overridden. package featuregate +const ( + // CARMv2 is the name of the CARMv2 feature. + CARMv2 = "CARMv2" +) + // defaultACKFeatureGates is a map of feature names to Feature structs // representing the default feature gates for ACK controllers. var defaultACKFeatureGates = FeatureGates{ // Set feature gates here - // "feature1": {Stage: Alpha, Enabled: false}, + CARMv2: {Stage: Alpha, Enabled: false}, } // FeatureStage represents the development stage of a feature. diff --git a/pkg/runtime/adoption_reconciler.go b/pkg/runtime/adoption_reconciler.go index 7ad9497..4c5f6fd 100644 --- a/pkg/runtime/adoption_reconciler.go +++ b/pkg/runtime/adoption_reconciler.go @@ -33,6 +33,7 @@ import ( ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" + "github.com/aws-controllers-k8s/runtime/pkg/featuregate" ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics" "github.com/aws-controllers-k8s/runtime/pkg/requeue" ackrtcache "github.com/aws-controllers-k8s/runtime/pkg/runtime/cache" @@ -109,10 +110,6 @@ func (r *adoptionReconciler) reconcile(ctx context.Context, req ctrlrt.Request) return ackerr.NotAdoptable } - // If a user specified a namespace with role ARN annotation, - // we need to get the role and set the accout ID to that role. - teamID := r.getTeamID(res) - // If a user has specified a namespace that is annotated with the // an owner account ID, we need an appropriate role ARN to assume // in order to perform the reconciliation. The roles ARN are typically @@ -123,31 +120,39 @@ func (r *adoptionReconciler) reconcile(ctx context.Context, req ctrlrt.Request) acctID, needCARMLookup := r.getOwnerAccountID(res) var roleARN ackv1alpha1.AWSResourceName - if teamID != "" { - roleARN, err = r.getTeamRoleARN(teamID) - if err != nil { - ackrtlog.InfoAdoptedResource(r.log, res, fmt.Sprintf("Unable to start adoption reconcilliation %s: %v", acctID, err)) - // r.getRoleARN errors are not terminal, we should requeue. - return requeue.NeededAfter(err, roleARNNotAvailableRequeueDelay) - } - parsedARN, err := arn.Parse(string(roleARN)) - if err != nil { - return fmt.Errorf("failed to parsed role ARN %q from namespace annotation: %v", roleARN, err) - } - acctID = ackv1alpha1.AWSAccountID(parsedARN.AccountID) - } else { - if needCARMLookup { - // This means that the user is specifying a namespace that is - // annotated with an owner account ID or team ID. We need to retrieve the - // roleARN from the ConfigMap and properly requeue if the roleARN - // is not available. - roleARN, err = r.getOwnerAccountRoleARN(acctID) + if r.cfg.FeatureGates.IsEnabled(featuregate.CARMv2) { + teamID := r.getTeamID(res) + if teamID != "" { + // The user is specifying a namespace that is annotated with a team ID. + // Requeue if the corresponding roleARN is not available in the CARMv2 configmap. + // Additionally, set the account ID to the role's account ID. + roleARN, err = r.getRoleARNv2(string(teamID)) + if err != nil { + ackrtlog.InfoAdoptedResource(r.log, res, fmt.Sprintf("Unable to start adoption reconcilliation %s: %v", acctID, err)) + return requeue.NeededAfter(err, roleARNNotAvailableRequeueDelay) + } + parsedARN, err := arn.Parse(string(roleARN)) + if err != nil { + return fmt.Errorf("parsing role ARN %q from namespace annotation: %v", roleARN, err) + } + acctID = ackv1alpha1.AWSAccountID(parsedARN.AccountID) + } else if needCARMLookup { + // The user is specifying a namespace that is annotated with an owner account ID. + // Requeue if the corresponding roleARN is not available in the CARMv2 configmap. + roleARN, err = r.getRoleARNv2(string(acctID)) if err != nil { ackrtlog.InfoAdoptedResource(r.log, res, fmt.Sprintf("Unable to start adoption reconcilliation %s: %v", acctID, err)) - // r.getRoleARN errors are not terminal, we should requeue. return requeue.NeededAfter(err, roleARNNotAvailableRequeueDelay) } } + } else if needCARMLookup { + // The user is specifying a namespace that is annotated with an owner account ID. + // Requeue if the corresponding roleARN is not available in the Accounts (CARMv1) configmap. + roleARN, err = r.getRoleARN(acctID) + if err != nil { + ackrtlog.InfoAdoptedResource(r.log, res, fmt.Sprintf("Unable to start adoption reconcilliation %s: %v", acctID, err)) + return requeue.NeededAfter(err, roleARNNotAvailableRequeueDelay) + } } region := r.getRegion(res) @@ -512,34 +517,29 @@ func (r *adoptionReconciler) getEndpointURL( return r.cfg.EndpointURL } -// getRoleARN return the Role ARN that should be assumed for accoutn ID -// in order to manage the resources. -func (r *adoptionReconciler) getOwnerAccountRoleARN( - acctID ackv1alpha1.AWSAccountID, -) (ackv1alpha1.AWSResourceName, error) { - roleARN, err := r.cache.CARMMaps.GetValue(ackrtcache.OwnerAccountIDPrefix + string(acctID)) - if err == ackrtcache.ErrCARMConfigMapNotFound || err == ackrtcache.ErrKeyNotFound { - // CARM map v2 not defined. Check v1 map. - roleARN, err = r.cache.Accounts.GetValue(string(acctID)) - if err != nil { - return "", fmt.Errorf("unable to retrieve role ARN for account %s: %v", acctID, err) - } - } else if err != nil { - return "", fmt.Errorf("unable to retrieve role ARN from CARM v2 for account %s: %v", acctID, err) +// getRoleARNv2 returns the Role ARN that should be assumed for the given account/team ID, +// from the CARMv2 configmap, in order to manage the resources. +func (r *adoptionReconciler) getRoleARNv2(id string) (ackv1alpha1.AWSResourceName, error) { + // use service level roleARN if present + serviceID := r.sc.GetMetadata().ServiceAlias + "." + id + if roleARN, err := r.cache.CARMMaps.GetValue(serviceID); err == nil { + return ackv1alpha1.AWSResourceName(roleARN), nil + } + // otherwise use account/team level roleARN + roleARN, err := r.cache.CARMMaps.GetValue(id) + if err != nil { + return "", fmt.Errorf("retrieving role ARN for account/team ID %q from %q configmap: %v", id, ackrtcache.ACKCARMMapV2, err) } return ackv1alpha1.AWSResourceName(roleARN), nil } -// getTeamRoleARN return the Role ARN that should be assumed for a team ID -// in order to manage the resources. -func (r *adoptionReconciler) getTeamRoleARN( - teamID ackv1alpha1.TeamID, -) (ackv1alpha1.AWSResourceName, error) { - roleARN, err := r.cache.CARMMaps.GetValue(ackrtcache.TeamIDPrefix + string(teamID)) - if err == ackrtcache.ErrCARMConfigMapNotFound || err == ackrtcache.ErrKeyNotFound { - return "", fmt.Errorf("unable to retrieve role ARN from CARM v2 for account %s: %v", teamID, err) +// getRoleARN returns the Role ARN that should be assumed for the given account ID, +// from the CARMv1 configmap, in order to manage the resources. +func (r *adoptionReconciler) getRoleARN(acctID ackv1alpha1.AWSAccountID) (ackv1alpha1.AWSResourceName, error) { + roleARN, err := r.cache.Accounts.GetValue(string(acctID)) + if err != nil { + return "", fmt.Errorf("retrieving role ARN for account ID %q from %q configMap: %v", acctID, ackrtcache.ACKRoleAccountMap, err) } - return ackv1alpha1.AWSResourceName(roleARN), nil } diff --git a/pkg/runtime/cache/account.go b/pkg/runtime/cache/account.go index e4e131b..c92cc47 100644 --- a/pkg/runtime/cache/account.go +++ b/pkg/runtime/cache/account.go @@ -60,7 +60,7 @@ type CARMMap struct { // NewCARMMapCache instanciate a new CARMMap. func NewCARMMapCache(log logr.Logger) *CARMMap { return &CARMMap{ - log: log.WithName("cache.account"), + log: log.WithName("cache.carm"), data: make(map[string]string), configMapCreated: false, } diff --git a/pkg/runtime/cache/cache.go b/pkg/runtime/cache/cache.go index a1f4011..d3efbc9 100644 --- a/pkg/runtime/cache/cache.go +++ b/pkg/runtime/cache/cache.go @@ -43,12 +43,6 @@ const ( // caching system not to set up resyncs with an authoritative state source // (i.e. a Kubernetes API server) on a periodic basis. informerResyncPeriod = 0 * time.Second - - // The prefix for owner account ID in the v2 CARM map. - OwnerAccountIDPrefix = "owner-account-id/" - - // The prefix for owner team ID in the v2 CARM map. - TeamIDPrefix = "team-id/" ) // ackSystemNamespace is the namespace in which we look up ACK system diff --git a/pkg/runtime/reconciler.go b/pkg/runtime/reconciler.go index 85344b8..fc18165 100644 --- a/pkg/runtime/reconciler.go +++ b/pkg/runtime/reconciler.go @@ -39,6 +39,7 @@ import ( ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" + "github.com/aws-controllers-k8s/runtime/pkg/featuregate" ackmetrics "github.com/aws-controllers-k8s/runtime/pkg/metrics" "github.com/aws-controllers-k8s/runtime/pkg/requeue" ackrtcache "github.com/aws-controllers-k8s/runtime/pkg/runtime/cache" @@ -226,10 +227,6 @@ func (r *resourceReconciler) Reconcile(ctx context.Context, req ctrlrt.Request) ctx = context.WithValue(ctx, ackrtlog.ContextKey, rlog) ctx = context.WithValue(ctx, "resourceNamespace", req.Namespace) - // If a user specified a namespace with team-id annotation, - // we need to get the role and set the accout ID to that role. - teamID := r.getTeamID(desired) - // If a user has specified a namespace that is annotated with the // an owner account ID, we need an appropriate role ARN to assume // in order to perform the reconciliation. The roles ARN are typically @@ -240,28 +237,38 @@ func (r *resourceReconciler) Reconcile(ctx context.Context, req ctrlrt.Request) acctID, needCARMLookup := r.getOwnerAccountID(desired) var roleARN ackv1alpha1.AWSResourceName - if teamID != "" { - roleARN, err = r.getTeamRoleARN(teamID) - if err != nil { - return r.handleCacheError(ctx, err, desired) - } - parsedARN, err := arn.Parse(string(roleARN)) - if err != nil { - return ctrlrt.Result{}, fmt.Errorf("failed to parsed role ARN %q from namespace annotation: %v", roleARN, err) - } - acctID = ackv1alpha1.AWSAccountID(parsedARN.AccountID) - } else { - if needCARMLookup { - // This means that the user is specifying a namespace that is - // annotated with an owner account ID or team ID. We need to retrieve the - // roleARN from the ConfigMap and properly requeue if the roleARN - // is not available. - roleARN, err = r.getOwnerAccountRoleARN(acctID) + if r.cfg.FeatureGates.IsEnabled(featuregate.CARMv2) { + teamID := r.getTeamID(desired) + if teamID != "" { + // The user is specifying a namespace that is annotated with a team ID. + // Requeue if the corresponding roleARN is not available in the CARMv2 configmap. + // Additionally, set the account ID to the role's account ID. + roleARN, err = r.getRoleARNv2(string(teamID)) + if err != nil { + return r.handleCacheError(ctx, err, desired) + } + parsedARN, err := arn.Parse(string(roleARN)) + if err != nil { + return ctrlrt.Result{}, fmt.Errorf("parsing role ARN %q from namespace annotation: %v", roleARN, err) + } + acctID = ackv1alpha1.AWSAccountID(parsedARN.AccountID) + } else if needCARMLookup { + // The user is specifying a namespace that is annotated with an owner account ID. + // Requeue if the corresponding roleARN is not available in the CARMv2 configmap. + roleARN, err = r.getRoleARNv2(string(acctID)) if err != nil { return r.handleCacheError(ctx, err, desired) } } + } else if needCARMLookup { + // The user is specifying a namespace that is annotated with an owner account ID. + // Requeue if the corresponding roleARN is not available in the Accounts (CARMv1) configmap. + roleARN, err = r.getRoleARN(acctID) + if err != nil { + return r.handleCacheError(ctx, err, desired) + } } + region := r.getRegion(desired) endpointURL := r.getEndpointURL(desired) gvk := r.rd.GroupVersionKind() @@ -1128,35 +1135,29 @@ func (r *resourceReconciler) getTeamID( return ackv1alpha1.TeamID("") } -// getRoleARN return the Role ARN that should be assumed for accoutn ID -// in order to manage the resources. -func (r *resourceReconciler) getOwnerAccountRoleARN( - acctID ackv1alpha1.AWSAccountID, -) (ackv1alpha1.AWSResourceName, error) { - roleARN, err := r.cache.CARMMaps.GetValue(ackrtcache.OwnerAccountIDPrefix + string(acctID)) - if err == ackrtcache.ErrCARMConfigMapNotFound || err == ackrtcache.ErrKeyNotFound { - // CARM map v2 not defined. Check v1 map. - roleARN, err = r.cache.Accounts.GetValue(string(acctID)) - if err != nil { - return "", fmt.Errorf("unable to retrieve role ARN for account %s: %v", acctID, err) - } - } else if err != nil { - return "", fmt.Errorf("unable to retrieve role ARN from CARM v2 for account %s: %v", acctID, err) +// getRoleARNv2 returns the Role ARN that should be assumed for the given account/team ID, +// from the CARMv2 configmap, in order to manage the resources. +func (r *resourceReconciler) getRoleARNv2(id string) (ackv1alpha1.AWSResourceName, error) { + // use service level roleARN if present + serviceID := r.sc.GetMetadata().ServiceAlias + "." + id + if roleARN, err := r.cache.CARMMaps.GetValue(serviceID); err == nil { + return ackv1alpha1.AWSResourceName(roleARN), nil + } + // otherwise use account/team level roleARN + roleARN, err := r.cache.CARMMaps.GetValue(id) + if err != nil { + return "", fmt.Errorf("retrieving role ARN for account/team ID %q from %q configmap: %v", id, ackrtcache.ACKCARMMapV2, err) } - return ackv1alpha1.AWSResourceName(roleARN), nil } -// getTeamRoleARN return the Role ARN that should be assumed for a team ID -// in order to manage the resources. -func (r *resourceReconciler) getTeamRoleARN( - teamID ackv1alpha1.TeamID, -) (ackv1alpha1.AWSResourceName, error) { - roleARN, err := r.cache.CARMMaps.GetValue(ackrtcache.TeamIDPrefix + string(teamID)) - if err == ackrtcache.ErrCARMConfigMapNotFound || err == ackrtcache.ErrKeyNotFound { - return "", fmt.Errorf("unable to retrieve role ARN from CARM v2 for account %s: %v", teamID, err) +// getRoleARN returns the Role ARN that should be assumed for the given account ID, +// from the CARMv1 configmap, in order to manage the resources. +func (r *resourceReconciler) getRoleARN(acctID ackv1alpha1.AWSAccountID) (ackv1alpha1.AWSResourceName, error) { + roleARN, err := r.cache.Accounts.GetValue(string(acctID)) + if err != nil { + return "", fmt.Errorf("retrieving role ARN for account ID %q from %q configMap: %v", acctID, ackrtcache.ACKRoleAccountMap, err) } - return ackv1alpha1.AWSResourceName(roleARN), nil }