diff --git a/changelogs/unreleased/9900-shubham-pampattiwar b/changelogs/unreleased/9900-shubham-pampattiwar new file mode 100644 index 000000000..034507c1f --- /dev/null +++ b/changelogs/unreleased/9900-shubham-pampattiwar @@ -0,0 +1 @@ +Skip VGS cleanup when backup did not use VolumeGroupSnapshots diff --git a/pkg/controller/restore_finalizer_controller.go b/pkg/controller/restore_finalizer_controller.go index f82216bc3..93652c0c2 100644 --- a/pkg/controller/restore_finalizer_controller.go +++ b/pkg/controller/restore_finalizer_controller.go @@ -301,8 +301,10 @@ func (ctx *finalizerContext) execute() (results.Result, results.Result) { pdpErrs := ctx.patchDynamicPVWithVolumeInfo() errs.Merge(&pdpErrs) - vgscWarnings := ctx.cleanupStubVGSC() - warnings.Merge(&vgscWarnings) + if ctx.hasVolumeGroupSnapshotHandles() { + vgscWarnings := ctx.cleanupStubVGSC() + warnings.Merge(&vgscWarnings) + } rehErrs := ctx.WaitRestoreExecHook() errs.Merge(&rehErrs) @@ -449,6 +451,15 @@ func (ctx *finalizerContext) patchDynamicPVWithVolumeInfo() (errs results.Result return errs } +func (ctx *finalizerContext) hasVolumeGroupSnapshotHandles() bool { + for _, vi := range ctx.volumeInfo { + if vi.CSISnapshotInfo != nil && vi.CSISnapshotInfo.VolumeGroupSnapshotHandle != "" { + return true + } + } + return false +} + // cleanupStubVGSC deletes stub VolumeGroupSnapshotContent objects that were // created during restore to satisfy CSI controller validation. These stubs are // labeled with velero.io/restore-name for identification. diff --git a/pkg/controller/restore_finalizer_controller_test.go b/pkg/controller/restore_finalizer_controller_test.go index 6fb5ba303..f07d2576c 100644 --- a/pkg/controller/restore_finalizer_controller_test.go +++ b/pkg/controller/restore_finalizer_controller_test.go @@ -743,6 +743,83 @@ func TestRestoreOperationList(t *testing.T) { } } +func TestHasVolumeGroupSnapshotHandles(t *testing.T) { + tests := []struct { + name string + volumeInfo []*volume.BackupVolumeInfo + expected bool + }{ + { + name: "nil volumeInfo", + volumeInfo: nil, + expected: false, + }, + { + name: "empty volumeInfo", + volumeInfo: []*volume.BackupVolumeInfo{}, + expected: false, + }, + { + name: "no CSISnapshotInfo", + volumeInfo: []*volume.BackupVolumeInfo{ + {PVCName: "pvc-1", BackupMethod: volume.NativeSnapshot}, + }, + expected: false, + }, + { + name: "CSISnapshotInfo with empty VolumeGroupSnapshotHandle", + volumeInfo: []*volume.BackupVolumeInfo{ + { + PVCName: "pvc-1", + BackupMethod: volume.CSISnapshot, + CSISnapshotInfo: &volume.CSISnapshotInfo{ + SnapshotHandle: "snap-1", + }, + }, + }, + expected: false, + }, + { + name: "one volume with VolumeGroupSnapshotHandle", + volumeInfo: []*volume.BackupVolumeInfo{ + { + PVCName: "pvc-1", + BackupMethod: volume.CSISnapshot, + CSISnapshotInfo: &volume.CSISnapshotInfo{ + SnapshotHandle: "snap-1", + VolumeGroupSnapshotHandle: "vgs-handle-1", + }, + }, + }, + expected: true, + }, + { + name: "mixed volumes only one with VolumeGroupSnapshotHandle", + volumeInfo: []*volume.BackupVolumeInfo{ + {PVCName: "pvc-1", BackupMethod: volume.NativeSnapshot}, + { + PVCName: "pvc-2", + BackupMethod: volume.CSISnapshot, + CSISnapshotInfo: &volume.CSISnapshotInfo{ + SnapshotHandle: "snap-2", + VolumeGroupSnapshotHandle: "vgs-handle-2", + }, + }, + }, + expected: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := &finalizerContext{ + volumeInfo: tc.volumeInfo, + } + assert.Equal(t, tc.expected, ctx.hasVolumeGroupSnapshotHandles()) + }) + } +} + func TestCleanupStubVGSC(t *testing.T) { snapshotHandle1 := "snap-handle-1" snapshotHandle2 := "snap-handle-2"