diff --git a/pkg/backup/actions/csi/pvc_action.go b/pkg/backup/actions/csi/pvc_action.go index ffe31d90ac..3766fc9752 100644 --- a/pkg/backup/actions/csi/pvc_action.go +++ b/pkg/backup/actions/csi/pvc_action.go @@ -35,6 +35,8 @@ import ( _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" crclient "sigs.k8s.io/controller-runtime/pkg/client" + "k8s.io/apimachinery/pkg/api/resource" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" "github.com/vmware-tanzu/velero/pkg/client" @@ -252,6 +254,24 @@ func (p *pvcBackupItemAction) Execute( return nil, nil, "", nil, err } + // Wait until VS associated VSC snapshot handle created before + // continue.we later requier the vsc restore size + vsc, err := csi.WaitUntilVSCHandleIsReady( + vs, + p.crClient, + p.log, + true, + backup.Spec.CSISnapshotTimeout.Duration, + ) + if err != nil { + p.log.Errorf( + "Fail to wait VolumeSnapshot turned to ReadyToUse: %s", + err.Error(), + ) + csi.CleanupVolumeSnapshot(vs, p.crClient, p.log) + return nil, nil, "", nil, errors.WithStack(err) + } + labels := map[string]string{ velerov1api.VolumeSnapshotLabel: vs.Name, velerov1api.BackupNameLabel: backup.Name, @@ -279,24 +299,6 @@ func (p *pvcBackupItemAction) Execute( "Backup": backup.Name, }) - // Wait until VS associated VSC snapshot handle created before - // returning with the Async operation for data mover. - _, err := csi.WaitUntilVSCHandleIsReady( - vs, - p.crClient, - p.log, - true, - backup.Spec.CSISnapshotTimeout.Duration, - ) - if err != nil { - dataUploadLog.Errorf( - "Fail to wait VolumeSnapshot turned to ReadyToUse: %s", - err.Error(), - ) - csi.CleanupVolumeSnapshot(vs, p.crClient, p.log) - return nil, nil, "", nil, errors.WithStack(err) - } - dataUploadLog.Info("Starting data upload of backup") dataUpload, err := createDataUpload( @@ -340,6 +342,12 @@ func (p *pvcBackupItemAction) Execute( dataUploadLog.Info("DataUpload is submitted successfully.") } } else { + err = setPVCRequestSizeToVSRestoreSize(&pvc, p.crClient, vs.Name, vsc, p.log) + if err != nil { + p.log.Errorf("Failed to set PVC request size: %s", err.Error()) + return nil, nil, "", nil, errors.WithStack(err) + } + additionalItems = []velero.ResourceIdentifier{ { GroupResource: kuberesource.VolumeSnapshots, @@ -564,3 +572,61 @@ func NewPvcBackupItemAction(f client.Factory) plugincommon.HandlerInitializer { }, nil } } + +func setPVCRequestSizeToVSRestoreSize( + pvc *corev1api.PersistentVolumeClaim, + crClient crclient.Client, + volumeSnapshotName string, + vsc *snapshotv1api.VolumeSnapshotContent, + logger logrus.FieldLogger, +) error { + vs := new(snapshotv1api.VolumeSnapshot) + if err := crClient.Get(context.TODO(), + crclient.ObjectKey{ + Namespace: pvc.Namespace, + Name: volumeSnapshotName, + }, + vs, + ); err != nil { + return errors.Wrapf(err, "Failed to get Volumesnapshot %s/%s to restore PVC %s/%s", + pvc.Namespace, volumeSnapshotName, pvc.Namespace, pvc.Name) + } + + if vsc.Status.RestoreSize != nil { + logger.Debugf("Patching PVC request size to fit the volumesnapshot restore size %d", vsc.Status.RestoreSize) + restoreSize := *resource.NewQuantity(*vsc.Status.RestoreSize, resource.BinarySI) + + // It is possible that the volume provider allocated a larger + // capacity volume than what was requested in the backed up PVC. + // In this scenario the volumesnapshot of the PVC will end being + // larger than its requested storage size. Such a PVC, on restore + // as-is, will be stuck attempting to use a VolumeSnapshot as a + // data source for a PVC that is not large enough. + // To counter that, here we set the storage request on the PVC + // to the larger of the PVC's storage request and the size of the + // VolumeSnapshot + setPVCStorageResourceRequest(pvc, restoreSize, logger) + } + + return nil +} + +func setPVCStorageResourceRequest( + pvc *corev1api.PersistentVolumeClaim, + restoreSize resource.Quantity, + log logrus.FieldLogger, +) { + { + if pvc.Spec.Resources.Requests == nil { + pvc.Spec.Resources.Requests = corev1api.ResourceList{} + } + + storageReq, exists := pvc.Spec.Resources.Requests[corev1api.ResourceStorage] + if !exists || storageReq.Cmp(restoreSize) < 0 { + pvc.Spec.Resources.Requests[corev1api.ResourceStorage] = restoreSize + rs := pvc.Spec.Resources.Requests[corev1api.ResourceStorage] + log.Infof("Resetting storage requests for PVC %s/%s to %s", + pvc.Namespace, pvc.Name, rs.String()) + } + } +} diff --git a/pkg/backup/actions/csi/pvc_action_test.go b/pkg/backup/actions/csi/pvc_action_test.go index 5af6e2f331..9005b517d2 100644 --- a/pkg/backup/actions/csi/pvc_action_test.go +++ b/pkg/backup/actions/csi/pvc_action_test.go @@ -37,6 +37,8 @@ import ( "k8s.io/apimachinery/pkg/util/wait" crclient "sigs.k8s.io/controller-runtime/pkg/client" + "k8s.io/apimachinery/pkg/api/resource" + "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" @@ -44,7 +46,6 @@ import ( factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks" "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" - "github.com/vmware-tanzu/velero/pkg/util/boolptr" ) func TestExecute(t *testing.T) { @@ -130,7 +131,7 @@ func TestExecute(t *testing.T) { }, { name: "Test ResourcePolicy", - backup: builder.ForBackup("velero", "test").ResourcePolicies("resourcePolicy").SnapshotVolumes(false).Result(), + backup: builder.ForBackup("velero", "test").ResourcePolicies("resourcePolicy").SnapshotVolumes(false).CSISnapshotTimeout(time.Duration(3600) * time.Second).Result(), resourcePolicy: builder.ForConfigMap("velero", "resourcePolicy").Data("policy", "{\"version\":\"v1\", \"volumePolicies\":[{\"conditions\":{\"csi\": {}},\"action\":{\"type\":\"snapshot\"}}]}").Result(), pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").StorageClass("testSC").Phase(corev1.ClaimBound).Result(), pv: builder.ForPersistentVolume("testPV").CSI("hostpath", "testVolume").Result(), @@ -170,7 +171,7 @@ func TestExecute(t *testing.T) { pvcMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.pvc) require.NoError(t, err) - if boolptr.IsSetToTrue(tc.backup.Spec.SnapshotMoveData) == true { + if tc.pvc != nil { go func() { var vsList v1.VolumeSnapshotList err := wait.PollUntilContextTimeout(context.Background(), 1*time.Second, 10*time.Second, true, func(ctx context.Context) (bool, error) { @@ -412,3 +413,72 @@ func TestNewPVCBackupItemAction(t *testing.T) { _, err1 := plugin1(logger) require.NoError(t, err1) } + +func TestPVCRequestSize(t *testing.T) { + logger := logrus.New() + + tests := []struct { + name string + pvcInitial string // initial storage request on the PVC (e.g. "1Gi" or "3Gi") + restoreSize string // restore size set in VSC.Status.RestoreSize (e.g. "2Gi") + expectedSize string // expected storage request on the PVC after update + }{ + { + name: "UpdateRequired: PVC request is lower than restore size", + pvcInitial: "1Gi", + restoreSize: "2Gi", + expectedSize: "2Gi", + }, + { + name: "NoUpdateRequired: PVC request is larger than restore size", + pvcInitial: "3Gi", + restoreSize: "2Gi", + expectedSize: "3Gi", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + crClient := velerotest.NewFakeControllerRuntimeClient(t) + + // Create a PVC with the initial storage request. + pvc := builder.ForPersistentVolumeClaim("velero", "testPVC"). + VolumeName("testPV"). + StorageClass("testSC"). + Result() + pvc.Spec.Resources.Requests = corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse(tc.pvcInitial), + } + + // Create a VolumeSnapshot required for the lookup + vs := &snapshotv1api.VolumeSnapshot{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testVS", + Namespace: "velero", + }, + } + require.NoError(t, crClient.Create(context.Background(), vs)) + + // Create a VolumeSnapshotContent with restore size + rsQty := resource.MustParse(tc.restoreSize) + rsValue := rsQty.Value() + vsc := &snapshotv1api.VolumeSnapshotContent{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testVSC", + }, + Status: &snapshotv1api.VolumeSnapshotContentStatus{ + RestoreSize: &rsValue, + }, + } + + // Call the function under test + err := setPVCRequestSizeToVSRestoreSize(pvc, crClient, "testVS", vsc, logger) + require.NoError(t, err) + + // Verify that the PVC storage request is updated as expected. + updatedSize := pvc.Spec.Resources.Requests[corev1.ResourceStorage] + expected := resource.MustParse(tc.expectedSize) + require.Equal(t, 0, updatedSize.Cmp(expected), "PVC storage request should be %s", tc.expectedSize) + }) + } +} diff --git a/pkg/restore/actions/csi/pvc_action.go b/pkg/restore/actions/csi/pvc_action.go index dd7893ff06..5ddf60eab6 100644 --- a/pkg/restore/actions/csi/pvc_action.go +++ b/pkg/restore/actions/csi/pvc_action.go @@ -21,11 +21,9 @@ import ( "encoding/json" "fmt" - snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumesnapshot/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" corev1api "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" @@ -33,9 +31,12 @@ import ( utilrand "k8s.io/apimachinery/pkg/util/rand" crclient "sigs.k8s.io/controller-runtime/pkg/client" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumesnapshot/v1" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" "github.com/vmware-tanzu/velero/pkg/client" + kuberesource "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/label" plugincommon "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" "github.com/vmware-tanzu/velero/pkg/plugin/velero" @@ -65,39 +66,6 @@ func (p *pvcRestoreItemAction) AppliesTo() (velero.ResourceSelector, error) { }, nil } -func resetPVCSpec(pvc *corev1api.PersistentVolumeClaim, vsName string) { - // Restore operation for the PVC will use the VolumeSnapshot as the data source. - // So clear out the volume name, which is a ref to the PV - pvc.Spec.VolumeName = "" - dataSource := &corev1api.TypedLocalObjectReference{ - APIGroup: &snapshotv1api.SchemeGroupVersion.Group, - Kind: "VolumeSnapshot", - Name: vsName, - } - pvc.Spec.DataSource = dataSource - pvc.Spec.DataSourceRef = nil -} - -func setPVCStorageResourceRequest( - pvc *corev1api.PersistentVolumeClaim, - restoreSize resource.Quantity, - log logrus.FieldLogger, -) { - { - if pvc.Spec.Resources.Requests == nil { - pvc.Spec.Resources.Requests = corev1api.ResourceList{} - } - - storageReq, exists := pvc.Spec.Resources.Requests[corev1api.ResourceStorage] - if !exists || storageReq.Cmp(restoreSize) < 0 { - pvc.Spec.Resources.Requests[corev1api.ResourceStorage] = restoreSize - rs := pvc.Spec.Resources.Requests[corev1api.ResourceStorage] - log.Infof("Resetting storage requests for PVC %s/%s to %s", - pvc.Namespace, pvc.Name, rs.String()) - } - } -} - // Execute modifies the PVC's spec to use the VolumeSnapshot object as the // data source ensuring that the newly provisioned volume can be pre-populated // with data from the VolumeSnapshot. @@ -139,6 +107,7 @@ func (p *pvcRestoreItemAction) Execute( operationID := "" + additionalItems := []velero.ResourceIdentifier{} if boolptr.IsSetToFalse(input.Restore.Spec.RestorePVs) { logger.Info("Restore did not request for PVs to be restored from snapshot") pvc.Spec.VolumeName = "" @@ -186,24 +155,27 @@ func (p *pvcRestoreItemAction) Execute( logger.Infof("DataDownload %s/%s is created successfully.", dataDownload.Namespace, dataDownload.Name) } else { - targetVSName := "" - if vsName, nameOK := pvcFromBackup.Annotations[velerov1api.VolumeSnapshotLabel]; nameOK { - targetVSName = util.GenerateSha256FromRestoreUIDAndVsName(string(input.Restore.UID), vsName) - } else { - logger.Info("Skipping PVCRestoreItemAction for PVC,", - "PVC does not have a CSI VolumeSnapshot.") - // Make no change in the input PVC. + //CSI restore + vsName, nameOK := pvcFromBackup.Annotations[velerov1api.VolumeSnapshotLabel] + if !nameOK { + logger.Info("Skipping PVCRestoreItemAction for PVC, PVC does not have a CSI VolumeSnapshot.") return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: input.Item, }, nil } - if err := restoreFromVolumeSnapshot( - &pvc, newNamespace, p.crClient, targetVSName, logger, - ); err != nil { - logger.Errorf("Failed to restore PVC from VolumeSnapshot.") - return nil, errors.WithStack(err) - } + //To avoid confilcs, vs and vsc get a new uniq name based in restore UID + // and vs name old name + newVSName := util.GenerateSha256FromRestoreUIDAndVsName(string(input.Restore.UID), vsName) + + p.log.Debugf("Setting PVC source to VolumeSnapshot new name: %s", newVSName) + resetPVCSourceToVolumeSnapshot(&pvc, newVSName) + + additionalItems = append(additionalItems, velero.ResourceIdentifier{ + GroupResource: kuberesource.VolumeSnapshots, + Name: vsName, + Namespace: pvc.Namespace, + }) } } @@ -214,11 +186,25 @@ func (p *pvcRestoreItemAction) Execute( logger.Info("Returning from PVCRestoreItemAction for PVC") return &velero.RestoreItemActionExecuteOutput{ - UpdatedItem: &unstructured.Unstructured{Object: pvcMap}, - OperationID: operationID, + UpdatedItem: &unstructured.Unstructured{Object: pvcMap}, + OperationID: operationID, + AdditionalItems: additionalItems, }, nil } +func resetPVCSourceToVolumeSnapshot(pvc *corev1api.PersistentVolumeClaim, vsName string) { + // Restore operation for the PVC will use the VolumeSnapshot as the data source. + // So clear out the volume name, which is a ref to the PV + pvc.Spec.VolumeName = "" + dataSource := &corev1api.TypedLocalObjectReference{ + APIGroup: &snapshotv1api.SchemeGroupVersion.Group, + Kind: "VolumeSnapshot", + Name: vsName, + } + pvc.Spec.DataSource = dataSource + pvc.Spec.DataSourceRef = nil +} + func (p *pvcRestoreItemAction) Name() string { return "PVCRestoreItemAction" } @@ -456,50 +442,6 @@ func newDataDownload( return dataDownload } -func restoreFromVolumeSnapshot( - pvc *corev1api.PersistentVolumeClaim, - newNamespace string, - crClient crclient.Client, - volumeSnapshotName string, - logger logrus.FieldLogger, -) error { - vs := new(snapshotv1api.VolumeSnapshot) - if err := crClient.Get(context.TODO(), - crclient.ObjectKey{ - Namespace: newNamespace, - Name: volumeSnapshotName, - }, - vs, - ); err != nil { - return errors.Wrapf(err, "Failed to get Volumesnapshot %s/%s to restore PVC %s/%s", - newNamespace, volumeSnapshotName, newNamespace, pvc.Name) - } - - if _, exists := vs.Annotations[velerov1api.VolumeSnapshotRestoreSize]; exists { - restoreSize, err := resource.ParseQuantity( - vs.Annotations[velerov1api.VolumeSnapshotRestoreSize]) - if err != nil { - return errors.Wrapf(err, - "Failed to parse %s from annotation on Volumesnapshot %s/%s into restore size", - vs.Annotations[velerov1api.VolumeSnapshotRestoreSize], vs.Namespace, vs.Name) - } - // It is possible that the volume provider allocated a larger - // capacity volume than what was requested in the backed up PVC. - // In this scenario the volumesnapshot of the PVC will end being - // larger than its requested storage size. Such a PVC, on restore - // as-is, will be stuck attempting to use a VolumeSnapshot as a - // data source for a PVC that is not large enough. - // To counter that, here we set the storage request on the PVC - // to the larger of the PVC's storage request and the size of the - // VolumeSnapshot - setPVCStorageResourceRequest(pvc, restoreSize, logger) - } - - resetPVCSpec(pvc, volumeSnapshotName) - - return nil -} - func restoreFromDataUploadResult( ctx context.Context, restore *velerov1api.Restore, diff --git a/pkg/restore/actions/csi/pvc_action_test.go b/pkg/restore/actions/csi/pvc_action_test.go index 224f188595..2a297134d9 100644 --- a/pkg/restore/actions/csi/pvc_action_test.go +++ b/pkg/restore/actions/csi/pvc_action_test.go @@ -155,7 +155,7 @@ func TestResetPVCSpec(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { before := tc.pvc.DeepCopy() - resetPVCSpec(&tc.pvc, tc.vsName) + resetPVCSourceToVolumeSnapshot(&tc.pvc, tc.vsName) assert.Equalf(t, tc.pvc.Name, before.Name, "unexpected change to Object.Name, Want: %s; Got %s", before.Name, tc.pvc.Name) assert.Equalf(t, tc.pvc.Namespace, before.Namespace, "unexpected change to Object.Namespace, Want: %s; Got %s", before.Namespace, tc.pvc.Namespace) @@ -171,101 +171,6 @@ func TestResetPVCSpec(t *testing.T) { } } -func TestResetPVCResourceRequest(t *testing.T) { - var storageReq50Mi, storageReq1Gi, cpuQty resource.Quantity - - storageReq50Mi, err := resource.ParseQuantity("50Mi") - assert.NoError(t, err) - storageReq1Gi, err = resource.ParseQuantity("1Gi") - assert.NoError(t, err) - cpuQty, err = resource.ParseQuantity("100m") - assert.NoError(t, err) - - testCases := []struct { - name string - pvc corev1api.PersistentVolumeClaim - restoreSize resource.Quantity - expectedStorageRequestQty string - }{ - { - name: "should set storage resource request from volumesnapshot, pvc has nil resource requests", - pvc: corev1api.PersistentVolumeClaim{ - Spec: corev1api.PersistentVolumeClaimSpec{ - Resources: corev1api.VolumeResourceRequirements{ - Requests: nil, - }, - }, - }, - restoreSize: storageReq50Mi, - expectedStorageRequestQty: "50Mi", - }, - { - name: "should set storage resource request from volumesnapshot, pvc has empty resource requests", - pvc: corev1api.PersistentVolumeClaim{ - Spec: corev1api.PersistentVolumeClaimSpec{ - Resources: corev1api.VolumeResourceRequirements{ - Requests: corev1api.ResourceList{}, - }, - }, - }, - restoreSize: storageReq50Mi, - expectedStorageRequestQty: "50Mi", - }, - { - name: "should merge resource requests from volumesnapshot into pvc with no storage resource requests", - pvc: corev1api.PersistentVolumeClaim{ - Spec: corev1api.PersistentVolumeClaimSpec{ - Resources: corev1api.VolumeResourceRequirements{ - Requests: corev1api.ResourceList{ - corev1api.ResourceCPU: cpuQty, - }, - }, - }, - }, - restoreSize: storageReq50Mi, - expectedStorageRequestQty: "50Mi", - }, - { - name: "should set storage resource request from volumesnapshot, pvc requests less storage", - pvc: corev1api.PersistentVolumeClaim{ - Spec: corev1api.PersistentVolumeClaimSpec{ - Resources: corev1api.VolumeResourceRequirements{ - Requests: corev1api.ResourceList{ - corev1api.ResourceStorage: storageReq50Mi, - }, - }, - }, - }, - restoreSize: storageReq1Gi, - expectedStorageRequestQty: "1Gi", - }, - { - name: "should not set storage resource request from volumesnapshot, pvc requests more storage", - pvc: corev1api.PersistentVolumeClaim{ - Spec: corev1api.PersistentVolumeClaimSpec{ - Resources: corev1api.VolumeResourceRequirements{ - Requests: corev1api.ResourceList{ - corev1api.ResourceStorage: storageReq1Gi, - }, - }, - }, - }, - restoreSize: storageReq50Mi, - expectedStorageRequestQty: "1Gi", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - log := logrus.New().WithField("unit-test", tc.name) - setPVCStorageResourceRequest(&tc.pvc, tc.restoreSize, log) - expected, err := resource.ParseQuantity(tc.expectedStorageRequestQty) - assert.NoError(t, err) - assert.Equal(t, expected, tc.pvc.Spec.Resources.Requests[corev1api.ResourceStorage]) - }) - } -} - func TestProgress(t *testing.T) { currentTime := time.Now() tests := []struct { @@ -486,13 +391,6 @@ func TestExecute(t *testing.T) { pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").Result(), expectedErr: "fail to get backup for restore: backups.velero.io \"testBackup\" not found", }, - { - name: "VolumeSnapshot cannot be found", - backup: builder.ForBackup("velero", "testBackup").Result(), - restore: builder.ForRestore("velero", "testRestore").ObjectMeta(builder.WithUID("restoreUID")).Backup("testBackup").Result(), - pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").ObjectMeta(builder.WithAnnotations(velerov1api.VolumeSnapshotLabel, "vsName")).Result(), - expectedErr: fmt.Sprintf("Failed to get Volumesnapshot velero/%s to restore PVC velero/testPVC: volumesnapshots.snapshot.storage.k8s.io \"%s\" not found", vsName, vsName), - }, { name: "Restore from VolumeSnapshot", backup: builder.ForBackup("velero", "testBackup").Result(), diff --git a/pkg/restore/actions/csi/volumesnapshot_action.go b/pkg/restore/actions/csi/volumesnapshot_action.go index d9a162d771..8ce68a573c 100644 --- a/pkg/restore/actions/csi/volumesnapshot_action.go +++ b/pkg/restore/actions/csi/volumesnapshot_action.go @@ -24,6 +24,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" crclient "sigs.k8s.io/controller-runtime/pkg/client" + "k8s.io/apimachinery/pkg/runtime/schema" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/client" plugincommon "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" @@ -106,12 +108,20 @@ func (p *volumeSnapshotRestoreItemAction) Execute( return nil, errors.WithStack(err) } - p.log.Infof(`Returning from VolumeSnapshotRestoreItemAction with - no additionalItems`) + // Adding VS's VolumeSnapshotContent + additionalItem := velero.ResourceIdentifier{ + GroupResource: schema.GroupResource{ + Group: "snapshot.storage.k8s.io", + Resource: "volumesnapshotcontents", + }, + Name: *vsFromBackup.Status.BoundVolumeSnapshotContentName, + } + + p.log.Infof("Returning from VolumeSnapshotRestoreItemAction with VolumeSnapshotContent as an additional item") return &velero.RestoreItemActionExecuteOutput{ UpdatedItem: &unstructured.Unstructured{Object: vsMap}, - AdditionalItems: []velero.ResourceIdentifier{}, + AdditionalItems: []velero.ResourceIdentifier{additionalItem}, }, nil } diff --git a/pkg/restore/actions/csi/volumesnapshot_action_test.go b/pkg/restore/actions/csi/volumesnapshot_action_test.go index e84079beea..61da89cf89 100644 --- a/pkg/restore/actions/csi/volumesnapshot_action_test.go +++ b/pkg/restore/actions/csi/volumesnapshot_action_test.go @@ -117,12 +117,6 @@ func TestVSExecute(t *testing.T) { restore: builder.ForRestore("velero", "restore").RestorePVs(false).Result(), expectErr: false, }, - { - name: "VS doesn't have VSC in status", - vs: builder.ForVolumeSnapshot("ns", "name").ObjectMeta(builder.WithAnnotations("1", "1")).Status().Result(), - restore: builder.ForRestore("velero", "restore").NamespaceMappings("ns", "newNS").Result(), - expectErr: true, - }, { name: "Normal case, VSC should be created", vs: builder.ForVolumeSnapshot("ns", "vsName").ObjectMeta(