Skip to content

fix(backups): fail Velero RestoreJob rename for non-VMInstance kinds - #4426

Merged
Andrey Kolkov (androndo) merged 3 commits into
mainfrom
fix/3537-velero-rename-kind-guard
Sep 24, 2026
Merged

Andrey Kolkov (androndo) merged 3 commits into
mainfrom
fix/3537-velero-rename-kind-guard

Conversation

@androndo

@androndo Andrey Kolkov (androndo) commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Fixes #3537.

A RestoreJob with spec.targetApplicationRef.name set to a new name and options.targetNamespace set to another namespace reported phase: Succeeded for every Velero-backed application, but only a VMInstance was actually renamed. postRestoreRename looks up the restored HelmRelease under the vm-instance- prefix, gets NotFound for any other kind and returns nil, so a Postgres, MariaDB, ClickHouse, MongoDB or standalone VMDisk copy came up under the source name and nothing was surfaced.

Renaming those kinds through the Velero strategy cannot work by construction: Velero DataUpload restores volumes under their original names, and only a VMInstance keeps using them after its HelmRelease is renamed (its disks keep their names and CDI adopts the restored PVCs). A renamed database Cluster or VMDisk renders fresh volumes for the new name and comes up empty next to orphaned source volumes.

This PR takes the short-term, correctness half of the issue: the rename is rejected up front for every kind other than VMInstance, before a new RestoreJob enters Running or a Velero Restore is created, with a Ready=False message that names the kind and points at omitting targetApplicationRef.name or, for kinds that have one, using the application's engine-native strategy (for Postgres that is the CNPG strategy, which supports restore-to-copy). The existing same-namespace guard keeps precedence, so its message is unchanged.

A rename RestoreJob that is already Running when a controller with this check starts is failed on its next reconcile, after its Velero Restore may have completed. Such a job says in its message that the application may already sit in the target namespace under its source name (a retry without the rename would otherwise trip failIfTargetExists on that copy), and its resource-modifier ConfigMap is released immediately rather than at the job's deletion. Jobs already Succeeded or Failed are never revisited.

Rename-on-restore for non-VM applications through engine-native recovery (the longer-term feature in the issue) is out of scope here. A standalone VMDisk is rejected as well: its rename needs a post-restore PVC rebind and DataVolume replacement that do not exist yet, tracked in #4428.

Tests: TestVeleroRenameSupported and TestReconcileVeleroRestore_RenameGuard. Rejected inputs: Postgres and VMDisk cross-namespace rename, and a VMDisk backup restored with a VMInstance target kind (the guard keys on the backup's kind, since that is what postRestoreRename acts on) → Failed with the new message; Postgres same-namespace rename → the pre-existing DataUpload message. Accepted inputs: VMInstance cross-namespace rename, a VMDisk copy that keeps its name, and a VMDisk in-place restore without targetApplicationRef → Running. The rejected cases fail on main (phase Running instead of Failed); the accepted cases fail if the IsRenamed conjunct is dropped. TestReconcileVeleroRestore_RenameGuard_InFlight covers the already-running case for VMDisk (Failed, message names the source-name copy, ConfigMap gone) and VMInstance (Succeeded).

Screenshots

N/A — no UI changes.

Downstream repositories

Walked the trigger map in docs/agents/contributing.md against the diff: two Go files under internal/backupcontroller/, no API, CRD, chart, values, schema or docs change.

Release note

fix(backups): a Velero-strategy RestoreJob that requests a different targetApplicationRef.name for an application kind other than VMInstance (Postgres, MariaDB, ClickHouse, MongoDB, VMDisk) now fails with an explicit message instead of reporting Succeeded with the application restored under its original name.

Summary by CodeRabbit

  • Bug Fixes
    • Velero restores now fail with a clear message when a requested application rename isn’t supported for that application type.
    • If an unsupported rename is detected after a restore has started, the restore is stopped and its temporary configuration is cleaned up. The message notes that resources may already have been restored under the original name.
    • Renames for VM instances and restores that keep the original application name continue to work.

A RestoreJob that set targetApplicationRef.name to a new name and
options.targetNamespace to another namespace reported Succeeded for any
Velero-backed application, but only a VMInstance was actually renamed.
postRestoreRename looks up the HelmRelease under the vm-instance- prefix,
gets NotFound for every other kind and returns nil, so a Postgres,
MariaDB, ClickHouse, MongoDB or standalone VMDisk copy came up under the
source name with no error surfaced.

Renaming those kinds through the Velero strategy cannot work: DataUpload
restores volumes under their original names, and only a VMInstance keeps
using them after its HelmRelease is renamed, because its disks keep their
names and CDI adopts the restored PVCs. A renamed database Cluster or
VMDisk renders fresh volumes and comes up empty.

Reject the rename up front for every kind other than VMInstance, before
the RestoreJob enters Running, with a message that points at omitting
targetApplicationRef.name or using the engine-native strategy. The
existing same-namespace guard keeps precedence, so its message is
unchanged.

Closes #3537

Assisted-By: LLM
Signed-off-by: Andrey Kolkov <[email protected]>
@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: cozystack/cozystack/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: e5e38a97-3143-4daa-8b49-d8933bc76dba

📥 Commits

Reviewing files that changed from the base of the PR and between 45f5698 and d50385f.

📒 Files selected for processing (2)
  • internal/backupcontroller/velerostrategy_controller.go
  • internal/backupcontroller/velerostrategy_controller_test.go

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Velero restore reconciliation now rejects rename requests for application kinds other than VMInstance. For jobs that have started, it also cleans up resource-modifier ConfigMaps and reports that resources may already have been restored under the source name.

Changes

Velero Restore Rename Guard

Layer / File(s) Summary
Rename support by application kind
internal/backupcontroller/velerostrategy_controller.go, internal/backupcontroller/velerostrategy_controller_test.go
The veleroRenameSupported helper returns true only for VMInstance. A table test checks VMInstance and unsupported kinds.
Restore rename validation and cleanup
internal/backupcontroller/velerostrategy_controller.go, internal/backupcontroller/velerostrategy_controller_test.go
Reconciliation fails rename requests for unsupported kinds. If a job has started, it deletes resource-modifier ConfigMaps and notes that the application may already have been restored under its source name. Tests cover rejected and allowed rename cases, including in-flight restores.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Bug fix · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to d5038

Unsupported Velero renames now fail explicitly without an identified merge-blocking regression.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.14% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 7 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: rejecting Velero RestoreJob renames for non-VMInstance kinds.
Linked Issues check ✅ Passed The change satisfies the coding requirements in [#3537]. reconcileVeleroRestore now rejects a renamed Velero restore when the backup kind is not VMInstance. The failure message identifies the unsu…
Out of Scope Changes check ✅ Passed The changed production code is limited to the Velero restore rename guard and its kind check. The added tests verify the guard and its required restore-job behavior. These changes directly support [#3…
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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 area/storage Issues or PRs related to storage (linstor, seaweedfs, bucket, velero, harbor) kind/bug Categorizes issue or PR as related to a bug size/L This PR changes 100-499 lines, ignoring generated files labels Sep 23, 2026
The guard's regression cases only exercised renames, so dropping the
IsRenamed conjunct (failing every non-VMInstance restore) or keying the
check on targetApplicationRef.kind instead of the backup's kind left the
package green. Add the inputs the guard must let through, a VMDisk copy
that keeps its name and a VMDisk in-place restore without
targetApplicationRef, and a VMDisk backup restored with a VMInstance
target kind, which must still be rejected by the source kind.

Assisted-By: LLM
Signed-off-by: Andrey Kolkov <[email protected]>
…t jobs

The refusal pointed every kind at "the application's engine-native
strategy", but VMDisk, the only non-VMInstance kind the default
BackupClass routes to Velero, has none. Offer that remedy only to kinds
that can have one.

The check also runs on every reconcile, so a rename RestoreJob that was
already Running when the controller picked up the check is failed after
its Velero Restore has completed. That job now says the application may
already sit in the target namespace under its source name (a retry that
drops the rename would otherwise trip failIfTargetExists on that copy
without explanation), and its resource-modifier ConfigMap is released
right away instead of at the job's deletion.

Assisted-By: LLM
Signed-off-by: Andrey Kolkov <[email protected]>

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

Verdict

LGTM with non-blocking notes

The guard rejects an unsupported Velero rename before the job goes Running (fresh job), or on the next reconcile for a job already in flight, with an explicit Ready=False message. Build, vet and test are green and the new tests are non-vacuous. One non-blocking note on the in-flight cleanup below.

Findings

  • [MINOR] internal/backupcontroller/velerostrategy_controller.go:623, In-flight cleanup releases the resource-modifier ConfigMap without gating on the Velero Restore phase

Caveats

  • Verified statically and by unit test only (hermetic review, no live cluster). The terminal-phase early return at internal/backupcontroller/restorejob_controller.go:96-100 means an already Succeeded/Failed job never re-enters this guard, so the in-flight branch fires only on a non-terminal job, which is what the PR body claims. The in-flight branch (velerostrategy_controller.go:620-626) leaves the Velero Restore CR to be reaped on RestoreJob deletion, so it adds no new leak. Its one asymmetry with the existing cleanup paths, the missing Velero-phase gate, is the MINOR note above.
  • Upgrade path (existing customer): the only affected state is a rename RestoreJob left Running by an older controller. It now fails with a message that names the possible source-name copy. Losing the prior Succeeded is not a regression, because that success was the bug: postRestoreRename returns nil on the NotFound for the vm-instance- prefixed HelmRelease, so non-VM kinds came up under the source name. Fresh-install, chart and migration surfaces are N/A on a Go-only diff.
  • The guard keys on backup.Spec.ApplicationRef.Kind (the source kind), not the user-supplied targetApplicationRef.kind. That is correct: Velero restores the source application's volumes, so only the source kind matters. A VMDisk backup restored with a VMInstance target kind is still rejected, and a test covers it.
  • golangci-lint reports issues in the package, all in files this PR does not touch (other strategy controllers, backupjob_controller_test.go), none in the two changed files. Pre-existing, out of scope.

Recommended follow-ups

  • Non-VM rename-on-restore via engine-native recovery, and the standalone VMDisk rebind (#4428), stay out of scope as stated. Not blocking.

if restoreJob.Status.StartedAt != nil {
// The job predates this check: its Velero Restore may already have
// restored the application under the source name.
r.cleanupResourceModifierConfigMaps(ctx, restoreJob)

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.

[MINOR] In-flight cleanup releases the resource-modifier ConfigMap without gating on the Velero Restore phase

The three existing calls to cleanupResourceModifierConfigMaps (:742, :748, :763) each run only after the Velero Restore reaches a terminal phase; the non-terminal branch at :733 requeues without cleanup. This new call site deletes the ConfigMap unconditionally. If the controller carrying this guard starts while an old-code rename job has a Velero Restore still in its pre-validation window (phase empty/New), the delete removes the ConfigMap before Velero reads it in validateAndComplete ([email protected] pkg/controller/restore_controller.go:407-426, a single kbClient.Get whose failure appends a ValidationError), so that Restore fails validation instead of completing. No data loss and the RestoreJob fails either way (a one-time migration race, and failing that stray Restore may even be preferable), so this is not blocking and may be intentional. Worth one line either way: TestReconcileVeleroRestore_RenameGuard_InFlight only exercises the Completed phase, so the non-terminal window is untested; a case with RestorePhaseNew (or empty) would document the intended behaviour.

@androndo
Andrey Kolkov (androndo) merged commit 61a7a25 into main Sep 24, 2026
49 checks passed
@androndo
Andrey Kolkov (androndo) deleted the fix/3537-velero-rename-kind-guard branch September 24, 2026 07:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/storage Issues or PRs related to storage (linstor, seaweedfs, bucket, velero, harbor) kind/bug Categorizes issue or PR as related to a bug size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(backups): RestoreJob targetApplicationRef rename is silently ignored for Velero-backed non-VM apps

2 participants