diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 06a0c30e29..79d4ea1c2b 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -136,6 +136,7 @@ Kubernetes: `>=1.29.0-0` | backups.google.bucket | string | `""` | | | backups.google.gkeEnvironment | bool | `false` | | | backups.google.path | string | `"/"` | | +| backups.instanceSidecarConfiguration | object | `{}` | Instance sidecar configuration for the ObjectStore CR (plugin mode only). Useful for setting env vars required by S3-compatible storage providers. See: https://cloudnative-pg.io/plugin-barman-cloud/docs/ | | backups.provider | string | `"s3"` | One of `s3`, `azure` or `google` | | backups.retentionPolicy | string | `"30d"` | Retention policy for backups | | backups.s3.accessKey | string | `""` | | @@ -145,7 +146,7 @@ Kubernetes: `>=1.29.0-0` | backups.s3.region | string | `""` | | | backups.s3.secretKey | string | `""` | | | backups.scheduledBackups[0].backupOwnerReference | string | `"self"` | Backup owner reference | -| backups.scheduledBackups[0].method | string | `"barmanObjectStore"` | Backup method, can be `barmanObjectStore` (default) or `volumeSnapshot` | +| backups.scheduledBackups[0].method | string | `"barmanObjectStore"` | Backup method, can be `barmanObjectStore` (default) or `volumeSnapshot`. When the barman-cloud plugin is enabled, `barmanObjectStore` is automatically upgraded to `plugin`. Use `volumeSnapshot` to bypass the plugin. | | backups.scheduledBackups[0].name | string | `"daily-backup"` | Scheduled backup name | | backups.scheduledBackups[0].schedule | string | `"0 0 0 * * *"` | Schedule in cron format | | backups.secret.create | bool | `true` | Whether to create a secret for the backup credentials | @@ -180,6 +181,7 @@ Kubernetes: `>=1.29.0-0` | cluster.monitoring.podMonitor.relabelings | list | `[]` | The list of relabelings for the PodMonitor. Applied to samples before scraping. | | cluster.monitoring.prometheusRule.enabled | bool | `true` | Whether to enable the PrometheusRule automated alerts | | cluster.monitoring.prometheusRule.excludeRules | list | `[]` | Exclude specified rules | +| cluster.plugins | list | `[]` | List of CNPG-I plugins to be loaded by the cluster. | | cluster.podSecurityContext | object | `{}` | Configure the Pod Security Context. See: https://cloudnative-pg.io/documentation/preview/security/ | | cluster.postgresGID | int | `-1` | The GID of the postgres user inside the image, defaults to 26 | | cluster.postgresUID | int | `-1` | The UID of the postgres user inside the image, defaults to 26 | diff --git a/charts/cluster/templates/_backup.tpl b/charts/cluster/templates/_backup.tpl index 60270ea699..753421ec02 100644 --- a/charts/cluster/templates/_backup.tpl +++ b/charts/cluster/templates/_backup.tpl @@ -2,6 +2,7 @@ {{- if .Values.backups.enabled }} backup: target: "prefer-standby" + {{- if eq (include "cluster.useBarmanCloudPlugin" .) "false" }} retentionPolicy: {{ .Values.backups.retentionPolicy }} barmanObjectStore: wal: @@ -19,5 +20,6 @@ backup: {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.backups "secretPrefix" "backup" }} {{- include "cluster.barmanObjectStoreConfig" $d | nindent 2 }} + {{- end }} {{- end }} {{- end }} diff --git a/charts/cluster/templates/_external_clusters.tpl b/charts/cluster/templates/_external_clusters.tpl index f2b0e8fa1c..80cba4baeb 100644 --- a/charts/cluster/templates/_external_clusters.tpl +++ b/charts/cluster/templates/_external_clusters.tpl @@ -10,11 +10,20 @@ externalClusters: - name: importSource {{- include "cluster.externalSourceCluster" .Values.recovery.import.source | nindent 4 }} {{- else if eq .Values.recovery.method "object_store" }} + {{- if eq (include "cluster.useBarmanCloudPlugin" .) "true" }} + - name: objectStoreRecoveryCluster + plugin: + name: {{ include "cluster.barmanCloudPluginName" . }} + parameters: + barmanObjectName: {{ include "cluster.barmanCloudObjectStoreName" . }} + serverName: {{ .Values.recovery.clusterName | default (include "cluster.fullname" .) }} + {{- else }} - name: objectStoreRecoveryCluster barmanObjectStore: serverName: {{ .Values.recovery.clusterName }} {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.recovery "secretPrefix" "recovery" -}} {{- include "cluster.barmanObjectStoreConfig" $d | nindent 4 }} + {{- end }} {{- end }} {{- else if eq .Values.mode "replica" }} - name: originCluster diff --git a/charts/cluster/templates/_helpers.tpl b/charts/cluster/templates/_helpers.tpl index 2bae419493..06ab69e910 100644 --- a/charts/cluster/templates/_helpers.tpl +++ b/charts/cluster/templates/_helpers.tpl @@ -144,3 +144,49 @@ Postgres GID {{- 26 -}} {{- end -}} {{- end -}} + + +{{/* +Canonical name of the barman-cloud CNPG-I plugin. +*/}} +{{- define "cluster.barmanCloudPluginName" -}} +barman-cloud.cloudnative-pg.io +{{- end -}} + + +{{/* +Returns true if the barman-cloud plugin is present and not explicitly disabled. +*/}} +{{- define "cluster.useBarmanCloudPlugin" -}} +{{- $pluginName := include "cluster.barmanCloudPluginName" . }} +{{- $hasPlugin := false }} +{{- if .Values.cluster.plugins }} + {{- range .Values.cluster.plugins }} + {{- if eq .name $pluginName }} + {{- if not (eq (toString (default true .enabled)) "false") }} + {{- $hasPlugin = true }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- $hasPlugin }} +{{- end }} + + +{{/* +ObjectStore name for the barman-cloud plugin, auto-derived or from plugin parameters. +*/}} +{{- define "cluster.barmanCloudObjectStoreName" -}} +{{- $pluginName := include "cluster.barmanCloudPluginName" . }} +{{- $name := printf "%s-object-store" (include "cluster.fullname" .) }} +{{- if .Values.cluster.plugins }} + {{- range .Values.cluster.plugins }} + {{- if eq .name $pluginName }} + {{- if and .parameters .parameters.barmanObjectName }} + {{- $name = .parameters.barmanObjectName }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- $name }} +{{- end }} diff --git a/charts/cluster/templates/cluster.yaml b/charts/cluster/templates/cluster.yaml index 4a17479773..cb61ae5cf6 100644 --- a/charts/cluster/templates/cluster.yaml +++ b/charts/cluster/templates/cluster.yaml @@ -67,6 +67,32 @@ spec: name: {{ . }} {{ end }} enablePDB: {{ .Values.cluster.enablePDB }} + {{- if .Values.cluster.plugins }} + plugins: + {{- range .Values.cluster.plugins }} + - name: {{ .name }} + {{- if hasKey . "enabled" }} + enabled: {{ .enabled }} + {{- end }} + {{- if .isWALArchiver }} + isWALArchiver: {{ .isWALArchiver }} + {{- end }} + {{- if and (eq .name (include "cluster.barmanCloudPluginName" $)) $.Values.backups.enabled }} + parameters: + barmanObjectName: {{ include "cluster.barmanCloudObjectStoreName" $ }} + {{- with .parameters }} + {{- range $k, $v := . }} + {{- if ne $k "barmanObjectName" }} + {{ $k }}: {{ $v | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- else if .parameters }} + parameters: + {{- toYaml .parameters | nindent 8 }} + {{- end }} + {{- end }} + {{- end }} postgresql: {{- if or (eq .Values.type "timescaledb") (not (empty .Values.cluster.postgresql.shared_preload_libraries)) }} shared_preload_libraries: diff --git a/charts/cluster/templates/objectStore.yaml b/charts/cluster/templates/objectStore.yaml new file mode 100644 index 0000000000..c82f08cfc1 --- /dev/null +++ b/charts/cluster/templates/objectStore.yaml @@ -0,0 +1,97 @@ +{{- if and .Values.backups.enabled (eq (include "cluster.useBarmanCloudPlugin" .) "true") }} +apiVersion: barmancloud.cnpg.io/v1 +kind: ObjectStore +metadata: + name: {{ include "cluster.barmanCloudObjectStoreName" . }} + namespace: {{ include "cluster.namespace" $ }} + {{- with .Values.cluster.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "cluster.labels" . | nindent 4 }} + {{- with .Values.cluster.additionalLabels }} + {{ toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.backups.instanceSidecarConfiguration }} + instanceSidecarConfiguration: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.backups.retentionPolicy }} + retentionPolicy: {{ . }} + {{- end }} + configuration: + {{- if .Values.backups.endpointURL }} + endpointURL: {{ .Values.backups.endpointURL | quote }} + {{- end }} + {{- if or (.Values.backups.endpointCA.create) (.Values.backups.endpointCA.name) }} + endpointCA: + name: {{ .Values.backups.endpointCA.name }} + key: {{ .Values.backups.endpointCA.key }} + {{- end }} + {{- if eq .Values.backups.provider "s3" }} + {{- if empty .Values.backups.endpointURL }} + endpointURL: "https://s3.{{ required "You need to specify S3 region if endpointURL is not specified." .Values.backups.s3.region }}.amazonaws.com" + {{- end }} + destinationPath: {{ default (printf "s3://%s%s" (required "You need to specify S3 bucket." .Values.backups.s3.bucket) .Values.backups.s3.path) .Values.backups.destinationPath }} + {{- $secretName := coalesce .Values.backups.secret.name (printf "%s-backup-s3-creds" (include "cluster.fullname" .)) }} + s3Credentials: + {{- if .Values.backups.s3.inheritFromIAMRole }} + inheritFromIAMRole: true + {{- else }} + accessKeyId: + name: {{ $secretName }} + key: ACCESS_KEY_ID + secretAccessKey: + name: {{ $secretName }} + key: ACCESS_SECRET_KEY + {{- end }} + {{- else if eq .Values.backups.provider "azure" }} + destinationPath: {{ default (printf "https://%s.%s.core.windows.net/%s%s" (required "You need to specify Azure storageAccount." .Values.backups.azure.storageAccount) .Values.backups.azure.serviceName .Values.backups.azure.containerName .Values.backups.azure.path) .Values.backups.destinationPath }} + {{- $secretName := coalesce .Values.backups.secret.name (printf "%s-backup-azure-creds" (include "cluster.fullname" .)) }} + azureCredentials: + {{- if .Values.backups.azure.inheritFromAzureAD }} + inheritFromAzureAD: true + {{- else if .Values.backups.azure.connectionString }} + connectionString: + name: {{ $secretName }} + key: AZURE_CONNECTION_STRING + {{- else }} + storageAccount: + name: {{ $secretName }} + key: AZURE_STORAGE_ACCOUNT + {{- if .Values.backups.azure.storageKey }} + storageKey: + name: {{ $secretName }} + key: AZURE_STORAGE_KEY + {{- else }} + storageSasToken: + name: {{ $secretName }} + key: AZURE_STORAGE_SAS_TOKEN + {{- end }} + {{- end }} + {{- else if eq .Values.backups.provider "google" }} + destinationPath: {{ default (printf "gs://%s%s" (required "You need to specify Google storage bucket." .Values.backups.google.bucket) .Values.backups.google.path) .Values.backups.destinationPath }} + {{- $secretName := coalesce .Values.backups.secret.name (printf "%s-backup-google-creds" (include "cluster.fullname" .)) }} + googleCredentials: + gkeEnvironment: {{ .Values.backups.google.gkeEnvironment }} + {{- if not .Values.backups.google.gkeEnvironment }} + applicationCredentials: + name: {{ $secretName }} + key: APPLICATION_CREDENTIALS + {{- end }} + {{- end }} + wal: + compression: {{ .Values.backups.wal.compression }} + {{- if .Values.backups.wal.encryption }} + encryption: {{ .Values.backups.wal.encryption }} + {{- end }} + maxParallel: {{ .Values.backups.wal.maxParallel }} + data: + compression: {{ .Values.backups.data.compression }} + {{- if .Values.backups.data.encryption }} + encryption: {{ .Values.backups.data.encryption }} + {{- end }} + jobs: {{ .Values.backups.data.jobs }} +{{- end }} diff --git a/charts/cluster/templates/scheduled-backups.yaml b/charts/cluster/templates/scheduled-backups.yaml index 47a0717d69..4d89e1b4f1 100644 --- a/charts/cluster/templates/scheduled-backups.yaml +++ b/charts/cluster/templates/scheduled-backups.yaml @@ -11,9 +11,15 @@ metadata: spec: immediate: true schedule: {{ .schedule | quote }} - method: {{ .method }} backupOwnerReference: {{ .backupOwnerReference }} cluster: name: {{ include "cluster.fullname" $context }} +{{- if and (eq (include "cluster.useBarmanCloudPlugin" $context) "true") (ne (default "" .method) "volumeSnapshot") }} + method: plugin + pluginConfiguration: + name: {{ include "cluster.barmanCloudPluginName" $context }} +{{- else }} + method: {{ default "barmanObjectStore" .method }} +{{- end }} {{ end -}} {{ end }} diff --git a/charts/cluster/test/postgresql-cluster-configuration/01-non_default_configuration_cluster-assert.yaml b/charts/cluster/test/postgresql-cluster-configuration/01-non_default_configuration_cluster-assert.yaml index 47439b88dd..ab9c6a480e 100644 --- a/charts/cluster/test/postgresql-cluster-configuration/01-non_default_configuration_cluster-assert.yaml +++ b/charts/cluster/test/postgresql-cluster-configuration/01-non_default_configuration_cluster-assert.yaml @@ -49,6 +49,12 @@ spec: name: supersecret-secret enableSuperuserAccess: true enablePDB: false + plugins: + - name: cnpg-i-plugin-example.my-org.io + enabled: true + parameters: + key1: value1 + key2: value2 certificates: serverCASecret: ca-secret serverTLSSecret: tls-secret diff --git a/charts/cluster/test/postgresql-cluster-configuration/01-non_default_configuration_cluster.yaml b/charts/cluster/test/postgresql-cluster-configuration/01-non_default_configuration_cluster.yaml index 16a048c17c..3bf134fa44 100644 --- a/charts/cluster/test/postgresql-cluster-configuration/01-non_default_configuration_cluster.yaml +++ b/charts/cluster/test/postgresql-cluster-configuration/01-non_default_configuration_cluster.yaml @@ -87,6 +87,12 @@ cluster: inRoles: - pg_monitor - pg_signal_backend + plugins: + - name: cnpg-i-plugin-example.my-org.io + enabled: true + parameters: + key1: value1 + key2: value2 postgresql: ldap: server: 'openldap.default.svc.cluster.local' diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 85645a49a7..b59bfc8f99 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -91,6 +91,9 @@ } } }, + "instanceSidecarConfiguration": { + "type": "object" + }, "provider": { "type": "string" }, @@ -281,6 +284,9 @@ } } }, + "plugins": { + "type": "array" + }, "podSecurityContext": { "type": "object" }, diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index 2c7e8b5c15..fedc52d8e9 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -350,6 +350,12 @@ cluster: # - name: custom-queries-secret # key: custom-queries + # -- List of CNPG-I plugins to be loaded by the cluster. + # @default -- `[]` + plugins: [] + # - name: barman-cloud.cloudnative-pg.io + # isWALArchiver: true + postgresql: # -- PostgreSQL configuration options (postgresql.conf) parameters: {} @@ -486,9 +492,21 @@ backups: schedule: "0 0 0 * * *" # -- Backup owner reference backupOwnerReference: self - # -- Backup method, can be `barmanObjectStore` (default) or `volumeSnapshot` + # -- Backup method, can be `barmanObjectStore` (default) or `volumeSnapshot`. + # When the barman-cloud plugin is enabled, `barmanObjectStore` is automatically + # upgraded to `plugin`. Use `volumeSnapshot` to bypass the plugin. method: barmanObjectStore + # -- Instance sidecar configuration for the ObjectStore CR (plugin mode only). + # Useful for setting env vars required by S3-compatible storage providers. + # See: https://cloudnative-pg.io/plugin-barman-cloud/docs/ + instanceSidecarConfiguration: {} + # env: + # - name: AWS_REQUEST_CHECKSUM_CALCULATION + # value: when_required + # - name: AWS_RESPONSE_CHECKSUM_VALIDATION + # value: when_required + # -- Retention policy for backups retentionPolicy: "30d"