-
Notifications
You must be signed in to change notification settings - Fork 0
fix: own runtime service accounts and optional Helm ECR identity #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # Runtime service account ownership | ||
|
|
||
| Provider stacks manage their stable service accounts through Kubernetes Objects. | ||
| DeploymentRuntimeConfigs reference those accounts with | ||
| `deploymentTemplate.spec.template.spec.serviceAccountName`. This preserves the | ||
| names used by Pod Identity and Kubernetes RBAC while allowing multiple package | ||
| revisions, and the AWS family providers, to use the same account. | ||
|
|
||
| Functions use Crossplane's generated per-revision accounts; they have no | ||
| external bindings that require a stable account name. | ||
|
|
||
| Do not set `serviceAccountTemplate.metadata.name` for shared accounts. Crossplane | ||
| then tries to make each package revision their controller, and Kubernetes rejects | ||
| the competing controller owner references. | ||
|
|
||
| ## Private ECR Helm charts | ||
|
|
||
| `HelmProviderStack.spec.aws` optionally enables EKS Pod Identity for the Helm | ||
| runtime. Set `enabled: true`, `region`, and `ecrRepositoryArns` to the private | ||
| chart repositories it may read. `aws.providerConfigRef` selects the AWS provider | ||
| configuration and defaults to the cluster name. No AWS identity is created when | ||
| this option is omitted or disabled. | ||
|
|
||
| The stack binds the role to its own stable runtime service account. Its policy | ||
| allows ECR token generation and read access only to the supplied repositories; | ||
| it does not grant repository creation, upstream import, or image publication. | ||
| This is separate from Crossplane package-manager authentication. | ||
|
|
||
| Application environments should create their own namespaced Helm | ||
| `ProviderConfig` with `credentials.source: InjectedIdentity`. The shared | ||
| Helm runtime's AWS access belongs to the provider stack. | ||
|
|
||
| Existing Helm pods must be recreated after the Pod Identity association is ready | ||
| to receive EKS credential injection. | ||
|
|
||
| ## Migrating existing installations | ||
|
|
||
| Existing accounts can still carry an owner reference to a package revision from | ||
| the previous templates. After the new runtime configuration and ServiceAccount | ||
| Object have reconciled, inspect each affected account and remove only that | ||
| obsolete ProviderRevision controller reference. Preserve unrelated metadata and | ||
| any new owner references. Do not delete the account: existing RBAC and Pod | ||
| Identity associations refer to its stable name. | ||
|
|
||
| Verify all provider and function packages become Healthy before removing old | ||
| registry infrastructure or pruning inactive package revisions. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| # code: language=yaml | ||
| {{- if $state.enabled }} | ||
| --- | ||
| apiVersion: kubernetes.m.crossplane.io/v1alpha1 | ||
| kind: Object | ||
| metadata: | ||
| name: sa-aws | ||
| annotations: | ||
| {{ setResourceNameAnnotation "sa-aws" }} | ||
| labels: {{ $state.labels | toJson }} | ||
| spec: | ||
| managementPolicies: {{ $state.managementPolicies | toJson }} | ||
| forProvider: | ||
| manifest: | ||
| apiVersion: v1 | ||
| kind: ServiceAccount | ||
| metadata: | ||
| name: {{ $state.podIdentity.saName }} | ||
| namespace: {{ $state.crossplane.namespace }} | ||
| labels: {{ $state.labels | toJson }} | ||
| providerConfigRef: | ||
| name: {{ $state.kubernetesProviderConfigRef.name }} | ||
| kind: {{ $state.kubernetesProviderConfigRef.kind }} | ||
| {{- /* Keep the stable account until all package Objects are removed. */}} | ||
| {{- $consumers := list "provider-family-aws" }} | ||
| {{- range $subName, $_ := $state.aws.selectedProviders }} | ||
| {{- $consumers = append $consumers (printf "provider-aws-%s" $subName) }} | ||
| {{- end }} | ||
| {{- $observed := $.observed.resources | default dict }} | ||
| {{- $account := (get $observed "sa-aws" | default dict).resource | default dict }} | ||
| {{- $accountReady := false }} | ||
| {{- range ($account.status | default dict).conditions | default list }} | ||
| {{- if and (eq .type "Ready") (eq .status "True") }}{{- $accountReady = true }}{{- end }} | ||
| {{- end }} | ||
| {{- range $consumer := $consumers }} | ||
| {{- $resource := (get $observed $consumer | default dict).resource | default dict }} | ||
| {{- $ready := false }} | ||
| {{- range ($resource.status | default dict).conditions | default list }} | ||
| {{- if and (eq .type "Ready") (eq .status "True") }}{{- $ready = true }}{{- end }} | ||
| {{- end }} | ||
| {{- if and $accountReady $ready }} | ||
| --- | ||
| apiVersion: protection.crossplane.io/v1beta1 | ||
| kind: Usage | ||
| metadata: | ||
| name: delete-{{ $consumer }}-before-sa-aws | ||
| annotations: | ||
| {{ setResourceNameAnnotation (printf "usage-sa-aws-%s" $consumer) }} | ||
| labels: {{ $state.labels | toJson }} | ||
| spec: | ||
| replayDeletion: true | ||
| of: | ||
| apiVersion: kubernetes.m.crossplane.io/v1alpha1 | ||
| kind: Object | ||
| resourceRef: | ||
| name: sa-aws | ||
| by: | ||
| apiVersion: kubernetes.m.crossplane.io/v1alpha1 | ||
| kind: Object | ||
| resourceRef: | ||
| name: {{ $consumer }} | ||
| {{- end }} | ||
| {{- end }} | ||
| {{- end }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| # code: language=yaml | ||
| {{- if $state.enabled }} | ||
| --- | ||
| apiVersion: kubernetes.m.crossplane.io/v1alpha1 | ||
| kind: Object | ||
| metadata: | ||
| name: sa-github | ||
| annotations: | ||
| {{ setResourceNameAnnotation "sa-github" }} | ||
| labels: {{ $state.labels | toJson }} | ||
| spec: | ||
| managementPolicies: {{ $state.managementPolicies | toJson }} | ||
| forProvider: | ||
| manifest: | ||
| apiVersion: v1 | ||
| kind: ServiceAccount | ||
| metadata: | ||
| name: {{ $state.provider.runtimeConfigName }} | ||
| namespace: {{ $state.crossplane.namespace }} | ||
| labels: {{ $state.labels | toJson }} | ||
| providerConfigRef: | ||
| name: {{ $state.kubernetesProviderConfigRef.name }} | ||
| kind: {{ $state.kubernetesProviderConfigRef.kind }} | ||
| {{- /* Keep the stable account until all package Objects are removed. */}} | ||
| {{- $consumers := list "provider-github" }} | ||
| {{- $observed := $.observed.resources | default dict }} | ||
| {{- $account := (get $observed "sa-github" | default dict).resource | default dict }} | ||
| {{- $accountReady := false }} | ||
| {{- range ($account.status | default dict).conditions | default list }} | ||
| {{- if and (eq .type "Ready") (eq .status "True") }}{{- $accountReady = true }}{{- end }} | ||
| {{- end }} | ||
| {{- range $consumer := $consumers }} | ||
| {{- $resource := (get $observed $consumer | default dict).resource | default dict }} | ||
| {{- $ready := false }} | ||
| {{- range ($resource.status | default dict).conditions | default list }} | ||
| {{- if and (eq .type "Ready") (eq .status "True") }}{{- $ready = true }}{{- end }} | ||
| {{- end }} | ||
| {{- if and $accountReady $ready }} | ||
| --- | ||
| apiVersion: protection.crossplane.io/v1beta1 | ||
| kind: Usage | ||
| metadata: | ||
| name: delete-{{ $consumer }}-before-sa-github | ||
| annotations: | ||
| {{ setResourceNameAnnotation (printf "usage-sa-github-%s" $consumer) }} | ||
| labels: {{ $state.labels | toJson }} | ||
| spec: | ||
| replayDeletion: true | ||
| of: | ||
| apiVersion: kubernetes.m.crossplane.io/v1alpha1 | ||
| kind: Object | ||
| resourceRef: | ||
| name: sa-github | ||
| by: | ||
| apiVersion: kubernetes.m.crossplane.io/v1alpha1 | ||
| kind: Object | ||
| resourceRef: | ||
| name: {{ $consumer }} | ||
| {{- end }} | ||
| {{- end }} | ||
| {{- end }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # code: language=yaml | ||
| {{- $aws := $spec.aws | default dict }} | ||
| {{- $awsConfig := $aws.providerConfigRef | default dict }} | ||
| {{- $state = set $state "aws" (dict | ||
| "enabled" ($aws.enabled | default false) | ||
| "region" ($aws.region | default "") | ||
| "ecrRepositoryArns" ($aws.ecrRepositoryArns | default list) | ||
| "providerConfigRef" (dict | ||
| "name" ($awsConfig.name | default $state.clusterName) | ||
| "kind" ($awsConfig.kind | default "ProviderConfig") | ||
| ) | ||
| ) }} | ||
| {{- $entry := get ($.observed.resources | default dict) "helm-pod-identity" | default dict }} | ||
| {{- $resource := $entry.resource | default dict }} | ||
| {{- $ready := false }} | ||
| {{- range ($resource.status | default dict).conditions | default list }} | ||
| {{- if and (eq .type "Ready") (eq .status "True") }}{{- $ready = true }}{{- end }} | ||
| {{- end }} | ||
| {{- $state = set $state "observed" (merge $state.observed (dict "podIdentity" (dict "ready" $ready))) }} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| # code: language=yaml | ||
| {{- if and $state.enabled $state.aws.enabled }} | ||
| --- | ||
| apiVersion: aws.hops.ops.com.ai/v1alpha1 | ||
| kind: PodIdentity | ||
| metadata: | ||
| name: {{ $state.name }}-helm-ecr | ||
| annotations: | ||
| {{ setResourceNameAnnotation "helm-pod-identity" }} | ||
| labels: {{ $state.labels | toJson }} | ||
| spec: | ||
| managementPolicies: {{ $state.managementPolicies | toJson }} | ||
| clusterName: {{ $state.clusterName }} | ||
| region: {{ $state.aws.region | quote }} | ||
| providerConfigRef: {{ $state.aws.providerConfigRef | toJson }} | ||
| serviceAccount: | ||
| name: {{ $state.provider.runtimeConfigName }} | ||
| namespace: {{ $state.crossplane.namespace }} | ||
| inlinePolicy: | ||
| - name: pull-ecr-charts | ||
| policy: | | ||
| { | ||
| "Version": "2012-10-17", | ||
| "Statement": [ | ||
| { | ||
| "Effect": "Allow", | ||
| "Action": "ecr:GetAuthorizationToken", | ||
| "Resource": "*" | ||
| }, | ||
| { | ||
| "Effect": "Allow", | ||
| "Action": ["ecr:BatchCheckLayerAvailability", "ecr:GetDownloadUrlForLayer", "ecr:BatchGetImage"], | ||
| "Resource": {{ $state.aws.ecrRepositoryArns | toJson }} | ||
| } | ||
| ] | ||
| } | ||
| tags: {{ $state.tags | toJson }} | ||
| {{- if and $state.observed.provider.ready $state.observed.podIdentity.ready }} | ||
| --- | ||
| apiVersion: protection.crossplane.io/v1beta1 | ||
| kind: Usage | ||
| metadata: | ||
| name: {{ $state.name }}-delete-helm-provider-before-pod-identity | ||
| annotations: | ||
| {{ setResourceNameAnnotation "usage-helm-pod-identity" }} | ||
| labels: {{ $state.labels | toJson }} | ||
| spec: | ||
| replayDeletion: true | ||
| of: | ||
| apiVersion: aws.hops.ops.com.ai/v1alpha1 | ||
| kind: PodIdentity | ||
| resourceRef: | ||
| name: {{ $state.name }}-helm-ecr | ||
| by: | ||
| apiVersion: kubernetes.m.crossplane.io/v1alpha1 | ||
| kind: Object | ||
| resourceRef: | ||
| name: provider-helm | ||
| {{- end }} | ||
| {{- end }} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: hops-ops/aws-crossplane-stack
Length of output: 7585
🏁 Script executed:
Repository: hops-ops/aws-crossplane-stack
Length of output: 32040
Add an enabled-AWS readiness-gate assertion.
The enabled-AWS test only asserts that the
PodIdentityresource renders. It does not provide an observedhelm-pod-identitywith a Ready provider, or assert top-levelstatus.ready. A regression that removes or inverts thepodIdentity.readygate can pass. Add fixtures and assertions that keep top-level status unready until PodIdentity reportsReady=True.🤖 Prompt for AI Agents