-
Notifications
You must be signed in to change notification settings - Fork 3
Add crossplane aws support #411
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
Changes from all commits
ff59578
a3fc678
63d6360
83f1db3
128e12d
f7566f9
c161113
d11645d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| {{/* | ||
| Crossplane enabled check | ||
| */}} | ||
| {{- define "grafana.crossplane.enabled" -}} | ||
| {{- if and .Values.postgresqlCluster.crossplane.enabled .Values.postgresqlCluster.crossplane.clusterName -}} | ||
| true | ||
| {{- else -}} | ||
| false | ||
| {{- end -}} | ||
| {{- end -}} | ||
|
|
||
| {{/* | ||
| Crossplane is AWS/CAPA | ||
| */}} | ||
| {{- define "grafana.crossplane.isAWS" -}} | ||
| {{- if eq .Values.postgresqlCluster.crossplane.provider "aws" -}} | ||
| true | ||
| {{- else -}} | ||
| false | ||
| {{- end -}} | ||
| {{- end -}} | ||
|
|
||
| {{/* | ||
| Merge tags from cluster CR with user-provided tags | ||
| Returns tags as a map: {foo: "bar"} | ||
| */}} | ||
| {{- define "grafana.crossplane.tags" -}} | ||
| {{- $clusterName := .Values.postgresqlCluster.crossplane.clusterName -}} | ||
| {{- $clusterNamespace := .Values.postgresqlCluster.crossplane.clusterNamespace -}} | ||
| {{- $provider := .Values.postgresqlCluster.crossplane.provider -}} | ||
| {{- $tags := dict -}} | ||
| {{- if eq $provider "aws" -}} | ||
| {{- $awsCluster := lookup "infrastructure.cluster.x-k8s.io/v1beta2" "AWSCluster" $clusterNamespace $clusterName -}} | ||
| {{- if $awsCluster -}} | ||
| {{- if $awsCluster.spec.additionalTags -}} | ||
| {{- range $key, $value := $awsCluster.spec.additionalTags -}} | ||
| {{- $_ := set $tags $key $value -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- $defaultTags := dict | ||
| "app" "grafana-postgresql" | ||
| "managed-by" "crossplane" | ||
| -}} | ||
| {{- $tags = merge $tags $defaultTags -}} | ||
| {{- $userTags := .Values.postgresqlCluster.crossplane.tags | default list -}} | ||
| {{- range $tag := $userTags -}} | ||
| {{- $_ := set $tags (index $tag "key") (index $tag "value") -}} | ||
| {{- end -}} | ||
| {{- $tags | toYaml -}} | ||
| {{- end -}} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| {{/* | ||
| Get AWS Account ID from AWSCluster identity | ||
| Supports both AWSClusterRoleIdentity and AWSClusterControllerIdentity | ||
| */}} | ||
| {{- define "grafana.crossplane.aws.accountId" -}} | ||
| {{- $clusterName := .Values.postgresqlCluster.crossplane.clusterName -}} | ||
| {{- $clusterNamespace := .Values.postgresqlCluster.crossplane.clusterNamespace -}} | ||
| {{- $accountId := "" -}} | ||
| {{- $awsCluster := lookup "infrastructure.cluster.x-k8s.io/v1beta2" "AWSCluster" $clusterNamespace $clusterName -}} | ||
| {{- if $awsCluster -}} | ||
| {{- if $awsCluster.spec.identityRef -}} | ||
| {{- $identityName := $awsCluster.spec.identityRef.name -}} | ||
| {{- $identityKind := $awsCluster.spec.identityRef.kind | default "AWSClusterControllerIdentity" -}} | ||
| {{- if eq $identityKind "AWSClusterRoleIdentity" -}} | ||
| {{- $identity := lookup "infrastructure.cluster.x-k8s.io/v1beta2" "AWSClusterRoleIdentity" "" $identityName -}} | ||
| {{- if $identity -}} | ||
| {{- /* Extract account ID from roleARN like arn:aws:iam::758407694730:role/... */ -}} | ||
| {{- $roleARN := $identity.spec.roleARN -}} | ||
| {{- $parts := regexSplit "::" $roleARN -1 -}} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ARNs cannot be expected to contain There's also no need to implement
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then in that case, can we find those values in the configs repos? Because the central place for configuration is shared-configs, yet more and more things are being set in other places
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, I agree about the lookup but then where can we collect the Cluster Tags to have it applied to the MC crossplane resources? |
||
| {{- if gt (len $parts) 1 -}} | ||
| {{- $accountId = index (regexSplit ":" (index $parts 1) -1) 0 -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- else -}} | ||
| {{- $identity := lookup "infrastructure.cluster.x-k8s.io/v1beta2" "AWSClusterControllerIdentity" "" $identityName -}} | ||
| {{- if $identity -}} | ||
| {{- $accountId = $identity.spec.awsAccountID -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- $accountId -}} | ||
| {{- end -}} | ||
|
|
||
| {{/* | ||
| Get OIDC Provider URL from cluster | ||
| First tries annotation aws.giantswarm.io/irsa-trust-domains, then falls back to identity | ||
| */}} | ||
| {{- define "grafana.crossplane.aws.oidcProvider" -}} | ||
| {{- $clusterName := .Values.postgresqlCluster.crossplane.clusterName -}} | ||
| {{- $clusterNamespace := .Values.postgresqlCluster.crossplane.clusterNamespace -}} | ||
| {{- $oidcProvider := "" -}} | ||
| {{- $awsCluster := lookup "infrastructure.cluster.x-k8s.io/v1beta2" "AWSCluster" $clusterNamespace $clusterName -}} | ||
| {{- if $awsCluster -}} | ||
| {{- /* First try to get from annotation (Giant Swarm specific) */ -}} | ||
| {{- if $awsCluster.metadata.annotations -}} | ||
| {{- $oidcProvider = index $awsCluster.metadata.annotations "aws.giantswarm.io/irsa-trust-domains" | default "" -}} | ||
| {{- end -}} | ||
| {{- /* If not found in annotation, try identity ref */ -}} | ||
| {{- if and (not $oidcProvider) $awsCluster.spec.identityRef -}} | ||
| {{- $identityName := $awsCluster.spec.identityRef.name -}} | ||
| {{- $identityKind := $awsCluster.spec.identityRef.kind | default "AWSClusterControllerIdentity" -}} | ||
| {{- if eq $identityKind "AWSClusterControllerIdentity" -}} | ||
| {{- $identity := lookup "infrastructure.cluster.x-k8s.io/v1beta2" "AWSClusterControllerIdentity" "" $identityName -}} | ||
| {{- if and $identity $identity.spec.oidc -}} | ||
| {{- $oidcProvider = $identity.spec.oidc.issuerURL | trimPrefix "https://" -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- end -}} | ||
| {{- $oidcProvider -}} | ||
| {{- end -}} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| {{- if and .Values.postgresqlCluster.enabled .Values.postgresqlCluster.backup.enabled .Values.postgresqlCluster.crossplane.enabled (include "grafana.crossplane.isAWS" .) .Values.postgresqlCluster.crossplane.aws.enabled }} | ||
| {{- $bucketName := .Values.postgresqlCluster.crossplane.aws.bucket.name }} | ||
| --- | ||
| # Configures lifecycle policies for the PostgreSQL backup S3 bucket | ||
| # to automatically expire old backups after a configured number of days | ||
| apiVersion: s3.aws.upbound.io/v1beta1 | ||
| kind: BucketLifecycleConfiguration | ||
| metadata: | ||
| name: {{ $bucketName }} | ||
| namespace: {{ .Release.Namespace }} | ||
| annotations: | ||
| crossplane.io/external-name: {{ $bucketName }} | ||
| labels: | ||
| {{- include "grafana.labels" . | nindent 4 }} | ||
| app.kubernetes.io/component: storage | ||
| spec: | ||
| managementPolicies: | ||
| {{- if .Values.postgresqlCluster.crossplane.observeOnly }} | ||
| - Observe | ||
| {{- else }} | ||
| - "*" | ||
| {{- end }} | ||
| forProvider: | ||
| bucketRef: | ||
| name: {{ $bucketName }} | ||
| region: {{ .Values.postgresqlCluster.crossplane.region }} | ||
| rule: | ||
| - id: Expiration | ||
| status: Enabled | ||
| expiration: | ||
| - days: {{ .Values.postgresqlCluster.crossplane.aws.bucket.lifecycleDays }} | ||
| providerConfigRef: | ||
| name: {{ .Values.postgresqlCluster.crossplane.providerConfigRef }} | ||
| {{- end }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| {{- if and .Values.postgresqlCluster.enabled .Values.postgresqlCluster.backup.enabled .Values.postgresqlCluster.crossplane.enabled (include "grafana.crossplane.isAWS" .) .Values.postgresqlCluster.crossplane.aws.enabled }} | ||
| {{- $bucketName := .Values.postgresqlCluster.crossplane.aws.bucket.name }} | ||
| {{- $isChinaRegion := hasPrefix "cn-" .Values.postgresqlCluster.crossplane.region }} | ||
| --- | ||
| # Defines the S3 bucket policy for the PostgreSQL backup bucket | ||
| # to enforce secure transport (TLS/SSL) for all access | ||
| apiVersion: s3.aws.upbound.io/v1beta1 | ||
| kind: BucketPolicy | ||
| metadata: | ||
| name: {{ $bucketName }} | ||
| namespace: {{ .Release.Namespace }} | ||
| labels: | ||
| {{- include "grafana.labels" . | nindent 4 }} | ||
| app.kubernetes.io/component: storage | ||
| annotations: | ||
| crossplane.io/external-name: {{ $bucketName }} | ||
| spec: | ||
| managementPolicies: | ||
| {{- if .Values.postgresqlCluster.crossplane.observeOnly }} | ||
| - Observe | ||
| {{- else }} | ||
| - "*" | ||
| {{- end }} | ||
| forProvider: | ||
| bucketRef: | ||
| name: {{ $bucketName }} | ||
| policy: | | ||
| { | ||
| "Version": "2012-10-17", | ||
| "Statement": [ | ||
| { | ||
| "Sid": "EnforceSSLOnly", | ||
| "Effect": "Deny", | ||
| "Principal": "*", | ||
| "Action": ["s3:*"], | ||
| "Resource": [ | ||
| "arn:aws{{- if $isChinaRegion }}-cn{{- end }}:s3:::{{ $bucketName }}", | ||
| "arn:aws{{- if $isChinaRegion }}-cn{{- end }}:s3:::{{ $bucketName }}/*" | ||
| ], | ||
| "Condition": { | ||
| "Bool": { | ||
| "aws:SecureTransport": "false" | ||
QuentinBisson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| region: {{ .Values.postgresqlCluster.crossplane.region }} | ||
| providerConfigRef: | ||
| name: {{ .Values.postgresqlCluster.crossplane.providerConfigRef }} | ||
| {{- end }} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| {{- if and .Values.postgresqlCluster.enabled .Values.postgresqlCluster.backup.enabled .Values.postgresqlCluster.crossplane.enabled (include "grafana.crossplane.isAWS" .) .Values.postgresqlCluster.crossplane.aws.enabled }} | ||
| {{- $bucketName := .Values.postgresqlCluster.crossplane.aws.bucket.name }} | ||
| --- | ||
| # Blocks all public access to the PostgreSQL backup S3 bucket | ||
| # as access is restricted to authorized services via VPC endpoints | ||
| apiVersion: s3.aws.upbound.io/v1beta1 | ||
| kind: BucketPublicAccessBlock | ||
| metadata: | ||
| name: {{ $bucketName }} | ||
| namespace: {{ .Release.Namespace }} | ||
| annotations: | ||
| crossplane.io/external-name: {{ $bucketName }} | ||
| labels: | ||
| {{- include "grafana.labels" . | nindent 4 }} | ||
| app.kubernetes.io/component: storage | ||
| spec: | ||
| managementPolicies: | ||
| {{- if .Values.postgresqlCluster.crossplane.observeOnly }} | ||
| - Observe | ||
| {{- else }} | ||
| - "*" | ||
| {{- end }} | ||
| forProvider: | ||
| bucketRef: | ||
| name: {{ $bucketName }} | ||
| region: {{ .Values.postgresqlCluster.crossplane.region }} | ||
| blockPublicAcls: true | ||
| blockPublicPolicy: true | ||
| ignorePublicAcls: true | ||
| # Prevent public access to the bucket as we use VPC endpoints | ||
| restrictPublicBuckets: true | ||
QuentinBisson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| providerConfigRef: | ||
| name: {{ .Values.postgresqlCluster.crossplane.providerConfigRef }} | ||
| {{- end }} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| {{- if and .Values.postgresqlCluster.enabled .Values.postgresqlCluster.backup.enabled .Values.postgresqlCluster.crossplane.enabled (include "grafana.crossplane.isAWS" .) .Values.postgresqlCluster.crossplane.aws.enabled }} | ||
| {{- $tags := include "grafana.crossplane.tags" . | fromYaml }} | ||
| {{- $bucketName := .Values.postgresqlCluster.crossplane.aws.bucket.name }} | ||
| --- | ||
| # Creates the S3 bucket for storing PostgreSQL backups | ||
| # managed by CloudNativePG with barman-cloud | ||
| apiVersion: s3.aws.upbound.io/v1beta2 | ||
| kind: Bucket | ||
| metadata: | ||
| name: {{ $bucketName }} | ||
| namespace: {{ .Release.Namespace }} | ||
| labels: | ||
| {{- include "grafana.labels" . | nindent 4 }} | ||
| app.kubernetes.io/component: storage | ||
| annotations: | ||
| crossplane.io/external-name: {{ $bucketName }} | ||
| spec: | ||
| managementPolicies: | ||
| {{- if .Values.postgresqlCluster.crossplane.observeOnly }} | ||
| - Observe | ||
| {{- else }} | ||
| - "*" | ||
| {{- end }} | ||
| forProvider: | ||
| forceDestroy: false | ||
| objectLockEnabled: false | ||
| region: {{ .Values.postgresqlCluster.crossplane.region }} | ||
| {{- if $tags }} | ||
| tags: | ||
| {{- range $key, $value := $tags }} | ||
| {{ $key }}: {{ $value | quote }} | ||
| {{- end }} | ||
| {{- end }} | ||
| providerConfigRef: | ||
| name: {{ .Values.postgresqlCluster.crossplane.providerConfigRef }} | ||
| {{- end }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| {{- if and .Values.postgresqlCluster.enabled .Values.postgresqlCluster.backup.enabled .Values.postgresqlCluster.crossplane.enabled (include "grafana.crossplane.isAWS" .) .Values.postgresqlCluster.crossplane.aws.enabled .Values.postgresqlCluster.crossplane.aws.iam.enabled }} | ||
| {{- $bucketName := .Values.postgresqlCluster.crossplane.aws.bucket.name }} | ||
| {{- $namespace := .Release.Namespace }} | ||
| {{- $tags := include "grafana.crossplane.tags" . | fromYaml }} | ||
| {{- $oidcProvider := include "grafana.crossplane.aws.oidcProvider" . }} | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should use our existing ConfigMap for Crossplane-related resources to read the plural OIDC providers list: In default apps within cluster-aws, such as aws-nth-bundle, we pull the config map in directly (see here). As Theo commented, that would be a more direct way than Ensure you support multiple URLs since migrated Vintage AWS clusters may still have the old and new OIDC provider.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue is that this app is managed by collections so it should not have to mount this config right? Can we defined it in shared configs instead? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you refer to where
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AndiDog it is now deployed from https://github.com/giantswarm/management-cluster-bases/blob/convert-to-hr-tempo/bases/collections/shared/base/grafana.yaml FYI, I'm moving forward here because I will have to change the other apps as well anyway but I have no issues improving it with what you find :) |
||
| {{- $isChinaRegion := hasPrefix "cn-" .Values.postgresqlCluster.crossplane.region }} | ||
| {{- $serviceAccountName := .Values.postgresqlCluster.name }} | ||
| --- | ||
| # Creates an IAM role for PostgreSQL service accounts to access the backup S3 bucket | ||
| # using IRSA (IAM Roles for Service Accounts) with OIDC authentication | ||
| apiVersion: iam.aws.upbound.io/v1beta1 | ||
| kind: Role | ||
| metadata: | ||
| name: {{ $bucketName }} | ||
| namespace: {{ $namespace }} | ||
| labels: | ||
| {{- include "grafana.labels" . | nindent 4 }} | ||
| app.kubernetes.io/component: iam | ||
| annotations: | ||
| crossplane.io/external-name: {{ $bucketName }} | ||
| spec: | ||
| managementPolicies: | ||
| {{- if .Values.postgresqlCluster.crossplane.observeOnly }} | ||
| - Observe | ||
| {{- else }} | ||
| - "*" | ||
| {{- end }} | ||
| forProvider: | ||
QuentinBisson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| assumeRolePolicy: | | ||
| { | ||
| "Version": "2012-10-17", | ||
| "Statement": [ | ||
| { | ||
| "Effect": "Allow", | ||
| "Principal": { | ||
| "Federated": "arn:aws{{- if $isChinaRegion }}-cn{{- end }}:iam::{{ include "grafana.crossplane.aws.accountId" . }}:oidc-provider/{{ $oidcProvider }}" | ||
| }, | ||
| "Action": "sts:AssumeRoleWithWebIdentity", | ||
| "Condition": { | ||
| "StringEquals": { | ||
| "{{ $oidcProvider }}:sub": "system:serviceaccount:{{ $namespace }}:{{ $serviceAccountName }}", | ||
| "{{ $oidcProvider }}:aud": "sts.amazonaws.com{{- if $isChinaRegion }}.cn{{- end }}" | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| "Effect": "Allow", | ||
| "Principal": { | ||
| "Federated": "arn:aws{{- if $isChinaRegion }}-cn{{- end }}:iam::{{ include "grafana.crossplane.aws.accountId" . }}:oidc-provider/{{ $oidcProvider }}" | ||
| }, | ||
| "Action": "sts:AssumeRoleWithWebIdentity", | ||
| "Condition": { | ||
| "StringEquals": { | ||
| "{{ $oidcProvider }}:sub": "system:serviceaccount:{{ $namespace }}:grafana-postgresql-recovery-test", | ||
| "{{ $oidcProvider }}:aud": "sts.amazonaws.com{{- if $isChinaRegion }}.cn{{- end }}" | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| "Effect": "Allow", | ||
| "Principal": { | ||
| "Federated": "arn:aws{{- if $isChinaRegion }}-cn{{- end }}:iam::{{ include "grafana.crossplane.aws.accountId" . }}:oidc-provider/{{ $oidcProvider }}" | ||
| }, | ||
| "Action": "sts:AssumeRoleWithWebIdentity", | ||
| "Condition": { | ||
| "StringEquals": { | ||
| "{{ $oidcProvider }}:sub": "system:serviceaccount:{{ $namespace }}:plugin-barman-cloud", | ||
| "{{ $oidcProvider }}:aud": "sts.amazonaws.com{{- if $isChinaRegion }}.cn{{- end }}" | ||
| } | ||
| } | ||
| } | ||
| ] | ||
| } | ||
| inlinePolicy: | ||
| - name: {{ $bucketName }} | ||
| policy: | | ||
| { | ||
| "Version": "2012-10-17", | ||
| "Statement": [ | ||
| { | ||
| "Effect": "Allow", | ||
| "Action": [ | ||
| "s3:ListBucket", | ||
| "s3:PutObject", | ||
| "s3:GetObject", | ||
| "s3:DeleteObject" | ||
| ], | ||
| "Resource": [ | ||
| "arn:aws{{- if $isChinaRegion }}-cn{{- end }}:s3:::{{ $bucketName }}", | ||
| "arn:aws{{- if $isChinaRegion }}-cn{{- end }}:s3:::{{ $bucketName }}/*" | ||
| ] | ||
| }, | ||
| { | ||
| "Effect": "Allow", | ||
| "Action": [ | ||
| "s3:GetAccessPoint", | ||
| "s3:GetAccountPublicAccessBlock", | ||
| "s3:ListAccessPoints" | ||
| ], | ||
| "Resource": "*" | ||
| } | ||
| ] | ||
| } | ||
| {{- if $tags }} | ||
| tags: | ||
| {{- range $key, $value := $tags }} | ||
| {{ $key }}: {{ $value | quote }} | ||
| {{- end }} | ||
| {{- end }} | ||
| providerConfigRef: | ||
| name: {{ .Values.postgresqlCluster.crossplane.providerConfigRef }} | ||
| {{- end }} | ||
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.
Can't we template those variable from values rather than looking them up within the cluster ? This is not a very common pattern across our platform plus it is complex and hard to read.
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.
I did that for the others so theoretically yes we could but having to define this value in multiple places does not make sense to me considering this will always run in CAPI MCs
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.
I also don't like the fact that we create a dependency on another object here, whenever this object being lookup change we have to change this. Is there a shared/default config we can use instead of this ?
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.
There might be yes, but also, we are dependent on crossplane and a lot of other components anyway.
I'm not sure the oidcProvider is defined in shared configs though @giantswarm/team-phoenix can you let us know?