From 614c8d455f2b486049da2c6e3dfea744ede867f3 Mon Sep 17 00:00:00 2001 From: LPW Date: Tue, 30 Dec 2025 12:36:13 -0500 Subject: [PATCH] Helm chart: render CNPG spec.backup + method inference for volume snapshots (and support spec.plugins) (#504) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add backup rendering logic and tests for CNPG Cluster CR - Implemented logic in `cnpg-cluster.yaml` to render `.spec.backup` based on `cnpg.cluster.backup` values. - Introduced validation for required fields and unsupported keys (e.g., `ttl`, `volumeSnapshot.enabled`) to avoid CRD warnings. - Added Helm unit tests to validate backup rendering for various scenarios: missing/invalid fields, inferred `method`, and unsupported keys. - Updated `README.md` and `values.yaml` with examples and documentation for backup configuration options. * Add plugin rendering logic and tests for CNPG Cluster CR - Implemented logic in `cnpg-cluster.yaml` to render `.spec.plugins` based on `cnpg.cluster.plugins` values. - Added Helm unit tests to validate plugin rendering scenarios: unset plugins and configured plugin values. - Updated `values.yaml` with examples and documentation for configuring CNPG plugins. * Update chart to v1.0.1 with CNPG backup and plugin enhancements - Add rendering logic for `Cluster.spec.backup`, inferring `method: volumeSnapshot` when applicable and validating required fields. - Add support for `Cluster.spec.plugins`, enabling barman-cloud plugin and WAL archiver configuration. - Strip unsupported keys (e.g., `backup.ttl`, `volumeSnapshot.enabled`) to prevent CRD warnings. - Update examples and documentation in `README.md` and `values.yaml`. * Keep Helm chart on same major version as app? * Versioning with monorepo * MD is tricky --------- Co-authored-by: Josh Waldrep Co-authored-by: Juan José Mata --- charts/sure/CHANGELOG.md | 12 ++++ charts/sure/Chart.yaml | 2 +- charts/sure/README.md | 25 ++++++++ charts/sure/templates/cnpg-cluster.yaml | 48 +++++++++++++++- charts/sure/tests/cnpg-backup_test.yaml | 73 ++++++++++++++++++++++++ charts/sure/tests/cnpg-plugins_test.yaml | 34 +++++++++++ charts/sure/values.yaml | 47 +++++++++++++++ 7 files changed, 238 insertions(+), 3 deletions(-) create mode 100644 charts/sure/CHANGELOG.md create mode 100644 charts/sure/tests/cnpg-backup_test.yaml create mode 100644 charts/sure/tests/cnpg-plugins_test.yaml diff --git a/charts/sure/CHANGELOG.md b/charts/sure/CHANGELOG.md new file mode 100644 index 000000000..3e5d6f603 --- /dev/null +++ b/charts/sure/CHANGELOG.md @@ -0,0 +1,12 @@ +### 0.0.0 + +- First (nightly/test) releases via + +### 0.6.5 + +- First version/release that aligns versions with monorepo +- CNPG: render `Cluster.spec.backup` from `cnpg.cluster.backup`. + - If `backup.method` is omitted and `backup.volumeSnapshot` is present, the chart will infer `method: volumeSnapshot`. + - For snapshot backups, `backup.volumeSnapshot.className` is required (template fails early if missing). + - Example-only keys like `backup.ttl` and `backup.volumeSnapshot.enabled` are stripped to avoid CRD warnings. +- CNPG: render `Cluster.spec.plugins` from `cnpg.cluster.plugins` (enables barman-cloud plugin / WAL archiver configuration). diff --git a/charts/sure/Chart.yaml b/charts/sure/Chart.yaml index 1208c08b5..fe8f98dcc 100644 --- a/charts/sure/Chart.yaml +++ b/charts/sure/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: sure description: Official Helm chart for deploying the Sure Rails app (web + Sidekiq) on Kubernetes with optional HA PostgreSQL (CloudNativePG) and Redis. type: application -version: 1.0.0 +version: 0.6.5 appVersion: "0.6.5" kubeVersion: ">=1.25.0-0" diff --git a/charts/sure/README.md b/charts/sure/README.md index 0f2c105ba..3182e39bd 100644 --- a/charts/sure/README.md +++ b/charts/sure/README.md @@ -144,6 +144,11 @@ cnpg: storage: size: 20Gi storageClassName: longhorn + # Optional: enable CNPG volume snapshot backups (requires a VolumeSnapshotClass) + backup: + method: volumeSnapshot + volumeSnapshot: + className: longhorn # Synchronous replication for stronger durability minSyncReplicas: 1 maxSyncReplicas: 2 @@ -187,6 +192,26 @@ simplefin: - The chart configures credentials via `spec.bootstrap.initdb.secret` rather than `managed.roles`. The operator expects the referenced Secret to contain `username` and `password` keys (configurable via values). - This chart generates the application DB Secret when `cnpg.cluster.secret.enabled=true` using the keys defined at `cnpg.cluster.secret.usernameKey` (default `username`) and `cnpg.cluster.secret.passwordKey` (default `password`). If you use an existing Secret (`cnpg.cluster.existingSecret`), ensure it contains these keys. The Cluster CR references the Secret by name and maps the keys accordingly. - If the CNPG operator is already installed cluster‑wide, you may set `cnpg.enabled=false` and keep `cnpg.cluster.enabled=true`. The chart will still render the `Cluster` CR and compute the in‑cluster `DATABASE_URL`. +- For backups, CNPG requires `spec.backup.method` to be explicit (for example `volumeSnapshot` or `barmanObjectStore`). This chart will infer `method: volumeSnapshot` if a `backup.volumeSnapshot` block is present. + - For snapshot backups, `backup.volumeSnapshot.className` must be set (the chart will fail the render if it is missing). + - The CNPG `spec.backup` schema does not support keys like `ttl` or `volumeSnapshot.enabled`; this chart strips those keys to avoid CRD warnings. + - Unknown `backup.method` values are passed through and left for CNPG to validate. + +Example (barman-cloud plugin for WAL archiving + snapshot backups): + +```yaml +cnpg: + cluster: + plugins: + - name: barman-cloud.cloudnative-pg.io + isWALArchiver: true + parameters: + barmanObjectName: minio-backups # references an ObjectStore CR + backup: + method: volumeSnapshot + volumeSnapshot: + className: longhorn +``` Additional default hardening: diff --git a/charts/sure/templates/cnpg-cluster.yaml b/charts/sure/templates/cnpg-cluster.yaml index 574858546..a19c789be 100644 --- a/charts/sure/templates/cnpg-cluster.yaml +++ b/charts/sure/templates/cnpg-cluster.yaml @@ -15,9 +15,53 @@ spec: {{- end }} storage: size: {{ .Values.cnpg.cluster.storage.size | default "10Gi" }} - {{- if .Values.cnpg.cluster.storage.storageClassName }} + {{ if .Values.cnpg.cluster.storage.storageClassName }} storageClass: {{ .Values.cnpg.cluster.storage.storageClassName }} - {{- end }} + {{ end }} + {{ with .Values.cnpg.cluster.backup }} + {{- $backup := deepCopy . }} + {{- /* CNPG `spec.backup` does not support these historical/example keys */ -}} + {{- $_ := unset $backup "ttl" -}} + {{- if and (hasKey $backup "volumeSnapshot") (kindIs "map" $backup.volumeSnapshot) -}} + {{- $_ := unset $backup.volumeSnapshot "enabled" -}} + {{- end -}} + + {{- $method := (get $backup "method") -}} + {{- if not $method -}} + {{- if hasKey $backup "volumeSnapshot" -}} + {{- $_ := set $backup "method" "volumeSnapshot" -}} + {{- $method = "volumeSnapshot" -}} + {{- else if hasKey $backup "barmanObjectStore" -}} + {{- $_ := set $backup "method" "barmanObjectStore" -}} + {{- $method = "barmanObjectStore" -}} + {{- end -}} + {{- end -}} + + {{- if not $method -}} + {{- fail "cnpg.cluster.backup is set but no backup method could be inferred. Set cnpg.cluster.backup.method explicitly (e.g. volumeSnapshot or barmanObjectStore)." -}} + {{- end -}} + + {{- if and (eq $method "volumeSnapshot") (not (hasKey $backup "volumeSnapshot")) -}} + {{- fail "cnpg.cluster.backup.method=volumeSnapshot requires cnpg.cluster.backup.volumeSnapshot to be set" -}} + {{- end -}} + {{- if and (eq $method "barmanObjectStore") (not (hasKey $backup "barmanObjectStore")) -}} + {{- fail "cnpg.cluster.backup.method=barmanObjectStore requires cnpg.cluster.backup.barmanObjectStore to be set" -}} + {{- end -}} + {{- if eq $method "volumeSnapshot" -}} + {{- if kindIs "map" $backup.volumeSnapshot -}} + {{- $_ := required "cnpg.cluster.backup.volumeSnapshot.className is required for volumeSnapshot backups" $backup.volumeSnapshot.className -}} + {{- else -}} + {{- fail "cnpg.cluster.backup.volumeSnapshot must be a map/object" -}} + {{- end -}} + {{- end -}} + + backup: + {{- toYaml $backup | nindent 4 }} + {{- end }} + {{ with .Values.cnpg.cluster.plugins }} + plugins: + {{- toYaml . | nindent 4 }} + {{ end }} {{- with .Values.cnpg.cluster.nodeSelector }} nodeSelector: {{- toYaml . | nindent 4 }} diff --git a/charts/sure/tests/cnpg-backup_test.yaml b/charts/sure/tests/cnpg-backup_test.yaml new file mode 100644 index 000000000..242413fa7 --- /dev/null +++ b/charts/sure/tests/cnpg-backup_test.yaml @@ -0,0 +1,73 @@ +suite: CNPG Cluster backup rendering +templates: + - templates/cnpg-cluster.yaml + +tests: + - it: renders no spec.backup when cnpg.cluster.backup is unset + set: + cnpg: + cluster: + enabled: true + asserts: + - notExists: + path: spec.backup + + - it: defaults method to volumeSnapshot when volumeSnapshot is present + set: + cnpg: + cluster: + enabled: true + backup: + volumeSnapshot: + className: longhorn + asserts: + - equal: + path: spec.backup.method + value: volumeSnapshot + - equal: + path: spec.backup.volumeSnapshot.className + value: longhorn + + - it: strips unsupported keys (ttl and volumeSnapshot.enabled) + set: + cnpg: + cluster: + enabled: true + backup: + method: volumeSnapshot + ttl: 168h + volumeSnapshot: + enabled: true + className: longhorn + asserts: + - equal: + path: spec.backup.method + value: volumeSnapshot + - notExists: + path: spec.backup.ttl + - notExists: + path: spec.backup.volumeSnapshot.enabled + + - it: fails when volumeSnapshot backups are enabled without className + set: + cnpg: + cluster: + enabled: true + backup: + method: volumeSnapshot + volumeSnapshot: {} + asserts: + - failedTemplate: + errorMessage: cnpg.cluster.backup.volumeSnapshot.className is required for volumeSnapshot backups + + - it: renders unknown backup method as-is (CNPG will validate) + set: + cnpg: + cluster: + enabled: true + backup: + method: madeUpMethod + asserts: + - equal: + path: spec.backup.method + value: madeUpMethod diff --git a/charts/sure/tests/cnpg-plugins_test.yaml b/charts/sure/tests/cnpg-plugins_test.yaml new file mode 100644 index 000000000..79dc0d3b3 --- /dev/null +++ b/charts/sure/tests/cnpg-plugins_test.yaml @@ -0,0 +1,34 @@ +suite: CNPG Cluster plugins rendering +templates: + - templates/cnpg-cluster.yaml + +tests: + - it: renders no spec.plugins when cnpg.cluster.plugins is unset + set: + cnpg: + cluster: + enabled: true + asserts: + - notExists: + path: spec.plugins + + - it: renders spec.plugins when cnpg.cluster.plugins is set + set: + cnpg: + cluster: + enabled: true + plugins: + - name: barman-cloud.cloudnative-pg.io + isWALArchiver: true + parameters: + barmanObjectName: minio-backups + asserts: + - equal: + path: spec.plugins[0].name + value: barman-cloud.cloudnative-pg.io + - equal: + path: spec.plugins[0].isWALArchiver + value: true + - equal: + path: spec.plugins[0].parameters.barmanObjectName + value: minio-backups diff --git a/charts/sure/values.yaml b/charts/sure/values.yaml index 87faec82a..aea5de0c1 100644 --- a/charts/sure/values.yaml +++ b/charts/sure/values.yaml @@ -65,6 +65,53 @@ cnpg: storage: size: 10Gi storageClassName: "" + # Optional CNPG backup configuration (rendered as `.spec.backup` on the Cluster CR) + # Example (CNPG volume snapshots): + # backup: + # method: volumeSnapshot + # volumeSnapshot: + # className: longhorn + # + # Example (CNPG barmanObjectStore backups): + # backup: + # method: barmanObjectStore + # barmanObjectStore: + # destinationPath: s3://my-bucket/cnpg + # endpointURL: https://s3.us-east-1.amazonaws.com + # s3Credentials: + # accessKeyId: + # name: my-s3-creds + # key: ACCESS_KEY_ID + # secretAccessKey: + # name: my-s3-creds + # key: SECRET_ACCESS_KEY + # + # NOTE: + # - The CNPG Cluster `spec.backup` schema does not support `enabled` or `ttl` keys. + # If you add them, this chart will ignore them to avoid CRD warnings. + # - This chart only hard-validates required fields for supported methods; unknown `method` values + # are passed through for CNPG to validate. + backup: {} + + # Optional CNPG plugin configuration (rendered as `.spec.plugins` on the Cluster CR) + # Example (barman-cloud plugin as WAL archiver): + # plugins: + # - name: barman-cloud.cloudnative-pg.io + # isWALArchiver: true + # parameters: + # barmanObjectName: minio-backups + # + # Example (complete setup: WAL archiving via barman-cloud plugin + snapshot backups): + # plugins: + # - name: barman-cloud.cloudnative-pg.io + # isWALArchiver: true + # parameters: + # barmanObjectName: minio-backups # references an ObjectStore CR + # backup: + # method: volumeSnapshot + # volumeSnapshot: + # className: longhorn + plugins: [] # auth config for application user appUser: sure appDatabase: sure