mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-28 10:45:44 +00:00
Identify the backed-up volume by CSI volume handle in the in-place restore pre-flight check (#10530)
The pre-flight check that verifies the existing PVC is still bound to the backed-up volume compared PV names. A block data mover restore of a file system volume recreates the PV under a new name (the volumeMode field is immutable), so a second in-place restore of the same workload failed the check even though the PVC was bound to the very same volume. Record the CSI volume handle in the backup volume info (PVInfo) and compare handles when both the backup and the bound PV record one; the PV name remains the fallback for non-CSI volumes and for backups taken before the handle was recorded. The handle reaches the PVC CSI RIA through the same carrier annotation mechanism as the source size. Signed-off-by: chlins <chlins.zhang@gmail.com>
This commit is contained in:
@@ -190,7 +190,11 @@ func (r *restorer) RestorePodVolumes(data RestoreData, tracker *volume.RestoreVo
|
||||
// restore's PodVolumeRestores complete.
|
||||
if data.Restore.IsVolumeDataInplaceRestore() && pvc != nil {
|
||||
pvName := backedUpPVName(data.BackupVolumeInfos, data.SourceNamespace, pvc.Name)
|
||||
if err := inplace.CheckPVCBoundToBackedUpPV(pvc, pvName, data.SourceNamespace); err != nil {
|
||||
var volumeHandle string
|
||||
if info := data.BackupVolumeInfos[pvName].PVInfo; info != nil {
|
||||
volumeHandle = info.VolumeHandle
|
||||
}
|
||||
if err := inplace.CheckPVCBoundToBackedUpVolume(r.ctx, r.crClient, pvc, pvName, volumeHandle, data.SourceNamespace); err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -367,6 +367,7 @@ func TestRestorePodVolumes(t *testing.T) {
|
||||
inplace: true,
|
||||
kubeClientObj: []runtime.Object{
|
||||
createNodeAgentDaemonset(),
|
||||
createPVObj(1, false),
|
||||
createPVCObj(1),
|
||||
func() *corev1api.Pod {
|
||||
pod := builder.ForPod("fake-ns", "other-pod").
|
||||
@@ -398,6 +399,7 @@ func TestRestorePodVolumes(t *testing.T) {
|
||||
inplace: true,
|
||||
kubeClientObj: []runtime.Object{
|
||||
createNodeAgentDaemonset(),
|
||||
createPVObj(1, false),
|
||||
createPVCObj(1),
|
||||
createGatedPodObj("old-restore-uid", 1),
|
||||
},
|
||||
@@ -423,6 +425,7 @@ func TestRestorePodVolumes(t *testing.T) {
|
||||
inplace: true,
|
||||
kubeClientObj: []runtime.Object{
|
||||
createNodeAgentDaemonset(),
|
||||
createPVObj(1, false),
|
||||
createPVCObj(1),
|
||||
},
|
||||
ctlClientObj: []runtime.Object{
|
||||
@@ -440,6 +443,72 @@ func TestRestorePodVolumes(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// The PV was recreated under a new name by a previous in-place restore
|
||||
// (block data mover on a file system volume) but is the same CSI volume.
|
||||
name: "in-place restore proceeds when the PVC is bound to the backed-up volume under a recreated PV name",
|
||||
pvbs: []*velerov1api.PodVolumeBackup{
|
||||
createPVBObj(true, true, 1, "kopia"),
|
||||
},
|
||||
inplace: true,
|
||||
kubeClientObj: []runtime.Object{
|
||||
createNodeAgentDaemonset(),
|
||||
createNodeObj(),
|
||||
func() *corev1api.PersistentVolume {
|
||||
pv := createPVObj(1, false)
|
||||
pv.Spec.CSI = &corev1api.CSIPersistentVolumeSource{Driver: "fake.csi", VolumeHandle: "vol-1"}
|
||||
return pv
|
||||
}(),
|
||||
createPVCObj(1),
|
||||
createGatedPodObj("fake-restore-uid", 1),
|
||||
createNodeAgentPodObj(true),
|
||||
},
|
||||
ctlClientObj: []runtime.Object{
|
||||
createBackupRepoObj(),
|
||||
},
|
||||
restoredPod: createPodObj(true, true, true, 1),
|
||||
sourceNamespace: "fake-ns",
|
||||
bsl: "fake-bsl",
|
||||
volumeInfos: map[string]volume.BackupVolumeInfo{
|
||||
"backed-up-pv": {PVCNamespace: "fake-ns", PVCName: "fake-pvc-1", PVInfo: &volume.PVInfo{VolumeHandle: "vol-1"}},
|
||||
},
|
||||
runtimeScheme: scheme,
|
||||
retPVRs: []*velerov1api.PodVolumeRestore{
|
||||
completedPVR,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "in-place restore blocked when the PVC is bound to a different CSI volume",
|
||||
pvbs: []*velerov1api.PodVolumeBackup{
|
||||
createPVBObj(true, true, 1, "kopia"),
|
||||
},
|
||||
inplace: true,
|
||||
kubeClientObj: []runtime.Object{
|
||||
createNodeAgentDaemonset(),
|
||||
func() *corev1api.PersistentVolume {
|
||||
pv := createPVObj(1, false)
|
||||
pv.Spec.CSI = &corev1api.CSIPersistentVolumeSource{Driver: "fake.csi", VolumeHandle: "vol-other"}
|
||||
return pv
|
||||
}(),
|
||||
createPVCObj(1),
|
||||
},
|
||||
ctlClientObj: []runtime.Object{
|
||||
createBackupRepoObj(),
|
||||
},
|
||||
restoredPod: createPodObj(true, true, true, 1),
|
||||
sourceNamespace: "fake-ns",
|
||||
bsl: "fake-bsl",
|
||||
volumeInfos: map[string]volume.BackupVolumeInfo{
|
||||
"fake-pv-1": {PVCNamespace: "fake-ns", PVCName: "fake-pvc-1", PVInfo: &volume.PVInfo{VolumeHandle: "vol-1"}},
|
||||
},
|
||||
runtimeScheme: scheme,
|
||||
errs: []expectError{
|
||||
{
|
||||
err: "in-place restore pre-flight check failed, skipping volume data restore: PVC fake-ns/fake-pvc-1 is bound to volume vol-other (PV fake-pv-1), but was bound to volume vol-1 (PV fake-pv-1) at backup time",
|
||||
prefixOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "in-place restore blocked when the PVC is too small for the source volume",
|
||||
pvbs: []*velerov1api.PodVolumeBackup{
|
||||
@@ -448,6 +517,7 @@ func TestRestorePodVolumes(t *testing.T) {
|
||||
inplace: true,
|
||||
kubeClientObj: []runtime.Object{
|
||||
createNodeAgentDaemonset(),
|
||||
createPVObj(1, false),
|
||||
func() *corev1api.PersistentVolumeClaim {
|
||||
pvc := createPVCObj(1)
|
||||
pvc.Status.Capacity = corev1api.ResourceList{corev1api.ResourceStorage: resource.MustParse("100Mi")}
|
||||
@@ -479,6 +549,7 @@ func TestRestorePodVolumes(t *testing.T) {
|
||||
inplace: true,
|
||||
kubeClientObj: []runtime.Object{
|
||||
createNodeAgentDaemonset(),
|
||||
createPVObj(1, false),
|
||||
createNodeObj(),
|
||||
createPVCObj(1),
|
||||
createGatedPodObj("fake-restore-uid", 1),
|
||||
|
||||
Reference in New Issue
Block a user