Merge branch 'main' into feat/global-policy

This commit is contained in:
Chlins Zhang
2026-06-25 15:04:28 +08:00
committed by GitHub
8 changed files with 485 additions and 46 deletions
+1
View File
@@ -0,0 +1 @@
Recall the old rebind volume way for the case that volumeMode is not changed; and use the new way for volumeMode changed case
+1
View File
@@ -0,0 +1 @@
Decide restorePVC volumeMode by data mover type for block data mover
@@ -949,6 +949,7 @@ func (r *DataDownloadReconciler) setupExposeParam(dd *velerov2alpha1api.DataDown
PriorityClassName: r.dataMovePriorityClass,
RestoreSize: dd.Spec.SnapshotSize,
CacheVolume: cacheVolume,
DataMover: dd.Spec.DataMover,
}, nil
}
@@ -1429,6 +1429,7 @@ func TestDataDownloadSetupExposeParam(t *testing.T) {
// Core fields
assert.Equal(t, baseDataDownload.Spec.TargetVolume.PVC, got.TargetPVCName)
assert.Equal(t, baseDataDownload.Spec.TargetVolume.Namespace, got.TargetNamespace)
assert.Equal(t, baseDataDownload.Spec.DataMover, got.DataMover)
// Labels and Annotations
assert.Equal(t, tt.want.labels, got.HostingPodLabels)
+133 -23
View File
@@ -31,6 +31,7 @@ import (
"k8s.io/client-go/kubernetes"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/vmware-tanzu/velero/pkg/datamover"
"github.com/vmware-tanzu/velero/pkg/nodeagent"
velerotypes "github.com/vmware-tanzu/velero/pkg/types"
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
@@ -80,6 +81,9 @@ type GenericRestoreExposeParam struct {
// CacheVolume specifies the info for cache volumes
CacheVolume *CacheConfigs
// DataMover is the data mover type, e.g., velero-fs, velero-block
DataMover string
}
// GenericRestoreRebindVolumeParam define the input param for Generic Restore Rebind Volume
@@ -192,10 +196,23 @@ func (e *genericRestoreExposer) Expose(ctx context.Context, ownerObject corev1ap
}
}
restorePVC, err := e.createRestorePVC(ctx, ownerObject, targetPVC, selectedNode, param.DataMover)
if err != nil {
return errors.Wrap(err, "error to create restore pvc")
}
curLog.WithField("pvc name", restorePVC.Name).Info("Restore PVC is created")
defer func() {
if err != nil {
kube.DeletePVAndPVCIfAny(ctx, e.kubeClient.CoreV1(), restorePVC.Name, restorePVC.Namespace, 0, curLog)
}
}()
restorePod, err := e.createRestorePod(
ctx,
ownerObject,
targetPVC,
restorePVC,
param.OperationTimeout,
param.HostingPodLabels,
param.HostingPodAnnotations,
@@ -219,19 +236,6 @@ func (e *genericRestoreExposer) Expose(ctx context.Context, ownerObject corev1ap
}
}()
restorePVC, err := e.createRestorePVC(ctx, ownerObject, targetPVC, selectedNode)
if err != nil {
return errors.Wrap(err, "error to create restore pvc")
}
curLog.WithField("pvc name", restorePVC.Name).Info("Restore PVC is created")
defer func() {
if err != nil {
kube.DeletePVAndPVCIfAny(ctx, e.kubeClient.CoreV1(), restorePVC.Name, restorePVC.Namespace, 0, curLog)
}
}()
return nil
}
@@ -396,7 +400,6 @@ func (e *genericRestoreExposer) CleanUp(ctx context.Context, ownerObject corev1a
}
func (e *genericRestoreExposer) RebindVolume(ctx context.Context, ownerObject corev1api.ObjectReference, param GenericRestoreRebindVolumeParam) error {
restorePodName := ownerObject.Name
restorePVCName := ownerObject.Name
curLog := e.log.WithFields(logrus.Fields{
@@ -415,6 +418,17 @@ func (e *genericRestoreExposer) RebindVolume(ctx context.Context, ownerObject co
return errors.Wrapf(err, "error to get PV from restore PVC %s", restorePVCName)
}
if kube.GetVolumeModeByPVC(targetPVC) != kube.GetVolumeModeByPV(restorePV) {
return e.rebindVolumeChangeMode(ctx, ownerObject, param, targetPVC, restorePV, curLog)
} else {
return e.rebindVolumeSameMode(ctx, ownerObject, param, targetPVC, restorePV, curLog)
}
}
func (e *genericRestoreExposer) rebindVolumeChangeMode(ctx context.Context, ownerObject corev1api.ObjectReference, param GenericRestoreRebindVolumeParam, targetPVC *corev1api.PersistentVolumeClaim, restorePV *corev1api.PersistentVolume, curLog logrus.FieldLogger) error {
restorePodName := ownerObject.Name
restorePVCName := ownerObject.Name
orgReclaim := restorePV.Spec.PersistentVolumeReclaimPolicy
curLog.WithField("restore PV", restorePV.Name).Info("Restore PV is retrieved")
@@ -440,6 +454,10 @@ func (e *genericRestoreExposer) RebindVolume(ctx context.Context, ownerObject co
}
}()
if retained != nil {
restorePV = retained
}
err = kube.EnsureDeletePod(ctx, e.kubeClient.CoreV1(), restorePodName, ownerObject.Namespace, param.OperationTimeout)
if err != nil {
return errors.Wrapf(err, "error to delete restore pod %s", restorePodName)
@@ -452,26 +470,26 @@ func (e *genericRestoreExposer) RebindVolume(ctx context.Context, ownerObject co
curLog.WithField("restore PVC", restorePVCName).Info("Restore PVC is deleted")
err = kube.WaitVolumeDetached(ctx, e.kubeClient.StorageV1(), retained.Name, param.OperationTimeout)
err = kube.WaitVolumeDetached(ctx, e.kubeClient.StorageV1(), restorePV.Name, param.OperationTimeout)
if err != nil {
return errors.Wrapf(err, "error waiting for retained PV %s to detach", retained.Name)
return errors.Wrapf(err, "error waiting for restore PV %s to detach", restorePV.Name)
}
curLog.WithField("retained PV", retained.Name).Info("Retained PV is detached")
curLog.WithField("restore PV", restorePV.Name).Info("Restore PV is detached")
rebindPV, err = kube.RebindPV(ctx, e.kubeClient.CoreV1(), uuid.NewString(), retained, targetPVC, orgReclaim, param.TargetFSType)
rebindPV, err = kube.RebindPV(ctx, e.kubeClient.CoreV1(), uuid.NewString(), restorePV, targetPVC, orgReclaim, param.TargetFSType)
if err != nil {
return errors.Wrapf(err, "error rebinding PV for target PVC %s", param.TargetPVCName)
}
curLog.WithField("rebind PV", rebindPV.Name).Info("Rebind PV is created")
err = kube.EnsureDeletePV(ctx, e.kubeClient.CoreV1(), retained.Name, param.OperationTimeout)
err = kube.EnsureDeletePV(ctx, e.kubeClient.CoreV1(), restorePV.Name, param.OperationTimeout)
if err != nil {
return errors.Wrapf(err, "error deleting PV %s", retained.Name)
return errors.Wrapf(err, "error deleting restore PV %s", restorePV.Name)
}
curLog.WithField("retained PV", retained.Name).Info("Retained PV is deleted")
curLog.WithField("restore PV", restorePV.Name).Info("Restore PV is deleted")
retained = nil
@@ -494,6 +512,90 @@ func (e *genericRestoreExposer) RebindVolume(ctx context.Context, ownerObject co
return nil
}
func (e *genericRestoreExposer) rebindVolumeSameMode(ctx context.Context, ownerObject corev1api.ObjectReference, param GenericRestoreRebindVolumeParam, targetPVC *corev1api.PersistentVolumeClaim, restorePV *corev1api.PersistentVolume, curLog logrus.FieldLogger) error {
restorePodName := ownerObject.Name
restorePVCName := ownerObject.Name
orgReclaim := restorePV.Spec.PersistentVolumeReclaimPolicy
curLog.WithField("restore PV", restorePV.Name).Info("Restore PV is retrieved")
retained, err := kube.SetPVReclaimPolicy(ctx, e.kubeClient.CoreV1(), restorePV, corev1api.PersistentVolumeReclaimRetain)
if err != nil {
return errors.Wrapf(err, "error to retain PV %s", restorePV.Name)
}
curLog.WithField("restore PV", restorePV.Name).WithField("retained", (retained != nil)).Info("Restore PV is retained")
defer func() {
if retained != nil {
curLog.WithField("retained PV", retained.Name).Info("Deleting retained PV on error")
kube.DeletePVIfAny(ctx, e.kubeClient.CoreV1(), retained.Name, curLog)
}
}()
if retained != nil {
restorePV = retained
}
err = kube.EnsureDeletePod(ctx, e.kubeClient.CoreV1(), restorePodName, ownerObject.Namespace, param.OperationTimeout)
if err != nil {
return errors.Wrapf(err, "error to delete restore pod %s", restorePodName)
}
err = kube.EnsureDeletePVC(ctx, e.kubeClient.CoreV1(), restorePVCName, ownerObject.Namespace, param.OperationTimeout)
if err != nil {
return errors.Wrapf(err, "error to delete restore PVC %s", restorePVCName)
}
curLog.WithField("restore PVC", restorePVCName).Info("Restore PVC is deleted")
err = kube.WaitVolumeDetached(ctx, e.kubeClient.StorageV1(), restorePV.Name, param.OperationTimeout)
if err != nil {
return errors.Wrapf(err, "error waiting for restore PV %s to detach", restorePV.Name)
}
curLog.WithField("restore PV", restorePV.Name).Info("Restore PV is detached")
_, err = kube.RebindPVC(ctx, e.kubeClient.CoreV1(), targetPVC, restorePV.Name)
if err != nil {
return errors.Wrapf(err, "error to rebind target PVC %s/%s to %s", targetPVC.Namespace, targetPVC.Name, restorePV.Name)
}
curLog.WithField("tartet PVC", fmt.Sprintf("%s/%s", targetPVC.Namespace, targetPVC.Name)).WithField("restore PV", restorePV.Name).Info("Target PVC is rebound to restore PV")
var matchLabel map[string]string
if targetPVC.Spec.Selector != nil {
matchLabel = targetPVC.Spec.Selector.MatchLabels
}
restorePVName := restorePV.Name
restorePV, err = kube.ResetPVBinding(ctx, e.kubeClient.CoreV1(), restorePV, matchLabel, targetPVC)
if err != nil {
return errors.Wrapf(err, "error to reset binding info for restore PV %s", restorePVName)
}
curLog.WithField("restore PV", restorePV.Name).Info("Restore PV is rebound")
restorePV, err = kube.WaitPVBound(ctx, e.kubeClient.CoreV1(), restorePV.Name, targetPVC.Name, targetPVC.Namespace, param.OperationTimeout)
if err != nil {
return errors.Wrapf(err, "error to wait restore PV bound, restore PV %s", restorePVName)
}
curLog.WithField("restore PV", restorePV.Name).Info("Restore PV is ready")
retained = nil
_, err = kube.SetPVReclaimPolicy(ctx, e.kubeClient.CoreV1(), restorePV, orgReclaim)
if err != nil {
curLog.WithField("restore PV", restorePV.Name).WithError(err).Warn("Restore PV's reclaim policy is not restored")
} else {
curLog.WithField("restore PV", restorePV.Name).Info("Restore PV's reclaim policy is restored")
}
return nil
}
func (e *genericRestoreExposer) createRestorePod(
ctx context.Context,
ownerObject corev1api.ObjectReference,
@@ -704,7 +806,7 @@ func (e *genericRestoreExposer) createRestorePod(
return e.kubeClient.CoreV1().Pods(ownerObject.Namespace).Create(ctx, pod, metav1.CreateOptions{})
}
func (e *genericRestoreExposer) createRestorePVC(ctx context.Context, ownerObject corev1api.ObjectReference, targetPVC *corev1api.PersistentVolumeClaim, selectedNode string) (*corev1api.PersistentVolumeClaim, error) {
func (e *genericRestoreExposer) createRestorePVC(ctx context.Context, ownerObject corev1api.ObjectReference, targetPVC *corev1api.PersistentVolumeClaim, selectedNode string, dataMover string) (*corev1api.PersistentVolumeClaim, error) {
restorePVCName := ownerObject.Name
pvcObj := &corev1api.PersistentVolumeClaim{
@@ -737,5 +839,13 @@ func (e *genericRestoreExposer) createRestorePVC(ctx context.Context, ownerObjec
}
}
if dataMover == datamover.DataMoverTypeVeleroBlock {
if pvcObj.Spec.VolumeMode == nil {
pvcObj.Spec.VolumeMode = new(corev1api.PersistentVolumeMode)
}
*pvcObj.Spec.VolumeMode = corev1api.PersistentVolumeBlock
}
return e.kubeClient.CoreV1().PersistentVolumeClaims(pvcObj.Namespace).Create(ctx, pvcObj, metav1.CreateOptions{})
}
+240 -23
View File
@@ -33,6 +33,7 @@ import (
clientTesting "k8s.io/client-go/testing"
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/datamover"
velerotest "github.com/vmware-tanzu/velero/pkg/test"
"github.com/vmware-tanzu/velero/pkg/util/kube"
)
@@ -61,6 +62,18 @@ func TestRestoreExpose(t *testing.T) {
},
}
modeFilesystem := corev1api.PersistentVolumeFilesystem
targetPVCObjWithVolumeMode := &corev1api.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Namespace: "fake-ns",
Name: "fake-target-pvc",
},
Spec: corev1api.PersistentVolumeClaimSpec{
StorageClassName: &scName,
VolumeMode: &modeFilesystem,
},
}
storageClass := &storagev1api.StorageClass{
ObjectMeta: metav1.ObjectMeta{
Name: "fake-sc",
@@ -107,6 +120,7 @@ func TestRestoreExpose(t *testing.T) {
targetNamespace string
kubeReactors []reactor
cacheVolume *CacheConfigs
dataMover string
expectBackupPod bool
expectBackupPVC bool
expectCachePVC bool
@@ -236,6 +250,34 @@ func TestRestoreExpose(t *testing.T) {
expectBackupPVC: true,
expectCachePVC: true,
},
{
name: "succeed with velero-block data mover",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObj,
daemonSet,
storageClass,
},
dataMover: datamover.DataMoverTypeVeleroBlock,
expectBackupPod: true,
expectBackupPVC: true,
},
{
name: "succeed with velero-block data mover and existing volume mode",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObjWithVolumeMode,
daemonSet,
storageClass,
},
dataMover: datamover.DataMoverTypeVeleroBlock,
expectBackupPod: true,
expectBackupPVC: true,
},
}
for _, test := range tests {
@@ -273,6 +315,7 @@ func TestRestoreExpose(t *testing.T) {
ExposeTimeout: time.Millisecond,
LoadAffinity: nil,
CacheVolume: test.cacheVolume,
DataMover: test.dataMover,
},
)
@@ -289,9 +332,13 @@ func TestRestoreExpose(t *testing.T) {
require.True(t, apierrors.IsNotFound(err))
}
_, err = exposer.kubeClient.CoreV1().PersistentVolumeClaims(ownerObject.Namespace).Get(t.Context(), ownerObject.Name, metav1.GetOptions{})
pvc, err := exposer.kubeClient.CoreV1().PersistentVolumeClaims(ownerObject.Namespace).Get(t.Context(), ownerObject.Name, metav1.GetOptions{})
if test.expectBackupPVC {
require.NoError(t, err)
if test.dataMover == datamover.DataMoverTypeVeleroBlock {
require.NotNil(t, pvc.Spec.VolumeMode)
require.Equal(t, corev1api.PersistentVolumeBlock, *pvc.Spec.VolumeMode)
}
} else {
require.True(t, apierrors.IsNotFound(err))
}
@@ -319,11 +366,27 @@ func TestRebindVolume(t *testing.T) {
},
}
targetPVCObj := &corev1api.PersistentVolumeClaim{
modeFilesystem := corev1api.PersistentVolumeFilesystem
modeBlock := corev1api.PersistentVolumeBlock
targetPVCObjChangeMode := &corev1api.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Namespace: "fake-ns",
Name: "fake-target-pvc",
},
Spec: corev1api.PersistentVolumeClaimSpec{
VolumeMode: &modeBlock,
},
}
targetPVCObjSameMode := &corev1api.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Namespace: "fake-ns",
Name: "fake-target-pvc",
},
Spec: corev1api.PersistentVolumeClaimSpec{
VolumeMode: &modeFilesystem,
},
}
restorePVCObj := &corev1api.PersistentVolumeClaim{
@@ -342,6 +405,7 @@ func TestRebindVolume(t *testing.T) {
},
Spec: corev1api.PersistentVolumeSpec{
PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete,
VolumeMode: &modeFilesystem,
},
}
@@ -374,17 +438,17 @@ func TestRebindVolume(t *testing.T) {
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObj,
targetPVCObjSameMode,
},
err: "error to get PV from restore PVC fake-restore: error to wait for rediness of PVC: error to get pvc velero/fake-restore: persistentvolumeclaims \"fake-restore\" not found",
},
{
name: "retain target pv fail",
name: "[change mode] retain target pv fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObj,
targetPVCObjChangeMode,
restorePVCObj,
restorePVObj,
},
@@ -400,12 +464,12 @@ func TestRebindVolume(t *testing.T) {
err: "error to retain PV fake-restore-pv: error patching PV: fake-patch-error",
},
{
name: "delete restore pod fail",
name: "[change mode] delete restore pod fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObj,
targetPVCObjChangeMode,
restorePVCObj,
restorePVObj,
restorePod,
@@ -422,12 +486,12 @@ func TestRebindVolume(t *testing.T) {
err: "error to delete restore pod fake-restore: error to delete pod fake-restore: fake-delete-error",
},
{
name: "delete restore pvc fail",
name: "[change mode] delete restore pvc fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObj,
targetPVCObjChangeMode,
restorePVCObj,
restorePVObj,
restorePod,
@@ -444,12 +508,12 @@ func TestRebindVolume(t *testing.T) {
err: "error to delete restore PVC fake-restore: error to delete pvc fake-restore: fake-delete-error",
},
{
name: "wait volume detached fail",
name: "[change mode] wait volume detached fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObj,
targetPVCObjChangeMode,
restorePVCObj,
restorePVObj,
restorePod,
@@ -463,15 +527,15 @@ func TestRebindVolume(t *testing.T) {
},
},
},
err: "error waiting for retained PV fake-restore-pv to detach: error listing volumeattachment: error listing volumeattachment: fake-list-error",
err: "error waiting for restore PV fake-restore-pv to detach: error listing volumeattachment: error listing volumeattachment: fake-list-error",
},
{
name: "rebind pv fail",
name: "[change mode] rebind pv fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObj,
targetPVCObjChangeMode,
restorePVCObj,
restorePVObj,
restorePod,
@@ -488,12 +552,12 @@ func TestRebindVolume(t *testing.T) {
err: "error rebinding PV for target PVC fake-target-pvc: fake-create-error",
},
{
name: "delete retained pv fail",
name: "[change mode] delete retained pv fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObj,
targetPVCObjChangeMode,
restorePVCObj,
restorePVObj,
restorePod,
@@ -503,19 +567,23 @@ func TestRebindVolume(t *testing.T) {
verb: "delete",
resource: "persistentvolumes",
reactorFunc: func(action clientTesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, errors.New("fake-delete-error")
// we want it to fail on the PV deletion but not the pod/pvc deletions
if action.(clientTesting.DeleteAction).GetName() == "fake-restore-pv" {
return true, nil, errors.New("fake-delete-error")
}
return false, nil, nil
},
},
},
err: "error deleting PV fake-restore-pv: error to delete pv fake-restore-pv: fake-delete-error",
err: "error deleting restore PV fake-restore-pv: error to delete pv fake-restore-pv: fake-delete-error",
},
{
name: "rebind target pvc fail",
name: "[change mode] rebind target pvc fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObj,
targetPVCObjChangeMode,
restorePVCObj,
restorePVObj,
restorePod,
@@ -532,18 +600,168 @@ func TestRebindVolume(t *testing.T) {
err: "error to rebind target PVC fake-ns/fake-target-pvc to",
},
{
name: "wait rebind PV ready fail",
name: "[change mode] wait rebind PV ready fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObj,
targetPVCObjChangeMode,
restorePVCObj,
restorePVObj,
restorePod,
},
err: "error to wait rebind PV ready, rebind PV",
},
{
name: "[same mode] retain target pv fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObjSameMode,
restorePVCObj,
restorePVObj,
},
kubeReactors: []reactor{
{
verb: "patch",
resource: "persistentvolumes",
reactorFunc: func(action clientTesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, errors.New("fake-patch-error")
},
},
},
err: "error to retain PV fake-restore-pv: error patching PV: fake-patch-error",
},
{
name: "[same mode] delete restore pod fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObjSameMode,
restorePVCObj,
restorePVObj,
restorePod,
},
kubeReactors: []reactor{
{
verb: "delete",
resource: "pods",
reactorFunc: func(action clientTesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, errors.New("fake-delete-error")
},
},
},
err: "error to delete restore pod fake-restore: error to delete pod fake-restore: fake-delete-error",
},
{
name: "[same mode] delete restore pvc fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObjSameMode,
restorePVCObj,
restorePVObj,
restorePod,
},
kubeReactors: []reactor{
{
verb: "delete",
resource: "persistentvolumeclaims",
reactorFunc: func(action clientTesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, errors.New("fake-delete-error")
},
},
},
err: "error to delete restore PVC fake-restore: error to delete pvc fake-restore: fake-delete-error",
},
{
name: "[same mode] wait volume detached fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObjSameMode,
restorePVCObj,
restorePVObj,
restorePod,
},
kubeReactors: []reactor{
{
verb: "list",
resource: "volumeattachments",
reactorFunc: func(action clientTesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, errors.New("fake-list-error")
},
},
},
err: "error waiting for restore PV fake-restore-pv to detach: error listing volumeattachment: error listing volumeattachment: fake-list-error",
},
{
name: "[same mode] rebind target pvc fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObjSameMode,
restorePVCObj,
restorePVObj,
restorePod,
},
kubeReactors: []reactor{
{
verb: "patch",
resource: "persistentvolumeclaims",
reactorFunc: func(action clientTesting.Action) (handled bool, ret runtime.Object, err error) {
return true, nil, errors.New("fake-patch-error")
},
},
},
err: "error to rebind target PVC fake-ns/fake-target-pvc to fake-restore-pv: error patching PVC: fake-patch-error",
},
{
name: "[same mode] reset pv binding fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObjSameMode,
restorePVCObj,
restorePVObj,
restorePod,
},
kubeReactors: []reactor{
{
verb: "patch",
resource: "persistentvolumes",
reactorFunc: func(action clientTesting.Action) (handled bool, ret runtime.Object, err error) {
// we need it to succeed on set reclaim policy, but fail on reset binding
patchAction := action.(clientTesting.PatchAction)
patchString := string(patchAction.GetPatch())
if patchString != `{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}` {
return true, nil, errors.New("fake-patch-error-reset")
}
return false, nil, nil
},
},
},
err: "error to reset binding info for restore PV fake-restore-pv: error patching PV: fake-patch-error-reset",
},
{
name: "[same mode] wait restore PV bound fail",
targetPVCName: "fake-target-pvc",
targetNamespace: "fake-ns",
ownerRestore: restore,
kubeClientObj: []runtime.Object{
targetPVCObjSameMode,
restorePVCObj,
restorePVObj,
restorePod,
},
err: "error to wait restore PV bound, restore PV fake-restore-pv: error to wait for bound of PV: context deadline exceeded",
},
}
for _, test := range tests {
@@ -583,7 +801,6 @@ func TestRebindVolume(t *testing.T) {
})
}
}
func TestRestorePeekExpose(t *testing.T) {
restore := &velerov1.Restore{
TypeMeta: metav1.TypeMeta{
+16
View File
@@ -773,3 +773,19 @@ func GetVolumeTopology(ctx context.Context, volumeClient corev1client.CoreV1Inte
return pv.Spec.NodeAffinity.Required, nil
}
func GetVolumeModeByPVC(pvc *corev1api.PersistentVolumeClaim) corev1api.PersistentVolumeMode {
if pvc.Spec.VolumeMode != nil {
return *pvc.Spec.VolumeMode
}
return corev1api.PersistentVolumeFilesystem
}
func GetVolumeModeByPV(pv *corev1api.PersistentVolume) corev1api.PersistentVolumeMode {
if pv.Spec.VolumeMode != nil {
return *pv.Spec.VolumeMode
}
return corev1api.PersistentVolumeFilesystem
}
+92
View File
@@ -730,3 +730,95 @@ func TestVerifyJsonConfigs(t *testing.T) {
})
}
}
func TestGetVolumeModeByPVC(t *testing.T) {
modeFilesystem := corev1api.PersistentVolumeFilesystem
modeBlock := corev1api.PersistentVolumeBlock
tests := []struct {
name string
pvc *corev1api.PersistentVolumeClaim
expected corev1api.PersistentVolumeMode
}{
{
name: "nil VolumeMode returns Filesystem",
pvc: &corev1api.PersistentVolumeClaim{
Spec: corev1api.PersistentVolumeClaimSpec{
VolumeMode: nil,
},
},
expected: corev1api.PersistentVolumeFilesystem,
},
{
name: "Filesystem VolumeMode returns Filesystem",
pvc: &corev1api.PersistentVolumeClaim{
Spec: corev1api.PersistentVolumeClaimSpec{
VolumeMode: &modeFilesystem,
},
},
expected: corev1api.PersistentVolumeFilesystem,
},
{
name: "Block VolumeMode returns Block",
pvc: &corev1api.PersistentVolumeClaim{
Spec: corev1api.PersistentVolumeClaimSpec{
VolumeMode: &modeBlock,
},
},
expected: corev1api.PersistentVolumeBlock,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := GetVolumeModeByPVC(test.pvc)
assert.Equal(t, test.expected, actual)
})
}
}
func TestGetVolumeModeByPV(t *testing.T) {
modeFilesystem := corev1api.PersistentVolumeFilesystem
modeBlock := corev1api.PersistentVolumeBlock
tests := []struct {
name string
pv *corev1api.PersistentVolume
expected corev1api.PersistentVolumeMode
}{
{
name: "nil VolumeMode returns Filesystem",
pv: &corev1api.PersistentVolume{
Spec: corev1api.PersistentVolumeSpec{
VolumeMode: nil,
},
},
expected: corev1api.PersistentVolumeFilesystem,
},
{
name: "Filesystem VolumeMode returns Filesystem",
pv: &corev1api.PersistentVolume{
Spec: corev1api.PersistentVolumeSpec{
VolumeMode: &modeFilesystem,
},
},
expected: corev1api.PersistentVolumeFilesystem,
},
{
name: "Block VolumeMode returns Block",
pv: &corev1api.PersistentVolume{
Spec: corev1api.PersistentVolumeSpec{
VolumeMode: &modeBlock,
},
},
expected: corev1api.PersistentVolumeBlock,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := GetVolumeModeByPV(test.pv)
assert.Equal(t, test.expected, actual)
})
}
}