mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-26 18:04:31 +00:00
Merge pull request #7630 from reasonerjt/restore-vol-info
Track and persist restore volume info
This commit is contained in:
@@ -42,6 +42,7 @@ const (
|
||||
DownloadTargetKindCSIBackupVolumeSnapshots DownloadTargetKind = "CSIBackupVolumeSnapshots"
|
||||
DownloadTargetKindCSIBackupVolumeSnapshotContents DownloadTargetKind = "CSIBackupVolumeSnapshotContents"
|
||||
DownloadTargetKindBackupVolumeInfos DownloadTargetKind = "BackupVolumeInfos"
|
||||
DownloadTargetKindRestoreVolumeInfo DownloadTargetKind = "RestoreVolumeInfo"
|
||||
)
|
||||
|
||||
// DownloadTarget is the specification for what kind of file to download, and the name of the
|
||||
|
||||
@@ -798,7 +798,7 @@ type tarWriter interface {
|
||||
func (kb *kubernetesBackupper) getVolumeInfos(
|
||||
backup velerov1api.Backup,
|
||||
log logrus.FieldLogger,
|
||||
) (persistence.BackupStore, []*volume.VolumeInfo, error) {
|
||||
) (persistence.BackupStore, []*volume.BackupVolumeInfo, error) {
|
||||
location := &velerov1api.BackupStorageLocation{}
|
||||
if err := kb.kbClient.Get(context.Background(), kbclient.ObjectKey{
|
||||
Namespace: backup.Namespace,
|
||||
@@ -825,7 +825,7 @@ func (kb *kubernetesBackupper) getVolumeInfos(
|
||||
|
||||
// updateVolumeInfos update the VolumeInfos according to the AsyncOperations
|
||||
func updateVolumeInfos(
|
||||
volumeInfos []*volume.VolumeInfo,
|
||||
volumeInfos []*volume.BackupVolumeInfo,
|
||||
unstructuredItems []unstructured.Unstructured,
|
||||
operations []*itemoperation.BackupOperation,
|
||||
log logrus.FieldLogger,
|
||||
@@ -874,7 +874,7 @@ func updateVolumeInfos(
|
||||
|
||||
func putVolumeInfos(
|
||||
backupName string,
|
||||
volumeInfos []*volume.VolumeInfo,
|
||||
volumeInfos []*volume.BackupVolumeInfo,
|
||||
backupStore persistence.BackupStore,
|
||||
) error {
|
||||
backupVolumeInfoBuf := new(bytes.Buffer)
|
||||
|
||||
@@ -4454,7 +4454,7 @@ func TestGetVolumeInfos(t *testing.T) {
|
||||
backupStore := new(persistencemocks.BackupStore)
|
||||
h.backupper.pluginManager = func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager }
|
||||
h.backupper.backupStoreGetter = NewFakeSingleObjectBackupStoreGetter(backupStore)
|
||||
backupStore.On("GetBackupVolumeInfos", "backup-01").Return([]*volume.VolumeInfo{}, nil)
|
||||
backupStore.On("GetBackupVolumeInfos", "backup-01").Return([]*volume.BackupVolumeInfo{}, nil)
|
||||
pluginManager.On("CleanupClients").Return()
|
||||
|
||||
backup := builder.ForBackup("velero", "backup-01").StorageLocation("default").Result()
|
||||
@@ -4474,8 +4474,8 @@ func TestUpdateVolumeInfos(t *testing.T) {
|
||||
name string
|
||||
operations []*itemoperation.BackupOperation
|
||||
dataUpload *velerov2alpha1.DataUpload
|
||||
volumeInfos []*volume.VolumeInfo
|
||||
expectedVolumeInfos []*volume.VolumeInfo
|
||||
volumeInfos []*volume.BackupVolumeInfo
|
||||
expectedVolumeInfos []*volume.BackupVolumeInfo
|
||||
}{
|
||||
{
|
||||
name: "CSISnapshot VolumeInfo update",
|
||||
@@ -4489,7 +4489,7 @@ func TestUpdateVolumeInfos(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
volumeInfos: []*volume.VolumeInfo{
|
||||
volumeInfos: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
CompletionTimestamp: &metav1.Time{},
|
||||
@@ -4498,7 +4498,7 @@ func TestUpdateVolumeInfos(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedVolumeInfos: []*volume.VolumeInfo{
|
||||
expectedVolumeInfos: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
CompletionTimestamp: &now,
|
||||
@@ -4519,7 +4519,7 @@ func TestUpdateVolumeInfos(t *testing.T) {
|
||||
SourceNamespace("ns-1").
|
||||
SourcePVC("pvc-1").
|
||||
Result(),
|
||||
volumeInfos: []*volume.VolumeInfo{
|
||||
volumeInfos: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
PVCName: "pvc-1",
|
||||
PVCNamespace: "ns-1",
|
||||
@@ -4529,7 +4529,7 @@ func TestUpdateVolumeInfos(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedVolumeInfos: []*volume.VolumeInfo{
|
||||
expectedVolumeInfos: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
PVCName: "pvc-1",
|
||||
PVCNamespace: "ns-1",
|
||||
@@ -4572,7 +4572,7 @@ func TestPutVolumeInfos(t *testing.T) {
|
||||
|
||||
backupStore.On("PutBackupVolumeInfos", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
require.NoError(t, putVolumeInfos(backupName, []*volume.VolumeInfo{}, backupStore))
|
||||
require.NoError(t, putVolumeInfos(backupName, []*volume.BackupVolumeInfo{}, backupStore))
|
||||
}
|
||||
|
||||
type fakeSingleObjectBackupStoreGetter struct {
|
||||
|
||||
@@ -52,11 +52,11 @@ type Request struct {
|
||||
itemOperationsList *[]*itemoperation.BackupOperation
|
||||
ResPolicies *resourcepolicies.Policies
|
||||
SkippedPVTracker *skipPVTracker
|
||||
VolumesInformation volume.VolumesInformation
|
||||
VolumesInformation volume.BackupVolumesInformation
|
||||
}
|
||||
|
||||
// VolumesInformation contains the information needs by generating
|
||||
// the backup VolumeInfo array.
|
||||
// BackupVolumesInformation contains the information needs by generating
|
||||
// the backup BackupVolumeInfo array.
|
||||
|
||||
// GetItemOperationsList returns ItemOperationsList, initializing it if necessary
|
||||
func (r *Request) GetItemOperationsList() *[]*itemoperation.BackupOperation {
|
||||
|
||||
@@ -75,6 +75,12 @@ func (v *VolumeSnapshotBuilder) SourcePVC(name string) *VolumeSnapshotBuilder {
|
||||
return v
|
||||
}
|
||||
|
||||
// SourceVolumeSnapshotContentName set the built VolumeSnapshot's spec.Source.VolumeSnapshotContentName
|
||||
func (v *VolumeSnapshotBuilder) SourceVolumeSnapshotContentName(name string) *VolumeSnapshotBuilder {
|
||||
v.object.Spec.Source.VolumeSnapshotContentName = &name
|
||||
return v
|
||||
}
|
||||
|
||||
// RestoreSize set the built VolumeSnapshot's status.RestoreSize.
|
||||
func (v *VolumeSnapshotBuilder) RestoreSize(size string) *VolumeSnapshotBuilder {
|
||||
resourceSize := resource.MustParse(size)
|
||||
|
||||
@@ -991,6 +991,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
||||
s.config.formatFlag.Parse(),
|
||||
s.config.defaultItemOperationTimeout,
|
||||
s.config.disableInformerCache,
|
||||
s.crClient,
|
||||
)
|
||||
|
||||
if err = r.SetupWithManager(s.mgr); err != nil {
|
||||
|
||||
@@ -439,8 +439,8 @@ func describeBackupVolumes(ctx context.Context, kbClient kbclient.Client, d *Des
|
||||
|
||||
d.Println("Backup Volumes:")
|
||||
|
||||
nativeSnapshots := []*volume.VolumeInfo{}
|
||||
csiSnapshots := []*volume.VolumeInfo{}
|
||||
nativeSnapshots := []*volume.BackupVolumeInfo{}
|
||||
csiSnapshots := []*volume.BackupVolumeInfo{}
|
||||
legacyInfoSource := false
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
@@ -463,7 +463,7 @@ func describeBackupVolumes(ctx context.Context, kbClient kbclient.Client, d *Des
|
||||
d.Printf("\t<error getting backup volume info: %v>\n", err)
|
||||
return
|
||||
} else {
|
||||
var volumeInfos []volume.VolumeInfo
|
||||
var volumeInfos []volume.BackupVolumeInfo
|
||||
if err := json.NewDecoder(buf).Decode(&volumeInfos); err != nil {
|
||||
d.Printf("\t<error reading backup volume info: %v>\n", err)
|
||||
return
|
||||
@@ -488,9 +488,9 @@ func describeBackupVolumes(ctx context.Context, kbClient kbclient.Client, d *Des
|
||||
describePodVolumeBackups(d, details, podVolumeBackupCRs)
|
||||
}
|
||||
|
||||
func retrieveNativeSnapshotLegacy(ctx context.Context, kbClient kbclient.Client, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) ([]*volume.VolumeInfo, error) {
|
||||
func retrieveNativeSnapshotLegacy(ctx context.Context, kbClient kbclient.Client, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) ([]*volume.BackupVolumeInfo, error) {
|
||||
status := backup.Status
|
||||
nativeSnapshots := []*volume.VolumeInfo{}
|
||||
nativeSnapshots := []*volume.BackupVolumeInfo{}
|
||||
|
||||
if status.VolumeSnapshotsAttempted == 0 {
|
||||
return nativeSnapshots, nil
|
||||
@@ -507,7 +507,7 @@ func retrieveNativeSnapshotLegacy(ctx context.Context, kbClient kbclient.Client,
|
||||
}
|
||||
|
||||
for _, snap := range snapshots {
|
||||
volumeInfo := volume.VolumeInfo{
|
||||
volumeInfo := volume.BackupVolumeInfo{
|
||||
PVName: snap.Spec.PersistentVolumeName,
|
||||
NativeSnapshotInfo: &volume.NativeSnapshotInfo{
|
||||
SnapshotHandle: snap.Status.ProviderSnapshotID,
|
||||
@@ -526,9 +526,9 @@ func retrieveNativeSnapshotLegacy(ctx context.Context, kbClient kbclient.Client,
|
||||
return nativeSnapshots, nil
|
||||
}
|
||||
|
||||
func retrieveCSISnapshotLegacy(ctx context.Context, kbClient kbclient.Client, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) ([]*volume.VolumeInfo, error) {
|
||||
func retrieveCSISnapshotLegacy(ctx context.Context, kbClient kbclient.Client, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) ([]*volume.BackupVolumeInfo, error) {
|
||||
status := backup.Status
|
||||
csiSnapshots := []*volume.VolumeInfo{}
|
||||
csiSnapshots := []*volume.BackupVolumeInfo{}
|
||||
|
||||
if status.CSIVolumeSnapshotsAttempted == 0 {
|
||||
return csiSnapshots, nil
|
||||
@@ -557,7 +557,7 @@ func retrieveCSISnapshotLegacy(ctx context.Context, kbClient kbclient.Client, ba
|
||||
}
|
||||
|
||||
for _, vsc := range vscList {
|
||||
volInfo := volume.VolumeInfo{
|
||||
volInfo := volume.BackupVolumeInfo{
|
||||
PreserveLocalSnapshot: true,
|
||||
CSISnapshotInfo: &volume.CSISnapshotInfo{
|
||||
VSCName: vsc.Name,
|
||||
@@ -598,7 +598,7 @@ func retrieveCSISnapshotLegacy(ctx context.Context, kbClient kbclient.Client, ba
|
||||
return csiSnapshots, nil
|
||||
}
|
||||
|
||||
func describeNativeSnapshots(d *Describer, details bool, infos []*volume.VolumeInfo) {
|
||||
func describeNativeSnapshots(d *Describer, details bool, infos []*volume.BackupVolumeInfo) {
|
||||
if len(infos) == 0 {
|
||||
d.Printf("\tVelero-Native Snapshots: <none included>\n")
|
||||
return
|
||||
@@ -610,7 +610,7 @@ func describeNativeSnapshots(d *Describer, details bool, infos []*volume.VolumeI
|
||||
}
|
||||
}
|
||||
|
||||
func describNativeSnapshot(d *Describer, details bool, info *volume.VolumeInfo) {
|
||||
func describNativeSnapshot(d *Describer, details bool, info *volume.BackupVolumeInfo) {
|
||||
if details {
|
||||
d.Printf("\t\t%s:\n", info.PVName)
|
||||
d.Printf("\t\t\tSnapshot ID:\t%s\n", info.NativeSnapshotInfo.SnapshotHandle)
|
||||
@@ -622,7 +622,7 @@ func describNativeSnapshot(d *Describer, details bool, info *volume.VolumeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
func describeCSISnapshots(d *Describer, details bool, infos []*volume.VolumeInfo, legacyInfoSource bool) {
|
||||
func describeCSISnapshots(d *Describer, details bool, infos []*volume.BackupVolumeInfo, legacyInfoSource bool) {
|
||||
if len(infos) == 0 {
|
||||
if legacyInfoSource {
|
||||
d.Printf("\tCSI Snapshots: <none included or not detectable>\n")
|
||||
@@ -638,14 +638,14 @@ func describeCSISnapshots(d *Describer, details bool, infos []*volume.VolumeInfo
|
||||
}
|
||||
}
|
||||
|
||||
func describeCSISnapshot(d *Describer, details bool, info *volume.VolumeInfo) {
|
||||
func describeCSISnapshot(d *Describer, details bool, info *volume.BackupVolumeInfo) {
|
||||
d.Printf("\t\t%s:\n", fmt.Sprintf("%s/%s", info.PVCNamespace, info.PVCName))
|
||||
|
||||
describeLocalSnapshot(d, details, info)
|
||||
describeDataMovement(d, details, info)
|
||||
}
|
||||
|
||||
func describeLocalSnapshot(d *Describer, details bool, info *volume.VolumeInfo) {
|
||||
func describeLocalSnapshot(d *Describer, details bool, info *volume.BackupVolumeInfo) {
|
||||
if !info.PreserveLocalSnapshot {
|
||||
return
|
||||
}
|
||||
@@ -665,7 +665,7 @@ func describeLocalSnapshot(d *Describer, details bool, info *volume.VolumeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
func describeDataMovement(d *Describer, details bool, info *volume.VolumeInfo) {
|
||||
func describeDataMovement(d *Describer, details bool, info *volume.BackupVolumeInfo) {
|
||||
if !info.SnapshotDataMoved {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -325,13 +325,13 @@ OrderedResources:
|
||||
func TestDescribeNativeSnapshots(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
volumeInfo []*volume.VolumeInfo
|
||||
volumeInfo []*volume.BackupVolumeInfo
|
||||
inputDetails bool
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "no details",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.NativeSnapshot,
|
||||
PVName: "pv-1",
|
||||
@@ -349,7 +349,7 @@ func TestDescribeNativeSnapshots(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "details",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.NativeSnapshot,
|
||||
PVName: "pv-1",
|
||||
@@ -390,27 +390,27 @@ func TestDescribeNativeSnapshots(t *testing.T) {
|
||||
func TestCSISnapshots(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
volumeInfo []*volume.VolumeInfo
|
||||
volumeInfo []*volume.BackupVolumeInfo
|
||||
inputDetails bool
|
||||
expect string
|
||||
legacyInfoSource bool
|
||||
}{
|
||||
{
|
||||
name: "empty info, not legacy",
|
||||
volumeInfo: []*volume.VolumeInfo{},
|
||||
volumeInfo: []*volume.BackupVolumeInfo{},
|
||||
expect: ` CSI Snapshots: <none included>
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "empty info, legacy",
|
||||
volumeInfo: []*volume.VolumeInfo{},
|
||||
volumeInfo: []*volume.BackupVolumeInfo{},
|
||||
legacyInfoSource: true,
|
||||
expect: ` CSI Snapshots: <none included or not detectable>
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "no details, local snapshot",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
PVCNamespace: "pvc-ns-1",
|
||||
@@ -432,7 +432,7 @@ func TestCSISnapshots(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "details, local snapshot",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
PVCNamespace: "pvc-ns-2",
|
||||
@@ -460,7 +460,7 @@ func TestCSISnapshots(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "no details, data movement",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
PVCNamespace: "pvc-ns-3",
|
||||
@@ -481,7 +481,7 @@ func TestCSISnapshots(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "details, data movement",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
PVCNamespace: "pvc-ns-4",
|
||||
@@ -506,7 +506,7 @@ func TestCSISnapshots(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "details, data movement, data mover is empty",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
PVCNamespace: "pvc-ns-5",
|
||||
|
||||
@@ -308,8 +308,8 @@ func describeBackupVolumesInSF(ctx context.Context, kbClient kbclient.Client, ba
|
||||
|
||||
backupVolumes := make(map[string]interface{})
|
||||
|
||||
nativeSnapshots := []*volume.VolumeInfo{}
|
||||
csiSnapshots := []*volume.VolumeInfo{}
|
||||
nativeSnapshots := []*volume.BackupVolumeInfo{}
|
||||
csiSnapshots := []*volume.BackupVolumeInfo{}
|
||||
legacyInfoSource := false
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
@@ -332,7 +332,7 @@ func describeBackupVolumesInSF(ctx context.Context, kbClient kbclient.Client, ba
|
||||
backupVolumes["errorGetBackupVolumeInfo"] = fmt.Sprintf("error getting backup volume info: %v", err)
|
||||
return
|
||||
} else {
|
||||
var volumeInfos []volume.VolumeInfo
|
||||
var volumeInfos []volume.BackupVolumeInfo
|
||||
if err := json.NewDecoder(buf).Decode(&volumeInfos); err != nil {
|
||||
backupVolumes["errorReadBackupVolumeInfo"] = fmt.Sprintf("error reading backup volume info: %v", err)
|
||||
return
|
||||
@@ -357,7 +357,7 @@ func describeBackupVolumesInSF(ctx context.Context, kbClient kbclient.Client, ba
|
||||
backupStatusInfo["backupVolumes"] = backupVolumes
|
||||
}
|
||||
|
||||
func describeNativeSnapshotsInSF(details bool, infos []*volume.VolumeInfo, backupVolumes map[string]interface{}) {
|
||||
func describeNativeSnapshotsInSF(details bool, infos []*volume.BackupVolumeInfo, backupVolumes map[string]interface{}) {
|
||||
if len(infos) == 0 {
|
||||
backupVolumes["nativeSnapshots"] = "<none included>"
|
||||
return
|
||||
@@ -370,7 +370,7 @@ func describeNativeSnapshotsInSF(details bool, infos []*volume.VolumeInfo, backu
|
||||
backupVolumes["nativeSnapshots"] = snapshotDetails
|
||||
}
|
||||
|
||||
func describNativeSnapshotInSF(details bool, info *volume.VolumeInfo, snapshotDetails map[string]interface{}) {
|
||||
func describNativeSnapshotInSF(details bool, info *volume.BackupVolumeInfo, snapshotDetails map[string]interface{}) {
|
||||
if details {
|
||||
snapshotInfo := make(map[string]string)
|
||||
snapshotInfo["snapshotID"] = info.NativeSnapshotInfo.SnapshotHandle
|
||||
@@ -384,7 +384,7 @@ func describNativeSnapshotInSF(details bool, info *volume.VolumeInfo, snapshotDe
|
||||
}
|
||||
}
|
||||
|
||||
func describeCSISnapshotsInSF(details bool, infos []*volume.VolumeInfo, backupVolumes map[string]interface{}, legacyInfoSource bool) {
|
||||
func describeCSISnapshotsInSF(details bool, infos []*volume.BackupVolumeInfo, backupVolumes map[string]interface{}, legacyInfoSource bool) {
|
||||
if len(infos) == 0 {
|
||||
if legacyInfoSource {
|
||||
backupVolumes["csiSnapshots"] = "<none included or not detectable>"
|
||||
@@ -401,7 +401,7 @@ func describeCSISnapshotsInSF(details bool, infos []*volume.VolumeInfo, backupVo
|
||||
backupVolumes["csiSnapshots"] = snapshotDetails
|
||||
}
|
||||
|
||||
func describeCSISnapshotInSF(details bool, info *volume.VolumeInfo, snapshotDetails map[string]interface{}) {
|
||||
func describeCSISnapshotInSF(details bool, info *volume.BackupVolumeInfo, snapshotDetails map[string]interface{}) {
|
||||
snapshotDetail := make(map[string]interface{})
|
||||
|
||||
describeLocalSnapshotInSF(details, info, snapshotDetail)
|
||||
@@ -411,7 +411,7 @@ func describeCSISnapshotInSF(details bool, info *volume.VolumeInfo, snapshotDeta
|
||||
}
|
||||
|
||||
// describeLocalSnapshotInSF describes CSI volume snapshot contents in structured format.
|
||||
func describeLocalSnapshotInSF(details bool, info *volume.VolumeInfo, snapshotDetail map[string]interface{}) {
|
||||
func describeLocalSnapshotInSF(details bool, info *volume.BackupVolumeInfo, snapshotDetail map[string]interface{}) {
|
||||
if !info.PreserveLocalSnapshot {
|
||||
return
|
||||
}
|
||||
@@ -434,7 +434,7 @@ func describeLocalSnapshotInSF(details bool, info *volume.VolumeInfo, snapshotDe
|
||||
}
|
||||
}
|
||||
|
||||
func describeDataMovementInSF(details bool, info *volume.VolumeInfo, snapshotDetail map[string]interface{}) {
|
||||
func describeDataMovementInSF(details bool, info *volume.BackupVolumeInfo, snapshotDetail map[string]interface{}) {
|
||||
if !info.SnapshotDataMoved {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -284,13 +284,13 @@ func TestDescribePodVolumeBackupsInSF(t *testing.T) {
|
||||
func TestDescribeNativeSnapshotsInSF(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
volumeInfo []*volume.VolumeInfo
|
||||
volumeInfo []*volume.BackupVolumeInfo
|
||||
inputDetails bool
|
||||
expect map[string]interface{}
|
||||
}{
|
||||
{
|
||||
name: "no details",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.NativeSnapshot,
|
||||
PVName: "pv-1",
|
||||
@@ -310,7 +310,7 @@ func TestDescribeNativeSnapshotsInSF(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "details",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.NativeSnapshot,
|
||||
PVName: "pv-1",
|
||||
@@ -348,21 +348,21 @@ func TestDescribeNativeSnapshotsInSF(t *testing.T) {
|
||||
func TestDescribeCSISnapshotsInSF(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
volumeInfo []*volume.VolumeInfo
|
||||
volumeInfo []*volume.BackupVolumeInfo
|
||||
inputDetails bool
|
||||
expect map[string]interface{}
|
||||
legacyInfoSource bool
|
||||
}{
|
||||
{
|
||||
name: "empty info, not legacy",
|
||||
volumeInfo: []*volume.VolumeInfo{},
|
||||
volumeInfo: []*volume.BackupVolumeInfo{},
|
||||
expect: map[string]interface{}{
|
||||
"csiSnapshots": "<none included>",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty info, legacy",
|
||||
volumeInfo: []*volume.VolumeInfo{},
|
||||
volumeInfo: []*volume.BackupVolumeInfo{},
|
||||
legacyInfoSource: true,
|
||||
expect: map[string]interface{}{
|
||||
"csiSnapshots": "<none included or not detectable>",
|
||||
@@ -370,7 +370,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "no details, local snapshot",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
PVCNamespace: "pvc-ns-1",
|
||||
@@ -395,7 +395,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "details, local snapshot",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
PVCNamespace: "pvc-ns-2",
|
||||
@@ -427,7 +427,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "no details, data movement",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
PVCNamespace: "pvc-ns-3",
|
||||
@@ -451,7 +451,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "details, data movement",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
PVCNamespace: "pvc-ns-4",
|
||||
@@ -480,7 +480,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "details, data movement, data mover is empty",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
PVCNamespace: "pvc-ns-4",
|
||||
|
||||
@@ -107,6 +107,7 @@ type restoreReconciler struct {
|
||||
|
||||
newPluginManager func(logger logrus.FieldLogger) clientmgmt.Manager
|
||||
backupStoreGetter persistence.ObjectBackupStoreGetter
|
||||
globalCrClient client.Client
|
||||
}
|
||||
|
||||
type backupInfo struct {
|
||||
@@ -127,6 +128,7 @@ func NewRestoreReconciler(
|
||||
logFormat logging.Format,
|
||||
defaultItemOperationTimeout time.Duration,
|
||||
disableInformerCache bool,
|
||||
globalCrClient client.Client,
|
||||
) *restoreReconciler {
|
||||
r := &restoreReconciler{
|
||||
ctx: ctx,
|
||||
@@ -145,6 +147,8 @@ func NewRestoreReconciler(
|
||||
// replaced with fakes for testing.
|
||||
newPluginManager: newPluginManager,
|
||||
backupStoreGetter: backupStoreGetter,
|
||||
|
||||
globalCrClient: globalCrClient,
|
||||
}
|
||||
|
||||
// Move the periodical backup and restore metrics computing logic from controllers to here.
|
||||
@@ -521,7 +525,7 @@ func (r *restoreReconciler) runValidatedRestore(restore *api.Restore, info backu
|
||||
return errors.Wrap(err, "fail to fetch CSI VolumeSnapshots metadata")
|
||||
}
|
||||
|
||||
backupVolumeInfoMap := make(map[string]volume.VolumeInfo)
|
||||
backupVolumeInfoMap := make(map[string]volume.BackupVolumeInfo)
|
||||
volumeInfos, err := backupStore.GetBackupVolumeInfos(restore.Spec.BackupName)
|
||||
if err != nil {
|
||||
restoreLog.WithError(err).Errorf("fail to get VolumeInfos metadata file for backup %s", restore.Spec.BackupName)
|
||||
@@ -540,16 +544,17 @@ func (r *restoreReconciler) runValidatedRestore(restore *api.Restore, info backu
|
||||
}
|
||||
|
||||
restoreReq := &pkgrestore.Request{
|
||||
Log: restoreLog,
|
||||
Restore: restore,
|
||||
Backup: info.backup,
|
||||
PodVolumeBackups: podVolumeBackups,
|
||||
VolumeSnapshots: volumeSnapshots,
|
||||
BackupReader: backupFile,
|
||||
ResourceModifiers: resourceModifiers,
|
||||
DisableInformerCache: r.disableInformerCache,
|
||||
CSIVolumeSnapshots: csiVolumeSnapshots,
|
||||
VolumeInfoMap: backupVolumeInfoMap,
|
||||
Log: restoreLog,
|
||||
Restore: restore,
|
||||
Backup: info.backup,
|
||||
PodVolumeBackups: podVolumeBackups,
|
||||
VolumeSnapshots: volumeSnapshots,
|
||||
BackupReader: backupFile,
|
||||
ResourceModifiers: resourceModifiers,
|
||||
DisableInformerCache: r.disableInformerCache,
|
||||
CSIVolumeSnapshots: csiVolumeSnapshots,
|
||||
BackupVolumeInfoMap: backupVolumeInfoMap,
|
||||
RestoreVolumeInfoTracker: volume.NewRestoreVolInfoTracker(restore, restoreLog, r.globalCrClient),
|
||||
}
|
||||
restoreWarnings, restoreErrors := r.restorer.RestoreWithResolvers(restoreReq, actionsResolver, pluginManager)
|
||||
|
||||
@@ -640,6 +645,11 @@ func (r *restoreReconciler) runValidatedRestore(restore *api.Restore, info backu
|
||||
r.logger.WithError(err).Error("Error uploading restore item action operation resource list to backup storage")
|
||||
}
|
||||
|
||||
restoreReq.RestoreVolumeInfoTracker.Populate(context.TODO(), restoreReq.RestoredResourceList())
|
||||
if err := putRestoreVolumeInfoList(restore, restoreReq.RestoreVolumeInfoTracker.Result(), backupStore); err != nil {
|
||||
r.logger.WithError(err).Error("Error uploading restored volume info to backup storage")
|
||||
}
|
||||
|
||||
if restore.Status.Errors > 0 {
|
||||
if inProgressOperations {
|
||||
r.logger.Debug("Restore WaitingForPluginOperationsPartiallyFailed")
|
||||
@@ -776,6 +786,22 @@ func putOperationsForRestore(restore *api.Restore, operations []*itemoperation.R
|
||||
return nil
|
||||
}
|
||||
|
||||
func putRestoreVolumeInfoList(restore *api.Restore, volInfoList []*volume.RestoreVolumeInfo, store persistence.BackupStore) error {
|
||||
buf := new(bytes.Buffer)
|
||||
gzw := gzip.NewWriter(buf)
|
||||
defer gzw.Close()
|
||||
|
||||
if err := json.NewEncoder(gzw).Encode(volInfoList); err != nil {
|
||||
return errors.Wrap(err, "error encoding restore volume info list to JSON")
|
||||
}
|
||||
|
||||
if err := gzw.Close(); err != nil {
|
||||
return errors.Wrap(err, "error closing gzip writer")
|
||||
}
|
||||
|
||||
return store.PutRestoreVolumeInfo(restore.Name, buf)
|
||||
}
|
||||
|
||||
func downloadToTempFile(backupName string, backupStore persistence.BackupStore, logger logrus.FieldLogger) (*os.File, error) {
|
||||
readCloser, err := backupStore.GetBackupContents(backupName)
|
||||
if err != nil {
|
||||
|
||||
@@ -91,11 +91,12 @@ func TestFetchBackupInfo(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
restorer = &fakeRestorer{kbClient: fakeClient}
|
||||
logger = velerotest.NewLogger()
|
||||
pluginManager = &pluginmocks.Manager{}
|
||||
backupStore = &persistencemocks.BackupStore{}
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
fakeGlobalClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
restorer = &fakeRestorer{kbClient: fakeClient}
|
||||
logger = velerotest.NewLogger()
|
||||
pluginManager = &pluginmocks.Manager{}
|
||||
backupStore = &persistencemocks.BackupStore{}
|
||||
)
|
||||
|
||||
defer restorer.AssertExpectations(t)
|
||||
@@ -114,6 +115,7 @@ func TestFetchBackupInfo(t *testing.T) {
|
||||
formatFlag,
|
||||
60*time.Minute,
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
)
|
||||
|
||||
if test.backupStoreError == nil {
|
||||
@@ -170,9 +172,10 @@ func TestProcessQueueItemSkips(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
restorer = &fakeRestorer{kbClient: fakeClient}
|
||||
logger = velerotest.NewLogger()
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
fakeGlobalClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
restorer = &fakeRestorer{kbClient: fakeClient}
|
||||
logger = velerotest.NewLogger()
|
||||
)
|
||||
|
||||
if test.restore != nil {
|
||||
@@ -192,6 +195,7 @@ func TestProcessQueueItemSkips(t *testing.T) {
|
||||
formatFlag,
|
||||
60*time.Minute,
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
)
|
||||
|
||||
_, err := r.Reconcile(context.Background(), ctrl.Request{NamespacedName: types.NamespacedName{
|
||||
@@ -432,11 +436,12 @@ func TestRestoreReconcile(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClientBuilder(t).Build()
|
||||
restorer = &fakeRestorer{kbClient: fakeClient}
|
||||
logger = velerotest.NewLogger()
|
||||
pluginManager = &pluginmocks.Manager{}
|
||||
backupStore = &persistencemocks.BackupStore{}
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClientBuilder(t).Build()
|
||||
fakeGlobalClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
restorer = &fakeRestorer{kbClient: fakeClient}
|
||||
logger = velerotest.NewLogger()
|
||||
pluginManager = &pluginmocks.Manager{}
|
||||
backupStore = &persistencemocks.BackupStore{}
|
||||
)
|
||||
|
||||
defer restorer.AssertExpectations(t)
|
||||
@@ -459,6 +464,7 @@ func TestRestoreReconcile(t *testing.T) {
|
||||
formatFlag,
|
||||
60*time.Minute,
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
)
|
||||
|
||||
r.clock = clocktesting.NewFakeClock(now)
|
||||
@@ -500,10 +506,11 @@ func TestRestoreReconcile(t *testing.T) {
|
||||
backupStore.On("PutRestoreResults", test.backup.Name, test.restore.Name, mock.Anything).Return(nil)
|
||||
backupStore.On("PutRestoredResourceList", test.restore.Name, mock.Anything).Return(nil)
|
||||
backupStore.On("PutRestoreItemOperations", mock.Anything, mock.Anything).Return(nil)
|
||||
backupStore.On("PutRestoreVolumeInfo", test.restore.Name, mock.Anything).Return(nil)
|
||||
if test.emptyVolumeInfo == true {
|
||||
backupStore.On("GetBackupVolumeInfos", test.backup.Name).Return(nil, nil)
|
||||
} else {
|
||||
backupStore.On("GetBackupVolumeInfos", test.backup.Name).Return([]*volume.VolumeInfo{}, nil)
|
||||
backupStore.On("GetBackupVolumeInfos", test.backup.Name).Return([]*volume.BackupVolumeInfo{}, nil)
|
||||
}
|
||||
|
||||
volumeSnapshots := []*volume.Snapshot{
|
||||
@@ -626,10 +633,11 @@ func TestValidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
|
||||
formatFlag := logging.FormatText
|
||||
|
||||
var (
|
||||
logger = velerotest.NewLogger()
|
||||
pluginManager = &pluginmocks.Manager{}
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
backupStore = &persistencemocks.BackupStore{}
|
||||
logger = velerotest.NewLogger()
|
||||
pluginManager = &pluginmocks.Manager{}
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
fakeGlobalClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
backupStore = &persistencemocks.BackupStore{}
|
||||
)
|
||||
|
||||
r := NewRestoreReconciler(
|
||||
@@ -645,6 +653,7 @@ func TestValidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
|
||||
formatFlag,
|
||||
60*time.Minute,
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
)
|
||||
|
||||
restore := &velerov1api.Restore{
|
||||
@@ -719,10 +728,11 @@ func TestValidateAndCompleteWithResourceModifierSpecified(t *testing.T) {
|
||||
formatFlag := logging.FormatText
|
||||
|
||||
var (
|
||||
logger = velerotest.NewLogger()
|
||||
pluginManager = &pluginmocks.Manager{}
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
backupStore = &persistencemocks.BackupStore{}
|
||||
logger = velerotest.NewLogger()
|
||||
pluginManager = &pluginmocks.Manager{}
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
fakeGlobalClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
backupStore = &persistencemocks.BackupStore{}
|
||||
)
|
||||
|
||||
r := NewRestoreReconciler(
|
||||
@@ -738,6 +748,7 @@ func TestValidateAndCompleteWithResourceModifierSpecified(t *testing.T) {
|
||||
formatFlag,
|
||||
60*time.Minute,
|
||||
false,
|
||||
fakeGlobalClient,
|
||||
)
|
||||
|
||||
restore := &velerov1api.Restore{
|
||||
|
||||
@@ -19,7 +19,6 @@ package controller
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -39,7 +38,6 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/metrics"
|
||||
"github.com/vmware-tanzu/velero/pkg/persistence"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
|
||||
"github.com/vmware-tanzu/velero/pkg/restore"
|
||||
kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/results"
|
||||
)
|
||||
@@ -150,7 +148,7 @@ func (r *restoreFinalizerReconciler) Reconcile(ctx context.Context, req ctrl.Req
|
||||
return ctrl.Result{}, errors.Wrap(err, "error getting restoredResourceList")
|
||||
}
|
||||
|
||||
restoredPVCList := getRestoredPVCFromRestoredResourceList(restoredResourceList)
|
||||
restoredPVCList := volume.RestoredPVCFromRestoredResourceList(restoredResourceList)
|
||||
|
||||
finalizerCtx := &finalizerContext{
|
||||
logger: log,
|
||||
@@ -238,7 +236,7 @@ type finalizerContext struct {
|
||||
logger logrus.FieldLogger
|
||||
restore *velerov1api.Restore
|
||||
crClient client.Client
|
||||
volumeInfo []*volume.VolumeInfo
|
||||
volumeInfo []*volume.BackupVolumeInfo
|
||||
restoredPVCList map[string]struct{}
|
||||
}
|
||||
|
||||
@@ -277,7 +275,7 @@ func (ctx *finalizerContext) patchDynamicPVWithVolumeInfo() (errs results.Result
|
||||
}
|
||||
|
||||
pvWaitGroup.Add(1)
|
||||
go func(volInfo volume.VolumeInfo, restoredNamespace string) {
|
||||
go func(volInfo volume.BackupVolumeInfo, restoredNamespace string) {
|
||||
defer pvWaitGroup.Done()
|
||||
|
||||
semaphore <- struct{}{}
|
||||
@@ -358,23 +356,6 @@ func (ctx *finalizerContext) patchDynamicPVWithVolumeInfo() (errs results.Result
|
||||
return errs
|
||||
}
|
||||
|
||||
func getRestoredPVCFromRestoredResourceList(restoredResourceList map[string][]string) map[string]struct{} {
|
||||
pvcKey := "v1/PersistentVolumeClaim"
|
||||
pvcList := make(map[string]struct{})
|
||||
|
||||
for _, pvc := range restoredResourceList[pvcKey] {
|
||||
// the format of pvc string in restoredResourceList is like: "namespace/pvcName(status)"
|
||||
// extract the substring before "(created)" if the status in rightmost Parenthesis is "created"
|
||||
r := regexp.MustCompile(`\(([^)]+)\)`)
|
||||
matches := r.FindAllStringSubmatch(pvc, -1)
|
||||
if len(matches) > 0 && matches[len(matches)-1][1] == restore.ItemRestoreResultCreated {
|
||||
pvcList[pvc[:len(pvc)-len("(created)")]] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
return pvcList
|
||||
}
|
||||
|
||||
func needPatch(newPV *v1.PersistentVolume, pvInfo *volume.PVInfo) bool {
|
||||
if newPV.Spec.PersistentVolumeReclaimPolicy != v1.PersistentVolumeReclaimPolicy(pvInfo.ReclaimPolicy) {
|
||||
return true
|
||||
|
||||
@@ -210,7 +210,7 @@ func TestUpdateResult(t *testing.T) {
|
||||
func TestPatchDynamicPVWithVolumeInfo(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
volumeInfo []*volume.VolumeInfo
|
||||
volumeInfo []*volume.BackupVolumeInfo
|
||||
restoredPVCNames map[string]struct{}
|
||||
restore *velerov1api.Restore
|
||||
restoredPVC []*corev1api.PersistentVolumeClaim
|
||||
@@ -220,21 +220,21 @@ func TestPatchDynamicPVWithVolumeInfo(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "no applicable volumeInfo",
|
||||
volumeInfo: []*volume.VolumeInfo{{BackupMethod: "VeleroNativeSnapshot", PVCName: "pvc1"}},
|
||||
volumeInfo: []*volume.BackupVolumeInfo{{BackupMethod: "VeleroNativeSnapshot", PVCName: "pvc1"}},
|
||||
restore: builder.ForRestore(velerov1api.DefaultNamespace, "restore").Result(),
|
||||
expectedPatch: nil,
|
||||
expectedErrNum: 0,
|
||||
},
|
||||
{
|
||||
name: "no restored PVC",
|
||||
volumeInfo: []*volume.VolumeInfo{{BackupMethod: "PodVolumeBackup", PVCName: "pvc1"}},
|
||||
volumeInfo: []*volume.BackupVolumeInfo{{BackupMethod: "PodVolumeBackup", PVCName: "pvc1"}},
|
||||
restore: builder.ForRestore(velerov1api.DefaultNamespace, "restore").Result(),
|
||||
expectedPatch: nil,
|
||||
expectedErrNum: 0,
|
||||
},
|
||||
{
|
||||
name: "no applicable pv patch",
|
||||
volumeInfo: []*volume.VolumeInfo{{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{{
|
||||
BackupMethod: "PodVolumeBackup",
|
||||
PVCName: "pvc1",
|
||||
PVName: "pv1",
|
||||
@@ -256,7 +256,7 @@ func TestPatchDynamicPVWithVolumeInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "an applicable pv patch",
|
||||
volumeInfo: []*volume.VolumeInfo{{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{{
|
||||
BackupMethod: "PodVolumeBackup",
|
||||
PVCName: "pvc1",
|
||||
PVName: "pv1",
|
||||
@@ -281,7 +281,7 @@ func TestPatchDynamicPVWithVolumeInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "a mapped namespace restore",
|
||||
volumeInfo: []*volume.VolumeInfo{{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{{
|
||||
BackupMethod: "PodVolumeBackup",
|
||||
PVCName: "pvc1",
|
||||
PVName: "pv1",
|
||||
@@ -306,7 +306,7 @@ func TestPatchDynamicPVWithVolumeInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "two applicable pv patches",
|
||||
volumeInfo: []*volume.VolumeInfo{{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{{
|
||||
BackupMethod: "PodVolumeBackup",
|
||||
PVCName: "pvc1",
|
||||
PVName: "pv1",
|
||||
@@ -354,7 +354,7 @@ func TestPatchDynamicPVWithVolumeInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "an applicable pv patch with bound error",
|
||||
volumeInfo: []*volume.VolumeInfo{{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{{
|
||||
BackupMethod: "PodVolumeBackup",
|
||||
PVCName: "pvc1",
|
||||
PVName: "pv1",
|
||||
@@ -375,7 +375,7 @@ func TestPatchDynamicPVWithVolumeInfo(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "two applicable pv patches with an error",
|
||||
volumeInfo: []*volume.VolumeInfo{{
|
||||
volumeInfo: []*volume.BackupVolumeInfo{{
|
||||
BackupMethod: "PodVolumeBackup",
|
||||
PVCName: "pvc1",
|
||||
PVName: "pv1",
|
||||
@@ -454,37 +454,3 @@ func TestPatchDynamicPVWithVolumeInfo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRestoredPVCFromRestoredResourceList(t *testing.T) {
|
||||
// test empty list
|
||||
restoredResourceList := map[string][]string{}
|
||||
actual := getRestoredPVCFromRestoredResourceList(restoredResourceList)
|
||||
assert.Empty(t, actual)
|
||||
|
||||
// test no match
|
||||
restoredResourceList = map[string][]string{
|
||||
"v1/PersistentVolumeClaim": {
|
||||
"namespace1/pvc1(updated)",
|
||||
},
|
||||
"v1/PersistentVolume": {
|
||||
"namespace1/pv(created)",
|
||||
},
|
||||
}
|
||||
actual = getRestoredPVCFromRestoredResourceList(restoredResourceList)
|
||||
assert.Empty(t, actual)
|
||||
|
||||
// test matches
|
||||
restoredResourceList = map[string][]string{
|
||||
"v1/PersistentVolumeClaim": {
|
||||
"namespace1/pvc1(created)",
|
||||
"namespace2/pvc2(updated)",
|
||||
"namespace3/pvc(3)(created)",
|
||||
},
|
||||
}
|
||||
expected := map[string]struct{}{
|
||||
"namespace1/pvc1": {},
|
||||
"namespace3/pvc(3)": {},
|
||||
}
|
||||
actual = getRestoredPVCFromRestoredResourceList(restoredResourceList)
|
||||
assert.Equal(t, expected, actual)
|
||||
}
|
||||
|
||||
@@ -61,3 +61,9 @@ func NewListOptionsForBackup(name string) metav1.ListOptions {
|
||||
LabelSelector: fmt.Sprintf("%s=%s", velerov1api.BackupNameLabel, GetValidName(name)),
|
||||
}
|
||||
}
|
||||
|
||||
// NewSelectorForRestore returns a Selector based on the restore name.
|
||||
// This is useful for interacting with Listers that need a Selector.
|
||||
func NewSelectorForRestore(name string) labels.Selector {
|
||||
return labels.SelectorFromSet(map[string]string{velerov1api.RestoreNameLabel: GetValidName(name)})
|
||||
}
|
||||
|
||||
@@ -314,16 +314,16 @@ func (_m *BackupStore) GetRestoreItemOperations(name string) ([]*itemoperation.R
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetBackupVolumeInfos provides a mock function with given fields: name
|
||||
func (_m *BackupStore) GetBackupVolumeInfos(name string) ([]*volume.VolumeInfo, error) {
|
||||
// GetRestoreItemOperations provides a mock function with given fields: name
|
||||
func (_m *BackupStore) GetBackupVolumeInfos(name string) ([]*volume.BackupVolumeInfo, error) {
|
||||
ret := _m.Called(name)
|
||||
|
||||
var r0 []*volume.VolumeInfo
|
||||
if rf, ok := ret.Get(0).(func(string) []*volume.VolumeInfo); ok {
|
||||
var r0 []*volume.BackupVolumeInfo
|
||||
if rf, ok := ret.Get(0).(func(string) []*volume.BackupVolumeInfo); ok {
|
||||
r0 = rf(name)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*volume.VolumeInfo)
|
||||
r0 = ret.Get(0).([]*volume.BackupVolumeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,6 +546,19 @@ func (_m *BackupStore) PutRestoredResourceList(restore string, results io.Reader
|
||||
return r0
|
||||
}
|
||||
|
||||
// PutRestoreVolumeInfo provides a mock function with given fields: restore, results
|
||||
func (_m *BackupStore) PutRestoreVolumeInfo(restore string, results io.Reader) error {
|
||||
ret := _m.Called(restore, results)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, io.Reader) error); ok {
|
||||
r0 = rf(restore, results)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
type mockConstructorTestingTNewBackupStore interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
|
||||
@@ -74,8 +74,8 @@ type BackupStore interface {
|
||||
GetCSIVolumeSnapshots(name string) ([]*snapshotv1api.VolumeSnapshot, error)
|
||||
GetCSIVolumeSnapshotContents(name string) ([]*snapshotv1api.VolumeSnapshotContent, error)
|
||||
GetCSIVolumeSnapshotClasses(name string) ([]*snapshotv1api.VolumeSnapshotClass, error)
|
||||
GetBackupVolumeInfos(name string) ([]*volume.VolumeInfo, error)
|
||||
PutBackupVolumeInfos(name string, volumeInfo io.Reader) error
|
||||
GetBackupVolumeInfos(name string) ([]*volume.BackupVolumeInfo, error)
|
||||
GetRestoreResults(name string) (map[string]results.Result, error)
|
||||
|
||||
// BackupExists checks if the backup metadata file exists in object storage.
|
||||
@@ -88,6 +88,7 @@ type BackupStore interface {
|
||||
PutRestoredResourceList(restore string, results io.Reader) error
|
||||
PutRestoreItemOperations(restore string, restoreItemOperations io.Reader) error
|
||||
GetRestoreItemOperations(name string) ([]*itemoperation.RestoreOperation, error)
|
||||
PutRestoreVolumeInfo(restore string, volumeInfo io.Reader) error
|
||||
DeleteRestore(name string) error
|
||||
GetRestoredResourceList(name string) (map[string][]string, error)
|
||||
|
||||
@@ -498,8 +499,8 @@ func (s *objectBackupStore) GetPodVolumeBackups(name string) ([]*velerov1api.Pod
|
||||
return podVolumeBackups, nil
|
||||
}
|
||||
|
||||
func (s *objectBackupStore) GetBackupVolumeInfos(name string) ([]*volume.VolumeInfo, error) {
|
||||
volumeInfos := make([]*volume.VolumeInfo, 0)
|
||||
func (s *objectBackupStore) GetBackupVolumeInfos(name string) ([]*volume.BackupVolumeInfo, error) {
|
||||
volumeInfos := make([]*volume.BackupVolumeInfo, 0)
|
||||
|
||||
res, err := tryGet(s.objectStore, s.bucket, s.layout.getBackupVolumeInfoKey(name))
|
||||
if err != nil {
|
||||
@@ -602,6 +603,10 @@ func (s *objectBackupStore) PutRestoreItemOperations(restore string, restoreItem
|
||||
return seekAndPutObject(s.objectStore, s.bucket, s.layout.getRestoreItemOperationsKey(restore), restoreItemOperations)
|
||||
}
|
||||
|
||||
func (s *objectBackupStore) PutRestoreVolumeInfo(restore string, volumeInfo io.Reader) error {
|
||||
return seekAndPutObject(s.objectStore, s.bucket, s.layout.getRestoreVolumeInfoKey(restore), volumeInfo)
|
||||
}
|
||||
|
||||
func (s *objectBackupStore) PutBackupItemOperations(backup string, backupItemOperations io.Reader) error {
|
||||
return seekAndPutObject(s.objectStore, s.bucket, s.layout.getBackupItemOperationsKey(backup), backupItemOperations)
|
||||
}
|
||||
@@ -638,6 +643,8 @@ func (s *objectBackupStore) GetDownloadURL(target velerov1api.DownloadTarget) (s
|
||||
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getBackupResultsKey(target.Name), DownloadURLTTL)
|
||||
case velerov1api.DownloadTargetKindBackupVolumeInfos:
|
||||
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getBackupVolumeInfoKey(target.Name), DownloadURLTTL)
|
||||
case velerov1api.DownloadTargetKindRestoreVolumeInfo:
|
||||
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getRestoreVolumeInfoKey(target.Name), DownloadURLTTL)
|
||||
default:
|
||||
return "", errors.Errorf("unsupported download target kind %q", target.Kind)
|
||||
}
|
||||
|
||||
@@ -132,3 +132,7 @@ func (l *ObjectStoreLayout) getBackupResultsKey(backup string) string {
|
||||
func (l *ObjectStoreLayout) getBackupVolumeInfoKey(backup string) string {
|
||||
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-volumeinfo.json.gz", backup))
|
||||
}
|
||||
|
||||
func (l *ObjectStoreLayout) getRestoreVolumeInfoKey(restore string) string {
|
||||
return path.Join(l.subdirs["restores"], restore, fmt.Sprintf("%s-volumeinfo.json.gz", restore))
|
||||
}
|
||||
|
||||
@@ -1068,17 +1068,17 @@ func TestNewObjectBackupStoreGetterConfig(t *testing.T) {
|
||||
func TestGetBackupVolumeInfos(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
volumeInfo []*volume.VolumeInfo
|
||||
volumeInfo []*volume.BackupVolumeInfo
|
||||
volumeInfoStr string
|
||||
expectedErr string
|
||||
expectedResult []*volume.VolumeInfo
|
||||
expectedResult []*volume.BackupVolumeInfo
|
||||
}{
|
||||
{
|
||||
name: "No VolumeInfos, expect no error.",
|
||||
},
|
||||
{
|
||||
name: "Valid VolumeInfo, should pass.",
|
||||
volumeInfo: []*volume.VolumeInfo{
|
||||
name: "Valid BackupVolumeInfo, should pass.",
|
||||
volumeInfo: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
PVCName: "pvcName",
|
||||
PVName: "pvName",
|
||||
@@ -1086,7 +1086,7 @@ func TestGetBackupVolumeInfos(t *testing.T) {
|
||||
SnapshotDataMoved: false,
|
||||
},
|
||||
},
|
||||
expectedResult: []*volume.VolumeInfo{
|
||||
expectedResult: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
PVCName: "pvcName",
|
||||
PVName: "pvName",
|
||||
@@ -1096,9 +1096,9 @@ func TestGetBackupVolumeInfos(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Invalid VolumeInfo string, should also pass.",
|
||||
name: "Invalid BackupVolumeInfo string, should also pass.",
|
||||
volumeInfoStr: `[{"abc": "123", "def": "456", "pvcName": "pvcName"}]`,
|
||||
expectedResult: []*volume.VolumeInfo{
|
||||
expectedResult: []*volume.BackupVolumeInfo{
|
||||
{
|
||||
PVCName: "pvcName",
|
||||
},
|
||||
@@ -1223,7 +1223,7 @@ func TestPutBackupVolumeInfos(t *testing.T) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
harness := newObjectBackupStoreTestHarness("foo", tc.prefix)
|
||||
|
||||
volumeInfos := []*volume.VolumeInfo{
|
||||
volumeInfos := []*volume.BackupVolumeInfo{
|
||||
{
|
||||
PVCName: "test",
|
||||
},
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.42.2. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
"github.com/vmware-tanzu/velero/pkg/podvolume"
|
||||
podvolume "github.com/vmware-tanzu/velero/pkg/podvolume"
|
||||
|
||||
volume "github.com/vmware-tanzu/velero/internal/volume"
|
||||
)
|
||||
|
||||
// Restorer is an autogenerated mock type for the Restorer type
|
||||
@@ -12,13 +14,17 @@ type Restorer struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// RestorePodVolumes provides a mock function with given fields: _a0
|
||||
func (_m *Restorer) RestorePodVolumes(_a0 podvolume.RestoreData) []error {
|
||||
ret := _m.Called(_a0)
|
||||
// RestorePodVolumes provides a mock function with given fields: _a0, _a1
|
||||
func (_m *Restorer) RestorePodVolumes(_a0 podvolume.RestoreData, _a1 *volume.RestoreVolumeInfoTracker) []error {
|
||||
ret := _m.Called(_a0, _a1)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for RestorePodVolumes")
|
||||
}
|
||||
|
||||
var r0 []error
|
||||
if rf, ok := ret.Get(0).(func(podvolume.RestoreData) []error); ok {
|
||||
r0 = rf(_a0)
|
||||
if rf, ok := ret.Get(0).(func(podvolume.RestoreData, *volume.RestoreVolumeInfoTracker) []error); ok {
|
||||
r0 = rf(_a0, _a1)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]error)
|
||||
@@ -27,3 +33,17 @@ func (_m *Restorer) RestorePodVolumes(_a0 podvolume.RestoreData) []error {
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewRestorer creates a new instance of Restorer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewRestorer(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Restorer {
|
||||
mock := &Restorer{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/vmware-tanzu/velero/internal/volume"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
@@ -51,7 +53,7 @@ type RestoreData struct {
|
||||
// Restorer can execute pod volume restores of volumes in a pod.
|
||||
type Restorer interface {
|
||||
// RestorePodVolumes restores all annotated volumes in a pod.
|
||||
RestorePodVolumes(RestoreData) []error
|
||||
RestorePodVolumes(RestoreData, *volume.RestoreVolumeInfoTracker) []error
|
||||
}
|
||||
|
||||
type restorer struct {
|
||||
@@ -114,7 +116,7 @@ func newRestorer(
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *restorer) RestorePodVolumes(data RestoreData) []error {
|
||||
func (r *restorer) RestorePodVolumes(data RestoreData, tracker *volume.RestoreVolumeInfoTracker) []error {
|
||||
volumesToRestore := getVolumeBackupInfoForPod(data.PodVolumeBackups, data.Pod, data.SourceNamespace)
|
||||
if len(volumesToRestore) == 0 {
|
||||
return nil
|
||||
@@ -229,6 +231,7 @@ ForEachVolume:
|
||||
if res.Status.Phase == velerov1api.PodVolumeRestorePhaseFailed {
|
||||
errs = append(errs, errors.Errorf("pod volume restore failed: %s", res.Status.Message))
|
||||
}
|
||||
tracker.TrackPodVolume(res)
|
||||
case err := <-r.nodeAgentCheck:
|
||||
errs = append(errs, err)
|
||||
break ForEachVolume
|
||||
|
||||
@@ -22,6 +22,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
appv1 "k8s.io/api/apps/v1"
|
||||
@@ -33,6 +35,7 @@ import (
|
||||
"k8s.io/client-go/tools/cache"
|
||||
ctrlfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/vmware-tanzu/velero/internal/volume"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/repository"
|
||||
@@ -418,7 +421,7 @@ func TestRestorePodVolumes(t *testing.T) {
|
||||
PodVolumeBackups: test.pvbs,
|
||||
SourceNamespace: test.sourceNamespace,
|
||||
BackupLocation: test.bsl,
|
||||
})
|
||||
}, volume.NewRestoreVolInfoTracker(restoreObj, logrus.New(), fakeCRClient))
|
||||
|
||||
if errs == nil {
|
||||
assert.Nil(t, test.errs)
|
||||
|
||||
@@ -44,6 +44,7 @@ type pvRestorer struct {
|
||||
volumeSnapshotterGetter VolumeSnapshotterGetter
|
||||
kbclient client.Client
|
||||
credentialFileStore credentials.FileStore
|
||||
volInfoTracker *volume.RestoreVolumeInfoTracker
|
||||
}
|
||||
|
||||
func (r *pvRestorer) executePVAction(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
||||
@@ -97,6 +98,11 @@ func (r *pvRestorer) executePVAction(obj *unstructured.Unstructured) (*unstructu
|
||||
if !ok {
|
||||
return nil, errors.Errorf("unexpected type %T", updated1)
|
||||
}
|
||||
var iops int64 = 0
|
||||
if snapshotInfo.volumeIOPS != nil {
|
||||
iops = *snapshotInfo.volumeIOPS
|
||||
}
|
||||
r.volInfoTracker.TrackNativeSnapshot(updated2.GetName(), snapshotInfo.providerSnapshotID, snapshotInfo.volumeType, snapshotInfo.volumeAZ, iops)
|
||||
return updated2, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/mock"
|
||||
@@ -39,6 +41,7 @@ func defaultBackup() *builder.BackupBuilder {
|
||||
}
|
||||
|
||||
func TestExecutePVAction_NoSnapshotRestores(t *testing.T) {
|
||||
fakeClient := velerotest.NewFakeControllerRuntimeClient(t)
|
||||
tests := []struct {
|
||||
name string
|
||||
obj *unstructured.Unstructured
|
||||
@@ -115,9 +118,10 @@ func TestExecutePVAction_NoSnapshotRestores(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := &pvRestorer{
|
||||
logger: velerotest.NewLogger(),
|
||||
restorePVs: tc.restore.Spec.RestorePVs,
|
||||
kbclient: velerotest.NewFakeControllerRuntimeClient(t),
|
||||
logger: velerotest.NewLogger(),
|
||||
restorePVs: tc.restore.Spec.RestorePVs,
|
||||
kbclient: velerotest.NewFakeControllerRuntimeClient(t),
|
||||
volInfoTracker: volume.NewRestoreVolInfoTracker(tc.restore, logrus.New(), fakeClient),
|
||||
}
|
||||
if tc.backup != nil {
|
||||
r.backup = tc.backup
|
||||
@@ -180,6 +184,7 @@ func TestExecutePVAction_SnapshotRestores(t *testing.T) {
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var (
|
||||
logger = velerotest.NewLogger()
|
||||
volumeSnapshotter = new(providermocks.VolumeSnapshotter)
|
||||
volumeSnapshotterGetter = providerToVolumeSnapshotterMap(map[string]vsv1.VolumeSnapshotter{
|
||||
tc.expectedProvider: volumeSnapshotter,
|
||||
@@ -192,11 +197,12 @@ func TestExecutePVAction_SnapshotRestores(t *testing.T) {
|
||||
}
|
||||
|
||||
r := &pvRestorer{
|
||||
logger: velerotest.NewLogger(),
|
||||
logger: logger,
|
||||
backup: tc.backup,
|
||||
volumeSnapshots: tc.volumeSnapshots,
|
||||
kbclient: fakeClient,
|
||||
volumeSnapshotterGetter: volumeSnapshotterGetter,
|
||||
volInfoTracker: volume.NewRestoreVolInfoTracker(tc.restore, logger, fakeClient),
|
||||
}
|
||||
|
||||
volumeSnapshotter.On("Init", mock.Anything).Return(nil)
|
||||
|
||||
+12
-11
@@ -52,17 +52,18 @@ func resourceKey(obj runtime.Object) string {
|
||||
type Request struct {
|
||||
*velerov1api.Restore
|
||||
|
||||
Log logrus.FieldLogger
|
||||
Backup *velerov1api.Backup
|
||||
PodVolumeBackups []*velerov1api.PodVolumeBackup
|
||||
VolumeSnapshots []*volume.Snapshot
|
||||
BackupReader io.Reader
|
||||
RestoredItems map[itemKey]restoredItemStatus
|
||||
itemOperationsList *[]*itemoperation.RestoreOperation
|
||||
ResourceModifiers *resourcemodifiers.ResourceModifiers
|
||||
DisableInformerCache bool
|
||||
CSIVolumeSnapshots []*snapshotv1api.VolumeSnapshot
|
||||
VolumeInfoMap map[string]volume.VolumeInfo
|
||||
Log logrus.FieldLogger
|
||||
Backup *velerov1api.Backup
|
||||
PodVolumeBackups []*velerov1api.PodVolumeBackup
|
||||
VolumeSnapshots []*volume.Snapshot
|
||||
BackupReader io.Reader
|
||||
RestoredItems map[itemKey]restoredItemStatus
|
||||
itemOperationsList *[]*itemoperation.RestoreOperation
|
||||
ResourceModifiers *resourcemodifiers.ResourceModifiers
|
||||
DisableInformerCache bool
|
||||
CSIVolumeSnapshots []*snapshotv1api.VolumeSnapshot
|
||||
BackupVolumeInfoMap map[string]volume.BackupVolumeInfo
|
||||
RestoreVolumeInfoTracker *volume.RestoreVolumeInfoTracker
|
||||
}
|
||||
|
||||
type restoredItemStatus struct {
|
||||
|
||||
+12
-9
@@ -276,6 +276,7 @@ func (kr *kubernetesRestorer) RestoreWithResolvers(
|
||||
volumeSnapshotterGetter: volumeSnapshotterGetter,
|
||||
kbclient: kr.kbClient,
|
||||
credentialFileStore: kr.credentialFileStore,
|
||||
volInfoTracker: req.RestoreVolumeInfoTracker,
|
||||
}
|
||||
|
||||
req.RestoredItems = make(map[itemKey]restoredItemStatus)
|
||||
@@ -323,7 +324,8 @@ func (kr *kubernetesRestorer) RestoreWithResolvers(
|
||||
disableInformerCache: req.DisableInformerCache,
|
||||
featureVerifier: kr.featureVerifier,
|
||||
hookTracker: hook.NewHookTracker(),
|
||||
volumeInfoMap: req.VolumeInfoMap,
|
||||
backupVolumeInfoMap: req.BackupVolumeInfoMap,
|
||||
restoreVolumeInfoTracker: req.RestoreVolumeInfoTracker,
|
||||
}
|
||||
|
||||
return restoreCtx.execute()
|
||||
@@ -376,7 +378,8 @@ type restoreContext struct {
|
||||
disableInformerCache bool
|
||||
featureVerifier features.Verifier
|
||||
hookTracker *hook.HookTracker
|
||||
volumeInfoMap map[string]volume.VolumeInfo
|
||||
backupVolumeInfoMap map[string]volume.BackupVolumeInfo
|
||||
restoreVolumeInfoTracker *volume.RestoreVolumeInfoTracker
|
||||
}
|
||||
|
||||
type resourceClientKey struct {
|
||||
@@ -1224,8 +1227,8 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
|
||||
return warnings, errs, itemExists
|
||||
}
|
||||
|
||||
if volumeInfo, ok := ctx.volumeInfoMap[obj.GetName()]; ok {
|
||||
ctx.log.Infof("Find VolumeInfo for PV %s.", obj.GetName())
|
||||
if volumeInfo, ok := ctx.backupVolumeInfoMap[obj.GetName()]; ok {
|
||||
ctx.log.Infof("Find BackupVolumeInfo for PV %s.", obj.GetName())
|
||||
|
||||
switch volumeInfo.BackupMethod {
|
||||
case volume.NativeSnapshot:
|
||||
@@ -1258,7 +1261,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
|
||||
// want to dynamically re-provision it.
|
||||
return warnings, errs, itemExists
|
||||
|
||||
// When the PV data is skipped from backup, it's VolumeInfo BackupMethod
|
||||
// When the PV data is skipped from backup, it's BackupVolumeInfo BackupMethod
|
||||
// is not set, and it will fall into the default case.
|
||||
default:
|
||||
if hasDeleteReclaimPolicy(obj.Object) {
|
||||
@@ -1277,9 +1280,9 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: VolumeInfo is adopted and old logic is deprecated in v1.13.
|
||||
// TODO: BackupVolumeInfo is adopted and old logic is deprecated in v1.13.
|
||||
// Remove the old logic in v1.15.
|
||||
ctx.log.Infof("Cannot find VolumeInfo for PV %s.", obj.GetName())
|
||||
ctx.log.Infof("Cannot find BackupVolumeInfo for PV %s.", obj.GetName())
|
||||
|
||||
switch {
|
||||
case hasSnapshot(name, ctx.volumeSnapshots):
|
||||
@@ -1899,7 +1902,7 @@ func restorePodVolumeBackups(ctx *restoreContext, createdObj *unstructured.Unstr
|
||||
SourceNamespace: originalNamespace,
|
||||
BackupLocation: ctx.backup.Spec.StorageLocation,
|
||||
}
|
||||
if errs := ctx.podVolumeRestorer.RestorePodVolumes(data); errs != nil {
|
||||
if errs := ctx.podVolumeRestorer.RestorePodVolumes(data, ctx.restoreVolumeInfoTracker); errs != nil {
|
||||
ctx.log.WithError(kubeerrs.NewAggregate(errs)).Error("unable to successfully complete pod volume restores of pod's volumes")
|
||||
|
||||
for _, err := range errs {
|
||||
@@ -2498,7 +2501,7 @@ func (ctx *restoreContext) handlePVHasNativeSnapshot(obj *unstructured.Unstructu
|
||||
|
||||
ctx.renamedPVs[oldName] = pvName
|
||||
retObj.SetName(pvName)
|
||||
|
||||
ctx.restoreVolumeInfoTracker.RenamePVForNativeSnapshot(oldName, pvName)
|
||||
// Add the original PV name as an annotation.
|
||||
annotations := retObj.GetAnnotations()
|
||||
if annotations == nil {
|
||||
|
||||
+23
-22
@@ -70,7 +70,7 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
|
||||
apiResources []*test.APIResource
|
||||
tarball io.Reader
|
||||
want map[*test.APIResource][]string
|
||||
volumeInfoMap map[string]volume.VolumeInfo
|
||||
volumeInfoMap map[string]volume.BackupVolumeInfo
|
||||
}{
|
||||
{
|
||||
name: "Restore PV with native snapshot",
|
||||
@@ -83,7 +83,7 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
|
||||
apiResources: []*test.APIResource{
|
||||
test.PVs(),
|
||||
},
|
||||
volumeInfoMap: map[string]volume.VolumeInfo{
|
||||
volumeInfoMap: map[string]volume.BackupVolumeInfo{
|
||||
"pv-1": {
|
||||
BackupMethod: volume.NativeSnapshot,
|
||||
PVName: "pv-1",
|
||||
@@ -107,11 +107,11 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
|
||||
apiResources: []*test.APIResource{
|
||||
test.PVs(),
|
||||
},
|
||||
volumeInfoMap: map[string]volume.VolumeInfo{
|
||||
volumeInfoMap: map[string]volume.BackupVolumeInfo{
|
||||
"pv-1": {
|
||||
BackupMethod: volume.PodVolumeBackup,
|
||||
PVName: "pv-1",
|
||||
PVBInfo: &volume.PodVolumeBackupInfo{
|
||||
PVBInfo: &volume.PodVolumeInfo{
|
||||
SnapshotHandle: "testSnapshotHandle",
|
||||
Size: 100,
|
||||
NodeName: "testNode",
|
||||
@@ -133,7 +133,7 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
|
||||
apiResources: []*test.APIResource{
|
||||
test.PVs(),
|
||||
},
|
||||
volumeInfoMap: map[string]volume.VolumeInfo{
|
||||
volumeInfoMap: map[string]volume.BackupVolumeInfo{
|
||||
"pv-1": {
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
SnapshotDataMoved: false,
|
||||
@@ -158,7 +158,7 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
|
||||
apiResources: []*test.APIResource{
|
||||
test.PVs(),
|
||||
},
|
||||
volumeInfoMap: map[string]volume.VolumeInfo{
|
||||
volumeInfoMap: map[string]volume.BackupVolumeInfo{
|
||||
"pv-1": {
|
||||
BackupMethod: volume.CSISnapshot,
|
||||
SnapshotDataMoved: true,
|
||||
@@ -186,7 +186,7 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
|
||||
apiResources: []*test.APIResource{
|
||||
test.PVs(),
|
||||
},
|
||||
volumeInfoMap: map[string]volume.VolumeInfo{
|
||||
volumeInfoMap: map[string]volume.BackupVolumeInfo{
|
||||
"pv-1": {
|
||||
PVName: "pv-1",
|
||||
Skipped: true,
|
||||
@@ -207,7 +207,7 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
|
||||
apiResources: []*test.APIResource{
|
||||
test.PVs(),
|
||||
},
|
||||
volumeInfoMap: map[string]volume.VolumeInfo{
|
||||
volumeInfoMap: map[string]volume.BackupVolumeInfo{
|
||||
"pv-1": {
|
||||
PVName: "pv-1",
|
||||
Skipped: true,
|
||||
@@ -235,13 +235,13 @@ func TestRestorePVWithVolumeInfo(t *testing.T) {
|
||||
h.restorer.featureVerifier = verifier
|
||||
|
||||
data := &Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: nil,
|
||||
VolumeSnapshots: nil,
|
||||
BackupReader: tc.tarball,
|
||||
VolumeInfoMap: tc.volumeInfoMap,
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: nil,
|
||||
VolumeSnapshots: nil,
|
||||
BackupReader: tc.tarball,
|
||||
BackupVolumeInfoMap: tc.volumeInfoMap,
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
data,
|
||||
@@ -3311,12 +3311,13 @@ func TestRestorePersistentVolumes(t *testing.T) {
|
||||
}
|
||||
|
||||
data := &Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
VolumeSnapshots: tc.volumeSnapshots,
|
||||
BackupReader: tc.tarball,
|
||||
CSIVolumeSnapshots: tc.csiVolumeSnapshots,
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
VolumeSnapshots: tc.volumeSnapshots,
|
||||
BackupReader: tc.tarball,
|
||||
CSIVolumeSnapshots: tc.csiVolumeSnapshots,
|
||||
RestoreVolumeInfoTracker: volume.NewRestoreVolInfoTracker(tc.restore, h.log, test.NewFakeControllerRuntimeClient(t)),
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
data,
|
||||
@@ -3441,7 +3442,7 @@ func TestRestoreWithPodVolume(t *testing.T) {
|
||||
BackupLocation: "",
|
||||
}
|
||||
restorer.
|
||||
On("RestorePodVolumes", expectedArgs).
|
||||
On("RestorePodVolumes", expectedArgs, mock.Anything).
|
||||
Return(nil)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user