diff --git a/.github/workflows/tests-cluster-chainsaw.yaml b/.github/workflows/tests-cluster-chainsaw.yaml index 90e07b4614..140a8cb129 100644 --- a/.github/workflows/tests-cluster-chainsaw.yaml +++ b/.github/workflows/tests-cluster-chainsaw.yaml @@ -42,6 +42,15 @@ jobs: - name: Deploy the operator uses: ./.github/actions/deploy-operator + - name: Install cert-manager + uses: ./.github/actions/deploy-cert-manager + + - name: Install Barman CNPG-I plugin + run: | + helm upgrade --install plugin-barman-cloud \ + --namespace cnpg-system \ + ./charts/plugin-barman-cloud + - name: Install Prometheus CRDs run: | helm repo add prometheus-community https://prometheus-community.github.io/helm-charts diff --git a/charts/cluster/README.md b/charts/cluster/README.md index 203f8c146d..0f398dfd4f 100644 --- a/charts/cluster/README.md +++ b/charts/cluster/README.md @@ -26,6 +26,42 @@ That being said, we welcome PRs that improve the chart, but please keep in mind single configuration that the operator provides and we may reject PRs that add too much complexity and maintenance difficulty to the chart. +Migrating to the Barman Cloud Plugin +------------------------------------ + +> [!WARNING] +> Starting with version 1.26, native backup and recovery capabilities are +> being **progressively phased out** of the core operator and moved to official +> CNPG-I plugins. This transition aligns with CloudNativePG's shift towards a +> **backup-agnostic architecture**, enabled by its extensible +> interface—**CNPG-I**—which standardizes the management of **WAL archiving**, +> **physical base backups**, and corresponding **recovery processes**. + +Migrating from the Built-in CloudNativePG Backup to the Barman Cloud CNPG-I +plugin is a straightforward, single step process, as the chart creates the +necessary `ObjectStore` resource as a helm hook before the cluster is updated. + +All you have to do is change the backup method from `barmanObjectStore` to +`plugin` and specify the plugin name. The same change should also be applied to +your scheduled backups. + +```diff +backups: + enabled: true +- method: barmanObjectStore ++ method: plugin ++ pluginConfiguration: ++ name: barman-cloud.cloudnative-pg.io + scheduledBackups: + - name: daily-backup + schedule: "0 0 0 * * *" + backupOwnerReference: self +- method: barmanObjectStore ++ method: plugin ++ pluginConfiguration: ++ name: barman-cloud.cloudnative-pg.io +``` + Getting Started --------------- @@ -136,6 +172,9 @@ Kubernetes: `>=1.29.0-0` | backups.google.bucket | string | `""` | | | backups.google.gkeEnvironment | bool | `false` | | | backups.google.path | string | `"/"` | | +| backups.instanceSidecarConfiguration | object | `{}` | The configuration for the Barman Cloud Plugin sidecar that runs in the instance pods. See: https://cloudnative-pg.io/plugin-barman-cloud/docs/next/plugin-barman-cloud.v1/#instancesidecarconfiguration | +| backups.method | string | `"barmanObjectStore"` | One of `barmanObjectStore` (default) or `plugin` | +| backups.pluginConfiguration | object | `{}` | | | backups.provider | string | `"s3"` | One of `s3`, `azure` or `google` | | backups.retentionPolicy | string | `"30d"` | Retention policy for backups | | backups.s3.accessKey | string | `""` | | @@ -145,8 +184,9 @@ 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), `volumeSnapshot` or `plugin`. | | backups.scheduledBackups[0].name | string | `"daily-backup"` | Scheduled backup name | +| backups.scheduledBackups[0].pluginConfiguration | object | `{}` | | | 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 | | backups.secret.name | string | `""` | Name of the backup credentials secret | @@ -182,6 +222,7 @@ Kubernetes: `>=1.29.0-0` | cluster.monitoring.prometheusRule.enabled | bool | `true` | Whether to enable the PrometheusRule automated alerts | | cluster.monitoring.prometheusRule.excludeRules | list | `[]` | Exclude specified rules | | cluster.monitoring.tls.enabled | bool | `false` | Whether to enable TLS on the metrics port. | +| cluster.plugins | list | `[]` | Plugins When adding `barman-cloud.cloudnative-pg.io`, just specify the plugin name and set `isWALArchiver` to true. The chart will then provision an `ObjectStore` resource with the configuration from the `backups.barmanObjectStore` section. | | 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 | @@ -255,7 +296,8 @@ Kubernetes: `>=1.29.0-0` | recovery.import.source.sslRootCertSecret.name | string | `""` | | | recovery.import.source.username | string | `""` | | | recovery.import.type | string | `"microservice"` | One of `microservice` or `monolith.` See: https://cloudnative-pg.io/documentation/current/database_import/#how-it-works | -| recovery.method | string | `"backup"` | Available recovery methods: * `backup` - Recovers a CNPG cluster from a CNPG backup (PITR supported) Needs to be on the same cluster in the same namespace. * `object_store` - Recovers a CNPG cluster from a barman object store (PITR supported). * `pg_basebackup` - Recovers a CNPG cluster viaa streaming replication protocol. Useful if you want to migrate databases to CloudNativePG, even from outside Kubernetes. * `import` - Import one or more databases from an existing Postgres cluster. | +| recovery.instanceSidecarConfiguration | object | `{}` | The configuration for the Barman Cloud Plugin sidecar that runs in the instance pods. See: https://cloudnative-pg.io/plugin-barman-cloud/docs/next/plugin-barman-cloud.v1/#instancesidecarconfiguration | +| recovery.method | string | `"backup"` | Available recovery methods: * `backup` - Recovers a CNPG cluster from a CNPG backup (PITR supported) Needs to be on the same cluster in the same namespace. * `plugin` - Recovers a CNPG cluster from a backup taken with a CloudNativePG plugin (e.g. barman-cloud). * `object_store` - Recovers a CNPG cluster from a barman object store (PITR supported). * `pg_basebackup` - Recovers a CNPG cluster viaa streaming replication protocol. Useful if you want to migrate databases to CloudNativePG, even from outside Kubernetes. * `import` - Import one or more databases from an existing Postgres cluster. | | recovery.owner | string | `""` | Name of the owner of the database in the instance to be used by applications. Defaults to the value of the `database` key. | | recovery.pgBaseBackup.database | string | `"app"` | Name of the database used by the application. Default: `app`. | | recovery.pgBaseBackup.owner | string | `""` | Name of the owner of the database in the instance to be used by applications. Defaults to the value of the `database` key. | @@ -277,6 +319,7 @@ Kubernetes: `>=1.29.0-0` | recovery.pgBaseBackup.source.username | string | `""` | | | recovery.pitrTarget | object | `{"time":""}` | Point in time recovery target. Specify one of the following: | | recovery.pitrTarget.time | string | `""` | Time in RFC3339 format | +| recovery.pluginConfiguration | object | `{}` | | | recovery.provider | string | `"s3"` | One of `s3`, `azure` or `google` | | recovery.s3.accessKey | string | `""` | | | recovery.s3.bucket | string | `""` | | diff --git a/charts/cluster/templates/NOTES.txt b/charts/cluster/templates/NOTES.txt index a0cc2fef3d..3092921a33 100644 --- a/charts/cluster/templates/NOTES.txt +++ b/charts/cluster/templates/NOTES.txt @@ -75,6 +75,14 @@ Configuration {{- $image = $image.imageName -}} {{- end }} +{{- $backupPlugin := "" -}} +{{- if eq .Values.backups.method "plugin" -}} + {{- $backupPlugin = (get (include "helpers.fetchKeyValue" (list .Values.cluster.plugins (list (list "isWALArchiver" true))) | fromYaml) "name") -}} + {{- if and (empty $backupPlugin) (eq (include "cluster.barmanPluginRequired" .) "true") -}} + {{- $backupPlugin = "barman-cloud.cloudnative-pg.io" -}} + {{- end -}} +{{- end }} + ╭───────────────────┬──────────────────────────────────────────────────────────╮ │ Configuration │ Value │ ┝━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┥ @@ -87,7 +95,7 @@ Configuration │ Instances │ {{ include (printf "%s%s" "cluster.color-" $redundancyColor) (printf "%-56s" (toString .Values.cluster.instances)) }} │ │ Backups │ {{ include (printf "%s%s" "cluster.color-" (ternary "ok" "error" .Values.backups.enabled)) (printf "%-56s" (ternary "Enabled" "Disabled" .Values.backups.enabled)) }} │ {{- if .Values.backups.enabled }} -│ Backup Provider │ {{ printf "%-56s" (title .Values.backups.provider) }} │ +│ Backup Provider │ {{ printf "%-56s" (cat (title .Values.backups.method) (empty $backupPlugin | ternary "" (printf "(%s)" $backupPlugin))) }} │ │ Scheduled Backups │ {{ printf "%-56s" $scheduledBackups }} │ {{- end }} │ Storage │ {{ printf "%-56s" .Values.cluster.storage.size }} │ diff --git a/charts/cluster/templates/_backup.tpl b/charts/cluster/templates/_backup.tpl index 60270ea699..4d39284c74 100644 --- a/charts/cluster/templates/_backup.tpl +++ b/charts/cluster/templates/_backup.tpl @@ -1,5 +1,5 @@ {{- define "cluster.backup" -}} -{{- if .Values.backups.enabled }} +{{- if and .Values.backups.enabled ((eq .Values.backups.method "barmanObjectStore")) }} backup: target: "prefer-standby" retentionPolicy: {{ .Values.backups.retentionPolicy }} diff --git a/charts/cluster/templates/_bootstrap.tpl b/charts/cluster/templates/_bootstrap.tpl index c9b92f6ec3..14ecfaa824 100644 --- a/charts/cluster/templates/_bootstrap.tpl +++ b/charts/cluster/templates/_bootstrap.tpl @@ -91,6 +91,8 @@ bootstrap: name: {{ .Values.recovery.backupName }} {{- else if eq .Values.recovery.method "object_store" }} source: objectStoreRecoveryCluster + {{- else if eq .Values.recovery.method "plugin" }} + source: pluginRecoveryCluster {{- end }} {{- end }} {{- else if eq .Values.mode "replica" }} diff --git a/charts/cluster/templates/_external_clusters.tpl b/charts/cluster/templates/_external_clusters.tpl index f2b0e8fa1c..d94f49ecff 100644 --- a/charts/cluster/templates/_external_clusters.tpl +++ b/charts/cluster/templates/_external_clusters.tpl @@ -1,7 +1,9 @@ {{- define "cluster.externalClusters" -}} {{- if eq .Values.mode "standalone" }} {{- else }} +{{- if not (and (eq .Values.mode "recovery") (eq .Values.recovery.method "backup")) }} externalClusters: +{{- end }} {{- if eq .Values.mode "recovery" }} {{- if eq .Values.recovery.method "pg_basebackup" }} - name: pgBaseBackupSource @@ -15,6 +17,17 @@ externalClusters: serverName: {{ .Values.recovery.clusterName }} {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.recovery "secretPrefix" "recovery" -}} {{- include "cluster.barmanObjectStoreConfig" $d | nindent 4 }} + {{- else if and (eq .Values.recovery.method "plugin") (eq .Values.recovery.pluginConfiguration.name "barman-cloud.cloudnative-pg.io") }} + - name: pluginRecoveryCluster + plugin: + {{- omit .Values.recovery.pluginConfiguration "parameters" | toYaml | nindent 6 }} + parameters: + {{- $pluginConfigurationParameters := omit (coalesce .Values.recovery.pluginConfiguration.parameters dict) "barmanObjectName" "serverName" -}} + {{ with $pluginConfigurationParameters }} + {{- toYaml . | nindent 8 -}} + {{ end }} + barmanObjectName: {{ include "cluster.fullname" . }}-recovery + serverName: {{ .Values.recovery.clusterName }} {{- 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..6628bac870 100644 --- a/charts/cluster/templates/_helpers.tpl +++ b/charts/cluster/templates/_helpers.tpl @@ -144,3 +144,99 @@ Postgres GID {{- 26 -}} {{- end -}} {{- end -}} + + +{{/* +Check if a list of objects has a set key value pairs +*/}} +{{- /* Usage: {{ include "helpers.hasKeyValue" (list .Values.list (list (list "key" "value"))) }} */}} +{{- /* Returns the string true if the list has an item matching the key value pairs */}} +{{- define "helpers.hasKeyValue" -}} + {{- $list := index . 0 }} + {{- $kvPairs := index . 1 }} + + {{- range $list }} + {{- $item := . }} + {{- $valid := true }} + {{- range $kvPairs }} + {{- $key := index . 0 -}} + {{- $value := index . 1 -}} + {{- if or (not (hasKey $item $key)) (not (eq (get $item $key) $value)) -}} + {{- $valid = false -}} + {{- break -}} + {{- end -}} + {{- end -}} + {{- if $valid -}} + {{- "true" -}} + {{- break -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* +Similar to the omit function, returns a list of items that do not match the key value pairs +*/}} +{{- /* Usage: {{ include "helpers.omitKeyValue" (list .Values.list (list (list "key" "value"))) }} */}} +{{- /* Returns a YAML list without the items matching the key value pairs */}} +{{- define "helpers.omitKeyValue" -}} + {{- $list := index . 0 }} + {{- $kvPairs := index . 1 }} + {{- $newList := list }} + + {{- range $list }} + {{- $item := . }} + {{- $shouldOmit := false }} + {{- range $kvPairs }} + {{- $key := index . 0 -}} + {{- $value := index . 1 -}} + {{- if and (hasKey $item $key) (eq (get $item $key) $value) -}} + {{- $shouldOmit = true -}} + {{- end -}} + {{- end -}} + {{- if not $shouldOmit -}} + {{- $newList = append $newList $item -}} + {{- end -}} + {{- end -}} + {{- $newList | toYaml }} +{{ end -}} + + +{{/* +Given a list of objects, returns the first item that matches the key value pairs specified +*/}} +{{- /* Usage: {{ include "helpers.fetchKeyValue" (list .Values.list (list (list "key" "value"))) }} */}} +{{- /* Returns a YAML object of the first item in the list has an item matching the key value pairs */}} +{{- define "helpers.fetchKeyValue" -}} + {{- $list := index . 0 }} + {{- $kvPairs := index . 1 }} + {{- range $list }} + {{- $item := . }} + {{- $valid := true }} + {{- range $kvPairs }} + {{- $key := index . 0 -}} + {{- $value := index . 1 -}} + {{- if or (not (hasKey $item $key)) (not (eq (get $item $key) $value)) -}} + {{- $valid = false -}} + {{- break -}} + {{- end -}} + {{- end -}} + {{- if $valid -}} + {{- toYaml $item -}} + {{- break -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{- define "cluster.barmanPluginEnabled" -}} + {{- eq (include "helpers.hasKeyValue" (list .Values.cluster.plugins (list (list "name" "barman-cloud.cloudnative-pg.io") (list "isWALArchiver" true)))) "true" -}} +{{- end -}} + +{{- define "cluster.barmanPluginRequired" -}} + {{- $required := false -}} + {{- if and .Values.backups.enabled (eq .Values.backups.method "plugin") -}} + {{- if eq .Values.backups.pluginConfiguration.name "barman-cloud.cloudnative-pg.io" -}} + {{- $required = true -}} + {{- end -}} + {{- end -}} + {{- $required -}} +{{- end -}} diff --git a/charts/cluster/templates/backup-objectstore.yaml b/charts/cluster/templates/backup-objectstore.yaml new file mode 100644 index 0000000000..7bedc6ee58 --- /dev/null +++ b/charts/cluster/templates/backup-objectstore.yaml @@ -0,0 +1,36 @@ +{{- if eq (include "cluster.barmanPluginRequired" .) "true" }} +apiVersion: barmancloud.cnpg.io/v1 +kind: ObjectStore +metadata: + name: {{ include "cluster.fullname" . }}-backups + namespace: {{ include "cluster.namespace" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,pre-rollback + {{- with .Values.cluster.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "cluster.labels" . | nindent 4 }} + {{- with .Values.cluster.additionalLabels }} + {{ toYaml . | nindent 4 }} + {{- end }} +spec: + retentionPolicy: {{ .Values.backups.retentionPolicy }} + configuration: + 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 }} + {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.backups "secretPrefix" "backup" }} + {{- include "cluster.barmanObjectStoreConfig" $d | nindent 2 }} + instanceSidecarConfiguration: + {{- .Values.backups.instanceSidecarConfiguration | toYaml | nindent 4 }} +{{- end }} diff --git a/charts/cluster/templates/ca-bundle.yaml b/charts/cluster/templates/ca-bundle.yaml index d4a27e1162..80981d68fb 100644 --- a/charts/cluster/templates/ca-bundle.yaml +++ b/charts/cluster/templates/ca-bundle.yaml @@ -1,3 +1,4 @@ +{{- if and .Values.backups.enabled (or ((eq .Values.backups.method "barmanObjectStore")) (eq (include "cluster.barmanPluginEnabled" .) "true")) }} {{- if .Values.backups.endpointCA.create }} apiVersion: v1 kind: Secret @@ -7,3 +8,4 @@ metadata: data: {{ .Values.backups.endpointCA.key | default "ca-bundle.crt" | quote }}: {{ .Values.backups.endpointCA.value }} {{- end }} +{{- end }} diff --git a/charts/cluster/templates/cluster.yaml b/charts/cluster/templates/cluster.yaml index a853019fed..957595d687 100644 --- a/charts/cluster/templates/cluster.yaml +++ b/charts/cluster/templates/cluster.yaml @@ -56,6 +56,9 @@ spec: primaryUpdateMethod: {{ .Values.cluster.primaryUpdateMethod }} primaryUpdateStrategy: {{ .Values.cluster.primaryUpdateStrategy }} + {{- with .Values.cluster.stopDelay }} + stopDelay: {{ . }} + {{- end }} logLevel: {{ .Values.cluster.logLevel }} {{- with .Values.cluster.certificates }} certificates: @@ -148,6 +151,28 @@ spec: {{- end }} tls: enabled: {{ .Values.cluster.monitoring.tls.enabled }} - {{ include "cluster.bootstrap" . | nindent 2 }} - {{ include "cluster.externalClusters" . | nindent 2 }} - {{ include "cluster.backup" . | nindent 2 }} + + {{ include "cluster.bootstrap" . | nindent 2 -}} + {{ include "cluster.externalClusters" . | nindent 2 -}} + {{ include "cluster.backup" . | nindent 2 -}} + + {{- if or (gt (len .Values.cluster.plugins) 0) (eq (include "cluster.barmanPluginRequired" .) "true") }} + plugins: + {{ $pluginsWithoutBarman := include "helpers.omitKeyValue" (list .Values.cluster.plugins (list (list "name" "barman-cloud.cloudnative-pg.io"))) | fromYamlArray }} + {{- range $pluginsWithoutBarman -}} + - {{ toYaml . | indent 6 | trim }} + {{- end }} + {{- if or (eq (include "cluster.barmanPluginEnabled" .) "true") (eq (include "cluster.barmanPluginRequired" .) "true") }} + {{- $barmanPlugin := include "helpers.fetchKeyValue" (list .Values.cluster.plugins (list (list "name" "barman-cloud.cloudnative-pg.io"))) | fromYaml }} + - name: barman-cloud.cloudnative-pg.io + enabled: {{ eq $barmanPlugin.enabled nil | ternary (eq (include "cluster.barmanPluginRequired" .) "true") $barmanPlugin.enabled }} + isWALArchiver: {{ eq $barmanPlugin.isWALArchiver nil | ternary (eq (include "cluster.barmanPluginRequired" .) "true") $barmanPlugin.isWALArchiver }} + parameters: + {{- $parameters := (coalesce $barmanPlugin.parameters dict) -}} + {{- $parametersWithoutObjectName := (omit $parameters "barmanObjectName") }} + {{ with $parametersWithoutObjectName }} + {{- toYaml . | indent 8 | trim }} + {{- end }} + barmanObjectName: {{ $parameters.barmanObjectName | default (printf "%s-backups" (include "cluster.fullname" .)) }} + {{- end }} + {{- end }} diff --git a/charts/cluster/templates/recovery-objectstore.yaml b/charts/cluster/templates/recovery-objectstore.yaml new file mode 100644 index 0000000000..d1b2f8c4d5 --- /dev/null +++ b/charts/cluster/templates/recovery-objectstore.yaml @@ -0,0 +1,23 @@ +{{- if and (eq .Values.mode "recovery") (eq .Values.recovery.method "plugin") (eq .Values.recovery.pluginConfiguration.name "barman-cloud.cloudnative-pg.io") }} +apiVersion: barmancloud.cnpg.io/v1 +kind: ObjectStore +metadata: + name: {{ include "cluster.fullname" . }}-recovery + namespace: {{ include "cluster.namespace" . }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade,pre-rollback + {{- with .Values.cluster.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "cluster.labels" . | nindent 4 }} + {{- with .Values.cluster.additionalLabels }} + {{ toYaml . | nindent 4 }} + {{- end }} +spec: + configuration: + {{- $d := dict "chartFullname" (include "cluster.fullname" .) "scope" .Values.recovery "secretPrefix" "recovery" -}} + {{- include "cluster.barmanObjectStoreConfig" $d | indent 2 }} + instanceSidecarConfiguration: + {{- .Values.recovery.instanceSidecarConfiguration | toYaml | nindent 4 }} +{{- end }} diff --git a/charts/cluster/templates/recovery-s3-creds.yaml b/charts/cluster/templates/recovery-s3-creds.yaml index d70ed2120f..cf170a647b 100644 --- a/charts/cluster/templates/recovery-s3-creds.yaml +++ b/charts/cluster/templates/recovery-s3-creds.yaml @@ -1,4 +1,4 @@ -{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.method "object_store") (eq .Values.recovery.provider "s3") (not .Values.recovery.s3.inheritFromIAMRole) .Values.recovery.secret.create }} +{{- if and (eq .Values.mode "recovery" ) (eq .Values.recovery.provider "s3") (not (has .Values.recovery.method (list "backup" "import" "pg_basebackup"))) (not .Values.recovery.s3.inheritFromIAMRole) .Values.recovery.secret.create }} apiVersion: v1 kind: Secret metadata: diff --git a/charts/cluster/templates/scheduled-backups.yaml b/charts/cluster/templates/scheduled-backups.yaml index 4db3d56c5f..4821d070ed 100644 --- a/charts/cluster/templates/scheduled-backups.yaml +++ b/charts/cluster/templates/scheduled-backups.yaml @@ -16,5 +16,9 @@ spec: backupOwnerReference: {{ .backupOwnerReference }} cluster: name: {{ include "cluster.fullname" $context }} + {{- with .pluginConfiguration }} + pluginConfiguration: + {{- toYaml . | nindent 4 }} + {{ end }} {{ end -}} {{ end }} diff --git a/charts/cluster/test/barman-plugin-backup-restore/00-minio_cleanup-assert.yaml b/charts/cluster/test/barman-plugin-backup-restore/00-minio_cleanup-assert.yaml new file mode 100644 index 0000000000..9c0f3eb480 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/00-minio_cleanup-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +status: + succeeded: 1 diff --git a/charts/cluster/test/barman-plugin-backup-restore/00-minio_cleanup.yaml b/charts/cluster/test/barman-plugin-backup-restore/00-minio_cleanup.yaml new file mode 100644 index 0000000000..4d8c6b1d17 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/00-minio_cleanup.yaml @@ -0,0 +1,16 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: minio-cleanup + image: minio/mc:latest + command: ['sh', '-c'] + args: + - | + mc alias set myminio https://minio.minio.svc.cluster.local minio minio123 + mc rm --recursive --force myminio/mybucket/barman-plugin-backup-restore diff --git a/charts/cluster/test/barman-plugin-backup-restore/01-barman-plugin-backup-restore_cluster-assert.yaml b/charts/cluster/test/barman-plugin-backup-restore/01-barman-plugin-backup-restore_cluster-assert.yaml new file mode 100644 index 0000000000..64335d206e --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/01-barman-plugin-backup-restore_cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: barman-plugin-backup-restore-cluster +status: + readyInstances: 2 diff --git a/charts/cluster/test/barman-plugin-backup-restore/01-barman-plugin-backup-restore_cluster.yaml b/charts/cluster/test/barman-plugin-backup-restore/01-barman-plugin-backup-restore_cluster.yaml new file mode 100644 index 0000000000..141de74506 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/01-barman-plugin-backup-restore_cluster.yaml @@ -0,0 +1,29 @@ +type: postgresql +mode: standalone +cluster: + instances: 2 + storage: + size: 256Mi + +backups: + enabled: true + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/barman-plugin-backup-restore/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" diff --git a/charts/cluster/test/barman-plugin-backup-restore/02-backup.yaml b/charts/cluster/test/barman-plugin-backup-restore/02-backup.yaml new file mode 100644 index 0000000000..508e58106d --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/02-backup.yaml @@ -0,0 +1,10 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-init-backup +spec: + cluster: + name: barman-plugin-backup-restore-cluster + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io diff --git a/charts/cluster/test/barman-plugin-backup-restore/02-backup_completed-assert.yaml b/charts/cluster/test/barman-plugin-backup-restore/02-backup_completed-assert.yaml new file mode 100644 index 0000000000..aa81876de0 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/02-backup_completed-assert.yaml @@ -0,0 +1,12 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-init-backup +spec: + cluster: + name: barman-plugin-backup-restore-cluster + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io +status: + phase: completed diff --git a/charts/cluster/test/barman-plugin-backup-restore/02-checkpoint.yaml b/charts/cluster/test/barman-plugin-backup-restore/02-checkpoint.yaml new file mode 100644 index 0000000000..83e50ab849 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/02-checkpoint.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: backup-checkpoint +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: create-checkpoint + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: barman-plugin-backup-restore-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + END_TIME=$(( $(date +%s) + 30 )) + while [ $(date +%s) -lt $END_TIME ]; do + psql "$DB_URI" -c "SELECT pg_switch_wal();CHECKPOINT;" + sleep 5 + done diff --git a/charts/cluster/test/barman-plugin-backup-restore/03-data_write-assert.yaml b/charts/cluster/test/barman-plugin-backup-restore/03-data_write-assert.yaml new file mode 100644 index 0000000000..831f963d9d --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/03-data_write-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +status: + succeeded: 1 diff --git a/charts/cluster/test/barman-plugin-backup-restore/03-data_write.yaml b/charts/cluster/test/barman-plugin-backup-restore/03-data_write.yaml new file mode 100644 index 0000000000..1093ad0168 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/03-data_write.yaml @@ -0,0 +1,54 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: configmap-creator-sa +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: configmap-creator +rules: +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["create"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: configmap-creator-binding +subjects: +- kind: ServiceAccount + name: configmap-creator-sa +roleRef: + kind: Role + name: configmap-creator + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +spec: + template: + spec: + serviceAccountName: configmap-creator-sa + restartPolicy: OnFailure + containers: + - name: data-write + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: barman-plugin-backup-restore-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client kubectl coreutils + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + psql "$DB_URI" -c "CREATE TABLE mygoodtable (id serial PRIMARY KEY);" + sleep 5 + DATE_NO_BAD_TABLE=$(date --rfc-3339=ns) + kubectl create configmap date-no-bad-table --from-literal=date="$DATE_NO_BAD_TABLE" + sleep 5 diff --git a/charts/cluster/test/barman-plugin-backup-restore/04-post_backup_data_write-assert.yaml b/charts/cluster/test/barman-plugin-backup-restore/04-post_backup_data_write-assert.yaml new file mode 100644 index 0000000000..ad9be77a7b --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/04-post_backup_data_write-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write-post-backup +status: + succeeded: 1 diff --git a/charts/cluster/test/barman-plugin-backup-restore/04-post_backup_data_write.yaml b/charts/cluster/test/barman-plugin-backup-restore/04-post_backup_data_write.yaml new file mode 100644 index 0000000000..c79858c652 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/04-post_backup_data_write.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write-post-backup +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-write + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: barman-plugin-backup-restore-cluster-superuser + key: uri + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + psql "$DB_URI" -c "CREATE TABLE mybadtable (id serial PRIMARY KEY);" diff --git a/charts/cluster/test/barman-plugin-backup-restore/05-recovery_backup_pitr_cluster-assert.yaml b/charts/cluster/test/barman-plugin-backup-restore/05-recovery_backup_pitr_cluster-assert.yaml new file mode 100644 index 0000000000..2b6b9651f1 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/05-recovery_backup_pitr_cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: recovery-backup-pitr-cluster +status: + readyInstances: 2 diff --git a/charts/cluster/test/barman-plugin-backup-restore/05-recovery_backup_pitr_cluster.yaml b/charts/cluster/test/barman-plugin-backup-restore/05-recovery_backup_pitr_cluster.yaml new file mode 100644 index 0000000000..8b6cb27f59 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/05-recovery_backup_pitr_cluster.yaml @@ -0,0 +1,53 @@ +type: postgresql +mode: recovery + +cluster: + instances: 2 + storage: + size: 256Mi + +recovery: + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io + clusterName: "barman-plugin-backup-restore-cluster" + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/barman-plugin-backup-restore/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" + +backups: + enabled: true + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/barman-plugin-backup-restore/v2" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" diff --git a/charts/cluster/test/barman-plugin-backup-restore/06-data_test-assert.yaml b/charts/cluster/test/barman-plugin-backup-restore/06-data_test-assert.yaml new file mode 100644 index 0000000000..6f14d5f231 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/06-data_test-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-backup-pitr +status: + succeeded: 1 diff --git a/charts/cluster/test/barman-plugin-backup-restore/06-data_test.yaml b/charts/cluster/test/barman-plugin-backup-restore/06-data_test.yaml new file mode 100644 index 0000000000..5fb4faf395 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/06-data_test.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-backup-pitr +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-test + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: recovery-backup-pitr-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + set -e + test "$(psql $DB_URI -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mygoodtable$$)' --csv -q 2>/dev/null)" = "t" + echo "Good table exists" + test "$(psql $DB_URI -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mybadtable$$)' --csv -q 2>/dev/null)" = "f" + echo "Bad table does not exist" diff --git a/charts/cluster/test/barman-plugin-backup-restore/chainsaw-test.yaml b/charts/cluster/test/barman-plugin-backup-restore/chainsaw-test.yaml new file mode 100644 index 0000000000..61fb0fdba8 --- /dev/null +++ b/charts/cluster/test/barman-plugin-backup-restore/chainsaw-test.yaml @@ -0,0 +1,122 @@ +## +# This test CNPG Cluster backups and PITR recovery with the Barman Cloud CNPG-I plugin +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: barman-plugin-backup-restore +spec: + timeouts: + apply: 1s + assert: 7m + cleanup: 1m + steps: + - name: Clear the MinIO bucket + try: + - apply: + file: ./00-minio_cleanup.yaml + - assert: + file: ./00-minio_cleanup-assert.yaml + - name: Install a standalone CNPG Cluster + try: + - script: + content: | + kubectl -n $NAMESPACE create secret generic kube-root-ca.crt --from-literal=ca.crt="$(kubectl -n kube-system get configmaps kube-root-ca.crt -o jsonpath='{.data.ca\.crt}')" --dry-run=client -o yaml | kubectl apply -f - + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./01-barman-plugin-backup-restore_cluster.yaml \ + --wait \ + barman-plugin-backup-restore ../../ + - assert: + file: ./01-barman-plugin-backup-restore_cluster-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - podLogs: + selector: cnpg.io/cluster=barman-plugin-backup-restore-cluster + - name: Create a backup + try: + - apply: + file: ./02-backup.yaml + - apply: + file: ./02-checkpoint.yaml + - assert: + file: ./02-backup_completed-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Backup + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - podLogs: + selector: cnpg.io/cluster=barman-plugin-backup-restore-cluster + - name: Write some data to the cluster + timeouts: + apply: 1s + assert: 30s + try: + - apply: + file: ./03-data_write.yaml + - assert: + file: ./03-data_write-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + - podLogs: + selector: batch.kubernetes.io/job-name=data-test + - name: Write more data to the database after the backup + try: + - apply: + file: ./04-post_backup_data_write.yaml + - assert: + file: ./04-post_backup_data_write-assert.yaml + timeouts: + apply: 1s + assert: 10m + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Backup + - name: Create a recovery cluster from backup with a PITR target + try: + - script: + content: | + DATE_NO_BAD_TABLE=$(kubectl -n $NAMESPACE get configmap date-no-bad-table -o 'jsonpath={.data.date}') + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./05-recovery_backup_pitr_cluster.yaml \ + --set recovery.pitrTarget.time="$DATE_NO_BAD_TABLE" \ + --wait \ + recovery-backup-pitr ../../ + - assert: + file: ./05-recovery_backup_pitr_cluster-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - podLogs: + selector: cnpg.io/cluster=recovery-backup-pitr-cluster + - name: Verify the pre-backup data on the recovery cluster exists but not the post-backup data + try: + - apply: + file: ./06-data_test.yaml + - assert: + file: ./06-data_test-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + selector: batch.kubernetes.io/job-name=data-test-backup-pitr + - podLogs: + selector: batch.kubernetes.io/job-name=data-test-backup-pitr + - name: Cleanup + try: + - script: + content: | + kubectl --namespace $NAMESPACE delete scheduledbackups.postgresql.cnpg.io --all + kubectl --namespace $NAMESPACE delete backups.postgresql.cnpg.io --all + helm uninstall --namespace $NAMESPACE barman-plugin-backup-restore diff --git a/charts/cluster/test/barman-plugin-explicit/01-barman_plugin_explicit_config_cluster-assert.yaml b/charts/cluster/test/barman-plugin-explicit/01-barman_plugin_explicit_config_cluster-assert.yaml new file mode 100644 index 0000000000..6d3310f623 --- /dev/null +++ b/charts/cluster/test/barman-plugin-explicit/01-barman_plugin_explicit_config_cluster-assert.yaml @@ -0,0 +1,14 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: barman-plugin-explicit-config-cluster +spec: + plugins: + - name: foo.example.com + enabled: true + isWALArchiver: false + parameters: + foo: bar + - name: barman-cloud.cloudnative-pg.io + enabled: true + isWALArchiver: true diff --git a/charts/cluster/test/barman-plugin-explicit/01-barman_plugin_explicit_config_cluster.yaml b/charts/cluster/test/barman-plugin-explicit/01-barman_plugin_explicit_config_cluster.yaml new file mode 100644 index 0000000000..dce181cf13 --- /dev/null +++ b/charts/cluster/test/barman-plugin-explicit/01-barman_plugin_explicit_config_cluster.yaml @@ -0,0 +1,37 @@ +type: postgresql +mode: standalone +cluster: + instances: 1 + storage: + size: 256Mi + plugins: + - name: foo.example.com + enabled: true + isWALArchiver: false + parameters: + foo: bar + - name: barman-cloud.cloudnative-pg.io + enabled: true + isWALArchiver: true + +backups: + enabled: true + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/barman-plugin-scheduledbackups/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + retentionPolicy: "30d" diff --git a/charts/cluster/test/barman-plugin-explicit/02-barman_plugin_config_override_cluster-assert.yaml b/charts/cluster/test/barman-plugin-explicit/02-barman_plugin_config_override_cluster-assert.yaml new file mode 100644 index 0000000000..e595e8bd2f --- /dev/null +++ b/charts/cluster/test/barman-plugin-explicit/02-barman_plugin_config_override_cluster-assert.yaml @@ -0,0 +1,17 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: barman-plugin-config-override-cluster +spec: + plugins: + - name: foo.example.com + enabled: true + isWALArchiver: false + parameters: + foo: bar + - name: barman-cloud.cloudnative-pg.io + enabled: false + isWALArchiver: false + parameters: + foo: bar + barmanObjectName: asd diff --git a/charts/cluster/test/barman-plugin-explicit/02-barman_plugin_config_override_cluster.yaml b/charts/cluster/test/barman-plugin-explicit/02-barman_plugin_config_override_cluster.yaml new file mode 100644 index 0000000000..a06a6eef10 --- /dev/null +++ b/charts/cluster/test/barman-plugin-explicit/02-barman_plugin_config_override_cluster.yaml @@ -0,0 +1,40 @@ +type: postgresql +mode: standalone +cluster: + instances: 1 + storage: + size: 256Mi + plugins: + - name: foo.example.com + enabled: true + isWALArchiver: false + parameters: + foo: bar + - name: barman-cloud.cloudnative-pg.io + enabled: false + isWALArchiver: false + parameters: + foo: bar + barmanObjectName: asd + +backups: + enabled: true + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/barman-plugin-scheduledbackups/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + retentionPolicy: "30d" diff --git a/charts/cluster/test/barman-plugin-explicit/chainsaw-test.yaml b/charts/cluster/test/barman-plugin-explicit/chainsaw-test.yaml new file mode 100644 index 0000000000..0fd08835e8 --- /dev/null +++ b/charts/cluster/test/barman-plugin-explicit/chainsaw-test.yaml @@ -0,0 +1,41 @@ +# Tests the Barman Cloud CNPG-I Plugin with explicit plugin configuration +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: barman-plugin-explicit +spec: + timeouts: + apply: 1s + assert: 5s + cleanup: 1m + steps: + - name: Install a cluster with a custom plugin section + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./01-barman_plugin_explicit_config_cluster.yaml \ + --wait \ + barman-plugin-explicit-config ../../ + - assert: + file: ./01-barman_plugin_explicit_config_cluster-assert.yaml + - name: Install the cluster with a custom plugin section that overrides the default configuration + try: + - script: + content: | + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./02-barman_plugin_config_override_cluster.yaml \ + --wait \ + barman-plugin-config-override ../../ + - assert: + file: ./02-barman_plugin_config_override_cluster-assert.yaml + - name: Cleanup + try: + - script: + content: | + helm --namespace $NAMESPACE uninstall barman-plugin-explicit-config + helm --namespace $NAMESPACE uninstall barman-plugin-config-override diff --git a/charts/cluster/test/barman-plugin-migration/00-minio_cleanup-assert.yaml b/charts/cluster/test/barman-plugin-migration/00-minio_cleanup-assert.yaml new file mode 100644 index 0000000000..9c0f3eb480 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/00-minio_cleanup-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +status: + succeeded: 1 diff --git a/charts/cluster/test/barman-plugin-migration/00-minio_cleanup.yaml b/charts/cluster/test/barman-plugin-migration/00-minio_cleanup.yaml new file mode 100644 index 0000000000..a06dbb2bf8 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/00-minio_cleanup.yaml @@ -0,0 +1,16 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: minio-cleanup + image: minio/mc:latest + command: ['sh', '-c'] + args: + - | + mc alias set myminio https://minio.minio.svc.cluster.local minio minio123 + mc rm --recursive --force myminio/mybucket/barman-plugin-migration diff --git a/charts/cluster/test/barman-plugin-migration/01-barman-plugin-migration_cluster-assert.yaml b/charts/cluster/test/barman-plugin-migration/01-barman-plugin-migration_cluster-assert.yaml new file mode 100644 index 0000000000..2475d88f6c --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/01-barman-plugin-migration_cluster-assert.yaml @@ -0,0 +1,7 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: barman-plugin-migration-cluster +status: + readyInstances: 2 + phase: Cluster in healthy state diff --git a/charts/cluster/test/barman-plugin-migration/01-barman-plugin-migration_cluster.yaml b/charts/cluster/test/barman-plugin-migration/01-barman-plugin-migration_cluster.yaml new file mode 100644 index 0000000000..738dbb8767 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/01-barman-plugin-migration_cluster.yaml @@ -0,0 +1,27 @@ +type: postgresql +mode: standalone +cluster: + instances: 2 + storage: + size: 256Mi + +backups: + enabled: true + method: barmanObjectStore + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/barman-plugin-migration/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" diff --git a/charts/cluster/test/barman-plugin-migration/02-backup.yaml b/charts/cluster/test/barman-plugin-migration/02-backup.yaml new file mode 100644 index 0000000000..8f6a692ab3 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/02-backup.yaml @@ -0,0 +1,7 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-init-backup +spec: + cluster: + name: barman-plugin-migration-cluster diff --git a/charts/cluster/test/barman-plugin-migration/02-backup_completed-assert.yaml b/charts/cluster/test/barman-plugin-migration/02-backup_completed-assert.yaml new file mode 100644 index 0000000000..1ffe611eeb --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/02-backup_completed-assert.yaml @@ -0,0 +1,9 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-init-backup +spec: + cluster: + name: barman-plugin-migration-cluster +status: + phase: completed diff --git a/charts/cluster/test/barman-plugin-migration/02-checkpoint.yaml b/charts/cluster/test/barman-plugin-migration/02-checkpoint.yaml new file mode 100644 index 0000000000..e740f9f831 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/02-checkpoint.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: backup-checkpoint +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: create-checkpoint + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: barman-plugin-migration-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + END_TIME=$(( $(date +%s) + 30 )) + while [ $(date +%s) -lt $END_TIME ]; do + psql "$DB_URI" -c "SELECT pg_switch_wal();CHECKPOINT;" + sleep 5 + done diff --git a/charts/cluster/test/barman-plugin-migration/03-data_write-assert.yaml b/charts/cluster/test/barman-plugin-migration/03-data_write-assert.yaml new file mode 100644 index 0000000000..831f963d9d --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/03-data_write-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +status: + succeeded: 1 diff --git a/charts/cluster/test/barman-plugin-migration/03-data_write.yaml b/charts/cluster/test/barman-plugin-migration/03-data_write.yaml new file mode 100644 index 0000000000..d8ff354d49 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/03-data_write.yaml @@ -0,0 +1,54 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: configmap-creator-sa +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: configmap-creator +rules: +- apiGroups: [""] + resources: ["configmaps"] + verbs: ["create"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: configmap-creator-binding +subjects: +- kind: ServiceAccount + name: configmap-creator-sa +roleRef: + kind: Role + name: configmap-creator + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write +spec: + template: + spec: + serviceAccountName: configmap-creator-sa + restartPolicy: OnFailure + containers: + - name: data-write + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: barman-plugin-migration-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client kubectl coreutils + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + psql "$DB_URI" -c "CREATE TABLE mygoodtable (id serial PRIMARY KEY);" + sleep 5 + DATE_NO_BAD_TABLE=$(date --rfc-3339=ns) + kubectl create configmap date-no-bad-table --from-literal=date="$DATE_NO_BAD_TABLE" + sleep 5 diff --git a/charts/cluster/test/barman-plugin-migration/04-barman-plugin-migration_cluster-healthy-assert.yaml b/charts/cluster/test/barman-plugin-migration/04-barman-plugin-migration_cluster-healthy-assert.yaml new file mode 100644 index 0000000000..56204cb54b --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/04-barman-plugin-migration_cluster-healthy-assert.yaml @@ -0,0 +1,19 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: barman-plugin-migration-cluster +status: + readyInstances: 2 + phase: Cluster in healthy state +spec: + plugins: + - name: barman-cloud.cloudnative-pg.io + enabled: true + isWALArchiver: true + parameters: + barmanObjectName: barman-plugin-migration-cluster-backups +--- +apiVersion: barmancloud.cnpg.io/v1 +kind: ObjectStore +metadata: + name: barman-plugin-migration-cluster-backups diff --git a/charts/cluster/test/barman-plugin-migration/04-barman-plugin-migration_cluster-upgrading-assert.yaml b/charts/cluster/test/barman-plugin-migration/04-barman-plugin-migration_cluster-upgrading-assert.yaml new file mode 100644 index 0000000000..6682a100fa --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/04-barman-plugin-migration_cluster-upgrading-assert.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Event +reason: UpgradingInstance +involvedObject: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + name: barman-plugin-migration-cluster diff --git a/charts/cluster/test/barman-plugin-migration/04-barman-plugin-migration_cluster.yaml b/charts/cluster/test/barman-plugin-migration/04-barman-plugin-migration_cluster.yaml new file mode 100644 index 0000000000..430dae6cff --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/04-barman-plugin-migration_cluster.yaml @@ -0,0 +1,29 @@ +type: postgresql +mode: standalone +cluster: + instances: 2 + storage: + size: 256Mi + +backups: + enabled: true + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/barman-plugin-migration/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" diff --git a/charts/cluster/test/barman-plugin-migration/05-backup.yaml b/charts/cluster/test/barman-plugin-migration/05-backup.yaml new file mode 100644 index 0000000000..dc17c75f2b --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/05-backup.yaml @@ -0,0 +1,10 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-migrate-backup +spec: + cluster: + name: barman-plugin-migration-cluster + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io diff --git a/charts/cluster/test/barman-plugin-migration/05-backup_completed-assert.yaml b/charts/cluster/test/barman-plugin-migration/05-backup_completed-assert.yaml new file mode 100644 index 0000000000..12a99f2470 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/05-backup_completed-assert.yaml @@ -0,0 +1,12 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + name: post-migrate-backup +spec: + cluster: + name: barman-plugin-migration-cluster + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io +status: + phase: completed diff --git a/charts/cluster/test/barman-plugin-migration/05-backup_running-assert.yaml b/charts/cluster/test/barman-plugin-migration/05-backup_running-assert.yaml new file mode 100644 index 0000000000..8a0ff0dc64 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/05-backup_running-assert.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Event +reason: Starting +involvedObject: + apiVersion: postgresql.cnpg.io/v1 + kind: Backup + name: post-migrate-backup diff --git a/charts/cluster/test/barman-plugin-migration/05-checkpoint.yaml b/charts/cluster/test/barman-plugin-migration/05-checkpoint.yaml new file mode 100644 index 0000000000..1374e993c4 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/05-checkpoint.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: post-migrate-backup-checkpoint +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: create-checkpoint + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: barman-plugin-migration-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + END_TIME=$(( $(date +%s) + 30 )) + while [ $(date +%s) -lt $END_TIME ]; do + psql "$DB_URI" -c "SELECT pg_switch_wal();CHECKPOINT;" + sleep 5 + done diff --git a/charts/cluster/test/barman-plugin-migration/06-post_backup_data_write-assert.yaml b/charts/cluster/test/barman-plugin-migration/06-post_backup_data_write-assert.yaml new file mode 100644 index 0000000000..ad9be77a7b --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/06-post_backup_data_write-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write-post-backup +status: + succeeded: 1 diff --git a/charts/cluster/test/barman-plugin-migration/06-post_backup_data_write.yaml b/charts/cluster/test/barman-plugin-migration/06-post_backup_data_write.yaml new file mode 100644 index 0000000000..87d1a33637 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/06-post_backup_data_write.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-write-post-backup +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-write + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: barman-plugin-migration-cluster-superuser + key: uri + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + psql "$DB_URI" -c "CREATE TABLE mybadtable (id serial PRIMARY KEY);" diff --git a/charts/cluster/test/barman-plugin-migration/07-recovery_backup_pitr_cluster-assert.yaml b/charts/cluster/test/barman-plugin-migration/07-recovery_backup_pitr_cluster-assert.yaml new file mode 100644 index 0000000000..2b6b9651f1 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/07-recovery_backup_pitr_cluster-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: recovery-backup-pitr-cluster +status: + readyInstances: 2 diff --git a/charts/cluster/test/barman-plugin-migration/07-recovery_backup_pitr_cluster.yaml b/charts/cluster/test/barman-plugin-migration/07-recovery_backup_pitr_cluster.yaml new file mode 100644 index 0000000000..55ccb69dd7 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/07-recovery_backup_pitr_cluster.yaml @@ -0,0 +1,50 @@ +type: postgresql +mode: recovery + +cluster: + instances: 2 + storage: + size: 256Mi + +recovery: + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/barman-plugin-migration/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + clusterName: "barman-plugin-migration-cluster" + retentionPolicy: "30d" + scheduledBackups: [] + +backups: + enabled: true + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/barman-plugin-migration/v2" + accessKey: "minio" + secretKey: "minio123" + region: "local" + scheduledBackups: [] + retentionPolicy: "30d" diff --git a/charts/cluster/test/barman-plugin-migration/08-data_test-assert.yaml b/charts/cluster/test/barman-plugin-migration/08-data_test-assert.yaml new file mode 100644 index 0000000000..6f14d5f231 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/08-data_test-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-backup-pitr +status: + succeeded: 1 diff --git a/charts/cluster/test/barman-plugin-migration/08-data_test.yaml b/charts/cluster/test/barman-plugin-migration/08-data_test.yaml new file mode 100644 index 0000000000..5fb4faf395 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/08-data_test.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: data-test-backup-pitr +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: data-test + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: recovery-backup-pitr-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + set -e + test "$(psql $DB_URI -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mygoodtable$$)' --csv -q 2>/dev/null)" = "t" + echo "Good table exists" + test "$(psql $DB_URI -t -c 'SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_name = $$mybadtable$$)' --csv -q 2>/dev/null)" = "f" + echo "Bad table does not exist" diff --git a/charts/cluster/test/barman-plugin-migration/chainsaw-test.yaml b/charts/cluster/test/barman-plugin-migration/chainsaw-test.yaml new file mode 100644 index 0000000000..9cfea96fb9 --- /dev/null +++ b/charts/cluster/test/barman-plugin-migration/chainsaw-test.yaml @@ -0,0 +1,171 @@ +## +# This test sets up a CNPG Cluster with MinIO backups and test that it can be migrated to the Barman Cloud CNPG-I plugin +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: barman-plugin-migration +spec: + timeouts: + apply: 1s + assert: 7m + cleanup: 1m + steps: + - name: Clear the MinIO bucket + try: + - apply: + file: ./00-minio_cleanup.yaml + - assert: + file: ./00-minio_cleanup-assert.yaml + - name: Install a standalone CNPG Cluster + try: + - script: + content: | + kubectl -n $NAMESPACE create secret generic kube-root-ca.crt --from-literal=ca.crt="$(kubectl -n kube-system get configmaps kube-root-ca.crt -o jsonpath='{.data.ca\.crt}')" --dry-run=client -o yaml | kubectl apply -f - + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./01-barman-plugin-migration_cluster.yaml \ + --wait \ + barman-plugin-migration ../../ + - assert: + file: ./01-barman-plugin-migration_cluster-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - podLogs: + selector: cnpg.io/cluster=barman-plugin-migration-cluster + - name: Create a backup + try: + - apply: + file: ./02-backup.yaml + - apply: + file: ./02-checkpoint.yaml + - assert: + file: ./02-backup_completed-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Backup + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - podLogs: + selector: cnpg.io/cluster=barman-plugin-backup-restore-cluster + - name: Write some data to the cluster + timeouts: + apply: 1s + assert: 1m + try: + - apply: + file: 03-data_write.yaml + - assert: + file: 03-data_write-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + - podLogs: + selector: batch.kubernetes.io/job-name=data-write + - name: Switch the cluster to using Barman CNPG-I for backups + try: + - script: + content: | + helm upgrade \ + --namespace $NAMESPACE \ + --values ./04-barman-plugin-migration_cluster.yaml \ + --wait \ + barman-plugin-migration ../../ + - assert: + file: ./04-barman-plugin-migration_cluster-upgrading-assert.yaml + - assert: + file: ./04-barman-plugin-migration_cluster-healthy-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - podLogs: + selector: cnpg.io/cluster=barman-plugin-migration-cluster + - name: Create a buckup with the Barman CNPG-I plugin + try: + - apply: + file: ./05-backup.yaml + - assert: + file: ./05-backup_running-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Backup + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - podLogs: + selector: cnpg.io/cluster=barman-plugin-migration-cluster + - name: Complete a backup + try: + - apply: + file: ./05-checkpoint.yaml + - assert: + file: ./05-backup_completed-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Backup + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - podLogs: + selector: cnpg.io/cluster=barman-plugin-migration-cluster + - name: Write more data to the database after the backup + try: + - apply: + file: ./06-post_backup_data_write.yaml + - assert: + file: ./06-post_backup_data_write-assert.yaml + timeouts: + apply: 1s + assert: 10m + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Backup + - name: Create a recovery cluster from backup with a PITR target + try: + - script: + content: | + DATE_NO_BAD_TABLE=$(kubectl -n $NAMESPACE get configmap date-no-bad-table -o 'jsonpath={.data.date}') + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./07-recovery_backup_pitr_cluster.yaml \ + --set recovery.pitrTarget.time="$DATE_NO_BAD_TABLE" \ + --wait \ + recovery-backup-pitr ../../ + - assert: + file: ./07-recovery_backup_pitr_cluster-assert.yaml + catch: + - describe: + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + - podLogs: + selector: cnpg.io/cluster=recovery-backup-pitr-cluster + - name: Verify the pre-backup data on the recovery cluster exists but not the post-backup data + try: + - apply: + file: ./08-data_test.yaml + - assert: + file: ./08-data_test-assert.yaml + catch: + - describe: + apiVersion: batch/v1 + kind: Job + selector: batch.kubernetes.io/job-name=data-test-backup-pitr + - podLogs: + selector: batch.kubernetes.io/job-name=data-test-backup-pitr + - name: Cleanup + try: + - script: + content: | + kubectl --namespace $NAMESPACE delete scheduledbackups.postgresql.cnpg.io --all + kubectl --namespace $NAMESPACE delete backups.postgresql.cnpg.io --all + helm uninstall --namespace $NAMESPACE barman-plugin-migration diff --git a/charts/cluster/test/barman-plugin-scheduledbackups/00-minio_cleanup-assert.yaml b/charts/cluster/test/barman-plugin-scheduledbackups/00-minio_cleanup-assert.yaml new file mode 100644 index 0000000000..9c0f3eb480 --- /dev/null +++ b/charts/cluster/test/barman-plugin-scheduledbackups/00-minio_cleanup-assert.yaml @@ -0,0 +1,6 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +status: + succeeded: 1 diff --git a/charts/cluster/test/barman-plugin-scheduledbackups/00-minio_cleanup.yaml b/charts/cluster/test/barman-plugin-scheduledbackups/00-minio_cleanup.yaml new file mode 100644 index 0000000000..4fef8e057d --- /dev/null +++ b/charts/cluster/test/barman-plugin-scheduledbackups/00-minio_cleanup.yaml @@ -0,0 +1,16 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: minio-cleanup +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: minio-cleanup + image: minio/mc:latest + command: ['sh', '-c'] + args: + - | + mc alias set myminio https://minio.minio.svc.cluster.local minio minio123 + mc rm --recursive --force myminio/mybucket/barman-plugin-scheduledbackups diff --git a/charts/cluster/test/barman-plugin-scheduledbackups/01-scheduledbackups_cluster-assert.yaml b/charts/cluster/test/barman-plugin-scheduledbackups/01-scheduledbackups_cluster-assert.yaml new file mode 100644 index 0000000000..cac56a54ac --- /dev/null +++ b/charts/cluster/test/barman-plugin-scheduledbackups/01-scheduledbackups_cluster-assert.yaml @@ -0,0 +1,68 @@ +apiVersion: postgresql.cnpg.io/v1 +kind: Cluster +metadata: + name: scheduledbackups-cluster +status: + readyInstances: 1 +--- +apiVersion: barmancloud.cnpg.io/v1 +kind: ObjectStore +metadata: + name: scheduledbackups-cluster-backups +spec: + retentionPolicy: 30d + configuration: + destinationPath: s3://mybucket/barman-plugin-scheduledbackups/v1 + endpointCA: + key: ca.crt + name: kube-root-ca.crt + endpointURL: https://minio.minio.svc.cluster.local + s3Credentials: + accessKeyId: + key: ACCESS_KEY_ID + name: scheduledbackups-cluster-backup-s3-creds + secretAccessKey: + key: ACCESS_SECRET_KEY + name: scheduledbackups-cluster-backup-s3-creds +--- +apiVersion: postgresql.cnpg.io/v1 +kind: ScheduledBackup +metadata: + name: scheduledbackups-cluster-daily-backup +spec: + immediate: true + schedule: "0 0 0 * * *" + method: plugin + backupOwnerReference: self + cluster: + name: scheduledbackups-cluster +--- +apiVersion: postgresql.cnpg.io/v1 +kind: ScheduledBackup +metadata: + name: scheduledbackups-cluster-weekly-backup +spec: + immediate: true + schedule: "0 0 0 * * 1" + method: plugin + backupOwnerReference: self + cluster: + name: scheduledbackups-cluster + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io +--- +apiVersion: postgresql.cnpg.io/v1 +kind: Backup +metadata: + ownerReferences: + - apiVersion: postgresql.cnpg.io/v1 + kind: ScheduledBackup + name: scheduledbackups-cluster-daily-backup +spec: + method: plugin + cluster: + name: scheduledbackups-cluster + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io +status: + phase: started diff --git a/charts/cluster/test/barman-plugin-scheduledbackups/01-scheduledbackups_cluster.yaml b/charts/cluster/test/barman-plugin-scheduledbackups/01-scheduledbackups_cluster.yaml new file mode 100644 index 0000000000..bc89a5898d --- /dev/null +++ b/charts/cluster/test/barman-plugin-scheduledbackups/01-scheduledbackups_cluster.yaml @@ -0,0 +1,47 @@ +type: postgresql +mode: standalone +cluster: + instances: 1 + # Use a short stopDelay so teardown is fast: the chart forces `immediate: true` + # on scheduled backups, so a backup can still be running at cleanup time, and + # Barman blocks instance shutdown until it completes. A low stopDelay caps the + # pod's terminationGracePeriodSeconds, force-stopping the instance promptly. + # This test only needs to observe that a backup was started. + stopDelay: 30 + storage: + size: 256Mi + +backups: + enabled: true + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io + provider: s3 + endpointURL: "https://minio.minio.svc.cluster.local" + endpointCA: + name: kube-root-ca.crt + key: ca.crt + wal: + encryption: "" + data: + encryption: "" + s3: + bucket: "mybucket" + path: "/barman-plugin-scheduledbackups/v1" + accessKey: "minio" + secretKey: "minio123" + region: "local" + retentionPolicy: "30d" + scheduledBackups: + - name: daily-backup + schedule: "0 0 0 * * *" + backupOwnerReference: self + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io + - name: weekly-backup + schedule: "0 0 0 * * 1" + backupOwnerReference: self + method: plugin + pluginConfiguration: + name: barman-cloud.cloudnative-pg.io diff --git a/charts/cluster/test/barman-plugin-scheduledbackups/01.1-checkpoint.yaml b/charts/cluster/test/barman-plugin-scheduledbackups/01.1-checkpoint.yaml new file mode 100644 index 0000000000..6770a95c95 --- /dev/null +++ b/charts/cluster/test/barman-plugin-scheduledbackups/01.1-checkpoint.yaml @@ -0,0 +1,27 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: scheduledbackups-cluster-checkpoint +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: create-checkpoint + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: scheduledbackups-cluster-superuser + key: uri + image: alpine:3.19 + command: ['sh', '-c'] + args: + - | + apk --no-cache add postgresql-client + DB_URI=$(echo $DB_URI | sed "s|/\*|/|" ) + END_TIME=$(( $(date +%s) + 30 )) + while [ $(date +%s) -lt $END_TIME ]; do + psql "$DB_URI" -c "SELECT pg_switch_wal();CHECKPOINT;" + sleep 5 + done diff --git a/charts/cluster/test/barman-plugin-scheduledbackups/chainsaw-test.yaml b/charts/cluster/test/barman-plugin-scheduledbackups/chainsaw-test.yaml new file mode 100644 index 0000000000..e84283ba58 --- /dev/null +++ b/charts/cluster/test/barman-plugin-scheduledbackups/chainsaw-test.yaml @@ -0,0 +1,33 @@ +# Tests the Barman Cloud CNPG-I Plugin with ScheduledBackups +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + name: barman-plugin-scheduledbackups +spec: + timeouts: + apply: 1s + assert: 2m + cleanup: 5m + steps: + - name: Install the cluster with ScheduledBackups + try: + - script: + content: | + kubectl -n $NAMESPACE create secret generic kube-root-ca.crt --from-literal=ca.crt="$(kubectl -n kube-system get configmaps kube-root-ca.crt -o jsonpath='{.data.ca\.crt}')" --dry-run=client -o yaml | kubectl apply -f - + helm upgrade \ + --install \ + --namespace $NAMESPACE \ + --values ./01-scheduledbackups_cluster.yaml \ + --wait \ + scheduledbackups ../../ + - assert: + file: ./01-scheduledbackups_cluster-assert.yaml + - apply: + file: ./01.1-checkpoint.yaml + - name: Cleanup + try: + - script: + content: | + kubectl --namespace $NAMESPACE delete scheduledbackups.postgresql.cnpg.io --all + kubectl --namespace $NAMESPACE delete backups.postgresql.cnpg.io --all + helm --namespace $NAMESPACE uninstall scheduledbackups diff --git a/charts/cluster/values.schema.json b/charts/cluster/values.schema.json index 9d4e44357b..8ff0061bda 100644 --- a/charts/cluster/values.schema.json +++ b/charts/cluster/values.schema.json @@ -123,6 +123,20 @@ "required": [], "type": "object" }, + "instanceSidecarConfiguration": { + "description": "The configuration for the Barman Cloud Plugin sidecar that runs in the instance pods. See: https://cloudnative-pg.io/plugin-barman-cloud/docs/next/plugin-barman-cloud.v1/#instancesidecarconfiguration", + "required": [], + "type": "object" + }, + "method": { + "default": "barmanObjectStore", + "description": "One of `barmanObjectStore` (default) or `plugin`", + "type": "string" + }, + "pluginConfiguration": { + "required": [], + "type": "object" + }, "provider": { "default": "s3", "description": "One of `s3`, `azure` or `google`", @@ -176,7 +190,7 @@ }, "method": { "default": "barmanObjectStore", - "description": "Backup method, can be `barmanObjectStore` (default) or `volumeSnapshot`", + "description": "Backup method, can be `barmanObjectStore` (default), `volumeSnapshot` or `plugin`.", "type": "string" }, "name": { @@ -184,6 +198,10 @@ "description": "Scheduled backup name", "type": "string" }, + "pluginConfiguration": { + "required": [], + "type": "object" + }, "schedule": { "default": "0 0 0 * * *", "description": "Schedule in cron format", @@ -444,6 +462,13 @@ "required": [], "type": "object" }, + "plugins": { + "description": "Plugins\nWhen adding `barman-cloud.cloudnative-pg.io`, just specify the plugin name and set `isWALArchiver` to true. The\nchart will then provision an `ObjectStore` resource with the configuration from the `backups.barmanObjectStore` section.", + "items": { + "required": [] + }, + "type": "array" + }, "podSecurityContext": { "description": "Configure the Pod Security Context.\nSee: https://cloudnative-pg.io/documentation/preview/security/", "required": [], @@ -511,6 +536,12 @@ "description": "Strategy to follow to upgrade the primary server during a rolling update procedure, after all replicas have been\nsuccessfully updated: it can be automated (unsupervised - default) or manual (supervised)", "type": "string" }, + "stopDelay": { + "default": 1800, + "description": "The time in seconds that is allowed for the instance to wait for the shutdown to complete before being forcefully\nterminated. Also sets the pod's terminationGracePeriodSeconds. Defaults to 1800 (30m) when unset.", + "minimum": 1, + "type": "integer" + }, "priorityClassName": { "default": "", "type": "string" @@ -901,9 +932,14 @@ "required": [], "type": "object" }, + "instanceSidecarConfiguration": { + "description": "The configuration for the Barman Cloud Plugin sidecar that runs in the instance pods. See: https://cloudnative-pg.io/plugin-barman-cloud/docs/next/plugin-barman-cloud.v1/#instancesidecarconfiguration", + "required": [], + "type": "object" + }, "method": { "default": "backup", - "description": "Available recovery methods:\n* `backup` - Recovers a CNPG cluster from a CNPG backup (PITR supported) Needs to be on the same cluster in the same namespace.\n* `object_store` - Recovers a CNPG cluster from a barman object store (PITR supported).\n* `pg_basebackup` - Recovers a CNPG cluster viaa streaming replication protocol. Useful if you want to\n migrate databases to CloudNativePG, even from outside Kubernetes.\n* `import` - Import one or more databases from an existing Postgres cluster.", + "description": "Available recovery methods:\n* `backup` - Recovers a CNPG cluster from a CNPG backup (PITR supported) Needs to be on the same cluster in the same namespace.\n* `plugin` - Recovers a CNPG cluster from a backup taken with a CloudNativePG plugin (e.g. barman-cloud).\n* `object_store` - Recovers a CNPG cluster from a barman object store (PITR supported).\n* `pg_basebackup` - Recovers a CNPG cluster viaa streaming replication protocol. Useful if you want to\n migrate databases to CloudNativePG, even from outside Kubernetes.\n* `import` - Import one or more databases from an existing Postgres cluster.", "type": "string" }, "owner": { @@ -1039,6 +1075,10 @@ "required": [], "type": "object" }, + "pluginConfiguration": { + "required": [], + "type": "object" + }, "provider": { "default": "s3", "description": "One of `s3`, `azure` or `google`", diff --git a/charts/cluster/values.yaml b/charts/cluster/values.yaml index 52c7cf8045..be717af53b 100644 --- a/charts/cluster/values.yaml +++ b/charts/cluster/values.yaml @@ -28,6 +28,7 @@ mode: standalone recovery: # -- Available recovery methods: # * `backup` - Recovers a CNPG cluster from a CNPG backup (PITR supported) Needs to be on the same cluster in the same namespace. + # * `plugin` - Recovers a CNPG cluster from a backup taken with a CloudNativePG plugin (e.g. barman-cloud). # * `object_store` - Recovers a CNPG cluster from a barman object store (PITR supported). # * `pg_basebackup` - Recovers a CNPG cluster viaa streaming replication protocol. Useful if you want to # migrate databases to CloudNativePG, even from outside Kubernetes. @@ -48,6 +49,15 @@ recovery: database: app # -- Name of the owner of the database in the instance to be used by applications. Defaults to the value of the `database` key. owner: "" + + pluginConfiguration: {} +# name: barman-cloud.cloudnative-pg.io +# parameters: +# serverName: cluster-example + + # Barman Object Store configuration applicable to both `barmanObjectStore` and `plugin` methods when using + # the `barman-cloud.cloudnative-pg.io` plugin. + # -- Overrides the provider specific default endpoint. endpointURL: "" # Defaults to: @@ -98,6 +108,9 @@ recovery: # -- Name of the backup credentials secret name: "" + # -- The configuration for the Barman Cloud Plugin sidecar that runs in the instance pods. See: https://cloudnative-pg.io/plugin-barman-cloud/docs/next/plugin-barman-cloud.v1/#instancesidecarconfiguration + instanceSidecarConfiguration: {} + # See https://cloudnative-pg.io/documentation/current/bootstrap/#bootstrap-from-a-live-cluster-pg_basebackup pgBaseBackup: # -- Name of the database used by the application. Default: `app`. @@ -240,6 +253,11 @@ cluster: # successfully updated: it can be automated (unsupervised - default) or manual (supervised) primaryUpdateStrategy: unsupervised + # -- The time in seconds that is allowed for the instance to wait for the shutdown + # to complete before being forcefully terminated. Also sets the pod's terminationGracePeriodSeconds. + # Defaults to 1800 (30m) when unset, per the operator default. + # stopDelay: 1800 + # -- The instances' log level, one of the following values: error, warning, info (default), debug, trace logLevel: "info" @@ -289,6 +307,14 @@ cluster: # See: https://cloudnative-pg.io/documentation/current/kubernetes_upgrade/#pod-disruption-budgets enablePDB: true + # -- Plugins + # When adding `barman-cloud.cloudnative-pg.io`, just specify the plugin name and set `isWALArchiver` to true. The + # chart will then provision an `ObjectStore` resource with the configuration from the `backups.barmanObjectStore` section. + plugins: [] + # - name: barman-cloud.cloudnative-pg.io + # enabled: true + # isWALArchiver: true + # -- This feature enables declarative management of existing roles, as well as the creation of new roles if they are not # already present in the database. # See: https://cloudnative-pg.io/documentation/current/declarative_role_management/ @@ -442,6 +468,15 @@ backups: # -- You need to configure backups manually, so backups are disabled by default. enabled: false + # -- One of `barmanObjectStore` (default) or `plugin` + method: barmanObjectStore + + pluginConfiguration: {} +# name: barman-cloud.cloudnative-pg.io + + # Barman Object Store configuration applicable to both `barmanObjectStore` and `plugin` methods when using + # the `barman-cloud.cloudnative-pg.io` plugin. + # -- Overrides the provider specific default endpoint. endpointURL: "" # Defaults to: @@ -493,6 +528,9 @@ backups: # -- Name of the backup credentials secret name: "" + # -- The configuration for the Barman Cloud Plugin sidecar that runs in the instance pods. See: https://cloudnative-pg.io/plugin-barman-cloud/docs/next/plugin-barman-cloud.v1/#instancesidecarconfiguration + instanceSidecarConfiguration: {} + wal: # -- WAL compression method. One of `` (for no compression), `gzip`, `bzip2` or `snappy`. compression: gzip @@ -516,8 +554,10 @@ backups: schedule: "0 0 0 * * *" # -- Backup owner reference backupOwnerReference: self - # -- Backup method, can be `barmanObjectStore` (default) or `volumeSnapshot` + # -- Backup method, can be `barmanObjectStore` (default), `volumeSnapshot` or `plugin`. method: barmanObjectStore + pluginConfiguration: {} +# name: barman-cloud.cloudnative-pg.io # -- Retention policy for backups retentionPolicy: "30d"