Skip to content

feat(backups): restore vmi to copy in another namespace - #2329

Merged
Andrey Kolkov (androndo) merged 1 commit into
mainfrom
feat/restore-vm-to-copy
Apr 9, 2026
Merged

Andrey Kolkov (androndo) merged 1 commit into
mainfrom
feat/restore-vm-to-copy

Conversation

@androndo

@androndo Andrey Kolkov (androndo) commented Apr 3, 2026 •

Copy link
Copy Markdown
Contributor

What this PR does

Release note

add ability for restoring VMI to copy in another namespace

Summary by CodeRabbit

  • New Features

    • RestoreJob CR supports a free-form spec.options payload; restores now support in-place preserves (IP/MAC, PVC handling), cross-namespace copy restores, and safe rename semantics.
  • Documentation

    • Added admin and tenant walkthroughs for Velero-backed VMInstance/VMDisk backup & restore.
  • Examples

    • New stepwise demo scripts, helpers, orchestration (run-all) and cleanup scripts.
  • Tests

    • Extensive unit tests covering restore option parsing, target resolution, and restore side effects.
  • Chores

    • RBAC updated to allow HelmRelease create/delete and namespace listing for restore flows.

@coderabbitai

coderabbitai Bot commented Apr 3, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Adds an optional free-form spec.options to RestoreJob CRD and implements typed parsing and restore-control flow (in-place vs cross-namespace), including PVC/OVN handling, HelmRelease rename logic, resource-modifier generation, RBAC updates, example/demo scripts, and unit tests.

Changes

Cohort / File(s) Summary
API Type & CRD
api/backups/v1alpha1/restorejob_types.go, packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml
Added spec.options (*runtime.RawExtension) to RestoreJobSpec and CRD schema with preserve-unknown-fields to accept driver-specific restore options.
Core Reconciler Logic
internal/backupcontroller/velerostrategy_controller.go, internal/backupcontroller/restorejob_controller.go
Added typed CommonRestoreOptions/RestoreOptions, option defaulting/getters, restore-target resolution (IsCopy/IsRenamed), validation (namespace/HelmRelease checks), modified pre/post-restore flows (prepareForRestore, createVeleroRestore, postRestoreRename), resource-modifier ConfigMap generation (JSON-patch support), DataVolume deletion propagation change, and a ConfigMap cleanup helper.
Unit Tests
internal/backupcontroller/velerostrategy_controller_test.go
Large additions: option parsing/defaulting tests, restore-target resolution cases, reconciler side-effect tests (prepareForRestore, postRestoreRename, targetHelmReleaseExists), ConfigMap rule assertions, dynamic-client scaffolding, and other helpers.
RBAC
packages/system/backupstrategy-controller/templates/rbac.yaml
Expanded ClusterRole for Flux HelmReleases verbs to include create and delete; added namespaces get,list,watch rules for target-namespace validation.
Example scripts & helpers
examples/backups/vmi/00-helpers.sh, examples/backups/vmi/run-all.sh, examples/backups/vmi/01-create-strategies.sh, examples/backups/vmi/02-create-backupclass.sh, examples/backups/vmi/03-create-vmdisk.sh, examples/backups/vmi/04-create-vminstance.sh, examples/backups/vmi/05-create-backupjob.sh, examples/backups/vmi/06-restore-in-place.sh, examples/backups/vmi/07-restore-to-copy.sh, examples/backups/vmi/cleanup.sh
Added demo helper library (logging, wait_for_field, resource_exists), step scripts to create strategies/BackupClass/VMDisk/VMInstance/BackupJob, in-place and cross-namespace restore scripts, cleanup and orchestration scripts.
Documentation
examples/backups/vmi/scenario-admin-prepare.md, examples/backups/vmi/scenario-user-backup.md, examples/backups/vmi/scenario-user-restore.md
New scenario docs describing admin preparation, user backup, and restore workflows matching the example scripts.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant KubernetesAPI
    participant RestoreJobReconciler
    participant VeleroController
    participant HelmReleaseController

    User->>KubernetesAPI: Create RestoreJob (spec.options, targetApplicationRef)
    KubernetesAPI->>RestoreJobReconciler: Reconcile event
    RestoreJobReconciler->>RestoreJobReconciler: Parse options & resolve target (namespace, name, IsCopy)

    alt Cross-namespace (IsCopy)
        RestoreJobReconciler->>KubernetesAPI: Verify target namespace exists
        RestoreJobReconciler->>KubernetesAPI: Check target HelmRelease (failIfTargetExists)
    else In-place
        RestoreJobReconciler->>KubernetesAPI: Prepare source app (suspend HelmRelease, stop VM)
        RestoreJobReconciler->>KubernetesAPI: Rename PVCs if KeepOriginalPVC=true
    end

    RestoreJobReconciler->>KubernetesAPI: Create ResourceModifier ConfigMap (patch rules)
    RestoreJobReconciler->>VeleroController: Create Velero Restore (namespaceMapping if copy)
    VeleroController->>KubernetesAPI: Execute restore using resource modifiers

    alt Rename required
        RestoreJobReconciler->>HelmReleaseController: Create target HelmRelease
        RestoreJobReconciler->>HelmReleaseController: Delete source HelmRelease
    end

    RestoreJobReconciler->>KubernetesAPI: Update RestoreJob status (Succeeded/Failed)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I parsed the options, hopped through each namespace lane,
I patched PVCs and kept IPs so networks stay sane,
HelmReleases leapt to new names with a twitch of my ear,
Velero hummed, helpers cheered — restores finished, nothing to fear!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and clearly summarizes the main feature addition: enabling VMInstance restoration as a copy to another namespace, which is the primary objective reflected across multiple modified files and comprehensive documentation.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/restore-vm-to-copy

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 and usage tips.

@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 introduces a comprehensive demonstration suite for VMInstance backup and restore operations, including shell scripts for environment setup, resource provisioning, and job execution, alongside detailed markdown documentation. Feedback indicates that several fields used in the RestoreJob examples, such as the options block and its sub-fields, appear to be missing from the underlying API definition, which will cause the jobs to fail or the fields to be ignored. Additionally, there is a typo in the keepOriginalIpAndMac field name and an inconsistency between the documentation and the scripts regarding the placement of the targetNamespace field.

Comment thread examples/backups/vmi/07-restore-to-copy.sh Outdated
Comment thread examples/backups/vmi/07-restore-to-copy.sh Outdated
Comment thread examples/backups/vmi/scenario-user-restore.md Outdated
@androndo
Andrey Kolkov (androndo) force-pushed the feat/restore-vm-to-copy branch 4 times, most recently from 1af834c to c4a2dc3 Compare April 6, 2026 07:44
@androndo
Andrey Kolkov (androndo) marked this pull request as ready for review April 6, 2026 07:44
@dosubot dosubot Bot added size/XXL This PR changes 1000+ lines, ignoring generated files kind/feature Categorizes issue or PR as related to a new feature labels Apr 6, 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: 7

🧹 Nitpick comments (1)
internal/backupcontroller/velerostrategy_controller_test.go (1)

1102-1113: Use strings.Contains from the standard library.

The custom containsString helper reimplements functionality available in the strings package.

♻️ Suggested refactor
+import "strings"
+
 // containsString reports whether substr appears in s.
 func containsString(s, substr string) bool {
-	if len(substr) == 0 || len(s) < len(substr) {
-		return false
-	}
-	for i := 0; i <= len(s)-len(substr); i++ {
-		if s[i:i+len(substr)] == substr {
-			return true
-		}
-	}
-	return false
+	return strings.Contains(s, substr)
 }

Or simply replace calls to containsString(s, substr) with strings.Contains(s, substr) and remove this helper entirely.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/backupcontroller/velerostrategy_controller_test.go` around lines
1102 - 1113, The helper containsString reimplements standard behavior; replace
all calls to containsString(s, substr) with strings.Contains(s, substr), remove
the containsString function, and add the "strings" import where needed (or
remove it if already present); ensure any tests compile by updating imports and
cleaning up the removed helper references (function name: containsString).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/backups/v1alpha1/restorejob_types.go`:
- Around line 46-50: The generated deepcopy for RestoreJobSpec misses
deep-copying Options (*runtime.RawExtension); regenerate
zz_generated.deepcopy.go (e.g., run your repo's codegen/make generate or
controller-gen deepcopy) so RestoreJobSpec.DeepCopyInto properly allocates a new
runtime.RawExtension and copies the Raw byte slice, or manually update
RestoreJobSpec.DeepCopyInto to set out.Options = new(runtime.RawExtension) and
copy the Options.Raw bytes, ensuring Options is fully deep-copied rather than
shared.

In `@examples/backups/vmi/03-create-vmdisk.sh`:
- Line 39: The success message currently declares readiness prematurely in the
call to log_success ("VMDisk is ready (image download may still be in
progress)"); update that call to use a truthful status like "VMDisk created" or
replace it with a real readiness check and only call log_success once the VMDisk
is confirmed ready. Locate the log_success invocation and change the message
text to "VMDisk created" (or implement and call a readiness-check function
before logging readiness).

In `@examples/backups/vmi/06-restore-in-place.sh`:
- Around line 27-31: The heredoc uses an unquoted delimiter (<<EOF) so backticks
in the comment for keepOriginalPVC (`<name>-orig-<hash>`) are treated as command
substitution and break the script; fix by preventing expansion — either change
the heredoc to a quoted delimiter (e.g., <<'EOF') or replace/escape the
backticks in the keepOriginalPVC line (e.g., use '<name>-orig-<hash>' or
\`\<name\>-orig-\<hash\>\`) so the literal string is preserved.

In `@examples/backups/vmi/07-restore-to-copy.sh`:
- Around line 40-45: The unquoted heredoc (<<EOF) contains backticks around
`<name>-orig-<hash>` which causes bash to perform command substitution; fix by
either quoting the heredoc delimiter (use <<'EOF') or replacing the backticks
with single quotes around the literal string `<name>-orig-<hash>` so the text is
preserved literally; update the heredoc that contains the options block (the
<<EOF section) accordingly to prevent shell expansion.

In `@examples/backups/vmi/cleanup.sh`:
- Around line 33-64: Replace the current "kubectl delete ... 2>/dev/null ||
log_warning ..." pattern with "kubectl delete ... --ignore-not-found" for each
delete call so missing resources are handled by kubectl while real errors still
surface (do not swallow stderr). Concretely, update the delete invocations for
restore-to-copy-test and restore-in-place-test (kubectl delete restorejob
restore-to-copy-test / restore-in-place-test), backupjob and backup (kubectl
delete backupjob test-backup, kubectl delete backup test-backup), vminstance and
vmdisk (kubectl delete vminstance test, kubectl delete vmdisk ubuntu-source),
backupclass (kubectl delete backupclass velero), both Velero strategies (kubectl
delete velero.strategy.backups.cozystack.io vminstance-strategy and
vmdisk-strategy) and the namespace delete (kubectl delete namespace
"$TARGET_NAMESPACE"), removing the "2>/dev/null || log_warning ..." suffix in
each case and adding "--ignore-not-found" to the kubectl command.

In `@internal/backupcontroller/velerostrategy_controller.go`:
- Around line 807-823: The JSON Patch remove operations on resourceModifierRule
for persistentvolumeclaims (the jsonPatch entries with Operation "remove" and
Paths "/spec/selector" and "/spec/volumeName") can fail if those fields are
absent; change each remove-only patch to a conditional test+remove pair: add a
preceding jsonPatch with Operation "test" that checks the field exists (e.g.,
Path "/spec/selector" or "/spec/volumeName" with an appropriate Value presence
check) and only then perform the "remove" patch, so the patch sequence for the
resourceModifierRule that targets PVCs uses test before remove to avoid
ErrMissing restoration failures.

In `@packages/system/backupstrategy-controller/templates/rbac.yaml`:
- Around line 50-52: The RBAC template currently grants cluster-wide
create/delete for HelmReleases while the controller allows
RestoreJob.spec.options.targetNamespace to differ from backup.Namespace and then
creates/deletes HelmReleases (the "copy" restore path around the logic that
handles targetNamespace at the code handling lines ~156–158); add an
authorization check in the controller before any HelmRelease create/delete:
either reject requests where RestoreJob.spec.options.targetNamespace !=
backup.Namespace or perform a Kubernetes SubjectAccessReview for resource
"helmreleases" in the target namespace for the current ServiceAccount/user and
only proceed if the SAR allows the verb ("create" or "delete"); implement this
check in the function that handles the copy-restore HelmRelease operations (the
code that reads RestoreJob.spec.options.targetNamespace and invokes HelmRelease
create/delete) so cross-namespace writes are explicitly authorized.

---

Nitpick comments:
In `@internal/backupcontroller/velerostrategy_controller_test.go`:
- Around line 1102-1113: The helper containsString reimplements standard
behavior; replace all calls to containsString(s, substr) with
strings.Contains(s, substr), remove the containsString function, and add the
"strings" import where needed (or remove it if already present); ensure any
tests compile by updating imports and cleaning up the removed helper references
(function name: containsString).
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 947d6f08-63ee-4e9b-bd9d-d738fbab67be

📥 Commits

Reviewing files that changed from the base of the PR and between 38624b7 and c4a2dc3.

📒 Files selected for processing (18)
  • api/backups/v1alpha1/restorejob_types.go
  • examples/backups/vmi/00-helpers.sh
  • examples/backups/vmi/01-create-strategies.sh
  • examples/backups/vmi/02-create-backupclass.sh
  • examples/backups/vmi/03-create-vmdisk.sh
  • examples/backups/vmi/04-create-vminstance.sh
  • examples/backups/vmi/05-create-backupjob.sh
  • examples/backups/vmi/06-restore-in-place.sh
  • examples/backups/vmi/07-restore-to-copy.sh
  • examples/backups/vmi/cleanup.sh
  • examples/backups/vmi/run-all.sh
  • examples/backups/vmi/scenario-admin-prepare.md
  • examples/backups/vmi/scenario-user-backup.md
  • examples/backups/vmi/scenario-user-restore.md
  • internal/backupcontroller/velerostrategy_controller.go
  • internal/backupcontroller/velerostrategy_controller_test.go
  • packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml
  • packages/system/backupstrategy-controller/templates/rbac.yaml

Comment on lines +46 to +50
// Options is a driver-specific blob of restore options, typed based on
// targetApplicationRef and the current controller implementation.
// +optional
// +kubebuilder:pruning:PreserveUnknownFields
Options *runtime.RawExtension `json:"options,omitempty"`

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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Inspect RestoreJobSpec deepcopy implementation:"
rg -n -A30 -B2 'func \(in \*RestoreJobSpec\) DeepCopyInto\(out \*RestoreJobSpec\)' api/backups/v1alpha1/zz_generated.deepcopy.go

echo
echo "Check whether Options is handled in generated deepcopy:"
rg -n 'Options|RawExtension' api/backups/v1alpha1/zz_generated.deepcopy.go

Repository: cozystack/cozystack

Length of output: 1387


Regenerate deepcopy code for Options *runtime.RawExtension.

The RestoreJobSpec.DeepCopyInto method in zz_generated.deepcopy.go does not deep-copy the Options field, allowing copied objects to share the same Raw backing bytes. Run code generation to include the missing deep-copy logic for this field.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/backups/v1alpha1/restorejob_types.go` around lines 46 - 50, The generated
deepcopy for RestoreJobSpec misses deep-copying Options (*runtime.RawExtension);
regenerate zz_generated.deepcopy.go (e.g., run your repo's codegen/make generate
or controller-gen deepcopy) so RestoreJobSpec.DeepCopyInto properly allocates a
new runtime.RawExtension and copies the Raw byte slice, or manually update
RestoreJobSpec.DeepCopyInto to set out.Options = new(runtime.RawExtension) and
copy the Options.Raw bytes, ensuring Options is fully deep-copied rather than
shared.


separator

log_success "VMDisk is ready (image download may still be in progress)"

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.

⚠️ Potential issue | 🟡 Minor

Avoid claiming readiness before a readiness condition is checked.

Line 39 says “ready” even though download may still be in progress. Prefer “created” (or add an actual wait/readiness check).

Suggested wording fix
-log_success "VMDisk is ready (image download may still be in progress)"
+log_success "VMDisk created (image download may still be in progress)"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
log_success "VMDisk is ready (image download may still be in progress)"
log_success "VMDisk created (image download may still be in progress)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/03-create-vmdisk.sh` at line 39, The success message
currently declares readiness prematurely in the call to log_success ("VMDisk is
ready (image download may still be in progress)"); update that call to use a
truthful status like "VMDisk created" or replace it with a real readiness check
and only call log_success once the VMDisk is confirmed ready. Locate the
log_success invocation and change the message text to "VMDisk created" (or
implement and call a readiness-check function before logging readiness).

Comment on lines +27 to +31
options:
failIfTargetExists: true # if true, restore will fail when the target resource already exists
keepOriginalPVC: true # renames original VMI PVC before restore to `<name>-orig-<hash>`, only for in-place restore
keepOriginalIpAndMac: true # restores original IP and MAC address of VMI via OVN annotations
EOF

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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
bash -n examples/backups/vmi/06-restore-in-place.sh

Repository: cozystack/cozystack

Length of output: 45


🏁 Script executed:

#!/bin/bash
# First, read the file around line 29 to see the actual heredoc
head -n 35 examples/backups/vmi/06-restore-in-place.sh | tail -n +20

Repository: cozystack/cozystack

Length of output: 569


🏁 Script executed:

#!/bin/bash
# Look for the entire heredoc block containing line 29
sed -n '15,40p' examples/backups/vmi/06-restore-in-place.sh

Repository: cozystack/cozystack

Length of output: 848


🏁 Script executed:

#!/bin/bash
# Test actual execution - run the script and capture stderr
timeout 5 bash examples/backups/vmi/06-restore-in-place.sh 2>&1 | head -50 || true

Repository: cozystack/cozystack

Length of output: 108


🏁 Script executed:

#!/bin/bash
# Test if backticks in unquoted heredoc cause command substitution failure
bash -c '
cat <<EOF
# Comment with `<name>-orig-<hash>` backticks
EOF
' 2>&1

Repository: cozystack/cozystack

Length of output: 200


🏁 Script executed:

#!/bin/bash
# Compare: test with quoted heredoc (safe version)
bash -c '
cat <<'"'"'EOF'"'"'
# Comment with `<name>-orig-<hash>` backticks
EOF
' 2>&1

Repository: cozystack/cozystack

Length of output: 108


🏁 Script executed:

#!/bin/bash
# Find the heredoc opening near line 29
grep -n "<<EOF\|<<'EOF'" examples/backups/vmi/06-restore-in-place.sh | head -5

Repository: cozystack/cozystack

Length of output: 90


🏁 Script executed:

#!/bin/bash
# Show lines around where the heredoc starts (likely before line 29)
sed -n '10,32p' examples/backups/vmi/06-restore-in-place.sh

Repository: cozystack/cozystack

Length of output: 923


Backticks in unquoted heredoc trigger command substitution failure

Line 29 contains backticks inside an unquoted <<EOF heredoc, causing bash to attempt command substitution on <name>-orig-<hash> at runtime, which fails with a syntax error.

💡 Safe fix
-    keepOriginalPVC: true # renames original VMI PVC before restore to `<name>-orig-<hash>`, only for in-place restore
+    keepOriginalPVC: true # renames original VMI PVC before restore to '<name>-orig-<hash>', only for in-place restore
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
options:
failIfTargetExists: true # if true, restore will fail when the target resource already exists
keepOriginalPVC: true # renames original VMI PVC before restore to `<name>-orig-<hash>`, only for in-place restore
keepOriginalIpAndMac: true # restores original IP and MAC address of VMI via OVN annotations
EOF
options:
failIfTargetExists: true # if true, restore will fail when the target resource already exists
keepOriginalPVC: true # renames original VMI PVC before restore to '<name>-orig-<hash>', only for in-place restore
keepOriginalIpAndMac: true # restores original IP and MAC address of VMI via OVN annotations
EOF
🧰 Tools
🪛 Shellcheck (0.11.0)

[info] 29-29: The mentioned syntax error was in this simple command.

(SC1009)


[error] 29-29: Couldn't parse this redirection. Fix to allow more checks.

(SC1073)


[error] 29-29: Fix any mentioned problems and try again.

(SC1072)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/06-restore-in-place.sh` around lines 27 - 31, The
heredoc uses an unquoted delimiter (<<EOF) so backticks in the comment for
keepOriginalPVC (`<name>-orig-<hash>`) are treated as command substitution and
break the script; fix by preventing expansion — either change the heredoc to a
quoted delimiter (e.g., <<'EOF') or replace/escape the backticks in the
keepOriginalPVC line (e.g., use '<name>-orig-<hash>' or
\`\<name\>-orig-\<hash\>\`) so the literal string is preserved.

Comment on lines +40 to +45
options: # runtime.RawExtension, typed based on targetApplicationRef and current controller implementation (for additional restore options)
targetNamespace: ${TARGET_NAMESPACE} # when set to a different namespace, triggers cross-namespace restore via Velero namespaceMapping
failIfTargetExists: true # if true, restore will fail when the target resource already exists
keepOriginalPVC: false # renames original VMI PVC before restore to `<name>-orig-<hash>`, only for in-place restore
keepOriginalIpAndMac: false # restores original IP and MAC address of VMI via OVN annotations
EOF

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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
bash -n examples/backups/vmi/07-restore-to-copy.sh

Repository: cozystack/cozystack

Length of output: 45


🏁 Script executed:

cd examples/backups/vmi && head -n 50 07-restore-to-copy.sh | tail -n +30

Repository: cozystack/cozystack

Length of output: 907


🏁 Script executed:

cd examples/backups/vmi && head -n 45 07-restore-to-copy.sh | tail -n +20

Repository: cozystack/cozystack

Length of output: 1211


Fix backticks in unquoted heredoc to prevent command substitution

Line 43 uses backticks inside an unquoted heredoc delimiter (<<EOF). Bash will attempt command substitution on `<name>-orig-<hash>` when the script runs, causing it to fail because that string is not a valid command.

Replace backticks with single quotes:

Fix
-    keepOriginalPVC: false # renames original VMI PVC before restore to `<name>-orig-<hash>`, only for in-place restore
+    keepOriginalPVC: false # renames original VMI PVC before restore to '<name>-orig-<hash>', only for in-place restore
🧰 Tools
🪛 Shellcheck (0.11.0)

[info] 43-43: The mentioned syntax error was in this simple command.

(SC1009)


[error] 43-43: Couldn't parse this redirection. Fix to allow more checks.

(SC1073)


[error] 43-43: Fix any mentioned problems and try again.

(SC1072)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/07-restore-to-copy.sh` around lines 40 - 45, The
unquoted heredoc (<<EOF) contains backticks around `<name>-orig-<hash>` which
causes bash to perform command substitution; fix by either quoting the heredoc
delimiter (use <<'EOF') or replacing the backticks with single quotes around the
literal string `<name>-orig-<hash>` so the text is preserved literally; update
the heredoc that contains the options block (the <<EOF section) accordingly to
prevent shell expansion.

Comment on lines +33 to +64
kubectl delete restorejob restore-to-copy-test -n "$NAMESPACE" 2>/dev/null || log_warning "RestoreJob restore-to-copy-test not found"
kubectl delete restorejob restore-in-place-test -n "$NAMESPACE" 2>/dev/null || log_warning "RestoreJob restore-in-place-test not found"

separator

log_step "Deleting BackupJob and Backup..."
kubectl delete backupjob test-backup -n "$NAMESPACE" 2>/dev/null || log_warning "BackupJob test-backup not found"
kubectl delete backup test-backup -n "$NAMESPACE" 2>/dev/null || log_warning "Backup test-backup not found"

separator

log_step "Deleting VMInstance..."
kubectl delete vminstance test -n "$NAMESPACE" 2>/dev/null || log_warning "VMInstance test not found"

log_step "Deleting VMDisk..."
kubectl delete vmdisk ubuntu-source -n "$NAMESPACE" 2>/dev/null || log_warning "VMDisk ubuntu-source not found"

separator

log_step "Deleting BackupClass..."
kubectl delete backupclass velero 2>/dev/null || log_warning "BackupClass velero not found"

separator

log_step "Deleting Velero strategies..."
kubectl delete velero.strategy.backups.cozystack.io vminstance-strategy 2>/dev/null || log_warning "Strategy vminstance-strategy not found"
kubectl delete velero.strategy.backups.cozystack.io vmdisk-strategy 2>/dev/null || log_warning "Strategy vmdisk-strategy not found"

separator

log_step "Deleting target namespace..."
kubectl delete namespace "$TARGET_NAMESPACE" 2>/dev/null || log_warning "Namespace $TARGET_NAMESPACE not found"

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n 'kubectl delete .*2>/dev/null \|\| log_warning' examples/backups/vmi/cleanup.sh

Repository: cozystack/cozystack

Length of output: 1274


🌐 Web query:

kubectl delete --ignore-not-found flag documentation

💡 Result:

In kubectl delete, the --ignore-not-found flag is documented as:

  • --ignore-not-found: “Treat ‘resource not found’ as a successful delete. Defaults to ‘true’ when --all is specified.” [1]

Official docs: kubectl delete command reference (Options section). [1]

Sources
[1] Kubernetes docs — “kubectl delete” reference page (kubernetes.io)

Citations:


🏁 Script executed:

cd examples/backups/vmi && head -n 70 cleanup.sh | tail -n +1

Repository: cozystack/cozystack

Length of output: 2606


🏁 Script executed:

# Check the log_warning function and overall context
rg -B5 "log_warning" examples/backups/vmi/cleanup.sh | head -40

Repository: cozystack/cozystack

Length of output: 1593


Use --ignore-not-found to distinguish "resource not found" from real errors.

Current pattern (2>/dev/null || log_warning) masks all stderr and cannot distinguish between expected missing resources and actual failures (authentication errors, connectivity issues, API server errors). This allows the script to report successful cleanup even when deletions fail due to real problems.

Replace the pattern with kubectl delete ... --ignore-not-found, which gracefully handles missing resources while preserving real error signals. The suggested fix is correct; given the script uses set -euo pipefail, failing on real deletion errors is appropriate for a cleanup utility.

Apply to all 10 delete calls in lines 33–64.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/cleanup.sh` around lines 33 - 64, Replace the current
"kubectl delete ... 2>/dev/null || log_warning ..." pattern with "kubectl delete
... --ignore-not-found" for each delete call so missing resources are handled by
kubectl while real errors still surface (do not swallow stderr). Concretely,
update the delete invocations for restore-to-copy-test and restore-in-place-test
(kubectl delete restorejob restore-to-copy-test / restore-in-place-test),
backupjob and backup (kubectl delete backupjob test-backup, kubectl delete
backup test-backup), vminstance and vmdisk (kubectl delete vminstance test,
kubectl delete vmdisk ubuntu-source), backupclass (kubectl delete backupclass
velero), both Velero strategies (kubectl delete
velero.strategy.backups.cozystack.io vminstance-strategy and vmdisk-strategy)
and the namespace delete (kubectl delete namespace "$TARGET_NAMESPACE"),
removing the "2>/dev/null || log_warning ..." suffix in each case and adding
"--ignore-not-found" to the kubectl command.

Comment thread internal/backupcontroller/velerostrategy_controller.go
Comment on lines 50 to +52
- apiGroups: ["helm.toolkit.fluxcd.io"]
resources: ["helmreleases"]
verbs: ["get", "update"]
verbs: ["get", "create", "update", "delete"]

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "1) Locate where target namespace is derived from RestoreJob options"
rg -n -C3 'target.?namespace|target\.Namespace|parseVmiRestoreOptions|spec\.options|RestoreJob' internal/backupcontroller/velerostrategy_controller.go

echo
echo "2) Locate authorization / allowlist checks before cross-namespace writes"
rg -n -C3 'SubjectAccessReview|SelfSubjectAccessReview|authorize|allowed.*namespace|forbidden|same namespace|tenant' internal/backupcontroller/velerostrategy_controller.go

Repository: cozystack/cozystack

Length of output: 15352


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check RBAC for RestoreJob and Backup objects ==="
rg -n 'apiGroups|resources.*RestoreJob|resources.*Backup|verbs' packages/system/backupstrategy-controller/templates/rbac.yaml | head -50

echo
echo "=== Check if Backup has namespace constraints ==="
rg -n 'BackupNamespace|Namespace.*Backup|backup\.Namespace' internal/backupcontroller/velerostrategy_controller.go | head -30

echo
echo "=== Check RestoreJob ownership/namespace relationship ==="
rg -n -A2 'type RestoreJob|RestoreJob.*struct' internal/backupcontroller/ --type yaml --type go | head -40

echo
echo "=== Check if BackupStrategy enforces namespace scoping ==="
rg -n 'BackupStrategy|strategy.*Namespace|ownerRef' internal/backupcontroller/velerostrategy_controller.go | head -30

Repository: cozystack/cozystack

Length of output: 3051


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check RestoreJob reconciler setup and namespace constraints ==="
rg -n -B5 -A10 'RestoreJobReconciler.*Reconcile|func.*RestoreJob.*Reconcile' internal/backupcontroller/velerostrategy_controller.go | head -50

echo
echo "=== Look for any namespace authorization in controller initialization ==="
rg -n 'SetupWithManager|namespaces|namespace.*watch' internal/backupcontroller/velerostrategy_controller.go | head -20

Repository: cozystack/cozystack

Length of output: 3056


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Full RBAC file content ==="
cat packages/system/backupstrategy-controller/templates/rbac.yaml

echo
echo "=== Check for ClusterRoleBinding subject scope ==="
rg -n 'ClusterRoleBinding|RoleBinding|subjects' packages/system/backupstrategy-controller/templates/rbac.yaml

Repository: cozystack/cozystack

Length of output: 3705


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for RestoreJob CRD definition to verify namespace scope ==="
fd -type f -name '*.yaml' -o -name '*.go' | xargs rg -l 'RestoreJob.*scope|kind: RestoreJob' | head -5

echo
echo "=== Check if there's cross-namespace validation between RestoreJob and Backup ==="
rg -n 'restoreJob.Namespace.*backup.Namespace|cross.*namespace|AllowCrossNamespace' internal/backupcontroller/

echo
echo "=== Check RestoreJob controller setup for namespace filtering ==="
rg -n 'SetupWithManager|For\(&backupsv1alpha1.RestoreJob' internal/backupcontroller/restorejob_controller.go -A5 | head -30

Repository: cozystack/cozystack

Length of output: 492


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check RestoreJob CRD scope ==="
head -30 packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml

echo
echo "=== Search for RoleBinding in RBAC ==="
rg -n 'RoleBinding|Subject|ServiceAccount' packages/system/backupstrategy-controller/templates/

echo
echo "=== Check if RestoreJob must be in same namespace as Backup ==="
rg -n 'restoreJob\.Namespace|same.*namespace|namespace.*mismatch' internal/backupcontroller/restorejob_controller.go -B2 -A2

Repository: cozystack/cozystack

Length of output: 1699


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check Backup CRD scope and namespace constraints ==="
head -30 packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml

echo
echo "=== Look for any validation that RestoreJob namespace must equal Backup namespace ==="
rg -n -C5 'restoreJob.*backup|namespace.*validation|forbidden.*namespace' internal/backupcontroller/restorejob_controller.go

Repository: cozystack/cozystack

Length of output: 4880


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check RestoreJob API spec to see how it references Backup ==="
rg -n -A20 'type RestoreJob struct|type RestoreJobSpec struct|BackupRef' packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml | head -60

echo
echo "=== Read rbac-bind.yaml for RoleBinding details ==="
cat packages/system/backupstrategy-controller/templates/rbac-bind.yaml

echo
echo "=== Check if there is validation that Backup must be in same namespace ==="
rg -n 'backupRef|GetBackup|restoreJob.*Namespace.*backup' internal/backupcontroller/restorejob_controller.go -B2 -A2

Repository: cozystack/cozystack

Length of output: 2149


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Find where Backup is fetched using BackupRef ==="
rg -n -B5 -A10 'backupRef|Get.*Backup|client.ObjectKey.*Backup' internal/backupcontroller/restorejob_controller.go | head -80

echo
echo "=== Check the exact line where Backup is retrieved ==="
sed -n '80,130p' internal/backupcontroller/restorejob_controller.go

Repository: cozystack/cozystack

Length of output: 115


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Look at RestoreJob reconciler around line 80-130 ==="
sed -n '80,130p' internal/backupcontroller/restorejob_controller.go

echo
echo "=== Search for where Backup object is retrieved ==="
rg -n 'r\.Get.*backup|client\.Get.*Backup' internal/backupcontroller/restorejob_controller.go -B2 -A2

echo
echo "=== Look for Step 1 that fetches Backup ==="
rg -n 'Fetch.*Backup|Step 1|backup :=' internal/backupcontroller/restorejob_controller.go -B3 -A3

Repository: cozystack/cozystack

Length of output: 2864


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Verify if there's any namespace policy enforcement or comments about it ==="
rg -n -B3 -A3 'namespace|policy|authorization|tenant' internal/backupcontroller/velerostrategy_controller.go | grep -i 'namespace\|policy\|author\|tenant' | head -20

echo
echo "=== Check if targetNamespace validation exists ==="
rg -n -B5 -A5 'TargetNamespace' internal/backupcontroller/velerostrategy_controller.go | head -40

echo
echo "=== Look for any TODO or FIXME comments about cross-namespace ==="
rg -n 'TODO|FIXME|XXX|HACK' internal/backupcontroller/velerostrategy_controller.go

Repository: cozystack/cozystack

Length of output: 3156


Cross-namespace HelmRelease write access lacks explicit authorization validation.

Line 52 grants cluster-wide create/delete for HelmReleases. The code allows RestoreJob.spec.options.targetNamespace to be set to any namespace, and when this differs from backup.Namespace, the controller creates/deletes HelmReleases in the arbitrary target namespace (the "copy" restore path, confirmed at lines 156–158).

No SubjectAccessReview or explicit namespace authorization check is performed before these cross-namespace writes. Add validation to either reject cross-namespace restores by policy or explicitly authorize them via SubjectAccessReview before HelmRelease operations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/system/backupstrategy-controller/templates/rbac.yaml` around lines
50 - 52, The RBAC template currently grants cluster-wide create/delete for
HelmReleases while the controller allows RestoreJob.spec.options.targetNamespace
to differ from backup.Namespace and then creates/deletes HelmReleases (the
"copy" restore path around the logic that handles targetNamespace at the code
handling lines ~156–158); add an authorization check in the controller before
any HelmRelease create/delete: either reject requests where
RestoreJob.spec.options.targetNamespace != backup.Namespace or perform a
Kubernetes SubjectAccessReview for resource "helmreleases" in the target
namespace for the current ServiceAccount/user and only proceed if the SAR allows
the verb ("create" or "delete"); implement this check in the function that
handles the copy-restore HelmRelease operations (the code that reads
RestoreJob.spec.options.targetNamespace and invokes HelmRelease create/delete)
so cross-namespace writes are explicitly authorized.

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

♻️ Duplicate comments (2)
examples/backups/vmi/cleanup.sh (1)

33-64: ⚠️ Potential issue | 🟠 Major

Don’t suppress real deletion failures.

The 2>/dev/null || log_warning ... pattern hides API/auth/connectivity errors and makes cleanup look successful when it isn’t. Prefer --ignore-not-found so only true “not found” is tolerated.

Suggested fix
-kubectl delete restorejob restore-to-copy-test -n "$NAMESPACE" 2>/dev/null || log_warning "RestoreJob restore-to-copy-test not found"
-kubectl delete restorejob restore-in-place-test -n "$NAMESPACE" 2>/dev/null || log_warning "RestoreJob restore-in-place-test not found"
+kubectl delete restorejob restore-to-copy-test -n "$NAMESPACE" --ignore-not-found
+kubectl delete restorejob restore-in-place-test -n "$NAMESPACE" --ignore-not-found

-kubectl delete backupjob test-backup -n "$NAMESPACE" 2>/dev/null || log_warning "BackupJob test-backup not found"
-kubectl delete backup test-backup -n "$NAMESPACE" 2>/dev/null || log_warning "Backup test-backup not found"
+kubectl delete backupjob test-backup -n "$NAMESPACE" --ignore-not-found
+kubectl delete backup test-backup -n "$NAMESPACE" --ignore-not-found

-kubectl delete vminstance test -n "$NAMESPACE" 2>/dev/null || log_warning "VMInstance test not found"
+kubectl delete vminstance test -n "$NAMESPACE" --ignore-not-found

-kubectl delete vmdisk ubuntu-source -n "$NAMESPACE" 2>/dev/null || log_warning "VMDisk ubuntu-source not found"
+kubectl delete vmdisk ubuntu-source -n "$NAMESPACE" --ignore-not-found

-kubectl delete backupclass velero 2>/dev/null || log_warning "BackupClass velero not found"
+kubectl delete backupclass velero --ignore-not-found

-kubectl delete velero.strategy.backups.cozystack.io vminstance-strategy 2>/dev/null || log_warning "Strategy vminstance-strategy not found"
-kubectl delete velero.strategy.backups.cozystack.io vmdisk-strategy 2>/dev/null || log_warning "Strategy vmdisk-strategy not found"
+kubectl delete velero.strategy.backups.cozystack.io vminstance-strategy --ignore-not-found
+kubectl delete velero.strategy.backups.cozystack.io vmdisk-strategy --ignore-not-found

-kubectl delete namespace "$TARGET_NAMESPACE" 2>/dev/null || log_warning "Namespace $TARGET_NAMESPACE not found"
+kubectl delete namespace "$TARGET_NAMESPACE" --ignore-not-found
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/cleanup.sh` around lines 33 - 64, The cleanup script
currently suppresses all deletion errors by redirecting stderr and falling back
to log_warning (e.g., the kubectl delete lines for restorejob, backupjob,
backup, vminstance, vmdisk, backupclass, velero.strategy and namespace); change
each kubectl delete invocation to use the --ignore-not-found flag instead of
`2>/dev/null || log_warning ...` so kubectl only tolerates NotFound, and remove
the stderr redirection; keep explicit log_step calls and add a conditional
log_warning only when kubectl exits with a non-zero status that is not a
NotFound error to ensure API/auth/connectivity failures are visible (apply this
change for the commands targeting restore-to-copy-test/restore-in-place-test,
test-backup, ubuntu-source, vminstance-strategy/vmdisk-strategy, velero, and the
namespace deletion).
examples/backups/vmi/03-create-vmdisk.sh (1)

39-39: ⚠️ Potential issue | 🟡 Minor

Avoid claiming readiness before readiness is checked.

Line 39 still reports “ready” even though image download may still be ongoing. Please keep this status truthful (or add a real readiness wait first).

Suggested fix
-log_success "VMDisk is ready (image download may still be in progress)"
+log_success "VMDisk created (image download may still be in progress)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/03-create-vmdisk.sh` at line 39, The script currently
calls log_success "VMDisk is ready (image download may still be in progress)"
which falsely asserts readiness; either change that message to a truthful status
(e.g., "VMDisk created; image download may still be in progress") or implement a
real readiness check before calling log_success. Locate the log_success
invocation in 03-create-vmdisk.sh, and either update the text to avoid claiming
readiness or add a wait function (e.g., wait_for_image_download /
wait_for_vmdisk_ready) that polls the image/disk state and only calls
log_success once the download/ready condition is confirmed.
🧹 Nitpick comments (2)
internal/backupcontroller/velerostrategy_controller_test.go (1)

1202-1213: Consider using strings.Contains from standard library.

The custom containsString function replicates strings.Contains from the standard library, which is already imported elsewhere in the codebase.

Proposed simplification
-// containsString reports whether substr appears in s.
-func containsString(s, substr string) bool {
-	if len(substr) == 0 || len(s) < len(substr) {
-		return false
-	}
-	for i := 0; i <= len(s)-len(substr); i++ {
-		if s[i:i+len(substr)] == substr {
-			return true
-		}
-	}
-	return false
-}
+import "strings"
+
+// Then use strings.Contains(s, substr) directly in tests
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/backupcontroller/velerostrategy_controller_test.go` around lines
1202 - 1213, The containsString helper duplicates standard library behavior;
remove the custom containsString function and replace its usages with
strings.Contains from the standard library (add "strings" to imports where
needed), ensuring all callers that referenced containsString(s, substr) now call
strings.Contains(s, substr) instead.
internal/backupcontroller/velerostrategy_controller.go (1)

1016-1082: postRestoreRename only handles VMInstance, not VMDisk.

The function hardcodes vmNamePrefix for HelmRelease naming, so it won't work correctly if target.AppKind is VMDisk. If VMDisk renames are needed in the future, this would silently use the wrong HelmRelease name.

Consider using helmReleaseNameForApp for consistency:

Proposed improvement
 func (r *RestoreJobReconciler) postRestoreRename(ctx context.Context, restoreJob *backupsv1alpha1.RestoreJob, backup *backupsv1alpha1.Backup, target restoreTarget) error {
 	logger := getLogger(ctx)
 	sourceAppName := backup.Spec.ApplicationRef.Name
-	sourceHRName := vmNamePrefix + sourceAppName
-	targetHRName := vmNamePrefix + target.AppName
+	sourceHRName := helmReleaseNameForApp(backup.Spec.ApplicationRef.Kind, sourceAppName)
+	targetHRName := helmReleaseNameForApp(target.AppKind, target.AppName)
+
+	// Skip rename for unsupported kinds
+	if sourceHRName == "" || targetHRName == "" {
+		logger.Debug("skipping post-restore rename for unsupported kind", "kind", target.AppKind)
+		return nil
+	}
 
 	logger.Debug("post-restore rename", "from", sourceHRName, "to", targetHRName, "namespace", target.Namespace)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/backupcontroller/velerostrategy_controller.go` around lines 1016 -
1082, postRestoreRename currently constructs HelmRelease names with the
hardcoded vmNamePrefix which only works for VMInstance; replace that logic to
call helmReleaseNameForApp(target.AppKind, sourceAppName) and
helmReleaseNameForApp(target.AppKind, target.AppName) when computing
sourceHRName and targetHRName so VMDisk (or other kinds) use the correct
HelmRelease naming; update any references that used vmNamePrefix (in the debug
log and creation/deletion flows) to use these computed names and keep the rest
of the function unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/backups/vmi/04-create-vminstance.sh`:
- Line 43: The message "VMInstance is ready" is misleading because the script
only runs kubectl get; change the success message logged by log_success (the
call log_success "VMInstance is ready") to something like "VMInstance created"
OR add an actual readiness check before logging (e.g., use kubectl wait
--for=condition=Ready or poll kubectl get vminstance until status indicates
ready) and only call log_success once that readiness check passes; update the
log_success invocation accordingly.

In `@examples/backups/vmi/05-create-backupjob.sh`:
- Around line 38-39: The logged kubectl output format is inconsistent:
log_command shows "-o yaml" while the actual command uses "-o wide"; update one
so both match (e.g., change the executed command invoked by kubectl get
backupjob test-backup -n "$NAMESPACE" -o wide to use "-o yaml", or change the
log_command to advertise "-o wide") so that log_command and the kubectl
invocation (refer to the occurrences of log_command and the kubectl get
backupjob test-backup -n "$NAMESPACE" -o wide call) produce the same output
format.

---

Duplicate comments:
In `@examples/backups/vmi/03-create-vmdisk.sh`:
- Line 39: The script currently calls log_success "VMDisk is ready (image
download may still be in progress)" which falsely asserts readiness; either
change that message to a truthful status (e.g., "VMDisk created; image download
may still be in progress") or implement a real readiness check before calling
log_success. Locate the log_success invocation in 03-create-vmdisk.sh, and
either update the text to avoid claiming readiness or add a wait function (e.g.,
wait_for_image_download / wait_for_vmdisk_ready) that polls the image/disk state
and only calls log_success once the download/ready condition is confirmed.

In `@examples/backups/vmi/cleanup.sh`:
- Around line 33-64: The cleanup script currently suppresses all deletion errors
by redirecting stderr and falling back to log_warning (e.g., the kubectl delete
lines for restorejob, backupjob, backup, vminstance, vmdisk, backupclass,
velero.strategy and namespace); change each kubectl delete invocation to use the
--ignore-not-found flag instead of `2>/dev/null || log_warning ...` so kubectl
only tolerates NotFound, and remove the stderr redirection; keep explicit
log_step calls and add a conditional log_warning only when kubectl exits with a
non-zero status that is not a NotFound error to ensure API/auth/connectivity
failures are visible (apply this change for the commands targeting
restore-to-copy-test/restore-in-place-test, test-backup, ubuntu-source,
vminstance-strategy/vmdisk-strategy, velero, and the namespace deletion).

---

Nitpick comments:
In `@internal/backupcontroller/velerostrategy_controller_test.go`:
- Around line 1202-1213: The containsString helper duplicates standard library
behavior; remove the custom containsString function and replace its usages with
strings.Contains from the standard library (add "strings" to imports where
needed), ensuring all callers that referenced containsString(s, substr) now call
strings.Contains(s, substr) instead.

In `@internal/backupcontroller/velerostrategy_controller.go`:
- Around line 1016-1082: postRestoreRename currently constructs HelmRelease
names with the hardcoded vmNamePrefix which only works for VMInstance; replace
that logic to call helmReleaseNameForApp(target.AppKind, sourceAppName) and
helmReleaseNameForApp(target.AppKind, target.AppName) when computing
sourceHRName and targetHRName so VMDisk (or other kinds) use the correct
HelmRelease naming; update any references that used vmNamePrefix (in the debug
log and creation/deletion flows) to use these computed names and keep the rest
of the function unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 57db9670-019c-4857-a9a0-2423455e15b7

📥 Commits

Reviewing files that changed from the base of the PR and between c4a2dc3 and 9362ef9.

📒 Files selected for processing (19)
  • api/backups/v1alpha1/restorejob_types.go
  • examples/backups/vmi/00-helpers.sh
  • examples/backups/vmi/01-create-strategies.sh
  • examples/backups/vmi/02-create-backupclass.sh
  • examples/backups/vmi/03-create-vmdisk.sh
  • examples/backups/vmi/04-create-vminstance.sh
  • examples/backups/vmi/05-create-backupjob.sh
  • examples/backups/vmi/06-restore-in-place.sh
  • examples/backups/vmi/07-restore-to-copy.sh
  • examples/backups/vmi/cleanup.sh
  • examples/backups/vmi/run-all.sh
  • examples/backups/vmi/scenario-admin-prepare.md
  • examples/backups/vmi/scenario-user-backup.md
  • examples/backups/vmi/scenario-user-restore.md
  • internal/backupcontroller/restorejob_controller.go
  • internal/backupcontroller/velerostrategy_controller.go
  • internal/backupcontroller/velerostrategy_controller_test.go
  • packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml
  • packages/system/backupstrategy-controller/templates/rbac.yaml
✅ Files skipped from review due to trivial changes (2)
  • examples/backups/vmi/scenario-user-restore.md
  • examples/backups/vmi/scenario-admin-prepare.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • api/backups/v1alpha1/restorejob_types.go
  • packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml
  • packages/system/backupstrategy-controller/templates/rbac.yaml
  • examples/backups/vmi/00-helpers.sh


separator

log_success "VMInstance is ready"

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.

⚠️ Potential issue | 🟡 Minor

Don’t mark VMInstance as ready without a readiness check.

Line 43 currently implies readiness, but the script only confirms existence via kubectl get. Consider changing wording to “created” unless a real wait/check is added.

Suggested fix
-log_success "VMInstance is ready"
+log_success "VMInstance created"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
log_success "VMInstance is ready"
log_success "VMInstance created"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/04-create-vminstance.sh` at line 43, The message
"VMInstance is ready" is misleading because the script only runs kubectl get;
change the success message logged by log_success (the call log_success
"VMInstance is ready") to something like "VMInstance created" OR add an actual
readiness check before logging (e.g., use kubectl wait --for=condition=Ready or
poll kubectl get vminstance until status indicates ready) and only call
log_success once that readiness check passes; update the log_success invocation
accordingly.

Comment on lines +38 to +39
log_command "kubectl get backupjob test-backup -n $NAMESPACE -o yaml"
kubectl get backupjob test-backup -n "$NAMESPACE" -o wide

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.

⚠️ Potential issue | 🟡 Minor

Logged command and executed command are inconsistent.

Line 38 advertises -o yaml, but Line 39 runs -o wide. Please align them to avoid confusing output/debugging.

Suggested fix
 log_command "kubectl get backupjob test-backup -n $NAMESPACE -o yaml"
-kubectl get backupjob test-backup -n "$NAMESPACE" -o wide
+kubectl get backupjob test-backup -n "$NAMESPACE" -o yaml
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
log_command "kubectl get backupjob test-backup -n $NAMESPACE -o yaml"
kubectl get backupjob test-backup -n "$NAMESPACE" -o wide
log_command "kubectl get backupjob test-backup -n $NAMESPACE -o yaml"
kubectl get backupjob test-backup -n "$NAMESPACE" -o yaml
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/05-create-backupjob.sh` around lines 38 - 39, The logged
kubectl output format is inconsistent: log_command shows "-o yaml" while the
actual command uses "-o wide"; update one so both match (e.g., change the
executed command invoked by kubectl get backupjob test-backup -n "$NAMESPACE" -o
wide to use "-o yaml", or change the log_command to advertise "-o wide") so that
log_command and the kubectl invocation (refer to the occurrences of log_command
and the kubectl get backupjob test-backup -n "$NAMESPACE" -o wide call) produce
the same output format.

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

♻️ Duplicate comments (6)
examples/backups/vmi/05-create-backupjob.sh (1)

38-39: ⚠️ Potential issue | 🟡 Minor

Logged command and executed command are inconsistent.

Line 38 logs -o yaml but Line 39 executes -o wide. This makes debugging confusing.

Suggested fix
-log_command "kubectl get backupjob test-backup -n $NAMESPACE -o yaml"
-kubectl get backupjob test-backup -n "$NAMESPACE" -o wide
+log_command "kubectl get backupjob test-backup -n $NAMESPACE -o wide"
+kubectl get backupjob test-backup -n "$NAMESPACE" -o wide
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/05-create-backupjob.sh` around lines 38 - 39, The logged
command and the executed command are inconsistent: log_command logs "kubectl get
backupjob test-backup -n $NAMESPACE -o yaml" while the actual execution uses "-o
wide"; update one so both match (choose either YAML or wide) by changing the
argument in either the log_command call or the kubectl invocation so that
log_command and the kubectl command use the same "-o" flag (e.g., both "-o yaml"
or both "-o wide") to avoid confusion when debugging; refer to the existing
log_command invocation and the kubectl get backupjob test-backup -n "$NAMESPACE"
-o wide line to make the change.
examples/backups/vmi/04-create-vminstance.sh (1)

43-43: ⚠️ Potential issue | 🟡 Minor

Don't mark VMInstance as ready without a readiness check.

Line 43 claims readiness, but the script only verified existence via kubectl get. Consider changing wording to "verified" or adding a real readiness wait.

Suggested fix
-log_success "VMInstance is ready"
+log_success "VMInstance created and verified"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/04-create-vminstance.sh` at line 43, The script
currently calls log_success "VMInstance is ready" after only running kubectl
get, which only verifies existence; change this to either (A) update the message
to "VMInstance verified" or (B) implement a real readiness check before logging
success — e.g., use kubectl wait --for=condition=Ready (or poll the VMInstance
status field) with a sensible timeout and error handling, then call log_success
"VMInstance is ready" only after the wait/poll for readiness succeeds; locate
the call to log_success "VMInstance is ready" and the preceding kubectl get
invocation to apply the fix.
examples/backups/vmi/03-create-vmdisk.sh (1)

39-39: ⚠️ Potential issue | 🟡 Minor

Misleading "ready" status without actual readiness check.

The message claims the VMDisk is "ready" but only existence was verified. The parenthetical disclaimer suggests this isn't actually ready. Consider changing to "VMDisk verified" or adding a real readiness wait.

Suggested fix
-log_success "VMDisk is ready (image download may still be in progress)"
+log_success "VMDisk created (image download may still be in progress)"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/03-create-vmdisk.sh` at line 39, The log message
currently emitted by log_success ("VMDisk is ready (image download may still be
in progress)") is misleading because the script only verifies existence; change
the message to accurately reflect verification (e.g., "VMDisk verified (image
download may still be in progress)") or implement a real readiness check before
calling log_success — for a readiness check add a loop that inspects the
disk/image state (the place where the script currently verifies existence before
the log_success call) and only call log_success once the image download/state
indicates completion; update the invocation of log_success accordingly.
examples/backups/vmi/06-restore-in-place.sh (1)

14-31: ⚠️ Potential issue | 🔴 Critical

Backticks in unquoted heredoc break shell parsing.

Line 29 uses backticks inside an unquoted <<EOF block, so bash treats <name>-orig-<hash> as command substitution.

Proposed fix
-    keepOriginalPVC: true # renames original VMI PVC before restore to `<name>-orig-<hash>`, only for in-place restore
+    keepOriginalPVC: true # renames original VMI PVC before restore to '<name>-orig-<hash>', only for in-place restore
#!/bin/bash
set -euo pipefail

# Verify current parse status
bash -n examples/backups/vmi/06-restore-in-place.sh

# Inspect the affected heredoc lines
sed -n '27,31p' examples/backups/vmi/06-restore-in-place.sh

Expected result: bash -n succeeds after replacing backticks.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/06-restore-in-place.sh` around lines 14 - 31, The
heredoc used to render the RestoreJob contains backticks in the comment for
keepOriginalPVC ("<name>-orig-<hash>") which causes command substitution when
using an unquoted <<EOF; change the heredoc to a single-quoted delimiter
(<<'EOF') so the contents are treated literally (or alternatively escape the
backticks in the comment), e.g., update the kubectl apply -f - <<EOF block to
kubectl apply -f - <<'EOF' so the RestoreJob YAML (including the keepOriginalPVC
comment) parses correctly.
examples/backups/vmi/cleanup.sh (1)

33-64: ⚠️ Potential issue | 🟠 Major

Deletion pattern masks real kubectl errors.

The 2>/dev/null || log_warning "…not found" pattern suppresses all stderr and can misreport genuine failures as “not found”.

Proposed fix pattern (apply to all delete calls in this block)
-kubectl delete restorejob restore-to-copy-test -n "$NAMESPACE" 2>/dev/null || log_warning "RestoreJob restore-to-copy-test not found"
+kubectl delete restorejob restore-to-copy-test -n "$NAMESPACE" --ignore-not-found=true
#!/bin/bash
set -euo pipefail

# Find remaining masked delete calls in cleanup script
rg -n 'kubectl delete .*2>/dev/null \|\| log_warning' examples/backups/vmi/cleanup.sh

Expected result: no matches after migration to --ignore-not-found=true.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/cleanup.sh` around lines 33 - 64, Replace the unsafe
stderr-suppression pattern in cleanup.sh where delete calls use "2>/dev/null ||
log_warning ..." with kubectl's built-in ignore flag so real errors aren't
masked; for each kubectl delete (e.g., commands deleting restorejob
restore-to-copy-test, backupjob test-backup, backup test-backup, vminstance
test, vmdisk ubuntu-source, backupclass velero,
velero.strategy.backups.cozystack.io vminstance-strategy/vmdisk-strategy, and
namespace "$TARGET_NAMESPACE"), remove the "2>/dev/null || log_warning" idiom
and add "--ignore-not-found=true" to the kubectl delete invocation and keep
log_warning only for unexpected failures if you still want explicit warnings.
examples/backups/vmi/07-restore-to-copy.sh (1)

27-45: ⚠️ Potential issue | 🔴 Critical

Backticks in unquoted heredoc can break this script.

Line 43 uses backticks inside <<EOF; bash treats that as command substitution.

Proposed fix
-    keepOriginalPVC: false # renames original VMI PVC before restore to `<name>-orig-<hash>`, only for in-place restore
+    keepOriginalPVC: false # renames original VMI PVC before restore to '<name>-orig-<hash>', only for in-place restore
#!/bin/bash
set -euo pipefail

# Parse-check the script
bash -n examples/backups/vmi/07-restore-to-copy.sh

# Show affected heredoc lines
sed -n '40,45p' examples/backups/vmi/07-restore-to-copy.sh

Expected result: bash -n succeeds once backticks are removed/escaped.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/07-restore-to-copy.sh` around lines 27 - 45, The heredoc
used in the kubectl apply block (started with kubectl apply -f - <<EOF) contains
backticks in the comment on the keepOriginalPVC option (the string
`<name>-orig-<hash>` in the RestoreJob options) which causes command
substitution; fix it by making the heredoc literal (change <<EOF to <<'EOF') or
by escaping/backslash-quoting the backticks so the shell does not perform
command substitution, ensuring the RestoreJob options/comments remain unchanged
at runtime.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/backups/vmi/06-restore-in-place.sh`:
- Around line 43-44: The logged command string and the executed kubectl
invocation mismatch: log_command currently records "-o yaml" while the actual
kubectl call uses "-o wide"; update one so they match (either change the
log_command invocation to use "-o wide" or change the kubectl invocation to "-o
yaml")—edit the lines containing log_command "kubectl get restorejob
restore-in-place-test -n $NAMESPACE -o yaml" and kubectl get restorejob
restore-in-place-test -n "$NAMESPACE" -o wide to use the same output flag.

In `@examples/backups/vmi/07-restore-to-copy.sh`:
- Around line 57-58: The verification log and the executed kubectl command
disagree: log_command records "kubectl get restorejob restore-to-copy-test -n
$NAMESPACE -o yaml" while the actual command runs with "-o wide". Update one so
they match—either change the log_command invocation (log_command "kubectl get
restorejob restore-to-copy-test -n $NAMESPACE -o wide") to reflect the executed
output format, or change the executed kubectl command (kubectl get restorejob
restore-to-copy-test -n "$NAMESPACE" -o yaml) to match the logged "-o yaml";
adjust the flags consistently for the restorejob command and ensure both use the
same quoting style for $NAMESPACE.

In `@examples/backups/vmi/scenario-user-backup.md`:
- Around line 9-10: The Markdown line references a non-existent link
"scenario-admin.md"; open the examples/backups/vmi/scenario-user-backup.md file,
locate the sentence containing "scenario-admin.md" and either (A) replace the
broken link target with the correct existing prerequisite file (update the link
text/path so it points to a valid relative markdown file) or (B) remove the
entire prerequisite sentence if it no longer applies; ensure the final file
contains no dead links and that any remaining prerequisite reference points to
an actual document.

---

Duplicate comments:
In `@examples/backups/vmi/03-create-vmdisk.sh`:
- Line 39: The log message currently emitted by log_success ("VMDisk is ready
(image download may still be in progress)") is misleading because the script
only verifies existence; change the message to accurately reflect verification
(e.g., "VMDisk verified (image download may still be in progress)") or implement
a real readiness check before calling log_success — for a readiness check add a
loop that inspects the disk/image state (the place where the script currently
verifies existence before the log_success call) and only call log_success once
the image download/state indicates completion; update the invocation of
log_success accordingly.

In `@examples/backups/vmi/04-create-vminstance.sh`:
- Line 43: The script currently calls log_success "VMInstance is ready" after
only running kubectl get, which only verifies existence; change this to either
(A) update the message to "VMInstance verified" or (B) implement a real
readiness check before logging success — e.g., use kubectl wait
--for=condition=Ready (or poll the VMInstance status field) with a sensible
timeout and error handling, then call log_success "VMInstance is ready" only
after the wait/poll for readiness succeeds; locate the call to log_success
"VMInstance is ready" and the preceding kubectl get invocation to apply the fix.

In `@examples/backups/vmi/05-create-backupjob.sh`:
- Around line 38-39: The logged command and the executed command are
inconsistent: log_command logs "kubectl get backupjob test-backup -n $NAMESPACE
-o yaml" while the actual execution uses "-o wide"; update one so both match
(choose either YAML or wide) by changing the argument in either the log_command
call or the kubectl invocation so that log_command and the kubectl command use
the same "-o" flag (e.g., both "-o yaml" or both "-o wide") to avoid confusion
when debugging; refer to the existing log_command invocation and the kubectl get
backupjob test-backup -n "$NAMESPACE" -o wide line to make the change.

In `@examples/backups/vmi/06-restore-in-place.sh`:
- Around line 14-31: The heredoc used to render the RestoreJob contains
backticks in the comment for keepOriginalPVC ("<name>-orig-<hash>") which causes
command substitution when using an unquoted <<EOF; change the heredoc to a
single-quoted delimiter (<<'EOF') so the contents are treated literally (or
alternatively escape the backticks in the comment), e.g., update the kubectl
apply -f - <<EOF block to kubectl apply -f - <<'EOF' so the RestoreJob YAML
(including the keepOriginalPVC comment) parses correctly.

In `@examples/backups/vmi/07-restore-to-copy.sh`:
- Around line 27-45: The heredoc used in the kubectl apply block (started with
kubectl apply -f - <<EOF) contains backticks in the comment on the
keepOriginalPVC option (the string `<name>-orig-<hash>` in the RestoreJob
options) which causes command substitution; fix it by making the heredoc literal
(change <<EOF to <<'EOF') or by escaping/backslash-quoting the backticks so the
shell does not perform command substitution, ensuring the RestoreJob
options/comments remain unchanged at runtime.

In `@examples/backups/vmi/cleanup.sh`:
- Around line 33-64: Replace the unsafe stderr-suppression pattern in cleanup.sh
where delete calls use "2>/dev/null || log_warning ..." with kubectl's built-in
ignore flag so real errors aren't masked; for each kubectl delete (e.g.,
commands deleting restorejob restore-to-copy-test, backupjob test-backup, backup
test-backup, vminstance test, vmdisk ubuntu-source, backupclass velero,
velero.strategy.backups.cozystack.io vminstance-strategy/vmdisk-strategy, and
namespace "$TARGET_NAMESPACE"), remove the "2>/dev/null || log_warning" idiom
and add "--ignore-not-found=true" to the kubectl delete invocation and keep
log_warning only for unexpected failures if you still want explicit warnings.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: c274abf8-4f7b-4818-b1e8-6e192a530e44

📥 Commits

Reviewing files that changed from the base of the PR and between 9362ef9 and 2234824.

📒 Files selected for processing (19)
  • api/backups/v1alpha1/restorejob_types.go
  • examples/backups/vmi/00-helpers.sh
  • examples/backups/vmi/01-create-strategies.sh
  • examples/backups/vmi/02-create-backupclass.sh
  • examples/backups/vmi/03-create-vmdisk.sh
  • examples/backups/vmi/04-create-vminstance.sh
  • examples/backups/vmi/05-create-backupjob.sh
  • examples/backups/vmi/06-restore-in-place.sh
  • examples/backups/vmi/07-restore-to-copy.sh
  • examples/backups/vmi/cleanup.sh
  • examples/backups/vmi/run-all.sh
  • examples/backups/vmi/scenario-admin-prepare.md
  • examples/backups/vmi/scenario-user-backup.md
  • examples/backups/vmi/scenario-user-restore.md
  • internal/backupcontroller/restorejob_controller.go
  • internal/backupcontroller/velerostrategy_controller.go
  • internal/backupcontroller/velerostrategy_controller_test.go
  • packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml
  • packages/system/backupstrategy-controller/templates/rbac.yaml
✅ Files skipped from review due to trivial changes (5)
  • api/backups/v1alpha1/restorejob_types.go
  • examples/backups/vmi/scenario-admin-prepare.md
  • examples/backups/vmi/00-helpers.sh
  • examples/backups/vmi/scenario-user-restore.md
  • internal/backupcontroller/velerostrategy_controller_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/system/backup-controller/definitions/backups.cozystack.io_restorejobs.yaml

Comment on lines +43 to +44
log_command "kubectl get restorejob restore-in-place-test -n $NAMESPACE -o yaml"
kubectl get restorejob restore-in-place-test -n "$NAMESPACE" -o wide

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.

⚠️ Potential issue | 🟡 Minor

Logged command and executed command are inconsistent.

Line 43 logs -o yaml, but Line 44 runs -o wide. This makes script output misleading.

Proposed fix
 log_step "Verifying RestoreJob result..."
 log_command "kubectl get restorejob restore-in-place-test -n $NAMESPACE -o yaml"
-kubectl get restorejob restore-in-place-test -n "$NAMESPACE" -o wide
+kubectl get restorejob restore-in-place-test -n "$NAMESPACE" -o yaml
+kubectl get restorejob restore-in-place-test -n "$NAMESPACE" -o wide
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
log_command "kubectl get restorejob restore-in-place-test -n $NAMESPACE -o yaml"
kubectl get restorejob restore-in-place-test -n "$NAMESPACE" -o wide
log_command "kubectl get restorejob restore-in-place-test -n $NAMESPACE -o yaml"
kubectl get restorejob restore-in-place-test -n "$NAMESPACE" -o yaml
kubectl get restorejob restore-in-place-test -n "$NAMESPACE" -o wide
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/06-restore-in-place.sh` around lines 43 - 44, The logged
command string and the executed kubectl invocation mismatch: log_command
currently records "-o yaml" while the actual kubectl call uses "-o wide"; update
one so they match (either change the log_command invocation to use "-o wide" or
change the kubectl invocation to "-o yaml")—edit the lines containing
log_command "kubectl get restorejob restore-in-place-test -n $NAMESPACE -o yaml"
and kubectl get restorejob restore-in-place-test -n "$NAMESPACE" -o wide to use
the same output flag.

Comment on lines +57 to +58
log_command "kubectl get restorejob restore-to-copy-test -n $NAMESPACE -o yaml"
kubectl get restorejob restore-to-copy-test -n "$NAMESPACE" -o wide

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.

⚠️ Potential issue | 🟡 Minor

Verification logging does not match executed command.

Line 57 logs -o yaml, but Line 58 executes -o wide.

Proposed fix
 log_step "Verifying RestoreJob result..."
 log_command "kubectl get restorejob restore-to-copy-test -n $NAMESPACE -o yaml"
-kubectl get restorejob restore-to-copy-test -n "$NAMESPACE" -o wide
+kubectl get restorejob restore-to-copy-test -n "$NAMESPACE" -o yaml
+kubectl get restorejob restore-to-copy-test -n "$NAMESPACE" -o wide
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/07-restore-to-copy.sh` around lines 57 - 58, The
verification log and the executed kubectl command disagree: log_command records
"kubectl get restorejob restore-to-copy-test -n $NAMESPACE -o yaml" while the
actual command runs with "-o wide". Update one so they match—either change the
log_command invocation (log_command "kubectl get restorejob restore-to-copy-test
-n $NAMESPACE -o wide") to reflect the executed output format, or change the
executed kubectl command (kubectl get restorejob restore-to-copy-test -n
"$NAMESPACE" -o yaml) to match the logged "-o yaml"; adjust the flags
consistently for the restorejob command and ensure both use the same quoting
style for $NAMESPACE.

Comment on lines +9 to +10
- Cluster administrator has completed the setup from
[scenario-admin.md](scenario-admin.md) (strategies and BackupClass exist).

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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check available admin scenario docs
fd -i 'scenario-admin*.md' examples/backups/vmi

# Confirm current link target in this file
rg -n '\[scenario-admin\.md\]\(scenario-admin\.md\)' examples/backups/vmi/scenario-user-backup.md

Repository: cozystack/cozystack

Length of output: 142


Update the broken prerequisite link.

Line 10 references scenario-admin.md, which does not exist in this directory. Either update the link to the correct file or remove this line if the prerequisite is no longer applicable.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/backups/vmi/scenario-user-backup.md` around lines 9 - 10, The
Markdown line references a non-existent link "scenario-admin.md"; open the
examples/backups/vmi/scenario-user-backup.md file, locate the sentence
containing "scenario-admin.md" and either (A) replace the broken link target
with the correct existing prerequisite file (update the link text/path so it
points to a valid relative markdown file) or (B) remove the entire prerequisite
sentence if it no longer applies; ensure the final file contains no dead links
and that any remaining prerequisite reference points to an actual document.

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Apr 9, 2026
@androndo
Andrey Kolkov (androndo) merged commit 04c2f66 into main Apr 9, 2026
9 checks passed
@androndo
Andrey Kolkov (androndo) deleted the feat/restore-vm-to-copy branch April 9, 2026 12:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature Categorizes issue or PR as related to a new feature lgtm This PR has been approved by a maintainer size/XXL This PR changes 1000+ lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants