Helm chart: render CNPG spec.backup + method inference for volume snapshots (and support spec.plugins) (#504)

* 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 <joshua.waldrep5+github@gmail.com>
Co-authored-by: Juan José Mata <jjmata@jjmata.com>
This commit is contained in:
LPW
2025-12-30 12:36:13 -05:00
committed by GitHub
parent 528597c217
commit 614c8d455f
7 changed files with 238 additions and 3 deletions

12
charts/sure/CHANGELOG.md Normal file
View File

@@ -0,0 +1,12 @@
### 0.0.0
- First (nightly/test) releases via <https://we-promise.github.io/sure/index.yaml>
### 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).

View File

@@ -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"

View File

@@ -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 clusterwide, you may set `cnpg.enabled=false` and keep `cnpg.cluster.enabled=true`. The chart will still render the `Cluster` CR and compute the incluster `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:

View File

@@ -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 }}

View File

@@ -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

View File

@@ -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

View File

@@ -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