Skip to content

Commit

Permalink
put CARMv2 map behind a feature flag and add service level isolation …
Browse files Browse the repository at this point in the history
…support
  • Loading branch information
TiberiuGC committed Aug 9, 2024
1 parent 96a54b3 commit 2d1b192
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 100 deletions.
7 changes: 6 additions & 1 deletion pkg/featuregate/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
94 changes: 47 additions & 47 deletions pkg/runtime/adoption_reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/runtime/cache/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
6 changes: 0 additions & 6 deletions pkg/runtime/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
91 changes: 46 additions & 45 deletions pkg/runtime/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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
}

Expand Down

0 comments on commit 2d1b192

Please sign in to comment.