@@ -1395,15 +1395,15 @@ func TestHandleAccepted_ProceedsWhenOlderDUCompleted(t *testing.T) {
13951395 // It will proceed into prepareVMBackupTracker, which will fail because
13961396 // BSL credentials aren't fully set up in this test. That's fine — we're
13971397 // testing that the serialization guard does NOT block.
1398- // The important assertion is that DU-1's VMBT gets deleted (normal cleanup) .
1399- deletedVMBT := & kubevirtbackupv1alpha1.VirtualMachineBackupTracker {}
1398+ // The important assertion is that DU-1's VMBT is reused (not deleted) per issue #32 .
1399+ reusedVMBT := & kubevirtbackupv1alpha1.VirtualMachineBackupTracker {}
14001400 getErr := fakeClient .Get (context .Background (), types.NamespacedName {
14011401 Name : vmbt1 .Name , Namespace : vmNamespace ,
1402- }, deletedVMBT )
1402+ }, reusedVMBT )
14031403
1404- // prepareVMBackupTracker should have deleted DU-1's old VMBT
1405- if getErr = = nil {
1406- t .Error ("expected DU-1's VMBT to be deleted by prepareVMBackupTracker, but it still exists" )
1404+ // prepareVMBackupTracker should have reused DU-1's existing VMBT
1405+ if getErr ! = nil {
1406+ t .Errorf ("expected DU-1's VMBT to be reused by prepareVMBackupTracker, but it was deleted: %v" , getErr )
14071407 }
14081408 // We don't care about the error from handleAccepted itself — it may fail
14091409 // on BSL credential lookup. The point is it got past the serialization guard.
@@ -5513,16 +5513,17 @@ func TestPrepareVMBackupTracker_FromS3(t *testing.T) {
55135513 }
55145514}
55155515
5516- func TestPrepareVMBackupTracker_DeletesExisting (t * testing.T ) {
5517- // VMBT already exists in cluster → should be deleted and recreated
5516+ func TestPrepareVMBackupTracker_ReusesExisting (t * testing.T ) {
5517+ // VMBT already exists in cluster → should be reused (not deleted and recreated).
5518+ // Issue #32: VMBT must persist on-cluster for KubeVirt lifecycle events.
55185519 scheme := runtime .NewScheme ()
55195520 _ = velerov2alpha1 .AddToScheme (scheme )
55205521 _ = kubevirtbackupv1alpha1 .AddToScheme (scheme )
55215522 _ = corev1 .AddToScheme (scheme )
55225523
55235524 vmName := "test-vm"
55245525 vmNamespace := "test-ns"
5525- duName := "test-du-delete "
5526+ duName := "test-du-reuse "
55265527
55275528 du := & velerov2alpha1.DataUpload {
55285529 ObjectMeta : metav1.ObjectMeta {
@@ -5538,13 +5539,12 @@ func TestPrepareVMBackupTracker_DeletesExisting(t *testing.T) {
55385539 },
55395540 }
55405541
5541- // Pre-create an existing VMBT with stale label
5542+ // Pre-create an existing VMBT
55425543 existingVMBT := & kubevirtbackupv1alpha1.VirtualMachineBackupTracker {
55435544 ObjectMeta : metav1.ObjectMeta {
55445545 Name : "vmbt-" + vmName + "-old" ,
55455546 Namespace : vmNamespace ,
55465547 Labels : map [string ]string {
5547- "stale-label" : "old-value" ,
55485548 common .LabelVMNameHash : common .HashForLabel (vmName ),
55495549 },
55505550 },
@@ -5574,22 +5574,27 @@ func TestPrepareVMBackupTracker_DeletesExisting(t *testing.T) {
55745574 t .Fatalf ("unexpected error: %v" , err )
55755575 }
55765576
5577- // Verify new VMBT was created (should have our label, not the stale one)
5578- if vmbt .Labels ["stale-label" ] == "old-value" {
5579- t .Error ("expected old VMBT to be deleted and new one created, but old labels persist" )
5580- }
5581- if vmbt .Annotations [common .AnnotationDataUploadName ] != duName {
5582- t .Errorf ("expected new VMBT to have DataUpload annotation %q, got %q" ,
5583- duName , vmbt .Annotations [common .AnnotationDataUploadName ])
5577+ // Verify the existing VMBT was reused (same name)
5578+ if vmbt .Name != existingVMBT .Name {
5579+ t .Errorf ("expected existing VMBT %q to be reused, got %q" , existingVMBT .Name , vmbt .Name )
55845580 }
55855581 if vmbt .Labels [common .LabelVMNameHash ] != common .HashForLabel (vmName ) {
5586- t .Errorf ("expected new VMBT to have label %s, got %q" ,
5582+ t .Errorf ("expected VMBT to have label %s, got %q" ,
55875583 common .LabelVMNameHash , vmbt .Labels [common .LabelVMNameHash ])
55885584 }
5585+
5586+ // Verify the VMBT still exists on-cluster
5587+ preserved := & kubevirtbackupv1alpha1.VirtualMachineBackupTracker {}
5588+ if err := fakeClient .Get (context .Background (), types.NamespacedName {
5589+ Name : existingVMBT .Name , Namespace : vmNamespace ,
5590+ }, preserved ); err != nil {
5591+ t .Errorf ("existing VMBT was deleted when it should have been reused: %v" , err )
5592+ }
55895593}
55905594
5591- func TestPrepareVMBackupTracker_SkipsReferencedVMBT (t * testing.T ) {
5592- // A VMBT referenced by a non-terminal VMB must NOT be deleted.
5595+ func TestPrepareVMBackupTracker_ReusesVMBTEvenIfReferencedByActiveVMB (t * testing.T ) {
5596+ // A VMBT referenced by a non-terminal VMB is still reused (not deleted).
5597+ // Issue #32: VMBTs are never deleted — they persist on-cluster.
55935598 scheme := runtime .NewScheme ()
55945599 _ = velerov2alpha1 .AddToScheme (scheme )
55955600 _ = kubevirtbackupv1alpha1 .AddToScheme (scheme )
@@ -5668,25 +5673,26 @@ func TestPrepareVMBackupTracker_SkipsReferencedVMBT(t *testing.T) {
56685673 t .Fatalf ("unexpected error: %v" , err )
56695674 }
56705675
5671- // New VMBT should be created
5676+ // The existing VMBT should be reused
56725677 if vmbt == nil {
5673- t .Fatal ("expected new VMBT to be created " )
5678+ t .Fatal ("expected VMBT to be returned " )
56745679 }
5675- if vmbt .Name = = otherVMBT .Name {
5676- t .Error ( "new VMBT should not be the same object as the protected VMBT" )
5680+ if vmbt .Name ! = otherVMBT .Name {
5681+ t .Errorf ( "expected existing VMBT %q to be reused, got %q" , otherVMBT . Name , vmbt . Name )
56775682 }
56785683
5679- // The other VMBT must NOT be deleted
5684+ // The VMBT must still exist on-cluster
56805685 preserved := & kubevirtbackupv1alpha1.VirtualMachineBackupTracker {}
56815686 if err := fakeClient .Get (context .Background (), types.NamespacedName {
56825687 Name : otherVMBT .Name , Namespace : vmNamespace ,
56835688 }, preserved ); err != nil {
5684- t .Errorf ("referenced VMBT was deleted when it should have been preserved: %v" , err )
5689+ t .Errorf ("VMBT was deleted when it should have been preserved: %v" , err )
56855690 }
56865691}
56875692
5688- func TestPrepareVMBackupTracker_DeletesVMBTWithTerminalVMB (t * testing.T ) {
5689- // A VMBT referenced by a terminal VMB (Done=True) should be deleted normally.
5693+ func TestPrepareVMBackupTracker_ReusesVMBTWithTerminalVMB (t * testing.T ) {
5694+ // A VMBT referenced by a terminal VMB (Done=True) should be reused.
5695+ // Issue #32: VMBTs are never deleted — they persist on-cluster.
56905696 scheme := runtime .NewScheme ()
56915697 _ = velerov2alpha1 .AddToScheme (scheme )
56925698 _ = kubevirtbackupv1alpha1 .AddToScheme (scheme )
@@ -5765,17 +5771,21 @@ func TestPrepareVMBackupTracker_DeletesVMBTWithTerminalVMB(t *testing.T) {
57655771 OADPNamespace : vmNamespace ,
57665772 }
57675773
5768- _ , err := r .prepareVMBackupTracker (context .Background (), logr .Discard (), du , vmName , vmNamespace )
5774+ vmbt , err := r .prepareVMBackupTracker (context .Background (), logr .Discard (), du , vmName , vmNamespace )
57695775 if err != nil {
57705776 t .Fatalf ("unexpected error: %v" , err )
57715777 }
57725778
5773- // The old VMBT should be deleted (its VMB is terminal)
5774- deleted := & kubevirtbackupv1alpha1.VirtualMachineBackupTracker {}
5779+ // The old VMBT should be reused (not deleted)
5780+ if vmbt .Name != oldVMBT .Name {
5781+ t .Errorf ("expected VMBT %q to be reused, got %q" , oldVMBT .Name , vmbt .Name )
5782+ }
5783+
5784+ preserved := & kubevirtbackupv1alpha1.VirtualMachineBackupTracker {}
57755785 if err := fakeClient .Get (context .Background (), types.NamespacedName {
57765786 Name : oldVMBT .Name , Namespace : vmNamespace ,
5777- }, deleted ); err = = nil {
5778- t .Error ( "expected old VMBT to be deleted (VMB is terminal), but it still exists" )
5787+ }, preserved ); err ! = nil {
5788+ t .Errorf ( " VMBT was deleted when it should have been preserved: %v" , err )
57795789 }
57805790}
57815791
@@ -6021,3 +6031,92 @@ func TestSafeGenerateNamePrefix(t *testing.T) {
60216031 })
60226032 }
60236033}
6034+
6035+ // TestCleanupVMBackupResources_PreservesVMBT verifies that cleanupVMBackupResources
6036+ // deletes the VMB but preserves the VMBT on-cluster (issue #32).
6037+ func TestCleanupVMBackupResources_PreservesVMBT (t * testing.T ) {
6038+ scheme := runtime .NewScheme ()
6039+ _ = velerov2alpha1 .AddToScheme (scheme )
6040+ _ = kubevirtbackupv1alpha1 .AddToScheme (scheme )
6041+ _ = corev1 .AddToScheme (scheme )
6042+
6043+ vmName := "test-vm"
6044+ vmNamespace := "test-ns"
6045+
6046+ du := & velerov2alpha1.DataUpload {
6047+ ObjectMeta : metav1.ObjectMeta {
6048+ Name : "test-du-cleanup" ,
6049+ Namespace : "openshift-adp" ,
6050+ UID : types .UID ("cleanup-uid" ),
6051+ },
6052+ Spec : velerov2alpha1.DataUploadSpec {
6053+ DataMover : common .DataMoverKubeVirt ,
6054+ },
6055+ }
6056+
6057+ // VMB that should be deleted during cleanup
6058+ vmb := & kubevirtbackupv1alpha1.VirtualMachineBackup {
6059+ ObjectMeta : metav1.ObjectMeta {
6060+ Name : "vmb-test-cleanup" ,
6061+ Namespace : vmNamespace ,
6062+ Labels : map [string ]string {
6063+ common .LabelDataUploadUID : string (du .UID ),
6064+ },
6065+ },
6066+ Spec : kubevirtbackupv1alpha1.VirtualMachineBackupSpec {
6067+ Source : corev1.TypedLocalObjectReference {
6068+ APIGroup : strPtr ("backup.kubevirt.io" ),
6069+ Kind : "VirtualMachineBackupTracker" ,
6070+ Name : "vmbt-test-cleanup" ,
6071+ },
6072+ },
6073+ }
6074+
6075+ // VMBT that should be PRESERVED during cleanup
6076+ vmbt := & kubevirtbackupv1alpha1.VirtualMachineBackupTracker {
6077+ ObjectMeta : metav1.ObjectMeta {
6078+ Name : "vmbt-test-cleanup" ,
6079+ Namespace : vmNamespace ,
6080+ Labels : map [string ]string {
6081+ common .LabelVMNameHash : common .HashForLabel (vmName ),
6082+ },
6083+ },
6084+ Spec : kubevirtbackupv1alpha1.VirtualMachineBackupTrackerSpec {
6085+ Source : corev1.TypedLocalObjectReference {
6086+ APIGroup : strPtr ("kubevirt.io" ),
6087+ Kind : "VirtualMachine" ,
6088+ Name : vmName ,
6089+ },
6090+ },
6091+ }
6092+
6093+ fakeClient := fake .NewClientBuilder ().
6094+ WithScheme (scheme ).
6095+ WithObjects (du , vmb , vmbt ).
6096+ Build ()
6097+
6098+ r := & KubeVirtDataUploadReconciler {
6099+ Client : fakeClient ,
6100+ Scheme : scheme ,
6101+ Log : logr .Discard (),
6102+ OADPNamespace : "openshift-adp" ,
6103+ }
6104+
6105+ r .cleanupVMBackupResources (context .Background (), logr .Discard (), du , vmNamespace )
6106+
6107+ // VMB should be deleted
6108+ deletedVMB := & kubevirtbackupv1alpha1.VirtualMachineBackup {}
6109+ if err := fakeClient .Get (context .Background (), types.NamespacedName {
6110+ Name : vmb .Name , Namespace : vmNamespace ,
6111+ }, deletedVMB ); err == nil {
6112+ t .Error ("VMB should have been deleted during cleanup, but it still exists" )
6113+ }
6114+
6115+ // VMBT should be PRESERVED (not deleted)
6116+ preservedVMBT := & kubevirtbackupv1alpha1.VirtualMachineBackupTracker {}
6117+ if err := fakeClient .Get (context .Background (), types.NamespacedName {
6118+ Name : vmbt .Name , Namespace : vmNamespace ,
6119+ }, preservedVMBT ); err != nil {
6120+ t .Errorf ("VMBT should have been preserved during cleanup, but it was deleted: %v" , err )
6121+ }
6122+ }
0 commit comments