Merge branch 'main' into vgdp-ms-resume-pvb-pvr

This commit is contained in:
Lyndon-Li
2025-06-19 16:12:00 +08:00
18 changed files with 402 additions and 206 deletions
+4 -2
View File
@@ -823,7 +823,8 @@ func (kb *kubernetesBackupper) waitUntilPVBsProcessed(ctx context.Context, log l
}
for _, pvb := range pvbs {
pvbMap[pvb] = pvb.Status.Phase == velerov1api.PodVolumeBackupPhaseCompleted ||
pvb.Status.Phase == velerov1api.PodVolumeBackupPhaseFailed
pvb.Status.Phase == velerov1api.PodVolumeBackupPhaseFailed ||
pvb.Status.Phase == velerov1api.PodVolumeBackupPhaseCanceled
}
}
@@ -840,7 +841,8 @@ func (kb *kubernetesBackupper) waitUntilPVBsProcessed(ctx context.Context, log l
continue
}
if updatedPVB.Status.Phase == velerov1api.PodVolumeBackupPhaseCompleted ||
updatedPVB.Status.Phase == velerov1api.PodVolumeBackupPhaseFailed {
updatedPVB.Status.Phase == velerov1api.PodVolumeBackupPhaseFailed ||
updatedPVB.Status.Phase == velerov1api.PodVolumeBackupPhaseCanceled {
pvbMap[pvb] = true
continue
}
+8
View File
@@ -786,7 +786,11 @@ func describePodVolumeBackups(d *Describer, details bool, podVolumeBackups []vel
for _, phase := range []string{
string(velerov1api.PodVolumeBackupPhaseCompleted),
string(velerov1api.PodVolumeBackupPhaseFailed),
string(velerov1api.PodVolumeBackupPhaseCanceled),
"In Progress",
string(velerov1api.PodVolumeBackupPhaseCanceling),
string(velerov1api.PodVolumeBackupPhasePrepared),
string(velerov1api.PodVolumeBackupPhaseAccepted),
string(velerov1api.PodVolumeBackupPhaseNew),
} {
if len(backupsByPhase[phase]) == 0 {
@@ -822,7 +826,11 @@ func groupByPhase(backups []velerov1api.PodVolumeBackup) map[string][]velerov1ap
phaseToGroup := map[velerov1api.PodVolumeBackupPhase]string{
velerov1api.PodVolumeBackupPhaseCompleted: string(velerov1api.PodVolumeBackupPhaseCompleted),
velerov1api.PodVolumeBackupPhaseFailed: string(velerov1api.PodVolumeBackupPhaseFailed),
velerov1api.PodVolumeBackupPhaseCanceled: string(velerov1api.PodVolumeBackupPhaseCanceled),
velerov1api.PodVolumeBackupPhaseInProgress: "In Progress",
velerov1api.PodVolumeBackupPhaseCanceling: string(velerov1api.PodVolumeBackupPhaseCanceling),
velerov1api.PodVolumeBackupPhasePrepared: string(velerov1api.PodVolumeBackupPhasePrepared),
velerov1api.PodVolumeBackupPhaseAccepted: string(velerov1api.PodVolumeBackupPhaseAccepted),
velerov1api.PodVolumeBackupPhaseNew: string(velerov1api.PodVolumeBackupPhaseNew),
"": string(velerov1api.PodVolumeBackupPhaseNew),
}
@@ -572,6 +572,54 @@ func TestDescribePodVolumeBackups(t *testing.T) {
PodName("pod-2").
PodNamespace("pod-ns-1").
SnapshotID("snap-2").Result()
pvb3 := builder.ForPodVolumeBackup("test-ns1", "test-pvb3").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhaseFailed).
BackupStorageLocation("bsl-1").
Volume("vol-3").
PodName("pod-3").
PodNamespace("pod-ns-1").
SnapshotID("snap-3").Result()
pvb4 := builder.ForPodVolumeBackup("test-ns1", "test-pvb4").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhaseCanceled).
BackupStorageLocation("bsl-1").
Volume("vol-4").
PodName("pod-4").
PodNamespace("pod-ns-1").
SnapshotID("snap-4").Result()
pvb5 := builder.ForPodVolumeBackup("test-ns1", "test-pvb5").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhaseInProgress).
BackupStorageLocation("bsl-1").
Volume("vol-5").
PodName("pod-5").
PodNamespace("pod-ns-1").
SnapshotID("snap-5").Result()
pvb6 := builder.ForPodVolumeBackup("test-ns1", "test-pvb6").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhaseCanceling).
BackupStorageLocation("bsl-1").
Volume("vol-6").
PodName("pod-6").
PodNamespace("pod-ns-1").
SnapshotID("snap-6").Result()
pvb7 := builder.ForPodVolumeBackup("test-ns1", "test-pvb7").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhasePrepared).
BackupStorageLocation("bsl-1").
Volume("vol-7").
PodName("pod-7").
PodNamespace("pod-ns-1").
SnapshotID("snap-7").Result()
pvb8 := builder.ForPodVolumeBackup("test-ns1", "test-pvb6").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhaseAccepted).
BackupStorageLocation("bsl-1").
Volume("vol-8").
PodName("pod-8").
PodNamespace("pod-ns-1").
SnapshotID("snap-8").Result()
testcases := []struct {
name string
@@ -602,6 +650,28 @@ func TestDescribePodVolumeBackups(t *testing.T) {
Completed:
pod-ns-1/pod-1: vol-1
pod-ns-1/pod-2: vol-2
`,
},
{
name: "all phases with details",
inputPVBList: []velerov1api.PodVolumeBackup{*pvb1, *pvb2, *pvb3, *pvb4, *pvb5, *pvb6, *pvb7, *pvb8},
inputDetails: true,
expect: ` Pod Volume Backups - kopia:
Completed:
pod-ns-1/pod-1: vol-1
pod-ns-1/pod-2: vol-2
Failed:
pod-ns-1/pod-3: vol-3
Canceled:
pod-ns-1/pod-4: vol-4
In Progress:
pod-ns-1/pod-5: vol-5
Canceling:
pod-ns-1/pod-6: vol-6
Prepared:
pod-ns-1/pod-7: vol-7
Accepted:
pod-ns-1/pod-8: vol-8
`,
},
}
@@ -499,7 +499,11 @@ func describePodVolumeBackupsInSF(backups []velerov1api.PodVolumeBackup, details
for _, phase := range []string{
string(velerov1api.PodVolumeBackupPhaseCompleted),
string(velerov1api.PodVolumeBackupPhaseFailed),
string(velerov1api.PodVolumeBackupPhaseCanceled),
"In Progress",
string(velerov1api.PodVolumeBackupPhaseCanceling),
string(velerov1api.PodVolumeBackupPhasePrepared),
string(velerov1api.PodVolumeBackupPhaseAccepted),
string(velerov1api.PodVolumeBackupPhaseNew),
} {
if len(backupsByPhase[phase]) == 0 {
@@ -240,6 +240,55 @@ func TestDescribePodVolumeBackupsInSF(t *testing.T) {
PodNamespace("pod-ns-1").
SnapshotID("snap-2").Result()
pvb3 := builder.ForPodVolumeBackup("test-ns1", "test-pvb3").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhaseFailed).
BackupStorageLocation("bsl-1").
Volume("vol-3").
PodName("pod-3").
PodNamespace("pod-ns-1").
SnapshotID("snap-3").Result()
pvb4 := builder.ForPodVolumeBackup("test-ns1", "test-pvb4").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhaseCanceled).
BackupStorageLocation("bsl-1").
Volume("vol-4").
PodName("pod-4").
PodNamespace("pod-ns-1").
SnapshotID("snap-4").Result()
pvb5 := builder.ForPodVolumeBackup("test-ns1", "test-pvb5").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhaseInProgress).
BackupStorageLocation("bsl-1").
Volume("vol-5").
PodName("pod-5").
PodNamespace("pod-ns-1").
SnapshotID("snap-5").Result()
pvb6 := builder.ForPodVolumeBackup("test-ns1", "test-pvb6").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhaseCanceling).
BackupStorageLocation("bsl-1").
Volume("vol-6").
PodName("pod-6").
PodNamespace("pod-ns-1").
SnapshotID("snap-6").Result()
pvb7 := builder.ForPodVolumeBackup("test-ns1", "test-pvb7").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhasePrepared).
BackupStorageLocation("bsl-1").
Volume("vol-7").
PodName("pod-7").
PodNamespace("pod-ns-1").
SnapshotID("snap-7").Result()
pvb8 := builder.ForPodVolumeBackup("test-ns1", "test-pvb6").
UploaderType("kopia").
Phase(velerov1api.PodVolumeBackupPhaseAccepted).
BackupStorageLocation("bsl-1").
Volume("vol-8").
PodName("pod-8").
PodNamespace("pod-ns-1").
SnapshotID("snap-8").Result()
testcases := []struct {
name string
inputPVBList []velerov1api.PodVolumeBackup
@@ -268,6 +317,40 @@ func TestDescribePodVolumeBackupsInSF(t *testing.T) {
},
},
},
{
name: "all phases",
inputPVBList: []velerov1api.PodVolumeBackup{*pvb1, *pvb2, *pvb3, *pvb4, *pvb5, *pvb6, *pvb7, *pvb8},
inputDetails: true,
expect: map[string]any{
"podVolumeBackups": map[string]any{
"podVolumeBackupsDetails": map[string]any{
"Completed": []map[string]string{
{"pod-ns-1/pod-1": "vol-1"},
{"pod-ns-1/pod-2": "vol-2"},
},
"Failed": []map[string]string{
{"pod-ns-1/pod-3": "vol-3"},
},
"Canceled": []map[string]string{
{"pod-ns-1/pod-4": "vol-4"},
},
"In Progress": []map[string]string{
{"pod-ns-1/pod-5": "vol-5"},
},
"Canceling": []map[string]string{
{"pod-ns-1/pod-6": "vol-6"},
},
"Prepared": []map[string]string{
{"pod-ns-1/pod-7": "vol-7"},
},
"Accepted": []map[string]string{
{"pod-ns-1/pod-8": "vol-8"},
},
},
"uploderType": "kopia",
},
},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(tt *testing.T) {
+6
View File
@@ -362,8 +362,11 @@ func describePodVolumeRestores(d *Describer, restores []velerov1api.PodVolumeRes
// go through phases in a specific order
for _, phase := range []string{
string(velerov1api.PodVolumeRestorePhaseCompleted),
string(velerov1api.PodVolumeRestorePhaseCanceled),
string(velerov1api.PodVolumeRestorePhaseFailed),
"In Progress",
string(velerov1api.PodVolumeRestorePhasePrepared),
string(velerov1api.PodVolumeRestorePhaseAccepted),
string(velerov1api.PodVolumeRestorePhaseNew),
} {
if len(restoresByPhase[phase]) == 0 {
@@ -442,8 +445,11 @@ func groupRestoresByPhase(restores []velerov1api.PodVolumeRestore) map[string][]
phaseToGroup := map[velerov1api.PodVolumeRestorePhase]string{
velerov1api.PodVolumeRestorePhaseCompleted: string(velerov1api.PodVolumeRestorePhaseCompleted),
velerov1api.PodVolumeRestorePhaseCanceled: string(velerov1api.PodVolumeRestorePhaseCanceled),
velerov1api.PodVolumeRestorePhaseFailed: string(velerov1api.PodVolumeRestorePhaseFailed),
velerov1api.PodVolumeRestorePhaseInProgress: "In Progress",
velerov1api.PodVolumeRestorePhasePrepared: string(velerov1api.PodVolumeRestorePhasePrepared),
velerov1api.PodVolumeRestorePhaseAccepted: string(velerov1api.PodVolumeRestorePhaseAccepted),
velerov1api.PodVolumeRestorePhaseNew: string(velerov1api.PodVolumeRestorePhaseNew),
"": string(velerov1api.PodVolumeRestorePhaseNew),
}
+3 -1
View File
@@ -426,8 +426,10 @@ func (b *backupper) WaitAllPodVolumesProcessed(log logrus.FieldLogger) []*velero
continue
}
podVolumeBackups = append(podVolumeBackups, pvb)
if pvb.Status.Phase == velerov1api.PodVolumeBackupPhaseFailed || pvb.Status.Phase == velerov1api.PodVolumeBackupPhaseCanceled {
if pvb.Status.Phase == velerov1api.PodVolumeBackupPhaseFailed {
log.Errorf("pod volume backup failed: %s", pvb.Status.Message)
} else if pvb.Status.Phase == velerov1api.PodVolumeBackupPhaseCanceled {
log.Errorf("pod volume backup canceled: %s", pvb.Status.Message)
}
}
}
+3 -1
View File
@@ -228,8 +228,10 @@ ForEachVolume:
errs = append(errs, errors.New("timed out waiting for all PodVolumeRestores to complete"))
break ForEachVolume
case res := <-resultsChan:
if res.Status.Phase == velerov1api.PodVolumeRestorePhaseFailed || res.Status.Phase == velerov1api.PodVolumeRestorePhaseCanceled {
if res.Status.Phase == velerov1api.PodVolumeRestorePhaseFailed {
errs = append(errs, errors.Errorf("pod volume restore failed: %s", res.Status.Message))
} else if res.Status.Phase == velerov1api.PodVolumeRestorePhaseCanceled {
errs = append(errs, errors.Errorf("pod volume restore canceled: %s", res.Status.Message))
}
tracker.TrackPodVolume(res)
case err := <-r.nodeAgentCheck:
@@ -250,6 +250,7 @@ func TestMaintain(t *testing.T) {
tc.returnRepoWriter.On("AlsoLogToContentLog", mock.Anything).Return(nil)
tc.returnRepoWriter.On("Close", mock.Anything).Return(nil)
tc.returnRepoWriter.On("FindManifests", mock.Anything, mock.Anything).Return(nil, tc.findManifestError)
tc.returnRepoWriter.On("ClientOptions").Return(repo.ClientOptions{})
}
err := service.Maintain(ctx, tc.repoOptions)
+5 -5
View File
@@ -24,7 +24,7 @@ import (
"github.com/vmware-tanzu/velero/pkg/uploader"
"github.com/kopia/kopia/snapshot/snapshotfs"
"github.com/kopia/kopia/snapshot/upload"
)
// Throttle throttles controlle the interval of output result
@@ -63,7 +63,7 @@ type Progress struct {
outputThrottle Throttle // which control the frequency of update progress
updater uploader.ProgressUpdater //which kopia progress will call the UpdateProgress interface, the third party will implement the interface to do the progress update
log logrus.FieldLogger // output info into log when backup
estimationParam snapshotfs.EstimationParameters
estimationParam upload.EstimationParameters
}
func NewProgress(updater uploader.ProgressUpdater, interval time.Duration, log logrus.FieldLogger) *Progress {
@@ -73,8 +73,8 @@ func NewProgress(updater uploader.ProgressUpdater, interval time.Duration, log l
interval: interval,
},
updater: updater,
estimationParam: snapshotfs.EstimationParameters{
Type: snapshotfs.EstimationTypeClassic,
estimationParam: upload.EstimationParameters{
Type: upload.EstimationTypeClassic,
AdaptiveThreshold: 300000,
},
log: log,
@@ -169,7 +169,7 @@ func (p *Progress) ProgressBytes(processedBytes int64, totalBytes int64) {
func (p *Progress) FinishedFile(fname string, err error) {}
func (p *Progress) EstimationParameters() snapshotfs.EstimationParameters {
func (p *Progress) EstimationParameters() upload.EstimationParameters {
return p.estimationParam
}
+3 -3
View File
@@ -22,7 +22,7 @@ import (
"strings"
"sync/atomic"
"github.com/kopia/kopia/snapshot/snapshotfs"
"github.com/kopia/kopia/snapshot/upload"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -85,7 +85,7 @@ func NewKopiaUploaderProvider(
}
// CheckContext check context status check if context is timeout or cancel and backup restore once finished it will quit and return
func (kp *kopiaProvider) CheckContext(ctx context.Context, finishChan chan struct{}, restoreChan chan struct{}, uploader *snapshotfs.Uploader) {
func (kp *kopiaProvider) CheckContext(ctx context.Context, finishChan chan struct{}, restoreChan chan struct{}, uploader *upload.Uploader) {
select {
case <-finishChan:
kp.log.Infof("Action finished")
@@ -135,7 +135,7 @@ func (kp *kopiaProvider) RunBackup(
"parentSnapshot": parentSnapshot,
})
repoWriter := kopia.NewShimRepo(kp.bkRepo)
kpUploader := snapshotfs.NewUploader(repoWriter)
kpUploader := upload.NewUploader(repoWriter)
kpUploader.Progress = kopia.NewProgress(updater, backupProgressCheckInterval, log)
kpUploader.FailFast = true
quit := make(chan struct{})
+3 -3
View File
@@ -23,7 +23,7 @@ import (
"time"
"github.com/kopia/kopia/repo"
"github.com/kopia/kopia/snapshot/snapshotfs"
"github.com/kopia/kopia/snapshot/upload"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
@@ -172,7 +172,7 @@ func TestCheckContext(t *testing.T) {
name string
finishChan chan struct{}
restoreChan chan struct{}
uploader *snapshotfs.Uploader
uploader *upload.Uploader
expectCancel bool
expectBackup bool
expectRestore bool
@@ -181,7 +181,7 @@ func TestCheckContext(t *testing.T) {
name: "FinishChan",
finishChan: make(chan struct{}),
restoreChan: make(chan struct{}),
uploader: &snapshotfs.Uploader{},
uploader: &upload.Uploader{},
expectCancel: false,
expectBackup: false,
expectRestore: false,
+1 -1
View File
@@ -117,7 +117,7 @@ func newConfigCredential(creds map[string]string, options configCredentialOption
if username := creds[CredentialKeyUsername]; username != "" {
if password := creds[CredentialKeyPassword]; password != "" {
return azidentity.NewUsernamePasswordCredential(tenantID, clientID, username, password,
&azidentity.UsernamePasswordCredentialOptions{
&azidentity.UsernamePasswordCredentialOptions{ //nolint:staticcheck // will be solved by https://github.com/vmware-tanzu/velero/issues/9028
AdditionallyAllowedTenants: options.AdditionallyAllowedTenants,
ClientOptions: options.ClientOptions,
})
+1 -1
View File
@@ -70,7 +70,7 @@ func TestNewCredential(t *testing.T) {
os.Clearenv()
// managed identity credential
creds = map[string]string{}
creds = map[string]string{CredentialKeyClientID: "clientid"}
tokenCredential, err = NewCredential(creds, options)
require.NoError(t, err)
assert.IsType(t, &azidentity.ManagedIdentityCredential{}, tokenCredential)