Modify restore logic.

Signed-off-by: Xun Jiang <jxun@vmware.com>
This commit is contained in:
Xun Jiang
2023-12-05 10:01:16 +08:00
parent c77bec73bb
commit 28df14d9d5
4 changed files with 107 additions and 91 deletions
-1
View File
@@ -108,7 +108,6 @@ type PVInfo struct {
### How the VolumeInfo array is generated.
The function `persistBackup` has `backup *pkgbackup.Request` in parameters.
From it, the `VolumeSnapshots`, `PodVolumeBackups`, `CSISnapshots`, `itemOperationsList`, and `SkippedPVTracker` can be read. All of them will be iterated and merged into the `VolumeInfo` array, and then persisted into backup repository in function `persistBackup`.
After going through all the available sources, Velero will check whether there are still some PVs left. If there is any, Velero will generate VolumeInfo for them. The VolumeInfo will contain the PVC namespace, PVC name, PV name and the PVInfo structure.
Please notice that the change happened in async operations are not reflected in the new metadata file. The file only covers the volume changes happen in the Velero server process scope.
@@ -123,7 +123,7 @@ func Stream(ctx context.Context, kbClient kbclient.Client, namespace, name strin
ExpectContinueTimeout: defaultTransport.ExpectContinueTimeout,
}
httpReq, err := http.NewRequest(http.MethodGet, created.Status.DownloadURL, nil)
httpReq, err := http.NewRequestWithContext(context.TODO(), http.MethodGet, created.Status.DownloadURL, nil)
if err != nil {
return err
}
+3 -2
View File
@@ -523,8 +523,9 @@ func (r *restoreReconciler) runValidatedRestore(restore *api.Restore, info backu
backupVolumeInfoMap := make(map[string]internalVolume.VolumeInfo)
volumeInfos, err := backupStore.GetBackupVolumeInfos(restore.Spec.BackupName)
if err != nil || len(volumeInfos) == 0 {
restoreLog.Infof("Backup %s doesn't have volumeinfos metadata file.", restore.Spec.BackupName)
if err != nil {
restoreLog.WithError(err).Errorf("fail to get VolumeInfos metadata file for backup %s", restore.Spec.BackupName)
return errors.WithStack(err)
} else {
for _, volumeInfo := range volumeInfos {
backupVolumeInfoMap[volumeInfo.PVName] = *volumeInfo
+103 -87
View File
@@ -81,15 +81,6 @@ var resourceMustHave = []string{
"datauploads.velero.io",
}
const (
pvHasNativeSnapshot = iota + 1
pvHasPodVolumeBackup
pvHasCSIVolumeSnapshot
pvHasDataUpload
pvHasReclaimPolicyAsDelete
pvHasReclaimPolicyAsRetain
)
type VolumeSnapshotterGetter interface {
GetVolumeSnapshotter(name string) (vsv1.VolumeSnapshotter, error)
}
@@ -1244,28 +1235,56 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
}
if groupResource == kuberesource.PersistentVolumes {
pvCondition := 0
if volumeInfo, ok := ctx.volumeInfoMap[obj.GetName()]; ok {
ctx.log.Infof("Find VolumeInfo for PV %s.", obj.GetName())
switch volumeInfo.BackupMethod {
case internalVolume.NativeSnapshot:
pvCondition = pvHasNativeSnapshot
case internalVolume.PodVolumeBackup:
pvCondition = pvHasPodVolumeBackup
case internalVolume.CSISnapshot:
if volumeInfo.SnapshotDataMoved {
pvCondition = pvHasDataUpload
} else {
pvCondition = pvHasCSIVolumeSnapshot
obj, err = ctx.handlePVHasNativeSnapshot(obj, resourceClient)
if err != nil {
errs.Add(namespace, err)
return warnings, errs, itemExists
}
name = obj.GetName()
case internalVolume.PodVolumeBackup:
restoreLogger.Infof("Dynamically re-provisioning persistent volume because it has a pod volume backup to be restored.")
ctx.pvsToProvision.Insert(name)
// Return early because we don't want to restore the PV itself, we
// want to dynamically re-provision it.
return warnings, errs, itemExists
case internalVolume.CSISnapshot:
restoreLogger.Infof("Dynamically re-provisioning persistent volume because it has a CSI VolumeSnapshot or a related snapshot DataUpload.")
ctx.pvsToProvision.Insert(name)
if ready, err := ctx.featureVerifier.Verify(velerov1api.CSIFeatureFlag); !ready {
ctx.log.Errorf("Failed to verify CSI modules, ready %v, err %v", ready, err)
errs.Add(namespace, fmt.Errorf("CSI modules are not ready for restore. Check CSI feature is enabled and CSI plugin is installed"))
}
// Return early because we don't want to restore the PV itself, we
// want to dynamically re-provision it.
return warnings, errs, itemExists
// When the PV data is skipped from backup, it's VolumeInfo BackupMethod
// is not set, and it will fall into the default case.
default:
if hasDeleteReclaimPolicy(obj.Object) {
pvCondition = pvHasReclaimPolicyAsDelete
restoreLogger.Infof("Dynamically re-provisioning persistent volume because it doesn't have a snapshot and its reclaim policy is Delete.")
ctx.pvsToProvision.Insert(name)
// Return early because we don't want to restore the PV itself, we
// want to dynamically re-provision it.
return warnings, errs, itemExists
} else {
pvCondition = pvHasReclaimPolicyAsRetain
obj, err = ctx.handleSkippedPVHasRetainPolicy(obj, resourceID, restoreLogger)
if err != nil {
errs.Add(namespace, err)
return warnings, errs, itemExists
}
}
}
} else {
@@ -1275,80 +1294,53 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
switch {
case hasSnapshot(name, ctx.volumeSnapshots):
pvCondition = pvHasNativeSnapshot
obj, err = ctx.handlePVHasNativeSnapshot(obj, resourceClient)
if err != nil {
errs.Add(namespace, err)
return warnings, errs, itemExists
}
name = obj.GetName()
case hasPodVolumeBackup(obj, ctx):
pvCondition = pvHasPodVolumeBackup
restoreLogger.Infof("Dynamically re-provisioning persistent volume because it has a pod volume backup to be restored.")
ctx.pvsToProvision.Insert(name)
// Return early because we don't want to restore the PV itself, we
// want to dynamically re-provision it.
return warnings, errs, itemExists
case hasCSIVolumeSnapshot(ctx, obj):
pvCondition = pvHasCSIVolumeSnapshot
fallthrough
case hasSnapshotDataUpload(ctx, obj):
pvCondition = pvHasDataUpload
restoreLogger.Infof("Dynamically re-provisioning persistent volume because it has a CSI VolumeSnapshot or a related snapshot DataUpload.")
ctx.pvsToProvision.Insert(name)
if ready, err := ctx.featureVerifier.Verify(velerov1api.CSIFeatureFlag); !ready {
ctx.log.Errorf("Failed to verify CSI modules, ready %v, err %v", ready, err)
errs.Add(namespace, fmt.Errorf("CSI modules are not ready for restore. Check CSI feature is enabled and CSI plugin is installed"))
}
// Return early because we don't want to restore the PV itself, we
// want to dynamically re-provision it.
return warnings, errs, itemExists
case hasDeleteReclaimPolicy(obj.Object):
pvCondition = pvHasReclaimPolicyAsDelete
restoreLogger.Infof("Dynamically re-provisioning persistent volume because it doesn't have a snapshot and its reclaim policy is Delete.")
ctx.pvsToProvision.Insert(name)
// Return early because we don't want to restore the PV itself, we
// want to dynamically re-provision it.
return warnings, errs, itemExists
default:
pvCondition = pvHasReclaimPolicyAsRetain
obj, err = ctx.handleSkippedPVHasRetainPolicy(obj, resourceID, restoreLogger)
if err != nil {
errs.Add(namespace, err)
return warnings, errs, itemExists
}
}
}
switch pvCondition {
case pvHasNativeSnapshot:
obj, err = ctx.handlePVHasNativeSnapshot(obj, resourceClient)
if err != nil {
errs.Add(namespace, err)
return warnings, errs, itemExists
}
name = obj.GetName()
case pvHasPodVolumeBackup:
restoreLogger.Infof("Dynamically re-provisioning persistent volume because it has a pod volume backup to be restored.")
ctx.pvsToProvision.Insert(name)
// Return early because we don't want to restore the PV itself, we
// want to dynamically re-provision it.
return warnings, errs, itemExists
case pvHasCSIVolumeSnapshot:
fallthrough
case pvHasDataUpload:
restoreLogger.Infof("Dynamically re-provisioning persistent volume because it has a CSI VolumeSnapshot or a related snapshot DataUpload.")
ctx.pvsToProvision.Insert(name)
if ready, err := ctx.featureVerifier.Verify(velerov1api.CSIFeatureFlag); !ready {
ctx.log.Errorf("Failed to verify CSI modules, ready %v, err %v", ready, err)
errs.Add(namespace, fmt.Errorf("CSI modules are not ready for restore. Check CSI feature is enabled and CSI plugin is installed"))
}
// Return early because we don't want to restore the PV itself, we
// want to dynamically re-provision it.
return warnings, errs, itemExists
case pvHasReclaimPolicyAsDelete:
restoreLogger.Infof("Dynamically re-provisioning persistent volume because it doesn't have a snapshot and its reclaim policy is Delete.")
ctx.pvsToProvision.Insert(name)
// Return early because we don't want to restore the PV itself, we
// want to dynamically re-provision it.
return warnings, errs, itemExists
case pvHasReclaimPolicyAsRetain:
restoreLogger.Infof("Restoring persistent volume as-is because it doesn't have a snapshot and its reclaim policy is not Delete.")
// Check to see if the claimRef.namespace field needs to be remapped, and do so if necessary.
_, err = remapClaimRefNS(ctx, obj)
if err != nil {
errs.Add(namespace, err)
return warnings, errs, itemExists
}
obj = resetVolumeBindingInfo(obj)
// We call the pvRestorer here to clear out the PV's claimRef.UID,
// so it can be re-claimed when its PVC is restored and gets a new UID.
updatedObj, err := ctx.pvRestorer.executePVAction(obj)
if err != nil {
errs.Add(namespace, fmt.Errorf("error executing PVAction for %s: %v", resourceID, err))
return warnings, errs, itemExists
}
obj = updatedObj
}
}
objStatus, statusFieldExists, statusFieldErr := unstructured.NestedFieldCopy(obj.Object, "status")
@@ -2517,3 +2509,27 @@ func (ctx *restoreContext) handlePVHasNativeSnapshot(obj *unstructured.Unstructu
return retObj, nil
}
func (ctx *restoreContext) handleSkippedPVHasRetainPolicy(
obj *unstructured.Unstructured,
resourceID string,
logger logrus.FieldLogger,
) (*unstructured.Unstructured, error) {
logger.Infof("Restoring persistent volume as-is because it doesn't have a snapshot and its reclaim policy is not Delete.")
// Check to see if the claimRef.namespace field needs to be remapped, and do so if necessary.
if _, err := remapClaimRefNS(ctx, obj); err != nil {
return nil, err
}
obj = resetVolumeBindingInfo(obj)
// We call the pvRestorer here to clear out the PV's claimRef.UID,
// so it can be re-claimed when its PVC is restored and gets a new UID.
updatedObj, err := ctx.pvRestorer.executePVAction(obj)
if err != nil {
return nil, fmt.Errorf("error executing PVAction for %s: %v", resourceID, err)
}
return updatedObj, nil
}