Repository navigation
fix(backups): support cnpg-operator with barman plugin - #3313
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe change migrates CNPG backups to the barman-cloud plugin, adds its Helm chart and ObjectStore CRD, refreshes CloudNativePG resources, updates backup templates and tests, removes linstor-scheduler admission configuration, and adds an opt-in PostgreSQL backup/restore round-trip. ChangesCNPG backup plugin migration
Postgres operator and barman-cloud chart
Linstor scheduler chart refresh
PostgreSQL backup round-trip example
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related issues
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
e05acca to
2ffe43e
Compare
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request migrates the Cozystack CNPG backup strategy to use the barman-cloud plugin, replacing the deprecated spec.backup.barmanObjectStore field. This change involves introducing ObjectStore CRs for configuration management and updating the backupstrategy-controller to use Server-Side Apply (SSA) for managing these resources and cluster plugins. Additionally, the postgres-operator and linstor-scheduler charts were updated to support the new architecture and resolve compatibility issues with sidecar containers. Highlights
New Features🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request migrates the CloudNativePG (CNPG) backup strategy from the deprecated native barmanObjectStore configuration to the barman-cloud plugin, aligning with CNPG 1.27+ deprecations and 1.29 removals. This is achieved by updating the backup controller to SSA-apply ObjectStore CRs and patch the Cluster's spec.plugins, updating the Postgres application templates and tests, adding RBAC for the new objectstores resource, and deploying the plugin-barman-cloud subchart. Additionally, the linstor-scheduler is updated to version 0.2.5, disabling its admission webhook to prevent it from stripping restartPolicy from pods, which was breaking CNPG sidecars. Feedback is provided to guard the retentionPolicy field in the ObjectStore template with an if condition to prevent API server validation failures when the policy is not specified.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| spec: | ||
| retentionPolicy: {{ .Values.backup.retentionPolicy | quote }} | ||
| configuration: |
There was a problem hiding this comment.
If .Values.backup.retentionPolicy is empty or not specified, rendering retentionPolicy: "" will fail the custom resource validation on the API server, as the barman-cloud plugin validates this field against the regex ^[1-9][0-9]*[dwm]$. Guarding this field with an if condition prevents rendering an empty string when the retention policy is not set.
spec:
{{- if .Values.backup.retentionPolicy }}
retentionPolicy: {{ .Values.backup.retentionPolicy | quote }}
{{- end }}
configuration:There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/apps/postgres/README.md (1)
65-66: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueFix Markdown prose hardwrapping.
As per coding guidelines, use one continuous line per paragraph for Markdown prose and do not hardwrap.
♻️ Proposed fix
-End-to-end manifests live under [`examples/backups/postgres/`](../../../examples/backups/postgres/). -Briefly, the moving parts are: +End-to-end manifests live under [`examples/backups/postgres/`](../../../examples/backups/postgres/). Briefly, the moving parts are:🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/apps/postgres/README.md` around lines 65 - 66, Reformat the Markdown prose paragraph beginning with “End-to-end manifests live…” into a single continuous line by removing the hardwrap before “Briefly, the moving parts are:”. Preserve the wording and surrounding Markdown structure.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/apps/postgres/README.md`:
- Around line 65-66: Reformat the Markdown prose paragraph beginning with
“End-to-end manifests live…” into a single continuous line by removing the
hardwrap before “Briefly, the moving parts are:”. Preserve the wording and
surrounding Markdown structure.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 2e401325-9010-440e-a503-8a15420b8819
📒 Files selected for processing (13)
api/apps/v1alpha1/postgresql/types.godocs/operations/backup-classes.mdhack/e2e-chainsaw/postgres/postgres.yamlinternal/backupcontroller/cnpgstrategy_controller.gointernal/backupcontroller/cnpgstrategy_controller_test.gopackages/apps/postgres/README.mdpackages/apps/postgres/templates/db.yamlpackages/apps/postgres/tests/backup_storage_test.yamlpackages/apps/postgres/values.schema.jsonpackages/apps/postgres/values.yamlpackages/system/backupstrategy-controller/templates/rbac.yamlpackages/system/keycloak/templates/db.yamlpackages/system/postgres-rd/cozyrds/postgres.yaml
🚧 Files skipped from review as they are similar to previous changes (6)
- packages/system/backupstrategy-controller/templates/rbac.yaml
- packages/apps/postgres/templates/db.yaml
- packages/system/keycloak/templates/db.yaml
- internal/backupcontroller/cnpgstrategy_controller_test.go
- internal/backupcontroller/cnpgstrategy_controller.go
- packages/apps/postgres/tests/backup_storage_test.yaml
IvanHunters
left a comment
There was a problem hiding this comment.
Request changes
The barman-cloud plugin migration itself is solid: vendored charts match upstream, the SSA swap to spec.plugins is atomic under the driver's field manager, the LINSTOR webhook removal keeps extender-based locality intact, and build/vet/unit tests are green. One blocking regression on the restore path holds this back, plus two doc inaccuracies.
Blocking: recovery serverName default regression on the legacy chart flow
packages/apps/postgres/templates/db.yaml (externalClusters recovery block)
The chart emits externalClusters[].plugin.parameters.serverName only when .Values.bootstrap.serverName is set, same as the old barmanObjectStore block. The defaulting underneath changed, though:
- Native CNPG v1.28.1 (
api/v1/cluster_funcs.goGetServerName): an external cluster'sserverNamedefaults to the external cluster entry name, which the chart sets tobootstrap.oldName. - plugin-barman-cloud v0.13.0 (
internal/cnpgi/operator/config/config.go): the recoveryserverName, when the plugin parameter is absent, defaults tocluster.Name— the new cluster.
Failure scenario: a tenant on the legacy chart-managed flow restores release pg-old into a new release pg-new (bootstrap.enabled=true, oldName=pg-old, serverName empty — exactly what the values doc recommends). Pre-PR, recovery read s3://…/pg-old/; post-PR the plugin reads s3://…/pg-new/. That is either a hard bootstrap failure (backup not found, during a restore) or — if the pg-new prefix under the same destinationPath already holds another cluster's backups — a silent restore of the wrong cluster's data. The driver-managed platform flow is unaffected (the controller always sets Spec.Bootstrap.ServerName explicitly), so the e2e restore tests will not catch this.
Suggested fix:
serverName: {{ .Values.bootstrap.serverName | default .Values.bootstrap.oldName }}then update the bootstrap.serverName doc string and run make generate (README, values.schema.json, postgres-rd).
Non-blocking: doc inaccuracies
docs/operations/backup-classes.mdand the release note state existing clusters need the regenerated CNPG CRDs applied out of band because "Helm does not upgrade CRD schemas". These CRDs live undertemplates/crds/crds.yamlgated bycrds.create: true, so they are ordinary release manifests and upgrade with the release.helm.sh/resource-policy: keepprevents deletion, not updates. The real historical skew came from theimage.tagpin outrunning the vendored chart's CRDs, which this PR removes.- The claim that the pinned image variants no longer ship the
barman-cloud-*binaries holds for keycloak'sstandard-variant pin, butapps/postgrespins bare tags (thesystemflavor), which do include those binaries. The apps/postgres breakage was the operator/CRD skew, not missing binaries. Worth tightening the wording since it affects how operators reason about the upgrade window.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/operations/backup-classes.md`:
- Line 169: Update the sentence in the mixed-version window note to use the
American English adverb “afterward,” changing only the existing “afterwards”
wording in the BackupJob rerun instruction.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1251fa73-e249-4421-962a-28f39e8ca0f4
📒 Files selected for processing (18)
docs/operations/backup-classes.mdexamples/backups/postgres/00-helpers.shexamples/backups/postgres/10-cnpg-strategy.yamlexamples/backups/postgres/30-postgres-target.yamlexamples/backups/postgres/40-restorejob-to-copy.yamlexamples/backups/postgres/README.mdexamples/backups/postgres/cleanup.shexamples/backups/postgres/run-all.shhack/e2e-chainsaw/postgres/chainsaw-test.yamlinternal/backupcontroller/cnpgstrategy_controller.gointernal/backupcontroller/cnpgstrategy_controller_test.gopackages/apps/postgres/README.mdpackages/apps/postgres/templates/db.yamlpackages/apps/postgres/tests/backup_storage_test.yamlpackages/apps/postgres/values.schema.jsonpackages/apps/postgres/values.yamlpackages/system/keycloak/templates/db.yamlpackages/system/postgres-rd/cozyrds/postgres.yaml
🚧 Files skipped from review as they are similar to previous changes (9)
- packages/system/postgres-rd/cozyrds/postgres.yaml
- packages/apps/postgres/values.yaml
- packages/system/keycloak/templates/db.yaml
- packages/apps/postgres/values.schema.json
- packages/apps/postgres/templates/db.yaml
- internal/backupcontroller/cnpgstrategy_controller_test.go
- packages/apps/postgres/tests/backup_storage_test.yaml
- packages/apps/postgres/README.md
- internal/backupcontroller/cnpgstrategy_controller.go
There was a problem hiding this comment.
🧹 Nitpick comments (1)
hack/select-e2e_test.bats (1)
165-172: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove
|| trueto avoid masking script errors.Tolerating failures from
hack/select-e2e.shwith|| truemasks potential syntax or runtime errors in the script under test. Given that the"docs-only diff selects nothing"test on line 84 successfully runs the same script without|| truefor another empty-selection case,hack/select-e2e.shis expected to exit cleanly with status0even when no suites are selected.♻️ Proposed refactor
- output=$(hack/select-e2e.sh "$tmp/diff" "$tmp/sources") || true + output=$(hack/select-e2e.sh "$tmp/diff" "$tmp/sources")🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@hack/select-e2e_test.bats` around lines 165 - 172, Remove the trailing “|| true” from the hack/select-e2e.sh invocation in the “backup example without a matching suite selects nothing” test. Keep capturing output and asserting it is empty, while requiring the script to exit successfully so test failures are not masked.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@hack/select-e2e_test.bats`:
- Around line 165-172: Remove the trailing “|| true” from the hack/select-e2e.sh
invocation in the “backup example without a matching suite selects nothing”
test. Keep capturing output and asserting it is empty, while requiring the
script to exit successfully so test failures are not masked.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 454306df-5a09-4ee5-abf4-f36f0fb35863
📒 Files selected for processing (8)
api/apps/v1alpha1/postgresql/types.goexamples/backups/postgres/30-postgres-target.yamlexamples/backups/postgres/40-restorejob-to-copy.yamlexamples/backups/postgres/README.mdexamples/backups/postgres/run-all.shhack/e2e-chainsaw/postgres/chainsaw-test.yamlhack/select-e2e.shhack/select-e2e_test.bats
🚧 Files skipped from review as they are similar to previous changes (5)
- examples/backups/postgres/40-restorejob-to-copy.yaml
- examples/backups/postgres/README.md
- api/apps/v1alpha1/postgresql/types.go
- examples/backups/postgres/30-postgres-target.yaml
- examples/backups/postgres/run-all.sh
IvanHunters
left a comment
There was a problem hiding this comment.
Re-reviewed the pushed fixes (head b6ac429). The blocking recovery serverName regression from the previous round is resolved and verified end to end:
packages/apps/postgres/templates/db.yamlnow renders the recoveryserverNameunconditionally as{{ .Values.bootstrap.serverName | default .Values.bootstrap.oldName }}, restoring native-CNPG parity (default = the external-cluster entry name =oldName), and pins the backup-pathserverNameto.Release.Name(which equals the Cluster name, so the archive prefix is unchanged on upgrade from the native path). Keycloak pinskeycloak-dbthe same way.- The driver sets both
Bootstrap.OldNameandBootstrap.ServerNameto the source's recordedserverNameon restore, so an in-place restore into a differently-named release reads the source prefix. - Covered by a helm-unittest (
plugin serverName defaults to oldName (native parity)), a Go unit test, and the e2e round-trip restoring into a different release name.
I also re-checked the two doc findings: both are corrected (CRD-upgrade wording and the barman-binaries scoping in docs/operations/backup-classes.md).
The SSA swap is clean: no template still renders spec.backup.barmanObjectStore, and the driver applies only spec.plugins under the same field manager it used for spec.backup, so the native config is removed in a single atomic apply with no CEL-rule transient.
One must-fix before merge (currently keeping the pre-commit check red):
packages/apps/postgres/README.mdis a stale generated artifact.make -C packages/apps/postgres generatere-pads the Bootstrap parameters table after the newbootstrap.serverNamedescription; the committed README still has the old padding.values.schema.json,types.go, and thepostgres-rdschema are in sync; only README.md is stale. Runmake generateand commit README.md.
Nit:
packages/apps/postgres/templates/db.yaml(plugins block comment) still carries the over-generalized "the postgresql image variants this platform pins no longer ship the barman-cloud-* binaries" claim — false for this chart's bare system-flavor tags. Consider mirroring the corrected, scoped wording now indocs/operations/backup-classes.md.
Out of scope for this PR (pre-existing on main, flagging for a follow-up issue): a platform-flow (useSystemBucket: true) in-place restore cannot converge, because the restore patch sets bootstrap.enabled=true but the driver's partial app schema can neither carry nor clear useSystemBucket, while the chart hard-fails rendering on bootstrap.enabled && useSystemBucket. Not introduced here (the guard and patch shape both predate this PR's merge-base), so it should not block this change.
Signed-off-by: Andrey Kolkov <[email protected]>
The keycloak-db backup added in #3174 uses the in-tree native spec.backup.barmanObjectStore, whose barman-cloud-* binaries ship only in the CNPG system image variant; keycloak-db is pinned to the standard variant, so base backups fail with 'barman-cloud-backup: executable file not found'. The proposed workaround (#3306) switches keycloak-db to the deprecated system image variant when backups are enabled. Migrate keycloak-db to the barman-cloud plugin instead, matching the rest of this PR: render spec.plugins referencing a barmancloud.cnpg.io ObjectStore (the S3/barman config moves there) and a method=plugin ScheduledBackup, keeping the Cluster on the standard image (the plugin runs the barman tooling in a sidecar). This removes the need for the system-variant workaround entirely, so #3306 can be closed. #3300. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
…recovery rendering Review follow-ups for the CNPG barman-cloud plugin migration (#3300): - docs/operations/backup-classes.md: the 'Upgrade notes from chart-managed backups' section still described the removed native spec.backup.barmanObjectStore behavior. Rewrite it for the plugin flow (chart renders spec.plugins + a barmancloud.cnpg.io/ObjectStore; the driver SSA-applies an ObjectStore and patches spec.plugins), and document the real upgrade hazard: the plugin-barman-cloud operator + cert-manager must be present in cozy-postgres-operator or the Cluster references an unregistered plugin and backups/WAL archiving stall. - apps/postgres backup_storage_test.yaml: add a positive bootstrap-recovery rendering test. Asserts externalClusters[].plugin references barman-cloud.cloudnative-pg.io with barmanObjectName=<release>-recovery and serverName on the plugin parameter, and that the -recovery ObjectStore carries spec.configuration but NOT serverName (the plugin forbids it there). Previously the recovery path had only fail-fast guard tests, no positive coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
…eferences Review follow-ups (#3300): - Lifecycle/leak: applyClusterPluginBackup now sets an ownerReference from the driver-created barmancloud.cnpg.io/ObjectStore to its cnpg.io/Cluster, so Kubernetes garbage-collects it when the Cluster is deleted (tenant app teardown or the RestoreJob purge). In the platform useSystemBucket flow the chart does not render this ObjectStore, so previously it was orphaned in the tenant namespace. Drop the now-accurate-only 'delete' verb on objectstores from the driver RBAC (GC handles removal; the driver never deletes it) and correct the RBAC comment that falsely claimed the purge deletes it. - Diagnostics/comments: the RestoreJob WAL-archive-deadline failure message and several doc comments still told operators to check spec.backup.barmanObjectStore / externalClusters[].barmanObjectStore, fields no longer emitted after the migration. Point them at the barman-cloud plugin (spec.plugins + ObjectStore, externalClusters[].plugin) instead so they don't mislead during an incident. - Test: TestApplyClusterPluginBackup_PatchesExistingCluster now asserts the ObjectStore carries an ownerReference to the Cluster (kind/name/uid). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
Review follow-up (#3300): ensureCNPGBackup stamps spec.method=plugin + spec.pluginConfiguration on the postgresql.cnpg.io/Backup it creates — the field that routes a driver-initiated run through the barman-cloud plugin. If it regressed to the default (barmanObjectStore) method, CNPG would run the legacy method against a Cluster that no longer has one and every platform backup would fail. Add TestEnsureCNPGBackup_UsesPluginMethod asserting method, plugin name, and cluster ref. (The chart ScheduledBackup path was already covered; the driver path was not.) Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
…in flow Review follow-up (#3300): the apps/postgres user-facing docs still described the removed native spec.backup.barmanObjectStore / archive_command model. Update the values.yaml @field/@typedef/@PARAM annotations (useSystemBucket, schedule, S3CredentialsSecret, EndpointCA, endpointCA, the @PARAM backup note, bootstrap.serverName) and the hand-written README Operations prose to the barman-cloud plugin + ObjectStore model, then regenerate values.schema.json, the README parameters table, the api types, and the postgres-rd ApplicationDefinition schema (which feed the dashboard form help + cozyrd schema). Field names, defaults, and the storageClass immutability marker are unchanged; only backup/ bootstrap description text changed. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
…le e2e fixture Review follow-up (#3300): - docs/operations/backup-classes.md: add an operator-facing upgrade note for the two platform-wide changes this migration carries — the CloudNativePG operator bump to 1.28.1 (with the CRD-skew fix and the Helm-CRD-upgrade caveat) and the LINSTOR scheduler admission-webhook removal, which makes data-locality scheduling advisory cluster-wide (spec.schedulerName no longer auto-assigned; opt in via explicit schedulerName). These affect more than Postgres backups and were previously only noted in a chart values.yaml comment. - hack/e2e-chainsaw/postgres/postgres.yaml: drop stale restic-era backup fields (s3Region, s3Bucket, cleanupStrategy, resticPassword) that no longer exist in the Postgres backup schema; use current fields (destinationPath, endpointURL, retentionPolicy, 6-field schedule). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
Review follow-up (#3300, non-blocking): the barman-cloud ObjectStore CRD validates spec.retentionPolicy against ^[1-9][0-9]*[dwm]$, so an operator who explicitly sets backup.retentionPolicy="" would render retentionPolicy: "" and the API server would reject the ObjectStore, breaking backup provisioning. Guard the field in the apps/postgres and keycloak chart ObjectStores with {{- if .Values.backup.retentionPolicy }} (the Go driver path is already safe via omitempty). Add a helm-unittest asserting an empty retentionPolicy is omitted. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
Port examples/backups/postgres into an automated e2e, following the etcd backup precedent: the numbered example manifests become a runnable harness (00-helpers.sh, run-all.sh, cleanup.sh) that a human reads and the test drives, so the documented flow and the test can never drift. run-all.sh provisions a Bucket, deploys the source Postgres (writing a sentinel row), derives the two Secrets the barman-cloud ObjectStore references from the Bucket's BucketInfo and seaweedfs' CA, submits an ad-hoc BackupJob and waits for Succeeded, then restores to a copy and asserts the sentinel round-tripped through S3 — the in-cluster witness that a plugin backup is both taken and restorable end to end. A second Test document (postgres-2-backup-roundtrip) in the existing hack/e2e-chainsaw/postgres suite drives run-all.sh, gated out of CI behind POSTGRES_E2E_S3_ROUNDTRIP=1 (kind has no reachable, CA-trusted S3 endpoint, and a tenant Pod cannot cross the Cilium egress policy to tenant-root's seaweedfs). Placing it in the postgres suite (rather than a new dir) keeps it triggered by packages/apps/postgres changes under Test Impact Analysis. Co-Authored-By: Claude Opus 4.8 <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
…the e2e
Running the gated postgres backup e2e against a cluster whose apps still
render the pre-upgrade chart surfaced the CNPG webhook rejection
("Cannot enable a WAL archiver plugin when barmanObjectStore is
configured"): the driver cannot attach spec.plugins while the native
config is still present. Document the window and its resolution in the
upgrade notes.
Co-Authored-By: Claude Fable 5 <[email protected]>
Signed-off-by: Andrey Kolkov <[email protected]>
Two defects of the same class — the WAL-archive S3 prefix (serverName) changing or defaulting differently between components: Recovery default regression (review blocker): native CNPG defaulted an absent externalClusters serverName to the entry name (= bootstrap.oldName), but the barman-cloud plugin defaults it to the NEW cluster's name, so a legacy-flow restore with bootstrap.serverName unset would read the wrong S3 prefix (hard failure, or silently restoring another cluster's data). The chart now always emits serverName, defaulting to bootstrap.oldName. Live-cluster serverName flip (found by the gated postgres backup e2e on a real cluster): the chart renders spec.plugins without serverName (effective prefix = cluster name) while a strategy template may name a different one; the driver's ForceOwnership patch then flipped the prefix mid-stream, splitting the WAL archive across two prefixes — the WAL holding the backup's start was lost between them and the restore failed with "WAL not found". The driver now preserves the live Cluster's effective serverName (explicit parameter, or the cluster name when the plugin is attached without one) and only applies the strategy's value to a Cluster with no plugin attached; both charts pin serverName explicitly, and the example strategy no longer sets one. Co-Authored-By: Claude Fable 5 <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
…ple harness The CNPG strategy template renders <app>-cnpg-backup-creds / -ca against whichever application it drives, so a to-copy restore looks the pair up under the TARGET's name. The harness only created the source's pair; on a cluster with stale target Secrets from an earlier run the restore failed TLS verification against the wrong CA. Materialise the pair for the target too (kubectl apply, so stale content is corrected) and delete both pairs in cleanup. Co-Authored-By: Claude Fable 5 <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
Review follow-ups: the vendored cloudnative-pg chart ships the CNPG CRDs as ordinary release manifests (crds.create), so helm upgrade updates them with the operator — the out-of-band kubectl apply claim was wrong (the historical skew came from the image.tag pin, which this PR removes). And the missing barman-cloud-* binaries only affect the standard image variant keycloak-db pins; apps/postgres pins bare system-flavor tags that still ship them — its breakage was the operator/CRD skew. Co-Authored-By: Claude Fable 5 <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
…erName The pre-commit run-make-generate hook regenerates the Go types from the values annotations; the serverName default note landed in values.yaml, README, schema and postgres-rd but missed types.go. Co-Authored-By: Claude Fable 5 <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
Ungate postgres-2-backup-roundtrip: instead of an opt-in env flag it now runs whenever TIA selects the postgres suite (postgres changes, and every release cut), like the other chainsaw tests. The blocker was network placement, not capability: the S3 client is the barman-cloud sidecar inside the Postgres Pod, and the isolated e2e tenant's Cilium egress allowlist blocks it from tenant-root's seaweedfs — so the Test runs in tenant-root (same-tenant egress) against the in-cluster S3 endpoint, trusting its self-signed cert via the copied seaweedfs-system-ca-cert. This is exactly the recipe validated end to end on a real cluster. Also map examples/backups/<app>/ edits to that app's suite in hack/select-e2e.sh (the round-trip executes those scripts as its harness; previously such edits were silently ignored), with bats coverage for both the mapped and the no-suite case. Co-Authored-By: Claude Fable 5 <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
…cription make -C packages/apps/postgres generate re-pads the Bootstrap parameters table for the new bootstrap.serverName row; the committed README still carried the old padding and kept the pre-commit generate check red. Assisted-By: Claude <[email protected]> Co-Authored-By: Claude Fable 5 <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
…r webhook note The db.yaml plugins-block comment still carried the over-generalized claim that the pinned postgresql image variants no longer ship the barman-cloud-* binaries; mirror the scoped wording from docs/operations/backup-classes.md (only the standard variant keycloak-db pins lacks them; this chart's bare system-flavor tags still ship them but the flavor is deprecated upstream). After the rebase onto main this PR no longer touches linstor-scheduler: main re-vendored it to upstream chart 0.3.1 / extender v0.3.6, which keeps the admission webhook and stops stripping unknown pod fields (including initContainers[].restartPolicy the barman sidecar needs). The upgrade note claiming the webhook is removed and locality scheduling becomes advisory is rewritten as a hold-back warning. Also: afterwards -> afterward (American English, review nit). Assisted-By: Claude <[email protected]> Co-Authored-By: Claude Fable 5 <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
b6ac429 to
572492c
Compare
IvanHunters
left a comment
There was a problem hiding this comment.
The fixes from the previous rounds are all in and verified:
- The recovery
serverNameregression is resolved (recovery defaults tooldName, the backup path pins.Release.Name, the driver sets bothBootstrap.OldNameandBootstrap.ServerNameon restore) and survived the rebase intact. packages/apps/postgres/README.mdand the other generated artifacts are back in sync —make generateproduces an empty diff and thepre-commitcheck is green.- The barman-binaries and CRD-upgrade doc claims are correctly scoped now.
The SSA swap, ObjectStore GC via ownerReference, plugin CRD CEL rules, RBAC (no wildcards, delete deliberately omitted), and cert-manager mTLS ordering on cold install all check out; vendored charts are byte-identical to upstream.
Approving. One note before merge: the E2E run on this head failed in the tenant-root install phase on an unrelated vmalertmanager-alertmanager config-reloader crashloop, so the new postgres backup round-trip test did not actually execute. That failure is orthogonal to this PR, but it would be good to re-run E2E and confirm the round-trip goes green before merging.
Non-blocking nit: packages/system/postgres-operator/values.yaml still says "the standard postgresql image variant this platform pins" — reads as if the whole platform pins standard, while apps/postgres pins bare system-flavor tags. Consider mirroring the scoped wording used in db.yaml / backup-classes.md.
The backup roundtrip e2e copied the seaweedfs S3 CA from a Secret named seaweedfs-system-ca-cert in tenant-root, a name captured off a dev cluster. On current main the seaweedfs chart pins fullnameOverride: seaweedfs, so the cert-manager CA Certificate's secret is seaweedfs-ca-cert; the old name is absent and the first CI run of the e2e failed at step 05a with 'secrets "seaweedfs-system-ca-cert" not found'. Default S3_CA_SECRET to seaweedfs-ca-cert and, when that Secret is absent, auto-discover the seaweedfs CA Certificate (the seaweedfs-labelled one with spec.isCA=true) and read its secretName, so a future fullnameOverride change does not silently break the copy again. Fail with a clear message when neither resolves. README and chainsaw comment updated to match. Assisted-By: Claude <[email protected]> Co-Authored-By: Claude Fable 5 <[email protected]> Signed-off-by: Andrey Kolkov <[email protected]>
## What this PR does Makes PostgreSQL point-in-time recovery (PITR) a genuinely supported, tested capability for the new `backups.cozystack.io` API. Fixes #2774. Was **stacked on #3313** (`fix/3300-cnpg-barman-plugin`) for the `recoveryTime` → CNPG `bootstrap.recovery.recoveryTarget.targetTime` plumbing; #3313 has since merged and this branch is rebased on `main`. This PR adds the reliability + verification layer #2774 asked for. ### Changes - **Precise failure reason for an unreachable target.** The restore deadline (`spec.options.restoreTimeoutSeconds`, default 30m) stays the sole authority for "this restore is stuck" — the client waits the full window either way. What changes is the *diagnosis*: once the deadline has elapsed, if `recoveryTime` was set and the recovery Cluster never went healthy, the CNPG driver reads the `full-recovery` pod logs and — when several distinct attempts exited with PostgreSQL's `FATAL: recovery ended before configured recovery target was reached` — fails the RestoreJob with `RecoveryConverged=False` / `Ready=False` (reason `RecoveryTargetUnreachable`) and an actionable message, instead of a bare generic timeout. Timing is unchanged; only the reason/message differ. (For a faster rejection, set a shorter `restoreTimeoutSeconds`.) Design points: - **We deliberately do NOT trip early off the FATAL.** A valid near-now target emits that same FATAL transiently — repeatedly, for many attempts, on a slow archiver — before it converges, so any earlier trip would wrongly reject a recoverable restore (observed twice in CI). The FATAL is used only to *explain* a failure the deadline has already declared. - **Health wins unconditionally** — the deadline/classification check precedes the healthy check, but a recovery that converges within the window reaches success and is never classified as failed (a live dev7 restore did exactly this). - **Counting distinct FATAL-ing attempts** (not pod failures) tolerates a one-off archive-lag miss on a near-now target and ignores transient recovery-pod crashes (a node blip / brief API-server unreachability never emits that FATAL). - Adds the `pods/log` RBAC grant the log read needs (distinct subresource from `pods`); a `Forbidden` is logged loudly and surfaced in the failure message rather than silently disabling the classification. - **e2e** (`examples/backups/postgres/run-all.sh`, wired into the Chainsaw `postgres-2-backup-roundtrip` suite): - Step 45 — write `before`, capture server timestamp (µs precision), write `after`, restore to that timestamp, assert `before` present and `after` absent. - Step 46 — a `recoveryTime` an hour ahead must fail with reason `RecoveryTargetUnreachable` once the deadline elapses (asserts the *reason*, so it proves the classification fired, not just that a timeout occurred). - Chainsaw budget raised 25m → 40m to cover three sequential restores + the post-deadline classification. - **Docs + example**: uncommented `45-restorejob-pitr.yaml`; a `docs/operations/backup-classes.md` PITR section (recoverable window, discovering earliest/latest restorable time from the backup catalog since `firstRecoverabilityPoint` is unreliable under the plugin, archive-lag tolerance, idempotency under GitOps). ### Verification - `go build` / `go vet` clean; unit tests pass (`logIndicatesRecoveryTargetUnreachable`, `recoveryUnreachableFromLogs`, `recoveryPodsToInspect`, nil-Clientset guard, a reconcile-level success-path test asserting `Succeeded` + `RecoveryConverged=True`, and — added per review — `TestReconcileCNPGRestore_DeadlineClassifiesRecoveryTargetUnreachable`, a reconcile-level test driving the deadline branch through all three sub-paths: FATAL found → reason `RecoveryTargetUnreachable`, FATAL absent → generic timeout, `Forbidden` log read → generic timeout + missing-`pods/log`-RBAC note. It fails if the call-site reason is reverted to the generic one). - Reviewed via adversarial branch-review; fixes include a critical missing-RBAC dead-on-arrival, a classification false-positive on transient recovery-pod crashes, and a sub-second e2e flake. - **dev7**: backup + WAL archiving + recovery convergence verified live on the #3313 build. The load-bearing assumption of the classification — CNPG **accumulates** failed `full-recovery` pods rather than reusing one — was confirmed (observed 7 simultaneous failed recovery pods). A fully-green live step-46 run was blocked by intermittent COSI bucket-provisioning flakiness on dev7; the negative path is covered by the CI Chainsaw e2e. ### Downstream repositories Walked the trigger map in `docs/agents/contributing.md` against the diff. The only user-facing surface is the new `docs/operations/backup-classes.md` PITR section; the website keeps a hand-mirrored copy per docs version. - [ ] [cozystack/website](https://github.com/cozystack/website) - follow-up: PITR docs mirror warranted (not yet opened; same pending mirror as #3313's backup-classes changes) - [x] No other downstream repository is affected by this change ```release-note feat(backups): PostgreSQL RestoreJob now supports point-in-time recovery (PITR). Set spec.options.recoveryTime (RFC3339) to restore to an exact instant. A recoveryTime past the latest archived WAL still runs to the restore deadline (spec.options.restoreTimeoutSeconds, default 30m), but the failure is now reported with the precise reason RecoveryTargetUnreachable and an actionable message instead of a generic timeout; set a shorter restoreTimeoutSeconds for a faster rejection. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added PostgreSQL point-in-time recovery (PITR) support via RFC3339 `recoveryTime`. * Introduced a PITR RestoreJob example, plus helper variables and an unreachable-target scenario. * **Bug Fixes** * Improved restore deadline handling with deterministic, log-based fail-fast classification for unreachable recovery targets, including clearer conditions. * **Documentation** * Added detailed PITR operational documentation (recovery windows, failure behavior, and GitOps/idempotent behavior). * Expanded README and example comments for PITR verification and failure expectations. * **Tests** * Extended unit tests and end-to-end chainsaw workflow to cover PITR success and unreachable failures. * **Chores** * Updated RBAC to allow pod log reads; refined cleanup and run scripts for additional RestoreJob artifacts. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
What this PR does
Fixes #3300. Migrates CNPG Postgres backups off the deprecated native
spec.backup.barmanObjectStore(removed in CNPG 1.29; thebarman-cloud-*binaries it needs are absent from thestandardimage variant keycloak-db pins, and the bare system-flavor tags apps/postgres pins are deprecated upstream) onto the CloudNativePG Barman Cloud plugin, across every Postgres backup surface:apps/postgres,system/keycloak, and thecozy-default-cnpgBackupClass strategy driver. The chart rendersspec.plugins+ abarmancloud.cnpg.io/ObjectStoreCR; the backup-controller driver SSA-applies anObjectStoreand patchesspec.plugins;Backup/ScheduledBackupusemethod: plugin. Thepostgres-operatorchart vendors and co-locates theplugin-barman-cloudoperator (with its cert-manager-issued mTLS) so CNPG can discover it.One platform-level change is required for the plugin to run and ships with this PR (see the upgrade notes in
docs/operations/backup-classes.mdand the release note below): the CloudNativePG operator is bumped to 1.28.1 (also fixing an operator/CRD version skew that broke all backups). The other prerequisite — the LINSTOR scheduler admission webhook strippinginitContainers[].restartPolicycluster-wide, which broke the plugin's restartable sidecar — is already fixed onmain(f4aef73 re-vendorslinstor-schedulerto upstream chart 0.3.1 / extender v0.3.6, which keeps the webhook and stops stripping unknown pod fields), so after the rebase this PR no longer toucheslinstor-scheduler; just do not hold that package back on an older version when rolling out the plugin.Supersedes #3306 (the keycloak
system-image workaround): keycloak-db now backs up via the plugin on thestandardimage, so that stopgap is no longer needed.Screenshots
Downstream repositories
Release note
Close #3300, #3246
Summary by CodeRabbit
spec.backup.barmanObjectStorepath; updated gating, credential projection, and ObjectStore-related RBAC/cleanup.