Add e2e coverage for kubevirt-datamover VM backup/restore: multi-PVC, incremental sequence, and CBT restore - #2350
Conversation
|
Skipping CI for Draft Pull Request. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThe PR adds a multi-PVC CirrOS VM fixture, Velero and KubeVirt inspection helpers, readiness checks, and CSI datamover tests for restore and incremental backup behavior. ChangesKubeVirt datamover validation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant BackupRestoreTest
participant KubernetesAPI
participant CSIDataMover
participant VirtLauncherPod
BackupRestoreTest->>KubernetesAPI: Wait for VM PVCs to become Bound
BackupRestoreTest->>CSIDataMover: Create full backup
CSIDataMover->>KubernetesAPI: Record DataUpload and checkpoint
BackupRestoreTest->>VirtLauncherPod: Run virsh and restart VM
BackupRestoreTest->>CSIDataMover: Create incremental backup
CSIDataMover->>KubernetesAPI: Record backup type and checkpoint
BackupRestoreTest->>CSIDataMover: Restore both VM PVCs
Possibly related PRs
Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (2 errors, 3 warnings)
✅ Passed checks (10 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/virt_backup_restore_suite_test.go`:
- Line 96: Update the Eventually assertion for allBound to include a failure
message containing the relevant namespace and PVC names, so timeout reports
identify which restore resources failed.
- Line 84: Update the PVC lookup in the suite polling flow to create a bounded
timeout context for each Kubernetes PersistentVolumeClaims.Get call instead of
using context.Background(). Pass that context to Get and ensure the context is
canceled after the request, while preserving the existing ten-minute polling
behavior.
🪄 Autofix
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: Repository: openshift/coderabbit/.coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 5c2eeb1f-c30f-4c5e-8abd-dcc7735ac335
📒 Files selected for processing (2)
tests/e2e/sample-applications/virtual-machines/cirros-test/cirros-test-multipvc.yamltests/e2e/virt_backup_restore_suite_test.go
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
tests/e2e/virt_backup_restore_suite_test.go (1)
664-672: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThe spec depends on a counter value produced by earlier specs.
runSequenceBackup("full")here is correct only whenbackupCountis 3.ginkgo.Orderedpreserves order, but it does not guarantee that the two earlier specs ran. A focused run, a label filter, or a--focuson this single spec leavesbackupCountat 0, and the assertion then checks the wrong expected type. Derive the number of preceding incremental backups explicitly, or set the annotation value frombackupCountso the expectation stays consistent.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/e2e/virt_backup_restore_suite_test.go` around lines 664 - 672, Update the “hitting maxIncrementalBackups forces a full backup” spec so its expected backup type does not depend on earlier specs having run. Derive the required max-incremental-backups annotation or expected result from the current backupCount, ensuring focused or label-filtered execution still validates the intended full-backup behavior.tests/e2e/lib/virt_helpers.go (1)
1386-1394: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winHandle an unpopulated VMB status explicitly.
unstructured.NestedStringreturnsfound=falsewhenstatus.typeis absent. The code discards that bool, so a VirtualMachineBackup that is still initializing returns("", "", nil). The caller then fails withExpected: "full", Actual: "", which does not show that the status was not yet written. Return a distinct error when the field is missing, so the failure identifies the cause.♻️ Proposed change
- backupType, _, err = unstructured.NestedString(vmb.Object, "status", "type") - if err != nil { + backupType, found, err := unstructured.NestedString(vmb.Object, "status", "type") + if err != nil { return "", "", fmt.Errorf("failed to read status.type from VirtualMachineBackup %s/%s: %w", namespace, vmb.GetName(), err) } + if !found { + return "", "", fmt.Errorf("VirtualMachineBackup %s/%s has no status.type yet", namespace, vmb.GetName()) + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/e2e/lib/virt_helpers.go` around lines 1386 - 1394, Update the status extraction in the VirtualMachineBackup helper to retain and validate the found flags returned by unstructured.NestedString for status.type and status.checkpointName. Return a distinct, descriptive error when either required field is absent, while preserving the existing wrapped errors for extraction failures and the successful return path when both fields are present.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/e2e/lib/backup.go`:
- Around line 149-160: Update lib.Scheme initialization to register
velerov2alpha1 via AddToScheme alongside the existing velerov1 registration, so
GetDataUploadForBackup can list DataUploadList without an unregistered-kind
error.
In `@tests/e2e/virt_backup_restore_suite_test.go`:
- Around line 622-651: Add meaningful failure messages to every Gomega assertion
in the BeforeAll and AfterAll hooks, including namespace deletion/creation,
application installation, VM status polling, VM readiness, VM removal, and
namespace cleanup. Each message should identify the failed action and include
the relevant incSeqNamespace and incSeqVMName values where applicable.
- Around line 344-349: Remove the temporary KubeVirt datamover plugin override
from the DPA setup in the test, including the UnsupportedOverrides
initialization and assignment for KubeVirtDatamoverPluginImageKey. If the plugin
fix is not yet available, replace the personal mutable image with a
project-owned digest-pinned image and retain explicit tracking for removing the
override.
- Around line 589-597: Prevent the outer AfterEach teardown from running between
specs in the “Kubevirt datamover incremental backup sequence” Describe. Scope or
condition tearDownBackupAndRestore so the ordered sequence remains deployed,
then add cleanup in this Describe’s AfterAll to remove the sequence resources
and restore the expected final state.
---
Nitpick comments:
In `@tests/e2e/lib/virt_helpers.go`:
- Around line 1386-1394: Update the status extraction in the
VirtualMachineBackup helper to retain and validate the found flags returned by
unstructured.NestedString for status.type and status.checkpointName. Return a
distinct, descriptive error when either required field is absent, while
preserving the existing wrapped errors for extraction failures and the
successful return path when both fields are present.
In `@tests/e2e/virt_backup_restore_suite_test.go`:
- Around line 664-672: Update the “hitting maxIncrementalBackups forces a full
backup” spec so its expected backup type does not depend on earlier specs having
run. Derive the required max-incremental-backups annotation or expected result
from the current backupCount, ensuring focused or label-filtered execution still
validates the intended full-backup behavior.
🪄 Autofix
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: Repository: openshift/coderabbit/.coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: be76a5c0-d96a-49fb-a7cd-9b6173f7f69f
📒 Files selected for processing (4)
tests/e2e/lib/apps.gotests/e2e/lib/backup.gotests/e2e/lib/virt_helpers.gotests/e2e/virt_backup_restore_suite_test.go
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/e2e/lib/scheme.go (1)
31-31: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low valueHandle scheme registration errors consistently.
velerov2alpha1.AddToScheme(Scheme)currently returnsnilunconditionally, so this line cannot cause the statedDataUploadListfailure. Do not discard the error return; useutilruntime.Must(...)for all scheme registrations to fail fast if their behavior changes.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/e2e/lib/scheme.go` at line 31, Update the scheme initialization around velerov2alpha1.AddToScheme(Scheme) to pass its return value through utilruntime.Must instead of discarding it, and apply the same fail-fast wrapper consistently to every scheme registration in that initialization block.Source: Path instructions
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@tests/e2e/lib/scheme.go`:
- Line 31: Update the scheme initialization around
velerov2alpha1.AddToScheme(Scheme) to pass its return value through
utilruntime.Must instead of discarding it, and apply the same fail-fast wrapper
consistently to every scheme registration in that initialization block.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: afb294c1-b581-4b68-b0c5-c16a7bd1f4a9
📒 Files selected for processing (2)
tests/e2e/lib/scheme.gotests/e2e/virt_backup_restore_suite_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- tests/e2e/virt_backup_restore_suite_test.go
|
Re review 4859513816 and the two remaining nitpicks from 4859450952:
Note Responses generated with Claude |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: kaovilai The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
31998f5 to
5de0309
Compare
Adds a CirrOS VM template with two disks (existing rootdisk DataSource plus a blank data disk) and a corresponding backup/restore table entry using CSIDataMover, verifying both PVCs come back Bound after restore. Covers the "Multi-PVC VM" gap tracked in migtools/kubevirt-datamover-controller#99. Not yet run against a real cluster. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
Automates scenarios 1-3 from oadp-operator#2252 (a manual test writeup of kubevirt-datamover incremental-backup-sequence behavior): a full-then-incremental backup chain with no expected/actual type mismatch, a VM restart not invalidating the checkpoint chain, and hitting maxIncrementalBackups forcing a full backup via the per-VM override annotation. Scenario 4 (delete libvirt checkpoints with maxIncrementalBackups=0) hits an unfixed upstream bug (CNV-85377: virt-controller never falls back to full, VMB hangs Initializing forever) and is scaffolded as a real, compiling ginkgo.PIt rather than run, ready to flip to ginkgo.It once that bug is fixed. New lib helpers: VirtualMachineBackup GVR + GetVMBBackupType, GetDataUploadForBackup, GetVirtLauncherPod, RunVirshCommand, SetVMAnnotation, IsDeploymentRolledOut. Also pins the e2e DPA's kubevirt-datamover-plugin image to an unmerged PR build (quay.io/tkaovila/kubevirt-datamover-plugin:pr-41, see migtools/kubevirt-datamover-plugin#41) until that PR merges. Not yet run against a real cluster. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
- Register velero v2alpha1 in the e2e suite scheme (GetDataUploadForBackup lists DataUploadList, which was otherwise unregistered -> runtime error). - Fix a real bug: the outer AfterEach undeploys CSI+datamover and deletes cirros-test after every spec it fires for, including this Describe's specs -- which would tear the VM down between the incremental-sequence scenarios. Collapse them into a single ordered It (with ginkgo.By steps) that does its own full teardown at the end, and register the case via updateLastBRcase/prepareBackupAndRestore so the shared AfterEach's now- redundant cleanup is a harmless no-op instead of acting on stale state. - Use a bounded context for the PVC Get in vmPvcsBound, instead of context.Background(). - Add failure messages naming the namespace/VM/backup to gomega assertions across the new setup/teardown/backup-sequence code. Not fixed: the personal-registry kubevirt-datamover-plugin image override is flagged as a pre-merge security concern, but it's an explicit, already- documented temporary pin (see prior commit) needed to exercise an unmerged upstream PR -- removing it would defeat this PR's own purpose until migtools/kubevirt-datamover-plugin#41 merges. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
Matches the existing cmd/main.go convention (utilruntime.Must(X.AddToScheme(scheme))) instead of silently discarding registration errors via "_ =". Verified none of the 15 AddToScheme calls actually error (ran TestOADPE2E without a cluster; it got past package init/scheme registration and failed later on an unrelated connectivity assertion, not a panic). Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
GetVMBBackupType discarded the found bool from
unstructured.NestedString(status.type), so a VirtualMachineBackup
still initializing (no status.type yet) silently returned ("", "",
nil) instead of a clear error -- callers then failed with a
confusing "Expected: full, Actual: \"\"" instead of a message
identifying the real cause.
Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
Adds a second temporary image override (KubeVirtDatamoverControllerImageKey -> quay.io/tkaovila/kdm-controller:issue73-phase3) alongside the existing plugin override, to exercise migtools/kubevirt-datamover-controller#124 (DataDownload controller for VM restore, issue #73 phase 3) pre-merge -- directly relevant to restore-from-CBT coverage. Both overrides are temporary and should be removed once their respective PRs merge. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
Closes the #99 "Restore from KDM CBT backup" gap, now unblocked by the overridden kubevirt-datamover-controller image (migtools/kubevirt-datamover- controller#124, DataDownload controller, issue #73 phase 3). Per docs/design/kubevirt-datamover.md and the plugin's own README, restore just needs a normal Velero Restore -- the VirtualMachine RestoreItemAction plugin creates the DataDownload CR automatically from backup-recorded annotations, and separately discards the restored VMB/VMBT so restore doesn't re-trigger a backup. No manual CR driving needed, mirroring how backup already works. Verifies success via the existing generic lib.CreateRestoreFromBackup/IsRestoreDone/IsRestoreCompletedSuccessfully helpers plus a new lib.GetDataDownloadForRestore (mirrors GetDataUploadForBackup) confirming the DataDownload itself reached status.phase=Completed -- proving the datamover path actually engaged, not just that the restore completed some other way. Extracted runKubevirtDMBackup (shared create+wait+verify-backup helper) out of runSequenceBackup to avoid duplicating that logic a second time for the restore test. Also scaffolds two ginkgo.PIt (real, compiling, not run) for gaps explicitly still open per kubevirt-datamover-controller#73's phased plan: multi-disk restore (phase 4, not yet hardened -- new cirros-test-multipvc-cbt.yaml template ready for when it lands) and restore from an incremental (not full) backup (phase 5, not yet e2e-validated upstream even though the chain-rebase logic may already support it internally). Not yet run against a real cluster. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
…e gate - IsDeploymentRolledOut: also require Status.Replicas == wantReplicas alongside UpdatedReplicas/AvailableReplicas -- without it, a rollout scaling down old replicas could report done while stale pods still lingered. - GetVMBBackupType: check the found bool for status.checkpointName too (same fix already applied to status.type), so an unpopulated field returns a clear error instead of silently returning "". - Gate both kubevirt-datamover pre-merge image overrides behind an explicit OADP_E2E_KDM_PREMERGE_IMAGES=true env var instead of always-on, so this suite defaults to the operator's normal images for anyone else running it and only pulls the mutable personal-registry builds when deliberately testing those two PRs. Scheduled cluster-run job updated to set this env var. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
The manager ClusterRole shipped with OADP only granted access to datauploads/datauploads.status -- there was no datadownloads, datadownloads/status, or events permission at all. Without this, the controller would hit RBAC-denied errors reconciling any DataDownload, regardless of image correctness, once a real velero restore actually tried to drive it (which migtools/kubevirt-datamover-plugin#41 now makes possible). Synced config/kubevirt-datamover-controller_rbac/role.yaml and the matching block in bundle/manifests/oadp-operator.clusterserviceversion.yaml (serviceAccountName: oadp-kubevirt-datamover-controller-manager) to byte-match config/rbac/role.yaml from migtools/kubevirt-datamover-controller PR #124 (issue #73 phase 3, commit 825d176), which added these rules on the source side but were never pulled into OADP's bundled copy -- normally done via `make update-kubevirt-datamover-manifests KUBEVIRT_DATAMOVER_PATH=...`, done here by hand since no local checkout of that repo is available in this environment. This is a real, pre-existing gap (not introduced by this branch's other changes) that this branch's e2e work would otherwise have hit blind, so fixing it here rather than filing it separately. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
… to plugin PR #44 VirtualMachineBackupTracker deletion can leave a VirtualMachineBackup's backup.kubevirt.io/vmbackup-protection finalizer stuck (kubevirt#18724), blocking namespace deletion forever in the incremental-sequence and restore specs. IsNamespaceDeletedClearingStuckVMBFinalizers works around it by clearing stale finalizers on every poll; safe to remove once that kubevirt issue is fixed. Also fixes a real race: VirtualMachineBackup is ephemeral and can be deleted by virt-controller before the overall backup finishes uploading, so checking its status after backup completion (as before) could find nothing. runKubevirtDMBackup now takes an onDataUploadFound callback invoked as soon as the DataUpload appears, letting the incremental- sequence spec check VMB status while it still exists. migtools/kubevirt-datamover-plugin#41 merged; the e2e image override now points at #44 instead, which fixes a still-open bug where restored PVCs come back with stale spec.volumeName/status, causing the datamover controller to reject them as "already bound". Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
Resolving the CSV merge conflict against upstream's velero-SA RBAC restructuring (PR openshift#2349) by taking upstream's side lost a small generated permission block (events create/patch). make bundle regenerates it correctly. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
5de0309 to
c300f00
Compare
The kubevirt-datamover controller's flip-back logic (restoring a VM's run state once all sibling DataDownloads complete) needs to patch the VirtualMachine object, but the synced ClusterRole only granted get/list/watch. Sync from kubevirt-datamover-controller commit a69ee24 to add the missing verb. Also switch the e2e suite's pre-merge image overrides from mutable tags to digests, since both quay.io/tkaovila/* tags used here have already been rebuilt in place during development. Verified on a real cluster: "restore a VM from a full kubevirt-datamover CBT backup" now passes end-to-end (VM correctly halts at restore time, then flips back to Running once its DataDownload completes). Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
ClearStuckVMBFinalizers: clear only backup.kubevirt.io/vmbackup-protection instead of nulling the entire finalizer list, so any other finalizer a VMB might carry survives the workaround. GetVirtLauncherPod: skip pods that are terminating or not yet Running before matching on the domain annotation. During a VM restart (hit by the incremental-backup-sequence test), both the old and new virt-launcher pod can briefly share the same domain annotation; without this the old, terminating pod could be picked, breaking RunVirshCommand execs. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
migtools/kubevirt-datamover-plugin#44 iterated further (commit 4fb7ed9) after this suite's plugin digest was originally pinned, including a change to Progress()'s grace-period-anchor timing. Re-ran the restore-from-CBT-backup scenario against the new build to confirm that change doesn't affect this path: still passes end-to-end. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
The restore-from-CBT-backup scenario's target StorageProfile defaults to Block volumeMode on AWS gp3-csi, so this path already ran through kubevirt-datamover-controller's Block-volumeMode restore support -- but nothing asserted it. A cluster whose default StorageClass used Filesystem instead would still pass this test without ever touching that code path, silently losing the coverage. Extend GetDataDownloadForRestore to also surface the kubevirt-datamover.io/restore-block-mode annotation the plugin stamps on the DataDownload, and assert it's true. Cross-check against the actual restored PVC's spec.volumeMode as well, so the test fails if the annotation and the real PVC ever disagree. Verified on cluster: both assertions pass. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
Both companion-PR authors independently flagged the same gap when asked what e2e coverage was missing: a failed/canceled DataDownload leaves the VM halted forever by design, but nothing verified that. Adds a second restore phase to the existing CBT-restore It, reusing the same backup: forces a PVC binding conflict on the freshly-restored PVC (setting spec.volumeName is enough -- kdm-controller's Accepted check only inspects the PVC's own fields, not whether the referenced PV exists), which triggers the controller's real rejection path deterministically rather than racing its reconcile loop or patching DataDownload status by hand. Asserts the restore ends PartiallyFailed, the DataDownload is Failed, and the VM never reaches Running. This has to live inside the same It as the happy-path restore rather than a separate one: the suite's shared per-test AfterEach tears down the DPA (and velero/BSL with it) after every It, and a freshly recreated DPA gets a new random BSL S3 prefix each time, so a second It could never have restored from this same backup. Verified on cluster: forced conflict reaches PartiallyFailed in ~40s. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
kubevirt-datamover-plugin's clearPVCBinding clears spec.volumeName/ status and two pv.kubernetes.io/* annotations before returning a restored item, but never touches spec.selector -- safe only if these PVCs never carry one to begin with (always dynamically provisioned, never statically pre-bound). Neither this suite nor the plugin's own unit tests (pvc/restore_test.go has no fixture covering a selector) verified that premise; it was reasoned, not checked. Assert it directly against the live source PVC before backup. Verified on cluster: nil, as expected. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
oadp-e2e PR openshift#2350 (commit 30a3352) confirms on a real cluster that the source PVC has spec.selector == nil before backup, closing the last open piece of this deviation - combined with kdm-plugin's unit test, this is now RESOLVED rather than an open verification gap. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
kdm-controller: commit 6bc9566 (round 4 of iteration -- RBAC marker move, block-device output-path validation, PV-label crash-recovery timing, disk-name resolution, sparse-write -S 0 fix, timeout-path quiet-requeue). kdm-plugin: commit 8b05d38 -- Progress's first-DataDownload grace period is anchored to when the operation first observed an empty DataDownload list, rather than the restore's overall start time. Re-ran the full scenario (happy path, Block volumeMode, spec.selector, and the forced-PVC-conflict failure mode) against both tips together: still passes end-to-end. Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
|
/test all |
|
/retest-required |
|
@kaovilai: The following tests failed, say
Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
…shift#169 regression coverage The "restore a VM from a full kubevirt-datamover CBT backup" It now checksums the source disk before backup and the restored disk before the VM resumes, comparing the two -- logged rather than hard-asserted, since CirrOS ships no qemu-guest-agent and its own filesystem churn during a live-VM read produces mismatches unrelated to kubevirt-datamover's correctness (5/5 real attempts mismatched; see the comment above the comparison for the full investigation). Also asserts Block volumeMode is actually exercised, that source PVCs carry no spec.selector (a precondition kubevirt-datamover-plugin's restore path assumes), and adds a rejected-restore negative scenario (forced PVC binding conflict -> DataDownload fails -> VM stays halted). Adds a HasQemuGuestAgent helper and a VmBackupRestoreCase.HasGuestAgent field, cross-checked live against the VMI so the checksum strategy can't silently drift from what the fixture actually provides. Adds a third ginkgo.PIt for kubevirt-datamover-controller#169 (stale-sibling DataDownload blocking the VM run-state flip across separate restore attempts), gated on the same Phase 4 branch as the two existing multi-disk/incremental placeholders -- the fix already exists there, so this asserts the fixed behavior once Phase 4 lands rather than locking in the current bug. Makes the kubevirt-datamover-plugin/controller pre-merge image overrides unconditional rather than gated behind OADP_E2E_KDM_PREMERGE_IMAGES=true -- the env-var gate's silent fallback to the default (unrelated) images was the root cause of several hours of phantom debugging this session. New lib helpers: DeleteVeleroBackupAndRestore (velero-CLI-based backup/restore cleanup, safe to call before DPA teardown), ExecuteShellCommandInPod (extracted from ExecuteCommandInPodsSh for real shell scripts with pipes). Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
Important
Draft — waiting on companion PRs in other repos to be reviewed and merged first:
Both are required for this PR's "restore from a full kubevirt-datamover CBT backup" scenario (see Design below). This PR currently pins their pre-merge images by digest via
OADP_E2E_KDM_PREMERGE_IMAGES=truepurely for testing — those overrides get removed once both merge and their fixes ship in the default images. Re-marking ready for review once that lands.Summary
Adds e2e coverage for three kubevirt-datamover VM backup/restore scenarios: multi-PVC VMs, an incremental backup sequence, and restoring a VM from a full CBT backup. Closes coverage gaps tracked in migtools/kubevirt-datamover-controller#99, openshift/oadp-operator#2252, and migtools/kubevirt-datamover-controller#73 (phases 0-3; phases 4/5 are out of scope, see Known limitations).
Design
Multi-PVC VM backup/restore
tests/e2e/sample-applications/virtual-machines/cirros-test/cirros-test-multipvc.yaml: a CirrOS VM with two disks — the existing rootdisk plus a second blank 100Mi data disk, so no extra image fetch is needed.ginkgo.Entry("no-application CSI datamover backup and restore, multi-PVC CirrOS VM", ...), following the existing "no-application" CirrOS pattern withBackupRestoreType: lib.CSIDataMover.vmPvcsBound()verifies both PVCs exist and areBoundafter restore.Incremental backup sequence
maxIncrementalBackupsforces a full backup (via the per-VMkubevirt-datamover.io/max-incremental-backupsannotation override, which takes effect immediately with no controller rollout to wait for).tests/e2e/libhelpers:VirtualMachineBackupGVR +GetVMBBackupType(reads.status.type/.status.checkpointName),GetDataUploadForBackup(reads the controller'sexpected-backup-typeannotation),SetVMAnnotation,IsDeploymentRolledOut(generation/updatedReplicas-aware, unlike the existingIsDeploymentReady).maxIncrementalBackups=0) depends on an unfixed upstream bug (CNV-85377: virt-controller never falls back to full, the VMB hangsInitializingforever) and is scaffolded as a real, compilingginkgo.PIt, ready to flip toginkgo.Itonce that's fixed.Restore from a full kubevirt-datamover CBT backup
Restoring a VM from a kubevirt-datamover backup has to hold the VM inactive until its DataDownload(s) finish: otherwise virt-launcher spawns immediately on the restored VM object, and
WaitForFirstConsumerbinds the target PVC to a scratch volume before the DataDownload can rebind it. That race is closed by two companion PRs, both required together:RestoreItemActionV2halts the VM's run strategy at restore time and stashes the original value in an annotation.Completed— driven by a watch on the phase transition itself rather than a single completion call site, so a controller-restart-mid-restore resume path can't skip it.updateonkubevirt.io/virtualmachinesto perform that flip; this PR syncs that verb intoconfig/kubevirt-datamover-controller_rbac/role.yamland the bundle CSV.RunVirshCommand's virt-launcher pod lookup now skips pods that are terminating or not yetRunning: a VM restart (hit by the incremental-sequence test above) can briefly have both the old and new launcher pod sharing the same domain annotation.ClearStuckVMBFinalizers(workaround for kubevirt/kubevirt#18724: a deletedVirtualMachineBackupnever has itsvmbackup-protectionfinalizer released once its backing tracker is gone) now clears only that specific finalizer, rather than the VMB's entire finalizer list.GetDataDownloadForRestorealso surfaces thekubevirt-datamover.io/restore-block-modeannotation the plugin stamps on the DataDownload, and the test cross-checks it against the restored PVC's actualspec.volumeMode.The plugin and controller images are pinned by digest, not by tag, in
tests/e2e/virt_backup_restore_suite_test.go'sUnsupportedOverrides— gated behindOADP_E2E_KDM_PREMERGE_IMAGES=truesince these are personal pre-merge builds; the override and its digests are removed once both companion PRs merge and the fixes ship in the default images.Known limitations
ginkgo.PItand are explicitly out of scope here: multi-disk restore hardening (kubevirt-datamover-controller#73 phase 4) and incremental-chain restore validation (phase 5) — neither is implemented upstream yet.maxIncrementalBackups=0checkpoint-delete scenario stays pending on CNV-85377.Testing
All three in-scope scenarios pass end-to-end on a real OpenShift cluster (AWS, community HCO/KubeVirt) with the companion plugin and controller images deployed:
Always) once its DataDownload reachesCompleted; Block volumeMode confirmed via both the DataDownload annotation and the restored PVC itself.go build,go vet, andgolangci-lintall pass.Test plan
Note
Responses generated with Claude