Full backup for all data movers (#10185)

* support full backup for fs data mover

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>

* full backup for PVB

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>

* full backup for all data movers

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>

* fix UT error

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>

---------

Signed-off-by: Lyndon-Li <lyonghui@vmware.com>
This commit is contained in:
lyndon-li
2026-08-18 13:08:41 +08:00
committed by GitHub
parent 6256b4fcb4
commit fa95eb0aa7
12 changed files with 55 additions and 27 deletions
+1
View File
@@ -0,0 +1 @@
Support full backup for file system data mover and pod volume backup
@@ -96,6 +96,13 @@ spec:
description: Node is the name of the node that the Pod is running
on.
type: string
parentSnapshot:
description: |-
ParentSnapshot specifies the parent snapshot that current backup is based on.
If its value is "" or "auto", the data mover finds the recent backup of the same volume as parent.
If its value is "none", the data mover will do a full backup
If its value is a specific snapshotID, the data mover finds the specific snapshot as parent.
type: string
pod:
description: Pod is a reference to the pod containing the volume to
be backed up.
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -17,6 +17,6 @@ limitations under the License.
package shared
const (
DataUploadParentSnapshotNone = "none"
DataUploadParentSnapshotAuto = "auto"
ParentSnapshotNone = "none"
ParentSnapshotAuto = "auto"
)
@@ -61,6 +61,12 @@ type PodVolumeBackupSpec struct {
// Cancel indicates request to cancel the ongoing PodVolumeBackup. It can be set
// when the PodVolumeBackup is in InProgress phase
Cancel bool `json:"cancel,omitempty"`
// ParentSnapshot specifies the parent snapshot that current backup is based on.
// If its value is "" or "auto", the data mover finds the recent backup of the same volume as parent.
// If its value is "none", the data mover will do a full backup
// If its value is a specific snapshotID, the data mover finds the specific snapshot as parent.
ParentSnapshot string `json:"parentSnapshot,omitempty"`
}
// PodVolumeBackupPhase represents the lifecycle phase of a PodVolumeBackup.
+1 -1
View File
@@ -569,7 +569,7 @@ func newDataUpload(
parentSnapshot := ""
if backup.Spec.BackupType == velerov1api.BackupTypeFull {
parentSnapshot = veleroshared.DataUploadParentSnapshotNone
parentSnapshot = veleroshared.ParentSnapshotNone
}
dataMover := backup.Spec.DataMover
+10 -10
View File
@@ -184,7 +184,7 @@ func (r *BackupMicroService) RunCancelableDataPath(ctx context.Context) (string,
return "", errors.Wrap(err, "error to create data path")
}
log.Debug("Async fs br created")
log.Debug("Async br created")
if err := dp.Init(ctx, &datapath.InitParam{
BSLName: du.Spec.BackupStorageLocation,
@@ -198,7 +198,7 @@ func (r *BackupMicroService) RunCancelableDataPath(ctx context.Context) (string,
return "", errors.Wrap(err, "error to initialize data path")
}
log.Info("Async fs br init")
log.Info("Async br init")
tags := map[string]string{
velerov1api.AsyncOperationIDLabel: du.Labels[velerov1api.AsyncOperationIDLabel],
@@ -207,7 +207,7 @@ func (r *BackupMicroService) RunCancelableDataPath(ctx context.Context) (string,
// Modify the ParentSnapshot to "" and ForceFull to true when ParentSnapshot is "none".
parentSnapshot := du.Spec.ParentSnapshot
forceFull := false
if du.Spec.ParentSnapshot == veleroshared.DataUploadParentSnapshotNone {
if du.Spec.ParentSnapshot == veleroshared.ParentSnapshotNone {
parentSnapshot = ""
forceFull = true
}
@@ -231,7 +231,7 @@ func (r *BackupMicroService) RunCancelableDataPath(ctx context.Context) (string,
result := ""
select {
case <-ctx.Done():
err = errors.New("timed out waiting for fs backup to complete")
err = errors.New("timed out waiting for backup to complete")
break
case res := <-r.resultSignal:
err = res.err
@@ -315,9 +315,9 @@ func (r *BackupMicroService) OnDataUploadProgress(ctx context.Context, namespace
}
func (r *BackupMicroService) closeDataPath(ctx context.Context, duName string) {
fsBackup := r.dataPathMgr.GetAsyncBR(duName)
if fsBackup != nil {
fsBackup.Close(ctx)
asyncBR := r.dataPathMgr.GetAsyncBR(duName)
if asyncBR != nil {
asyncBR.Close(ctx)
}
r.dataPathMgr.RemoveAsyncBR(duName)
@@ -328,11 +328,11 @@ func (r *BackupMicroService) cancelDataUpload(du *velerov2alpha1api.DataUpload)
r.eventRecorder.Event(du, false, datapath.EventReasonCancelling, "Canceling for data upload %s", du.Name)
fsBackup := r.dataPathMgr.GetAsyncBR(du.Name)
if fsBackup == nil {
asyncBR := r.dataPathMgr.GetAsyncBR(du.Name)
if asyncBR == nil {
r.OnDataUploadCancelled(r.ctx, du.GetNamespace(), du.GetName())
r.eventRecorder.EndingEvent(du, false, datapath.EventReasonStopped, "Data path for %s exited without start", du.Name)
} else {
fsBackup.Cancel()
asyncBR.Cancel()
}
}
+1 -1
View File
@@ -345,7 +345,7 @@ func TestRunCancelableDataPath(t *testing.T) {
kubeClientObj: []runtime.Object{duInProgress},
dataPathStarted: true,
expectedEventMsg: fmt.Sprintf("Data path for %s stopped", dataUploadName),
expectedErr: "timed out waiting for fs backup to complete",
expectedErr: "timed out waiting for backup to complete",
},
{
name: "data path returns error",
+9 -9
View File
@@ -178,7 +178,7 @@ func (r *RestoreMicroService) RunCancelableDataPath(ctx context.Context) (string
}); err != nil {
return "", errors.Wrap(err, "error to initialize data path")
}
log.Info("fs init")
log.Info("Async br init")
if err := dp.StartRestore(dd.Spec.SnapshotID, r.sourceTargetPath, dd.Spec.DataMoverConfig, &datapath.RestoreStartParam{}); err != nil {
return "", errors.Wrap(err, "error starting data path restore")
@@ -190,7 +190,7 @@ func (r *RestoreMicroService) RunCancelableDataPath(ctx context.Context) (string
result := ""
select {
case <-ctx.Done():
err = errors.New("timed out waiting for fs restore to complete")
err = errors.New("timed out waiting for restore to complete")
break
case res := <-r.resultSignal:
err = res.err
@@ -199,7 +199,7 @@ func (r *RestoreMicroService) RunCancelableDataPath(ctx context.Context) (string
}
if err != nil {
log.WithError(err).Error("Async fs restore was not completed")
log.WithError(err).Error("Async restore was not completed")
}
r.eventRecorder.EndingEvent(dd, false, datapath.EventReasonStopped, "Data path for %s stopped", dd.Name)
@@ -272,9 +272,9 @@ func (r *RestoreMicroService) OnDataDownloadProgress(ctx context.Context, namesp
}
func (r *RestoreMicroService) closeDataPath(ctx context.Context, ddName string) {
fsRestore := r.dataPathMgr.GetAsyncBR(ddName)
if fsRestore != nil {
fsRestore.Close(ctx)
asyncBR := r.dataPathMgr.GetAsyncBR(ddName)
if asyncBR != nil {
asyncBR.Close(ctx)
}
r.dataPathMgr.RemoveAsyncBR(ddName)
@@ -285,11 +285,11 @@ func (r *RestoreMicroService) cancelDataDownload(dd *velerov2alpha1api.DataDownl
r.eventRecorder.Event(dd, false, datapath.EventReasonCancelling, "Canceling for data download %s", dd.Name)
fsBackup := r.dataPathMgr.GetAsyncBR(dd.Name)
if fsBackup == nil {
asyncBR := r.dataPathMgr.GetAsyncBR(dd.Name)
if asyncBR == nil {
r.OnDataDownloadCancelled(r.ctx, dd.GetNamespace(), dd.GetName())
r.eventRecorder.EndingEvent(dd, false, datapath.EventReasonStopped, "Data path for %s exited without start", dd.Name)
} else {
fsBackup.Cancel()
asyncBR.Cancel()
}
}
+1 -1
View File
@@ -291,7 +291,7 @@ func TestRunCancelableRestore(t *testing.T) {
kubeClientObj: []runtime.Object{ddInProgress},
dataPathStarted: true,
expectedEventMsg: fmt.Sprintf("Data path for %s stopped", dataDownloadName),
expectedErr: "timed out waiting for fs restore to complete",
expectedErr: "timed out waiting for restore to complete",
},
{
name: "data path returns error",
+11 -2
View File
@@ -32,6 +32,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/cache"
"github.com/vmware-tanzu/velero/internal/credentials"
veleroshared "github.com/vmware-tanzu/velero/pkg/apis/velero/shared"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
"github.com/vmware-tanzu/velero/pkg/datapath"
"github.com/vmware-tanzu/velero/pkg/repository"
@@ -192,10 +193,18 @@ func (r *BackupMicroService) RunCancelableDataPath(ctx context.Context) (string,
tags := map[string]string{}
// Modify the ParentSnapshot to "" and ForceFull to true when ParentSnapshot is "none".
parentSnapshot := pvb.Spec.ParentSnapshot
forceFull := false
if pvb.Spec.ParentSnapshot == veleroshared.ParentSnapshotNone {
parentSnapshot = ""
forceFull = true
}
if err := fsBackup.StartBackup(r.sourceTargetPath, pvb.Spec.UploaderSettings, &datapath.BackupStartParam{
RealSource: GetRealSource(pvb),
ParentSnapshot: "",
ForceFull: false,
ParentSnapshot: parentSnapshot,
ForceFull: forceFull,
Tags: tags,
}); err != nil {
return "", errors.Wrap(err, "error starting data path backup")
+5
View File
@@ -34,6 +34,7 @@ import (
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
"github.com/vmware-tanzu/velero/internal/resourcepolicies"
veleroshared "github.com/vmware-tanzu/velero/pkg/apis/velero/shared"
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
veleroclient "github.com/vmware-tanzu/velero/pkg/client"
"github.com/vmware-tanzu/velero/pkg/label"
@@ -598,5 +599,9 @@ func newPodVolumeBackup(backup *velerov1api.Backup, pod *corev1api.Pod, volume c
pvb.Spec.UploaderSettings = uploaderutil.StoreBackupConfig(backup.Spec.UploaderConfig)
}
if backup.Spec.BackupType == velerov1api.BackupTypeFull {
pvb.Spec.ParentSnapshot = veleroshared.ParentSnapshotNone
}
return pvb
}