Skip to content

fix(backups): support cnpg-operator with barman plugin - #3313

Merged
Andrey Kolkov (androndo) merged 18 commits into
mainfrom
fix/3300-cnpg-barman-plugin
Jul 21, 2026
Merged

Andrey Kolkov (androndo) merged 18 commits into
mainfrom
fix/3300-cnpg-barman-plugin

Conversation

@androndo

@androndo Andrey Kolkov (androndo) commented Jul 15, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Fixes #3300. Migrates CNPG Postgres backups off the deprecated native spec.backup.barmanObjectStore (removed in CNPG 1.29; the barman-cloud-* binaries it needs are absent from the standard image 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 the cozy-default-cnpg BackupClass strategy driver. The chart renders spec.plugins + a barmancloud.cnpg.io/ObjectStore CR; the backup-controller driver SSA-applies an ObjectStore and patches spec.plugins; Backup/ScheduledBackup use method: plugin. The postgres-operator chart vendors and co-locates the plugin-barman-cloud operator (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.md and 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 stripping initContainers[].restartPolicy cluster-wide, which broke the plugin's restartable sidecar — is already fixed on main (f4aef73 re-vendors linstor-scheduler to 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 touches linstor-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 the standard image, so that stopgap is no longer needed.

Screenshots

Downstream repositories

Release note

fix(backups): migrate CNPG Postgres backups to the CloudNativePG Barman Cloud plugin

Postgres backups (apps/postgres, system/keycloak, and the cozy-default-cnpg BackupClass strategy) now use the CloudNativePG Barman Cloud plugin (spec.plugins + a barmancloud.cnpg.io/ObjectStore, method: plugin) instead of the deprecated native spec.backup.barmanObjectStore (removed in CNPG 1.29; the barman-cloud-* binaries it needs are absent from the standard image variant keycloak-db pins, and the bare system-flavor tags apps/postgres pins are deprecated upstream). The barman-cloud plugin operator is installed alongside the CNPG operator.

One platform-level change rides along and requires operator attention on upgrade:
- The CloudNativePG operator is bumped to 1.28.1 (fleet-wide minor upgrade); this also fixes an operator-newer-than-its-CRDs skew that made the operator report "instance manager was restarted during backup" and fail every backup. The vendored chart ships the CNPG CRDs as ordinary release manifests (crds.create), so helm upgrade updates them with the operator; the skew came from the image.tag pin outrunning the chart's CRDs, which this PR removes.
- The plugin's restartable sidecar requires the linstor-scheduler admission webhook that no longer strips initContainers[].restartPolicy (upstream chart 0.3.1 / extender v0.3.6, shipped separately on main); do not hold the linstor-scheduler package back on an older version when rolling out the plugin.

Close #3300, #3246

Summary by CodeRabbit

  • New Features
    • Switched PostgreSQL CNPG backups/restores to the plugin-based Barman Cloud workflow, using dedicated ObjectStore resources for backup and recovery.
    • Added the Barman Cloud CNPG-I plugin chart (deployment, TLS, RBAC, leader election).
  • Bug Fixes
    • Improved backup/restore attachment messaging when the CNPG Cluster isn’t ready yet.
    • Preserves server-name behavior to avoid WAL-archive prefix issues; clarified endpoint CA handling.
  • Changes
    • Helm integration no longer uses the legacy spec.backup.barmanObjectStore path; updated gating, credential projection, and ObjectStore-related RBAC/cleanup.
    • Removed the linstor-scheduler admission webhook; updated CloudNativePG/operator versions and CRDs.
  • Tests
    • Updated controller and Helm-unittest checks for the plugin flow; added/extended end-to-end backup/restore coverage.
  • Documentation
    • Expanded upgrade/migration and backup documentation; updated backup example materials and comments.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The 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.

Changes

CNPG backup plugin migration

Layer / File(s) Summary
CNPG plugin contracts and controller reconciliation
internal/backupcontroller/..., api/backups/..., packages/system/backupstrategy-controller/definitions/*
CNPG types, ObjectStore models, SSA reconciliation, plugin-based Backup resources, server-name preservation, and recovery handling are updated.
Backup and recovery rendering
packages/apps/postgres/..., packages/system/keycloak/...
PostgreSQL and Keycloak templates render barman-cloud plugin references and primary or recovery ObjectStore resources.
Validation, permissions, and migration documentation
internal/backupcontroller/*_test.go, packages/apps/postgres/tests/*, packages/system/backupstrategy-controller/templates/rbac.yaml, docs/operations/*
Tests validate plugin wiring and gating, RBAC adds ObjectStore permissions, and documentation describes migration behavior.

Postgres operator and barman-cloud chart

Layer / File(s) Summary
Plugin chart and runtime resources
packages/system/postgres-operator/charts/plugin-barman-cloud/*
A complete plugin chart is added with metadata, values, schema, documentation, ObjectStore CRD, deployment, service, certificates, RBAC, and deployment tests.
CloudNativePG refresh
packages/system/postgres-operator/{Makefile,values.yaml}, charts/cloudnative-pg/*
CloudNativePG and plugin versions are updated, CRD schemas and monitoring queries are refreshed, webhook probes use a named port, and the operator image follows chart appVersion.

Linstor scheduler chart refresh

Layer / File(s) Summary
Scheduler chart and admission removal
packages/system/linstor-scheduler/*
The scheduler chart is upgraded, image fallback and RBAC are adjusted, and admission configuration is removed.

PostgreSQL backup round-trip example

Layer / File(s) Summary
Backup, restore, and e2e harness
examples/backups/postgres/*, hack/e2e-chainsaw/postgres/chainsaw-test.yaml, hack/select-e2e*
Helper and shell scripts provision storage, run backup and restore jobs, verify restored data, clean up resources, and expose an opt-in Chainsaw test.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

  • cozystack/cozystack#3174: Adds earlier Keycloak CNPG S3 backup wiring that this change replaces with the plugin-based flow.
  • cozystack/cozystack#3306: Also modifies Keycloak PostgreSQL backup handling around the legacy barmanObjectStore integration.

Suggested labels: area/platform, area/testing

Suggested reviewers: kvaps, sircthulhu, lexfrei, lllamnyp, myasnikovdaniil

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: moving CNPG backups to the Barman Cloud plugin.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/3300-cnpg-barman-plugin

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added size/XL This PR changes 500-999 lines, ignoring generated files area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review labels Jul 15, 2026
@androndo Andrey Kolkov (androndo) changed the title Fix/3300 cnpg barman plugin fix(backups): support cnpg-operator with barman plugin Jul 16, 2026
@github-actions github-actions Bot added area/storage Issues or PRs related to storage (linstor, seaweedfs, bucket, velero, harbor) kind/bug Categorizes issue or PR as related to a bug labels Jul 16, 2026
@androndo
Andrey Kolkov (androndo) force-pushed the fix/3300-cnpg-barman-plugin branch from e05acca to 2ffe43e Compare July 16, 2026 06:32
@androndo
Andrey Kolkov (androndo) marked this pull request as ready for review July 16, 2026 06:32
@dosubot dosubot Bot added the area/database Issues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse) label Jul 16, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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

  • Migration to barman-cloud plugin: Migrated the CNPG backup strategy from the deprecated spec.backup.barmanObjectStore field to the modern barman-cloud plugin architecture.
  • New ObjectStore CRs: Introduced ObjectStore custom resources to manage S3 and barman configurations, allowing for better decoupling and management.
  • Controller Refactoring: Updated the backupstrategy-controller to use Server-Side Apply (SSA) for managing ObjectStore resources and cluster plugins.
  • Chart Updates: Updated postgres-operator and linstor-scheduler charts to support the new plugin architecture and resolve sidecar compatibility issues.
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
  • Ignored by pattern: **/*.patch (1)
    • packages/system/linstor-scheduler/patches/disable-ca-key-rotation.patch
  • Ignored by pattern: **/charts/** (34)
    • packages/system/linstor-scheduler/charts/linstor-scheduler/Chart.yaml
    • packages/system/linstor-scheduler/charts/linstor-scheduler/templates/admission-deployment.yaml
    • packages/system/linstor-scheduler/charts/linstor-scheduler/templates/admission-service.yaml
    • packages/system/linstor-scheduler/charts/linstor-scheduler/templates/certmanager.yaml
    • packages/system/linstor-scheduler/charts/linstor-scheduler/templates/deployment.yaml
    • packages/system/linstor-scheduler/charts/linstor-scheduler/templates/mutatingwebhookconfiguration.yaml
    • packages/system/linstor-scheduler/charts/linstor-scheduler/templates/rbac.yaml
    • packages/system/linstor-scheduler/charts/linstor-scheduler/values.yaml
    • packages/system/postgres-operator/charts/cloudnative-pg/Chart.lock
    • packages/system/postgres-operator/charts/cloudnative-pg/Chart.yaml
    • packages/system/postgres-operator/charts/cloudnative-pg/README.md
    • packages/system/postgres-operator/charts/cloudnative-pg/templates/crds/crds.yaml
    • packages/system/postgres-operator/charts/cloudnative-pg/templates/deployment.yaml
    • packages/system/postgres-operator/charts/cloudnative-pg/values.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/.helmignore
    • packages/system/postgres-operator/charts/plugin-barman-cloud/Chart.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/LICENSE
    • packages/system/postgres-operator/charts/plugin-barman-cloud/README.md
    • packages/system/postgres-operator/charts/plugin-barman-cloud/README.md.gotmpl
    • packages/system/postgres-operator/charts/plugin-barman-cloud/templates/_helpers.tpl
    • packages/system/postgres-operator/charts/plugin-barman-cloud/templates/additional-rbac/leader_election.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/templates/certificate-issuer.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/templates/client-certificate.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/templates/configmap.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/templates/crds/crds.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/templates/deployment.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/templates/rbac.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/templates/server-certificate.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/templates/service.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/test/simple-deployment/01-simple_deployment-assert.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/test/simple-deployment/01-simple_deployment.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/test/simple-deployment/chainsaw-test.yaml
    • packages/system/postgres-operator/charts/plugin-barman-cloud/values.schema.json
    • packages/system/postgres-operator/charts/plugin-barman-cloud/values.yaml
  • Ignored by pattern: **/zz_generated.*.go (1)
    • internal/backupcontroller/cnpgtypes/zz_generated.deepcopy.go
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +253 to +255
spec:
retentionPolicy: {{ .Values.backup.retentionPolicy | quote }}
configuration:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/apps/postgres/README.md (1)

65-66: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fix 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

📥 Commits

Reviewing files that changed from the base of the PR and between cce29e2 and caed82e.

📒 Files selected for processing (13)
  • api/apps/v1alpha1/postgresql/types.go
  • docs/operations/backup-classes.md
  • hack/e2e-chainsaw/postgres/postgres.yaml
  • internal/backupcontroller/cnpgstrategy_controller.go
  • internal/backupcontroller/cnpgstrategy_controller_test.go
  • packages/apps/postgres/README.md
  • packages/apps/postgres/templates/db.yaml
  • packages/apps/postgres/tests/backup_storage_test.yaml
  • packages/apps/postgres/values.schema.json
  • packages/apps/postgres/values.yaml
  • packages/system/backupstrategy-controller/templates/rbac.yaml
  • packages/system/keycloak/templates/db.yaml
  • packages/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 IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.go GetServerName): an external cluster's serverName defaults to the external cluster entry name, which the chart sets to bootstrap.oldName.
  • plugin-barman-cloud v0.13.0 (internal/cnpgi/operator/config/config.go): the recovery serverName, when the plugin parameter is absent, defaults to cluster.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

  1. docs/operations/backup-classes.md and 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 under templates/crds/crds.yaml gated by crds.create: true, so they are ordinary release manifests and upgrade with the release. helm.sh/resource-policy: keep prevents deletion, not updates. The real historical skew came from the image.tag pin outrunning the vendored chart's CRDs, which this PR removes.
  2. The claim that the pinned image variants no longer ship the barman-cloud-* binaries holds for keycloak's standard-variant pin, but apps/postgres pins bare tags (the system flavor), 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.

@github-actions github-actions Bot added size/XXL This PR changes 1000+ lines, ignoring generated files and removed size/XL This PR changes 500-999 lines, ignoring generated files labels Jul 16, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between caed82e and fce0dc6.

📒 Files selected for processing (18)
  • docs/operations/backup-classes.md
  • examples/backups/postgres/00-helpers.sh
  • examples/backups/postgres/10-cnpg-strategy.yaml
  • examples/backups/postgres/30-postgres-target.yaml
  • examples/backups/postgres/40-restorejob-to-copy.yaml
  • examples/backups/postgres/README.md
  • examples/backups/postgres/cleanup.sh
  • examples/backups/postgres/run-all.sh
  • hack/e2e-chainsaw/postgres/chainsaw-test.yaml
  • internal/backupcontroller/cnpgstrategy_controller.go
  • internal/backupcontroller/cnpgstrategy_controller_test.go
  • packages/apps/postgres/README.md
  • packages/apps/postgres/templates/db.yaml
  • packages/apps/postgres/tests/backup_storage_test.yaml
  • packages/apps/postgres/values.schema.json
  • packages/apps/postgres/values.yaml
  • packages/system/keycloak/templates/db.yaml
  • packages/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

Comment thread docs/operations/backup-classes.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
hack/select-e2e_test.bats (1)

165-172: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove || true to avoid masking script errors.

Tolerating failures from hack/select-e2e.sh with || true masks 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 || true for another empty-selection case, hack/select-e2e.sh is expected to exit cleanly with status 0 even 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

📥 Commits

Reviewing files that changed from the base of the PR and between fce0dc6 and b6ac429.

📒 Files selected for processing (8)
  • api/apps/v1alpha1/postgresql/types.go
  • examples/backups/postgres/30-postgres-target.yaml
  • examples/backups/postgres/40-restorejob-to-copy.yaml
  • examples/backups/postgres/README.md
  • examples/backups/postgres/run-all.sh
  • hack/e2e-chainsaw/postgres/chainsaw-test.yaml
  • hack/select-e2e.sh
  • hack/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 IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.yaml now renders the recovery serverName unconditionally as {{ .Values.bootstrap.serverName | default .Values.bootstrap.oldName }}, restoring native-CNPG parity (default = the external-cluster entry name = oldName), and pins the backup-path serverName to .Release.Name (which equals the Cluster name, so the archive prefix is unchanged on upgrade from the native path). Keycloak pins keycloak-db the same way.
  • The driver sets both Bootstrap.OldName and Bootstrap.ServerName to the source's recorded serverName on 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.md is a stale generated artifact. make -C packages/apps/postgres generate re-pads the Bootstrap parameters table after the new bootstrap.serverName description; the committed README still has the old padding. values.schema.json, types.go, and the postgres-rd schema are in sync; only README.md is stale. Run make generate and 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 in docs/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.

Andrey Kolkov (androndo) and others added 6 commits July 16, 2026 21:44
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]>
Andrey Kolkov (androndo) and others added 11 commits July 16, 2026 21:44
…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]>
IvanHunters
IvanHunters previously approved these changes Jul 17, 2026

@IvanHunters IvanHunters left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fixes from the previous rounds are all in and verified:

  • The recovery serverName regression is resolved (recovery defaults to oldName, the backup path pins .Release.Name, the driver sets both Bootstrap.OldName and Bootstrap.ServerName on restore) and survived the rebase intact.
  • packages/apps/postgres/README.md and the other generated artifacts are back in sync — make generate produces an empty diff and the pre-commit check 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]>
@androndo
Andrey Kolkov (androndo) merged commit e73ad68 into main Jul 21, 2026
43 of 45 checks passed
@androndo
Andrey Kolkov (androndo) deleted the fix/3300-cnpg-barman-plugin branch July 21, 2026 10:45
Andrey Kolkov (androndo) added a commit that referenced this pull request Jul 23, 2026
## 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 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/database Issues or PRs related to managed databases (postgres, mariadb, redis, etcd, kafka, clickhouse) area/storage Issues or PRs related to storage (linstor, seaweedfs, bucket, velero, harbor) area/uncategorized PR auto-labeler could not map title scope to a known area/*; please review kind/bug Categorizes issue or PR as related to a bug size/XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CNPG backups broken on 1.6: standard image variant lacks barman-cloud but backups use native barmanObjectStore

3 participants