Skip to content

Commit cd39d65

Browse files
committed
feat: add IAMRoleSelector CRD/feature
Implements aws-controllers-k8s/community#2628 (mostly) Introduces a new IAMRoleSelector CRD that enables dynamic IAM role assignment based on namespace and resource type selectors. This feature provides an alternative to CARM for role selection and cannot be used simultaneously with CARM (enforced by validation). Key components: - New IAMRoleSelector CRD with namespace and resource type selectors - Selector matching logic with AND between selector types, OR within arrays - Dynamic informer-based cache for IAMRoleSelector resources - Integration into the reconciler to override CARM role selection - Alpha feature gate (IAMRoleSelector) defaulting to disabled Note: ResourceTypeSelector uses schema.GroupVersionKind in the API, which differs from the separate fields approach in the original types. This may need adjustment based on CRD generation requirements.
1 parent 2ab09c6 commit cd39d65

15 files changed

+1901
-40
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"). You may
4+
// not use this file except in compliance with the License. A copy of the
5+
// License is located at
6+
//
7+
// http://aws.amazon.com/apache2.0/
8+
//
9+
// or in the "license" file accompanying this file. This file is distributed
10+
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
// express or implied. See the License for the specific language governing
12+
// permissions and limitations under the License.
13+
14+
package v1alpha1
15+
16+
import (
17+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
18+
"k8s.io/apimachinery/pkg/runtime/schema"
19+
)
20+
21+
// LabelSelector is a label query over a set of resources.
22+
type LabelSelector struct {
23+
MatchLabels map[string]string `json:"matchLabels"`
24+
}
25+
26+
// IAMRoleSelectorSpec defines the desired state of IAMRoleSelector
27+
type NamespaceSelector struct {
28+
Names []string `json:"name"`
29+
LabelSelector LabelSelector `json:"labelSelector,omitempty"`
30+
}
31+
32+
type IAMRoleSelectorSpec struct {
33+
ARN string `json:"arn"`
34+
NamespaceSelector NamespaceSelector `json:"namespaceSelector,omitempty"`
35+
ResourceTypeSelector []schema.GroupVersionKind `json:"resourceTypeSelector,omitempty"`
36+
}
37+
38+
type IAMRoleSelectorStatus struct{}
39+
40+
// IAMRoleSelector is the schema for the IAMRoleSelector API.
41+
// +kubebuilder:object:root=true
42+
// +kubebuilder:subresource:status
43+
type IAMRoleSelector struct {
44+
metav1.TypeMeta `json:",inline"`
45+
metav1.ObjectMeta `json:"metadata,omitempty"`
46+
Spec IAMRoleSelectorSpec `json:"spec,omitempty"`
47+
Status IAMRoleSelectorStatus `json:"status,omitempty"`
48+
}
49+
50+
// +kubebuilder:object:root=true
51+
type IAMRoleSelectorList struct {
52+
metav1.TypeMeta `json:",inline"`
53+
metav1.ListMeta `json:"metadata,omitempty"`
54+
Items []IAMRoleSelector `json:"items"`
55+
}
56+
57+
func init() {
58+
SchemeBuilder.Register(&IAMRoleSelector{}, &IAMRoleSelectorList{})
59+
}

apis/core/v1alpha1/zz_generated.deepcopy.go

Lines changed: 139 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
apiVersion: apiextensions.k8s.io/v1
3+
kind: CustomResourceDefinition
4+
metadata:
5+
annotations:
6+
controller-gen.kubebuilder.io/version: v0.16.2
7+
name: iamroleselectors.services.k8s.aws
8+
spec:
9+
group: services.k8s.aws
10+
names:
11+
kind: IAMRoleSelector
12+
listKind: IAMRoleSelectorList
13+
plural: iamroleselectors
14+
singular: iamroleselector
15+
scope: Namespaced
16+
versions:
17+
- name: v1alpha1
18+
schema:
19+
openAPIV3Schema:
20+
description: IAMRoleSelector is the schema for the IAMRoleSelector API.
21+
properties:
22+
apiVersion:
23+
description: |-
24+
APIVersion defines the versioned schema of this representation of an object.
25+
Servers should convert recognized schemas to the latest internal value, and
26+
may reject unrecognized values.
27+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
28+
type: string
29+
kind:
30+
description: |-
31+
Kind is a string value representing the REST resource this object represents.
32+
Servers may infer this from the endpoint the client submits requests to.
33+
Cannot be updated.
34+
In CamelCase.
35+
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
36+
type: string
37+
metadata:
38+
type: object
39+
spec:
40+
properties:
41+
arn:
42+
type: string
43+
namespaceSelector:
44+
description: IAMRoleSelectorSpec defines the desired state of IAMRoleSelector
45+
properties:
46+
labelSelector:
47+
description: LabelSelector is a label query over a set of resources.
48+
properties:
49+
matchLabels:
50+
additionalProperties:
51+
type: string
52+
type: object
53+
required:
54+
- matchLabels
55+
type: object
56+
name:
57+
items:
58+
type: string
59+
type: array
60+
required:
61+
- name
62+
type: object
63+
resourceTypeSelector:
64+
items:
65+
description: |-
66+
GroupVersionKind unambiguously identifies a kind. It doesn't anonymously include GroupVersion
67+
to avoid automatic coercion. It doesn't use a GroupVersion to avoid custom marshalling
68+
type: object
69+
type: array
70+
required:
71+
- arn
72+
type: object
73+
status:
74+
type: object
75+
type: object
76+
served: true
77+
storage: true
78+
subresources:
79+
status: {}

config/crd/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
apiVersion: kustomize.config.k8s.io/v1beta1
44
kind: Kustomization
55
resources:
6+
- bases/services.k8s.aws_iamroleselectors.yaml
67
- bases/services.k8s.aws_adoptedresources.yaml
78
- bases/services.k8s.aws_fieldexports.yaml

pkg/config/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,11 @@ func (cfg *Config) Validate(ctx context.Context, options ...Option) error {
380380
return fmt.Errorf("error overriding feature gates: %v", err)
381381
}
382382

383+
// IAMRolerSelector cannotbe used with enable-carm=true
384+
if cfg.FeatureGates.IsEnabled(featuregate.IAMRoleSelector) && cfg.EnableCARM {
385+
return fmt.Errorf("cannot enable feature gate '%s' when flag '%s' is set to true", featuregate.IAMRoleSelector, flagEnableCARM)
386+
}
387+
383388
return nil
384389
}
385390

pkg/featuregate/features.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ const (
3131

3232
// ServiceLevelCARM is a feature gate for enabling CARM for service-level resources.
3333
ServiceLevelCARM = "ServiceLevelCARM"
34+
35+
// IAMRoleSelector is a feature gate for enabling the IAMRoleSelector feature and reconciler.
36+
IAMRoleSelector = "IAMRoleSelector"
3437
)
3538

3639
// defaultACKFeatureGates is a map of feature names to Feature structs
@@ -40,6 +43,7 @@ var defaultACKFeatureGates = FeatureGates{
4043
ReadOnlyResources: {Stage: Beta, Enabled: true},
4144
TeamLevelCARM: {Stage: Alpha, Enabled: false},
4245
ServiceLevelCARM: {Stage: Alpha, Enabled: false},
46+
IAMRoleSelector: {Stage: Alpha, Enabled: false},
4347
}
4448

4549
// FeatureStage represents the development stage of a feature.

pkg/runtime/adoption_reconciler.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ func (r *adoptionReconciler) getOwnerAccountID(
466466
) (ackv1alpha1.AWSAccountID, bool) {
467467
// look for owner account id in the namespace annotations
468468
namespace := res.GetNamespace()
469-
accID, ok := r.cache.Namespaces.GetOwnerAccountID(namespace)
469+
accID, ok := r.carmCache.Namespaces.GetOwnerAccountID(namespace)
470470
if ok {
471471
return ackv1alpha1.AWSAccountID(accID), true
472472
}
@@ -481,7 +481,7 @@ func (r *adoptionReconciler) getTeamID(
481481
) ackv1alpha1.TeamID {
482482
// look for team id in the namespace annotations
483483
namespace := res.GetNamespace()
484-
teamID, ok := r.cache.Namespaces.GetTeamID(namespace)
484+
teamID, ok := r.carmCache.Namespaces.GetTeamID(namespace)
485485
if ok {
486486
return ackv1alpha1.TeamID(teamID)
487487
}
@@ -497,7 +497,7 @@ func (r *adoptionReconciler) getEndpointURL(
497497
) string {
498498
// look for endpoint url in the namespace annotations
499499
namespace := res.GetNamespace()
500-
endpointURL, ok := r.cache.Namespaces.GetEndpointURL(namespace)
500+
endpointURL, ok := r.carmCache.Namespaces.GetEndpointURL(namespace)
501501
if ok {
502502
return endpointURL
503503
}
@@ -512,9 +512,9 @@ func (r *adoptionReconciler) getRoleARN(id string, cacheName string) (ackv1alpha
512512
var cache *ackrtcache.CARMMap
513513
switch cacheName {
514514
case ackrtcache.ACKRoleTeamMap:
515-
cache = r.cache.Teams
515+
cache = r.carmCache.Teams
516516
case ackrtcache.ACKRoleAccountMap:
517-
cache = r.cache.Accounts
517+
cache = r.carmCache.Accounts
518518
default:
519519
return "", fmt.Errorf("invalid cache name: %s", cacheName)
520520
}
@@ -552,7 +552,7 @@ func (r *adoptionReconciler) getRegion(
552552

553553
// look for default region in namespace metadata annotations
554554
ns := res.GetNamespace()
555-
defaultRegion, ok := r.cache.Namespaces.GetDefaultRegion(ns)
555+
defaultRegion, ok := r.carmCache.Namespaces.GetDefaultRegion(ns)
556556
if ok {
557557
return ackv1alpha1.AWSRegion(defaultRegion)
558558
}
@@ -623,7 +623,7 @@ func NewAdoptionReconcilerWithClient(
623623
log: log.WithName("adopted-reconciler"),
624624
cfg: cfg,
625625
metrics: metrics,
626-
cache: cache,
626+
carmCache: cache,
627627
kc: kc,
628628
apiReader: apiReader,
629629
},

0 commit comments

Comments
 (0)