diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..30eafff --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,30 @@ +name: Test + +on: + push: + branches: + - '*' + pull_request: {} + +env: + GO_VERSION: '1.17' + KUBERNETES_VERSION: '1.19.x' + +jobs: + run-tests: + runs-on: ubuntu-18.04 + + steps: + - name: checkout + uses: actions/checkout@v2 + + - name: install go + uses: actions/setup-go@v2 + with: + go-version: ${{ env.GO_VERSION }} + + - name: run tests + run: | + go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest + eval `setup-envtest use ${{ env.KUBERNETES_VERSION }} -p env` + make test diff --git a/Makefile b/Makefile index f36be0f..2c9255f 100644 --- a/Makefile +++ b/Makefile @@ -49,11 +49,8 @@ fmt: ## Run go fmt against code. vet: ## Run go vet against code. go vet ./... -ENVTEST_ASSETS_DIR=$(shell pwd)/testbin test: manifests generate fmt vet ## Run tests. - mkdir -p ${ENVTEST_ASSETS_DIR} - test -f ${ENVTEST_ASSETS_DIR}/setup-envtest.sh || curl -sSLo ${ENVTEST_ASSETS_DIR}/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.8.3/hack/setup-envtest.sh - source ${ENVTEST_ASSETS_DIR}/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); go test ./... -coverprofile cover.out + go test ./... -coverprofile cover.out ##@ Build diff --git a/PROJECT b/PROJECT index 6489686..2b6551f 100644 --- a/PROJECT +++ b/PROJECT @@ -6,11 +6,10 @@ repo: github.com/inovex/aws-auth-controller resources: - api: crdVersion: v1 - namespaced: false controller: true domain: awsauth.io group: crd - kind: AwsAuthMap + kind: AwsAuthMapSnippet path: github.com/inovex/aws-auth-controller/api/v1beta1 version: v1beta1 version: "3" diff --git a/README.md b/README.md index c68961a..9b64597 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,9 @@ To pick up on the example from the AWS documentation, that snippet would look like this in the CR: apiVersion: crd.awsauth.io/v1beta1 - kind: AwsAuthMap + kind: AwsAuthMapSnippet metadata: - name: awsauthmap-sample + name: awsauthmapsnippet-sample namespace: sample-namespace spec: mapRoles: @@ -57,17 +57,6 @@ When this resource is added to the cluster, the controller will modify the configmap to include the entries in this snippet. When it is removed, the respective entries will be removed, too. -## Implementation details - -The controller adds an annotation to the configmap `awsauth.io/authversion` -that holds an incrementing serial number to match the configmap to the -custom resources in the cluster. The same value is stored in the CR's -status so that out-of-sync resources can be identified. - -The controller calculates the SHA256 checksum of the snippet's content -(arns, usernames, groupnames) to detect changed snippets that need to be -updated in the configmap. The checksum is also stored in the status. - ## Controller deployment A working single-file deployment manifest is forthcoming. For now the @@ -93,10 +82,6 @@ It will use whatever kubectl context is currently active. ## TODOs * Semantic release versioning - * Clean up repository - * A validating webhook to - * check the ARN format - * check for duplicate ARNs - * check with AWS if ARN actually exists (?, needs IRSA) + * A validating webhook to check with AWS if ARN actually exists (?, needs IRSA) * Add `mapAccounts` if anybody needs it - * Tests, automated + * More tests diff --git a/api/v1beta1/awsauthmap_types.go b/api/v1beta1/awsauthmapsnippet_types.go similarity index 59% rename from api/v1beta1/awsauthmap_types.go rename to api/v1beta1/awsauthmapsnippet_types.go index ddb9a34..8f13d9e 100644 --- a/api/v1beta1/awsauthmap_types.go +++ b/api/v1beta1/awsauthmapsnippet_types.go @@ -17,10 +17,6 @@ limitations under the License. package v1beta1 import ( - "crypto/sha256" - "fmt" - "io" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -43,68 +39,43 @@ type MapUsersSpec struct { Groups []string `json:"groups"` } -// AwsAuthMapSpec defines the IAM role and user mappings to RBAC. -type AwsAuthMapSpec struct { +// AwsAuthMapSnippetSpec defines the IAM role and user mappings to RBAC. +type AwsAuthMapSnippetSpec struct { MapRoles []MapRolesSpec `json:"mapRoles,omitempty"` MapUsers []MapUsersSpec `json:"mapUsers,omitempty"` } -// CalcCheckSum adds all string values into a checksum for identifying changes. -func (s *AwsAuthMapSpec) CalcCheckSum() string { - shaHash := sha256.New() - for _, mr := range s.MapRoles { - io.WriteString(shaHash, mr.RoleArn) - io.WriteString(shaHash, mr.UserName) - for _, group := range mr.Groups { - io.WriteString(shaHash, group) - } - } - for _, mu := range s.MapUsers { - io.WriteString(shaHash, mu.UserArn) - io.WriteString(shaHash, mu.UserName) - for _, group := range mu.Groups { - io.WriteString(shaHash, group) - } - } - return fmt.Sprintf("%x", shaHash.Sum(nil)) -} - -// AwsAuthMapStatus defines the observed state of AwsAuthMap. -type AwsAuthMapStatus struct { +// AwsAuthMapSnippetStatus defines the observed state of AwsAuthMapSnippet. +type AwsAuthMapSnippetStatus struct { // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster // Important: Run "make" to regenerate code after modifying this file - MapVersion int `json:"mapVersion"` - CheckSum string `json:"checkSum"` + RoleArns []string `json:"roleArns,omitempty"` + UserArns []string `json:"userArns,omitempty"` } //+kubebuilder:object:root=true //+kubebuilder:subresource:status //+kubebuilder:printcolumn:name="MapVersion",type=integer,JSONPath=`.status.mapVersion` -// AwsAuthMap is the Schema for the awsauthmaps API -type AwsAuthMap struct { +// AwsAuthMapSnippet is the Schema for the awsauthmapsnippets API +type AwsAuthMapSnippet struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec AwsAuthMapSpec `json:"spec,omitempty"` - Status AwsAuthMapStatus `json:"status,omitempty"` -} - -// IsChanged indicates if the stored and calculated checksums differ. -func (m *AwsAuthMap) IsChanged() bool { - return m.Spec.CalcCheckSum() != m.Status.CheckSum + Spec AwsAuthMapSnippetSpec `json:"spec,omitempty"` + Status AwsAuthMapSnippetStatus `json:"status,omitempty"` } //+kubebuilder:object:root=true -// AwsAuthMapList contains a list of AwsAuthMap -type AwsAuthMapList struct { +// AwsAuthMapSnippetList contains a list of AwsAuthMapSnippet +type AwsAuthMapSnippetList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []AwsAuthMap `json:"items"` + Items []AwsAuthMapSnippet `json:"items"` } func init() { - SchemeBuilder.Register(&AwsAuthMap{}, &AwsAuthMapList{}) + SchemeBuilder.Register(&AwsAuthMapSnippet{}, &AwsAuthMapSnippetList{}) } diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index c4cca6e..65a5e82 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -26,26 +26,26 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AwsAuthMap) DeepCopyInto(out *AwsAuthMap) { +func (in *AwsAuthMapSnippet) DeepCopyInto(out *AwsAuthMapSnippet) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AwsAuthMap. -func (in *AwsAuthMap) DeepCopy() *AwsAuthMap { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AwsAuthMapSnippet. +func (in *AwsAuthMapSnippet) DeepCopy() *AwsAuthMapSnippet { if in == nil { return nil } - out := new(AwsAuthMap) + out := new(AwsAuthMapSnippet) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *AwsAuthMap) DeepCopyObject() runtime.Object { +func (in *AwsAuthMapSnippet) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -53,31 +53,31 @@ func (in *AwsAuthMap) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AwsAuthMapList) DeepCopyInto(out *AwsAuthMapList) { +func (in *AwsAuthMapSnippetList) DeepCopyInto(out *AwsAuthMapSnippetList) { *out = *in out.TypeMeta = in.TypeMeta in.ListMeta.DeepCopyInto(&out.ListMeta) if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]AwsAuthMap, len(*in)) + *out = make([]AwsAuthMapSnippet, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AwsAuthMapList. -func (in *AwsAuthMapList) DeepCopy() *AwsAuthMapList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AwsAuthMapSnippetList. +func (in *AwsAuthMapSnippetList) DeepCopy() *AwsAuthMapSnippetList { if in == nil { return nil } - out := new(AwsAuthMapList) + out := new(AwsAuthMapSnippetList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *AwsAuthMapList) DeepCopyObject() runtime.Object { +func (in *AwsAuthMapSnippetList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -85,7 +85,7 @@ func (in *AwsAuthMapList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AwsAuthMapSpec) DeepCopyInto(out *AwsAuthMapSpec) { +func (in *AwsAuthMapSnippetSpec) DeepCopyInto(out *AwsAuthMapSnippetSpec) { *out = *in if in.MapRoles != nil { in, out := &in.MapRoles, &out.MapRoles @@ -103,27 +103,37 @@ func (in *AwsAuthMapSpec) DeepCopyInto(out *AwsAuthMapSpec) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AwsAuthMapSpec. -func (in *AwsAuthMapSpec) DeepCopy() *AwsAuthMapSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AwsAuthMapSnippetSpec. +func (in *AwsAuthMapSnippetSpec) DeepCopy() *AwsAuthMapSnippetSpec { if in == nil { return nil } - out := new(AwsAuthMapSpec) + out := new(AwsAuthMapSnippetSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AwsAuthMapStatus) DeepCopyInto(out *AwsAuthMapStatus) { +func (in *AwsAuthMapSnippetStatus) DeepCopyInto(out *AwsAuthMapSnippetStatus) { *out = *in + if in.RoleArns != nil { + in, out := &in.RoleArns, &out.RoleArns + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.UserArns != nil { + in, out := &in.UserArns, &out.UserArns + *out = make([]string, len(*in)) + copy(*out, *in) + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AwsAuthMapStatus. -func (in *AwsAuthMapStatus) DeepCopy() *AwsAuthMapStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AwsAuthMapSnippetStatus. +func (in *AwsAuthMapSnippetStatus) DeepCopy() *AwsAuthMapSnippetStatus { if in == nil { return nil } - out := new(AwsAuthMapStatus) + out := new(AwsAuthMapSnippetStatus) in.DeepCopyInto(out) return out } diff --git a/config/crd/bases/crd.awsauth.io_awsauthmapsnippets.yaml b/config/crd/bases/crd.awsauth.io_awsauthmapsnippets.yaml new file mode 100644 index 0000000..5e8b0b6 --- /dev/null +++ b/config/crd/bases/crd.awsauth.io_awsauthmapsnippets.yaml @@ -0,0 +1,115 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + creationTimestamp: null + name: awsauthmapsnippets.crd.awsauth.io +spec: + group: crd.awsauth.io + names: + kind: AwsAuthMapSnippet + listKind: AwsAuthMapSnippetList + plural: awsauthmapsnippets + singular: awsauthmapsnippet + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.mapVersion + name: MapVersion + type: integer + name: v1beta1 + schema: + openAPIV3Schema: + description: AwsAuthMapSnippet is the Schema for the awsauthmapsnippets API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AwsAuthMapSnippetSpec defines the IAM role and user mappings + to RBAC. + properties: + mapRoles: + items: + description: MapRolesSpec defines a mapping of an IAM role to an + RBAC user and to RBAC groups. + properties: + groups: + items: + type: string + type: array + rolearn: + pattern: |- + ^arn:[^: + ]*:iam:[^: + ]*:[^: + ]*:role/.+$ + type: string + username: + type: string + required: + - groups + - rolearn + - username + type: object + type: array + mapUsers: + items: + description: MapUsersSpec defines a mapping of an IAM user to an + RBAC user and to RBAC groups. + properties: + groups: + items: + type: string + type: array + userarn: + pattern: |- + ^arn:[^: + ]*:iam:[^: + ]*:[^: + ]*:user/.+$ + type: string + username: + type: string + required: + - groups + - userarn + - username + type: object + type: array + type: object + status: + description: AwsAuthMapSnippetStatus defines the observed state of AwsAuthMapSnippet. + properties: + roleArns: + items: + type: string + type: array + userArns: + items: + type: string + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 52c454e..4a4fa88 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -2,18 +2,18 @@ # since it depends on service name and namespace that are out of this kustomize package. # It should be run by config/default resources: -- bases/crd.awsauth.io_awsauthmaps.yaml +- bases/crd.awsauth.io_awsauthmapsnippets.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. # patches here are for enabling the conversion webhook for each CRD -#- patches/webhook_in_awsauthmap.yaml +#- patches/webhook_in_awsauthmapsnippet.yaml #+kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. # patches here are for enabling the CA injection for each CRD -#- patches/cainjection_in_awsauthmap.yaml +#- patches/cainjection_in_awsauthmapsnippet.yaml #+kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/patches/cainjection_in_awsauthmap.yaml b/config/crd/patches/cainjection_in_awsauthmapsnippet.yaml similarity index 86% rename from config/crd/patches/cainjection_in_awsauthmap.yaml rename to config/crd/patches/cainjection_in_awsauthmapsnippet.yaml index cf0e59b..aa09cdd 100644 --- a/config/crd/patches/cainjection_in_awsauthmap.yaml +++ b/config/crd/patches/cainjection_in_awsauthmapsnippet.yaml @@ -4,4 +4,4 @@ kind: CustomResourceDefinition metadata: annotations: cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) - name: awsauthmap.crd.awsauth.io + name: awsauthmapsnippet.crd.awsauth.io diff --git a/config/crd/patches/webhook_in_awsauthmap.yaml b/config/crd/patches/webhook_in_awsauthmapsnippet.yaml similarity index 89% rename from config/crd/patches/webhook_in_awsauthmap.yaml rename to config/crd/patches/webhook_in_awsauthmapsnippet.yaml index 010cc25..9fd6e00 100644 --- a/config/crd/patches/webhook_in_awsauthmap.yaml +++ b/config/crd/patches/webhook_in_awsauthmapsnippet.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: - name: awsauthmap.crd.awsauth.io + name: awsauthmapsnippet.crd.awsauth.io spec: conversion: strategy: Webhook diff --git a/config/rbac/awsauthmap_editor_role.yaml b/config/rbac/awsauthmap_editor_role.yaml index 992d168..c048fdf 100644 --- a/config/rbac/awsauthmap_editor_role.yaml +++ b/config/rbac/awsauthmap_editor_role.yaml @@ -1,13 +1,13 @@ -# permissions for end users to edit awsauthmaps. +# permissions for end users to edit awsauthmapsnippets. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: awsauthmap-editor-role + name: awsauthmapsnippet-editor-role rules: - apiGroups: - crd.awsauth.io resources: - - awsauthmaps + - awsauthmapsnippets verbs: - create - delete @@ -19,6 +19,6 @@ rules: - apiGroups: - crd.awsauth.io resources: - - awsauthmaps/status + - awsauthmapsnippets/status verbs: - get diff --git a/config/rbac/awsauthmap_viewer_role.yaml b/config/rbac/awsauthmap_viewer_role.yaml index 4902571..f8e111a 100644 --- a/config/rbac/awsauthmap_viewer_role.yaml +++ b/config/rbac/awsauthmap_viewer_role.yaml @@ -1,13 +1,13 @@ -# permissions for end users to view awsauthmaps. +# permissions for end users to view awsauthmapsnippets. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: awsauthmap-viewer-role + name: awsauthmapsnippet-viewer-role rules: - apiGroups: - crd.awsauth.io resources: - - awsauthmaps + - awsauthmapsnippets verbs: - get - list @@ -15,6 +15,6 @@ rules: - apiGroups: - crd.awsauth.io resources: - - awsauthmaps/status + - awsauthmapsnippets/status verbs: - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 73d673d..24cffc5 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -9,7 +9,7 @@ rules: - apiGroups: - crd.awsauth.io resources: - - awsauthmaps + - awsauthmapsnippets verbs: - create - delete @@ -21,13 +21,13 @@ rules: - apiGroups: - crd.awsauth.io resources: - - awsauthmaps/finalizers + - awsauthmapsnippets/finalizers verbs: - update - apiGroups: - crd.awsauth.io resources: - - awsauthmaps/status + - awsauthmapsnippets/status verbs: - get - patch diff --git a/config/samples/crd_v1beta1_awsauthmap.yaml b/config/samples/crd_v1beta1_awsauthmapsnippet.yaml similarity index 90% rename from config/samples/crd_v1beta1_awsauthmap.yaml rename to config/samples/crd_v1beta1_awsauthmapsnippet.yaml index 4a3c499..e4311b2 100644 --- a/config/samples/crd_v1beta1_awsauthmap.yaml +++ b/config/samples/crd_v1beta1_awsauthmapsnippet.yaml @@ -1,7 +1,7 @@ apiVersion: crd.awsauth.io/v1beta1 -kind: AwsAuthMap +kind: AwsAuthMapSnippet metadata: - name: awsauthmap-sample + name: awsauthmapsnippet-sample namespace: sample-namespace spec: mapRoles: diff --git a/controllers/awsauth.go b/controllers/awsauth.go new file mode 100644 index 0000000..2f24078 --- /dev/null +++ b/controllers/awsauth.go @@ -0,0 +1,159 @@ +package controllers + +import ( + "context" + + crdv1beta1 "github.com/inovex/aws-auth-controller/api/v1beta1" + corev1 "k8s.io/api/core/v1" + apierrs "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/yaml" +) + +const CONFIG_MAP_NAMESPACE = "kube-system" +const CONFIG_MAP_NAME = "aws-auth" +const MAP_ROLES_KEY = "mapRoles" +const MAP_USERS_KEY = "mapUsers" +const MANAGED_ANNOTATION = "awsauth.io/managed" + +type MapRoles []crdv1beta1.MapRolesSpec +type MapUsers []crdv1beta1.MapUsersSpec + +type MapRolesByArn map[string]crdv1beta1.MapRolesSpec +type MapUsersByArn map[string]crdv1beta1.MapUsersSpec + +type AwsAuthMap struct { + client.Client + ConfigMap *corev1.ConfigMap + Roles *MapRolesByArn + Users *MapUsersByArn +} + +/* +GetAwsAuthMap constructs an AwsAuthMap instance and fills it with th data from +the ConfigMap. + +If the ConfigMap did not yet exist it is created, +*/ +func GetAwsAuthMap(client client.Client, ctx context.Context) (*AwsAuthMap, error) { + awsauthmap := &AwsAuthMap{Client: client} + if err := awsauthmap.Read(ctx); err != nil { + return nil, err + } + return awsauthmap, nil +} + +/* +getOrCreate reads the ConfigMap from the API or creates it if it did not exist. +*/ +func (a *AwsAuthMap) getOrCreate(ctx context.Context) error { + authCM := &corev1.ConfigMap{} + err := a.Get(ctx, client.ObjectKey{ + Namespace: CONFIG_MAP_NAMESPACE, + Name: CONFIG_MAP_NAME, + }, authCM) + + if err != nil { + // Check for missing ConfigMap and create + if apierrs.IsNotFound(err) { + authCM.ObjectMeta.Namespace = CONFIG_MAP_NAMESPACE + authCM.ObjectMeta.Name = CONFIG_MAP_NAME + authCM.Data = make(map[string]string) + authCM.Data[MAP_ROLES_KEY] = "" + authCM.Data[MAP_USERS_KEY] = "" + err = a.Create(ctx, authCM) + if err != nil { + return err + } + } else { + return err + } + } + a.ConfigMap = authCM + return nil +} + +/* +Read retrieves the ConfigMap, deserializes the contained YaML objects and maps +their content by ARN for easy manipulation. +*/ +func (a *AwsAuthMap) Read(ctx context.Context) error { + rolesByArn := MapRolesByArn{} + usersByArn := MapUsersByArn{} + + err := a.getOrCreate(ctx) + if err != nil { + return err + } + // Create map of MapRolesSpec + currentMapRoles := &MapRoles{} + err = yaml.Unmarshal([]byte(a.ConfigMap.Data[MAP_ROLES_KEY]), currentMapRoles) + if err != nil { + return err + } + for _, cmr := range *currentMapRoles { + rolesByArn[cmr.RoleArn] = cmr + } + // Create map of MapUsersSpec + currentMapUsers := &MapUsers{} + err = yaml.Unmarshal([]byte(a.ConfigMap.Data[MAP_USERS_KEY]), currentMapUsers) + if err != nil { + return err + } + for _, cmu := range *currentMapUsers { + usersByArn[cmu.UserArn] = cmu + } + a.Roles = &rolesByArn + a.Users = &usersByArn + return nil +} + +/* +Write serializes the mappings back to YaM and writes them to the ConfigMap +API object. + +It is important to re-use the ConfigMap object that was retrieved by Read so +that the contained ResourceVersion attribute can be evaluated by the API server +to catch concurrent writes. +*/ +func (a *AwsAuthMap) Write(ctx context.Context) error { + + mapRoles := MapRoles{} + mapUsers := MapUsers{} + + // Create arrays of map content + for _, rba := range *a.Roles { + mapRoles = append(mapRoles, rba) + } + for _, uba := range *a.Users { + mapUsers = append(mapUsers, uba) + } + + // Make Yaml strings from arrays + mapRolesYaml, err := yaml.Marshal(mapRoles) + if err != nil { + return err + } + mapUsersYaml, err := yaml.Marshal(mapUsers) + if err != nil { + return err + } + + if a.ConfigMap.ObjectMeta.Annotations == nil { + // No annotations yet. + a.ConfigMap.ObjectMeta.Annotations = make(map[string]string) + } + a.ConfigMap.ObjectMeta.Annotations[MANAGED_ANNOTATION] = "true" + + // Store Yaml mappings in ConfigMap. + a.ConfigMap.Data[MAP_ROLES_KEY] = string(mapRolesYaml) + a.ConfigMap.Data[MAP_USERS_KEY] = string(mapUsersYaml) + + err = a.Update(ctx, a.ConfigMap) + if err != nil { + // TODO: Deal with 409 responses (concurrent write). + return err + } + + return nil +} diff --git a/controllers/awsauthmap_controller.go b/controllers/awsauthmap_controller.go deleted file mode 100644 index 5d48e25..0000000 --- a/controllers/awsauthmap_controller.go +++ /dev/null @@ -1,224 +0,0 @@ -/* -Copyright 2021 inovex GmbH - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controllers - -import ( - "context" - "fmt" - "strconv" - "strings" - - corev1 "k8s.io/api/core/v1" - apierrs "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/yaml" - - awsauthv1beta1 "github.com/inovex/aws-auth-controller/api/v1beta1" -) - -// AwsAuthMapReconciler reconciles a AwsAuthMap object -type AwsAuthMapReconciler struct { - client.Client - Scheme *runtime.Scheme -} - -type MapRoles []awsauthv1beta1.MapRolesSpec -type MapUsers []awsauthv1beta1.MapUsersSpec - -//+kubebuilder:rbac:groups=crd.awsauth.io,resources=awsauthmaps,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=crd.awsauth.io,resources=awsauthmaps/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=crd.awsauth.io,resources=awsauthmaps/finalizers,verbs=update - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile -func (r *AwsAuthMapReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - logger := log.FromContext(ctx) - - logger.Info("Reconcile Request received", "objectName", req.NamespacedName) - currentVersion, err := r.findConfigMapVersion(ctx) - if err != nil { - return ctrl.Result{}, err - } - logger.Info("Read Version", "version", currentVersion) - - thisMap := &awsauthv1beta1.AwsAuthMap{} - err = r.Get(ctx, client.ObjectKey{ - Name: req.NamespacedName.Name, - Namespace: req.NamespacedName.Namespace}, - thisMap) - - outOfSync := false - - if err == nil { - if thisMap.IsChanged() { - logger.Info("Content changed, updating.") - outOfSync = true - } else if thisMap.Status.MapVersion == currentVersion { - logger.Info("Already synced", "version", currentVersion) - return ctrl.Result{}, nil - } - } else if apierrs.IsNotFound(err) { - logger.Info("Deletion event, updating.") - outOfSync = true - } else { - fmt.Printf("Error: %v\n", err) - return ctrl.Result{}, err - } - - mapList := &awsauthv1beta1.AwsAuthMapList{} - //selector := fields.ParseSelectorOrDie(fmt.Sprintf("status.mapVersion!=%d", currentVersion)) - err = r.List(ctx, mapList /*, client.MatchingFieldsSelector{selector}*/) - - totalCount := len(mapList.Items) - mapRoles := MapRoles{} - mapUsers := MapUsers{} - for _, authMap := range mapList.Items { - if authMap.Status.MapVersion != currentVersion { - outOfSync = true - logger.Info("Found out-of-sync map", "name", authMap.Name, "version", authMap.Status.MapVersion) - } - mapRoles = append(mapRoles, authMap.Spec.MapRoles...) - mapUsers = append(mapUsers, authMap.Spec.MapUsers...) - } - logger.Info("Maps counted", "total", totalCount, "out-of-sync", outOfSync) - - if !outOfSync { - return ctrl.Result{}, nil - } - - currentVersion++ - - err = r.updateConfigMap(ctx, mapRoles, mapUsers, currentVersion) - if err != nil { - return ctrl.Result{}, err - } - - for _, authMap := range mapList.Items { - authMap.Status.MapVersion = currentVersion - authMap.Status.CheckSum = authMap.Spec.CalcCheckSum() - err = r.Status().Update(ctx, &authMap) - if err != nil { - logger.Error(err, "Status Update failed", "name", authMap.Name, "version", currentVersion) - } else { - logger.Info("Status updated", "name", authMap.Name, "version", currentVersion) - } - } - logger.Info("Reconciliation completed", "newVersion", currentVersion) - return ctrl.Result{}, nil -} - -// SetupWithManager sets up the controller with the Manager. -func (r *AwsAuthMapReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&awsauthv1beta1.AwsAuthMap{}). - Complete(r) -} - -// findConfigMapVersion reads the current version of the aws-auth Config-Map from its annotations. -func (r *AwsAuthMapReconciler) findConfigMapVersion(ctx context.Context) (int, error) { - authCM := &corev1.ConfigMap{} - err := r.Get(ctx, client.ObjectKey{ - Namespace: "kube-system", - Name: "aws-auth", - }, authCM) - if err != nil { - if apierrs.IsNotFound(err) { - return 0, nil - } else { - return 0, err - } - } - version, ok := authCM.ObjectMeta.Annotations["awsauth.io/authversion"] - if !ok { - version = "0" - } - intVersion, err := strconv.Atoi(version) - if err != nil { - return 0, err - } - return intVersion, nil -} - -// updateConfigMap renders mappings in Yaml and writes them to the aws-auth ConfigMap. -func (r *AwsAuthMapReconciler) updateConfigMap(ctx context.Context, mapRoles MapRoles, mapUsers MapUsers, version int) error { - logger := log.FromContext(ctx) - - authCM := &corev1.ConfigMap{} - err := r.Get(ctx, client.ObjectKey{ - Namespace: "kube-system", - Name: "aws-auth", - }, authCM) - - // Check for missing ConfigMap - if err != nil { - if apierrs.IsNotFound(err) { - logger.Info("ConfigMap aws-auth missing, creating it now.") - authCM.ObjectMeta.Namespace = "kube-system" - authCM.ObjectMeta.Name = "aws-auth" - authCM.Data = make(map[string]string) - authCM.Data["mapRoles"] = "" - authCM.Data["mapUsers"] = "" - err = r.Create(ctx, authCM) - if err != nil { - return err - } - } else { - return err - } - } - - // Preserve system:node mappings - currentMapRoles := &MapRoles{} - err = yaml.Unmarshal([]byte(authCM.Data["mapRoles"]), currentMapRoles) - if err != nil { - return err - } - for _, cmr := range *currentMapRoles { - if strings.HasPrefix(cmr.UserName, "system:node:") { - mapRoles = append(mapRoles, cmr) - } - } - - mapRolesYaml, err := yaml.Marshal(mapRoles) - if err != nil { - return err - } - mapUsersYaml, err := yaml.Marshal(mapUsers) - if err != nil { - return err - } - - if authCM.ObjectMeta.Annotations == nil { - // No annotations yet. - authCM.ObjectMeta.Annotations = make(map[string]string) - } - authCM.ObjectMeta.Annotations["awsauth.io/authversion"] = fmt.Sprintf("%d", version) - authCM.Data["mapRoles"] = string(mapRolesYaml) - authCM.Data["mapUsers"] = string(mapUsersYaml) - - err = r.Update(ctx, authCM) - if err != nil { - return err - } - - return nil -} diff --git a/controllers/awsauthmapsnippet_controller.go b/controllers/awsauthmapsnippet_controller.go new file mode 100644 index 0000000..e6133f2 --- /dev/null +++ b/controllers/awsauthmapsnippet_controller.go @@ -0,0 +1,234 @@ +/* +Copyright 2021 inovex GmbH + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controllers + +import ( + "context" + + apierrs "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" + + crdv1beta1 "github.com/inovex/aws-auth-controller/api/v1beta1" +) + +// AwsAuthMapSnippetReconciler reconciles an AwsAuthMapSnippet object +type AwsAuthMapSnippetReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +const FINALIZER_NAME = "awsauth.io/finalizer" + +//+kubebuilder:rbac:groups=crd.awsauth.io,resources=awsauthmapsnippets,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=crd.awsauth.io,resources=awsauthmapsnippets/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=crd.awsauth.io,resources=awsauthmapsnippets/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.8.3/pkg/reconcile +func (r *AwsAuthMapSnippetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := log.FromContext(ctx) + logger.Info("Reconcile Request received", "objectName", req.NamespacedName) + + awsauthmap, err := GetAwsAuthMap(r.Client, ctx) + if err != nil { + return ctrl.Result{}, err + } + logger.Info("Read config map", "Roles", awsauthmap.Roles, "Users", awsauthmap.Users) + + snippet := &crdv1beta1.AwsAuthMapSnippet{} + err = r.Get(ctx, client.ObjectKey{ + Name: req.NamespacedName.Name, + Namespace: req.NamespacedName.Namespace}, + snippet) + + if err != nil { + if apierrs.IsNotFound(err) { + logger.Info("Resource already deleted, Reconciliation not needed.") + } + return ctrl.Result{}, client.IgnoreNotFound(err) + } + + // examine DeletionTimestamp to determine if object is under deletion + if snippet.ObjectMeta.DeletionTimestamp.IsZero() { + // The object is not being deleted, so if it does not have our finalizer, + // then lets add the finalizer and update the object. This is equivalent + // registering our finalizer. + if !containsString(snippet.GetFinalizers(), FINALIZER_NAME) { + controllerutil.AddFinalizer(snippet, FINALIZER_NAME) + if err := r.Update(ctx, snippet); err != nil { + return ctrl.Result{}, err + } + } + } else { + // The object is being deleted + if containsString(snippet.GetFinalizers(), FINALIZER_NAME) { + // our finalizer is present, so lets handle any external dependency + logger.Info("Finalizer called") + if err := r.CleanUpConfigMap(ctx, snippet, awsauthmap); err != nil { + return ctrl.Result{}, err + } + + // remove our finalizer from the list and update it. + controllerutil.RemoveFinalizer(snippet, FINALIZER_NAME) + if err := r.Update(ctx, snippet); err != nil { + return ctrl.Result{}, err + } + } + logger.Info("Finalizing completed") + return ctrl.Result{}, nil + } + + logger.Info("Updating ConfigMap") + if err := r.UpdateConfigMap(ctx, snippet, awsauthmap); err != nil { + logger.Error(err, "Failed to update ConfigMap") + return ctrl.Result{}, err + } + + if err := r.UpdateSnippetStatus(ctx, snippet); err != nil { + logger.Error(err, "Failed to update status") + return ctrl.Result{}, err + } + + logger.Info("Reconciliation completed") + return ctrl.Result{}, nil +} + +/* +UpdateSnippetStatus stores the ARNS that are being managed in the status +sub-object und updates the status. +*/ +func (r *AwsAuthMapSnippetReconciler) UpdateSnippetStatus(ctx context.Context, snippet *crdv1beta1.AwsAuthMapSnippet) error { + + snippet.Status.RoleArns = []string{} + snippet.Status.UserArns = []string{} + + for _, mr := range snippet.Spec.MapRoles { + snippet.Status.RoleArns = append(snippet.Status.RoleArns, mr.RoleArn) + } + for _, mu := range snippet.Spec.MapUsers { + snippet.Status.UserArns = append(snippet.Status.UserArns, mu.UserArn) + } + return r.Status().Update(ctx, snippet) +} + +/* +UpdateConfigMap removes obsolete entries from the ConfigMap and updates all +others. + +This also covers creation of new entries. +*/ +func (r *AwsAuthMapSnippetReconciler) UpdateConfigMap(ctx context.Context, snippet *crdv1beta1.AwsAuthMapSnippet, awsauth *AwsAuthMap) error { + + // find entries that need to be deleted + // (present in status, missing in spec) + for _, ra := range snippet.Status.RoleArns { + found := false + for _, role := range snippet.Spec.MapRoles { + if role.RoleArn == ra { + found = true + break + } + } + if !found { + delete(*awsauth.Roles, ra) + } + } + for _, ua := range snippet.Status.UserArns { + found := false + for _, user := range snippet.Spec.MapUsers { + if user.UserArn == ua { + found = true + break + } + } + if !found { + delete(*awsauth.Users, ua) + } + } + + // Update or create ARN mappings in the ConfigMap + for _, mr := range snippet.Spec.MapRoles { + (*awsauth.Roles)[mr.RoleArn] = mr + } + for _, mu := range snippet.Spec.MapUsers { + (*awsauth.Users)[mu.UserArn] = mu + } + + // Write the updated ConfigMap to the API + err := awsauth.Write(ctx) + if err != nil { + logger := log.FromContext(ctx) + logger.Error(err, "Error updating aws-auth ConfigMap") + return err + } + return nil +} + +/* +CleanUpConfigMap removes all ARN mappings from the ConfigMap that were managed +by this snippet. +*/ +func (r *AwsAuthMapSnippetReconciler) CleanUpConfigMap(ctx context.Context, snippet *crdv1beta1.AwsAuthMapSnippet, awsauth *AwsAuthMap) error { + for _, ra := range snippet.Status.RoleArns { + delete(*awsauth.Roles, ra) + } + for _, ua := range snippet.Status.UserArns { + delete(*awsauth.Users, ua) + } + + // Write the updated ConfigMap to the API + err := awsauth.Write(ctx) + if err != nil { + logger := log.FromContext(ctx) + logger.Error(err, "Error updating aws-auth ConfigMap") + return err + } + return nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *AwsAuthMapSnippetReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&crdv1beta1.AwsAuthMapSnippet{}). + Complete(r) +} + +// Helper functions to check and remove string from a slice of strings. +func containsString(slice []string, s string) bool { + for _, item := range slice { + if item == s { + return true + } + } + return false +} + +func removeString(slice []string, s string) (result []string) { + for _, item := range slice { + if item == s { + continue + } + result = append(result, item) + } + return +} diff --git a/controllers/awsauthmapsnippet_controller_test.go b/controllers/awsauthmapsnippet_controller_test.go new file mode 100644 index 0000000..2c64b96 --- /dev/null +++ b/controllers/awsauthmapsnippet_controller_test.go @@ -0,0 +1,103 @@ +package controllers + +import ( + "context" + "time" + + crdv1beta1 "github.com/inovex/aws-auth-controller/api/v1beta1" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/yaml" +) + +var _ = Describe("snippet controller", func() { + + It("should update status", func() { + const USER_ARN = "arn:aws:iam::123456789012:user/foobar" + snip := &crdv1beta1.AwsAuthMapSnippet{ + ObjectMeta: v1.ObjectMeta{ + Name: "testsnip", + Namespace: "default", + }, + Spec: crdv1beta1.AwsAuthMapSnippetSpec{ + MapUsers: []crdv1beta1.MapUsersSpec{ + { + UserArn: USER_ARN, + UserName: "foobar-name", + Groups: []string{"foobar-group"}, + }, + }, + }, + } + err := k8sClient.Create(context.Background(), snip) + Expect(err).ToNot(HaveOccurred()) + + Eventually(func() bool { + // check if status exists + err = k8sClient.Get(context.Background(), types.NamespacedName{ + Name: snip.Name, + Namespace: snip.Namespace, + }, snip) + if err != nil { + return false + } + if len(snip.Status.UserArns) != 1 { + return false + } + return snip.Status.UserArns[0] == USER_ARN + }, time.Second*10, time.Second).Should(BeTrue()) + + }) + It("should create the ConfigMap", func() { + const USER_ARN = "arn:aws:iam::123456789012:user/foobar" + snip := &crdv1beta1.AwsAuthMapSnippet{ + ObjectMeta: v1.ObjectMeta{ + Name: "testsnip2", + Namespace: "default", + }, + Spec: crdv1beta1.AwsAuthMapSnippetSpec{ + MapUsers: []crdv1beta1.MapUsersSpec{ + { + UserArn: USER_ARN, + UserName: "foobar-name", + Groups: []string{"foobar-group"}, + }, + }, + }, + } + err := k8sClient.Create(context.Background(), snip) + Expect(err).ToNot(HaveOccurred()) + + Eventually(func() bool { + cm := &corev1.ConfigMap{} + err = k8sClient.Get(context.Background(), types.NamespacedName{ + Name: CONFIG_MAP_NAME, + Namespace: CONFIG_MAP_NAMESPACE, + }, cm) + if err != nil { + return false + } + // Check if the mapping exists + mapYaml, ok := cm.Data[MAP_USERS_KEY] + if !ok { + return false + } + mapUsers := &MapUsers{} + // Check if the mapping is correct yaml + err = yaml.Unmarshal([]byte(mapYaml), mapUsers) + if err != nil { + return false + } + // Check if the mapping contains the ARN + for _, user := range *mapUsers { + if user.UserArn == USER_ARN { + return true + } + } + return false + }, time.Second*10, time.Second).Should(BeTrue()) + }) +}) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index f5fe433..0bd2a44 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -24,13 +24,15 @@ import ( . "github.com/onsi/gomega" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" "sigs.k8s.io/controller-runtime/pkg/envtest/printer" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" - awsauthv1beta1 "github.com/inovex/aws-auth-controller/api/v1beta1" + crdv1beta1 "github.com/inovex/aws-auth-controller/api/v1beta1" //+kubebuilder:scaffold:imports ) @@ -62,10 +64,7 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - err = awsauthv1beta1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - err = awsauthv1beta1.AddToScheme(scheme.Scheme) + err = crdv1beta1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) //+kubebuilder:scaffold:scheme @@ -74,6 +73,22 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(k8sClient).NotTo(BeNil()) + k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ + Scheme: scheme.Scheme, + }) + Expect(err).ToNot(HaveOccurred()) + + err = (&AwsAuthMapSnippetReconciler{ + Client: k8sClient, + Scheme: k8sManager.GetScheme(), + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + go func() { + defer GinkgoRecover() + Expect(k8sManager.Start(ctrl.SetupSignalHandler())).ToNot(HaveOccurred()) + }() + }, 60) var _ = AfterSuite(func() { diff --git a/main.go b/main.go index b99693a..0a8b76e 100644 --- a/main.go +++ b/main.go @@ -31,7 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" - awsauthv1beta1 "github.com/inovex/aws-auth-controller/api/v1beta1" + crdv1beta1 "github.com/inovex/aws-auth-controller/api/v1beta1" "github.com/inovex/aws-auth-controller/controllers" //+kubebuilder:scaffold:imports ) @@ -44,7 +44,7 @@ var ( func init() { utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - utilruntime.Must(awsauthv1beta1.AddToScheme(scheme)) + utilruntime.Must(crdv1beta1.AddToScheme(scheme)) //+kubebuilder:scaffold:scheme } @@ -78,11 +78,11 @@ func main() { os.Exit(1) } - if err = (&controllers.AwsAuthMapReconciler{ + if err = (&controllers.AwsAuthMapSnippetReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "AwsAuthMap") + setupLog.Error(err, "unable to create controller", "controller", "AwsAuthMapSnippet") os.Exit(1) } //+kubebuilder:scaffold:builder