diff --git a/changelogs/unreleased/7775-blackpiglet b/changelogs/unreleased/7775-blackpiglet new file mode 100644 index 000000000..ce79b6509 --- /dev/null +++ b/changelogs/unreleased/7775-blackpiglet @@ -0,0 +1 @@ +Add the result in the backup's VolumeInfo. \ No newline at end of file diff --git a/internal/volume/volumes_information.go b/internal/volume/volumes_information.go index 938b61893..88225474a 100644 --- a/internal/volume/volumes_information.go +++ b/internal/volume/volumes_information.go @@ -92,6 +92,9 @@ type BackupVolumeInfo struct { // Snapshot completes timestamp. CompletionTimestamp *metav1.Time `json:"completionTimestamp,omitempty"` + // Whether the volume data is backed up successfully. + Result VolumeResult `json:"result,omitempty"` + CSISnapshotInfo *CSISnapshotInfo `json:"csiSnapshotInfo,omitempty"` SnapshotDataMovementInfo *SnapshotDataMovementInfo `json:"snapshotDataMovementInfo,omitempty"` NativeSnapshotInfo *NativeSnapshotInfo `json:"nativeSnapshotInfo,omitempty"` @@ -99,6 +102,14 @@ type BackupVolumeInfo struct { PVInfo *PVInfo `json:"pvInfo,omitempty"` } +type VolumeResult string + +const ( + VolumeResultSucceeded VolumeResult = "succeeded" + VolumeResultFailed VolumeResult = "failed" + //VolumeResultCanceled VolumeResult = "canceled" +) + type RestoreVolumeInfo struct { // The name of the restored PVC PVCName string `json:"pvcName,omitempty"` @@ -139,6 +150,9 @@ type CSISnapshotInfo struct { // The Async Operation's ID. OperationID string `json:"operationID,omitempty"` + + // The VolumeSnapshot's Status.ReadyToUse value + ReadyToUse *bool } // SnapshotDataMovementInfo is used for displaying the snapshot data mover status. @@ -162,6 +176,9 @@ type SnapshotDataMovementInfo struct { // Moved snapshot data size. Size int64 `json:"size"` + + // The DataUpload's Status.Phase value + Phase velerov2alpha1.DataUploadPhase } // NativeSnapshotInfo is used for displaying the Velero native snapshot status. @@ -180,6 +197,9 @@ type NativeSnapshotInfo struct { // The cloud provider snapshot volume's IOPS. IOPS string `json:"iops"` + + // The NativeSnapshot's Status.Phase value + Phase SnapshotPhase } func newNativeSnapshotInfo(s *Snapshot) *NativeSnapshotInfo { @@ -192,6 +212,7 @@ func newNativeSnapshotInfo(s *Snapshot) *NativeSnapshotInfo { VolumeType: s.Spec.VolumeType, VolumeAZ: s.Spec.VolumeAZ, IOPS: strconv.FormatInt(iops, 10), + Phase: s.Status.Phase, } } @@ -219,6 +240,9 @@ type PodVolumeInfo struct { // The PVB-taken k8s node's name. // This field will be empty when the struct is used to represent a podvolumerestore. NodeName string `json:"nodeName,omitempty"` + + // The PVB's Status.Phase value + Phase velerov1api.PodVolumeBackupPhase } func newPodVolumeInfoFromPVB(pvb *velerov1api.PodVolumeBackup) *PodVolumeInfo { @@ -230,6 +254,7 @@ func newPodVolumeInfoFromPVB(pvb *velerov1api.PodVolumeBackup) *PodVolumeInfo { PodName: pvb.Spec.Pod.Name, PodNamespace: pvb.Spec.Pod.Namespace, NodeName: pvb.Spec.Node, + Phase: pvb.Status.Phase, } } @@ -349,13 +374,20 @@ func (v *BackupVolumesInformation) generateVolumeInfoForVeleroNativeSnapshot() { for _, nativeSnapshot := range v.NativeSnapshots { if pvcPVInfo := v.pvMap.retrieve(nativeSnapshot.Spec.PersistentVolumeName, "", ""); pvcPVInfo != nil { + volumeResult := VolumeResultFailed + if nativeSnapshot.Status.Phase == SnapshotPhaseCompleted { + volumeResult = VolumeResultSucceeded + } volumeInfo := &BackupVolumeInfo{ - BackupMethod: NativeSnapshot, - PVCName: pvcPVInfo.PVCName, - PVCNamespace: pvcPVInfo.PVCNamespace, - PVName: pvcPVInfo.PV.Name, - SnapshotDataMoved: false, - Skipped: false, + BackupMethod: NativeSnapshot, + PVCName: pvcPVInfo.PVCName, + PVCNamespace: pvcPVInfo.PVCNamespace, + PVName: pvcPVInfo.PV.Name, + SnapshotDataMoved: false, + Skipped: false, + // Only set Succeeded to true when the NativeSnapshot's phase is Completed, + // although NativeSnapshot doesn't check whether the snapshot creation result. + Result: volumeResult, NativeSnapshotInfo: newNativeSnapshotInfo(nativeSnapshot), PVInfo: &PVInfo{ ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy), @@ -451,6 +483,7 @@ func (v *BackupVolumesInformation) generateVolumeInfoForCSIVolumeSnapshot() { Driver: volumeSnapshotClass.Driver, SnapshotHandle: snapshotHandle, OperationID: operation.Spec.OperationID, + ReadyToUse: volumeSnapshot.Status.ReadyToUse, }, PVInfo: &PVInfo{ ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy), @@ -484,6 +517,14 @@ func (v *BackupVolumesInformation) generateVolumeInfoFromPVB() { CompletionTimestamp: pvb.Status.CompletionTimestamp, PVBInfo: newPodVolumeInfoFromPVB(pvb), } + + // Only set Succeeded to true when the PVB's phase is Completed. + if pvb.Status.Phase == velerov1api.PodVolumeBackupPhaseCompleted { + volumeInfo.Result = VolumeResultSucceeded + } else { + volumeInfo.Result = VolumeResultFailed + } + pvcName, err := pvcByPodvolume(context.TODO(), v.crClient, pvb.Spec.Pod.Name, pvb.Spec.Pod.Namespace, pvb.Spec.Volume) if err != nil { v.logger.WithError(err).Warn("Fail to get PVC from PodVolumeBackup: ", pvb.Name) @@ -582,6 +623,7 @@ func (v *BackupVolumesInformation) generateVolumeInfoFromDataUpload() { DataMover: dataMover, UploaderType: kopia, OperationID: operation.Spec.OperationID, + Phase: dataUpload.Status.Phase, }, PVInfo: &PVInfo{ ReclaimPolicy: string(pvcPVInfo.PV.Spec.PersistentVolumeReclaimPolicy), diff --git a/internal/volume/volumes_information_test.go b/internal/volume/volumes_information_test.go index a80661727..4aefad7e6 100644 --- a/internal/volume/volumes_information_test.go +++ b/internal/volume/volumes_information_test.go @@ -200,6 +200,58 @@ func TestGenerateVolumeInfoForVeleroNativeSnapshot(t *testing.T) { }, expectedVolumeInfos: []*BackupVolumeInfo{}, }, + { + name: "Normal native snapshot with failed phase", + pvMap: map[string]pvcPvInfo{ + "testPV": { + PVCName: "testPVC", + PVCNamespace: "velero", + PV: corev1api.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testPV", + Labels: map[string]string{"a": "b"}, + }, + Spec: corev1api.PersistentVolumeSpec{ + PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete, + }, + }, + }, + }, + nativeSnapshot: Snapshot{ + Spec: SnapshotSpec{ + PersistentVolumeName: "testPV", + VolumeIOPS: int64Ptr(100), + VolumeType: "ssd", + VolumeAZ: "us-central1-a", + }, + Status: SnapshotStatus{ + ProviderSnapshotID: "pvc-b31e3386-4bbb-4937-95d-7934cd62-b0a1-494b-95d7-0687440e8d0c", + Phase: SnapshotPhaseFailed, + }, + }, + expectedVolumeInfos: []*BackupVolumeInfo{ + { + PVCName: "testPVC", + PVCNamespace: "velero", + PVName: "testPV", + BackupMethod: NativeSnapshot, + Result: VolumeResultFailed, + PVInfo: &PVInfo{ + ReclaimPolicy: "Delete", + Labels: map[string]string{ + "a": "b", + }, + }, + NativeSnapshotInfo: &NativeSnapshotInfo{ + SnapshotHandle: "pvc-b31e3386-4bbb-4937-95d-7934cd62-b0a1-494b-95d7-0687440e8d0c", + VolumeType: "ssd", + VolumeAZ: "us-central1-a", + IOPS: "100", + Phase: SnapshotPhaseFailed, + }, + }, + }, + }, { name: "Normal native snapshot", pvMap: map[string]pvcPvInfo{ @@ -226,6 +278,7 @@ func TestGenerateVolumeInfoForVeleroNativeSnapshot(t *testing.T) { }, Status: SnapshotStatus{ ProviderSnapshotID: "pvc-b31e3386-4bbb-4937-95d-7934cd62-b0a1-494b-95d7-0687440e8d0c", + Phase: SnapshotPhaseCompleted, }, }, expectedVolumeInfos: []*BackupVolumeInfo{ @@ -234,6 +287,7 @@ func TestGenerateVolumeInfoForVeleroNativeSnapshot(t *testing.T) { PVCNamespace: "velero", PVName: "testPV", BackupMethod: NativeSnapshot, + Result: VolumeResultSucceeded, PVInfo: &PVInfo{ ReclaimPolicy: "Delete", Labels: map[string]string{ @@ -245,6 +299,7 @@ func TestGenerateVolumeInfoForVeleroNativeSnapshot(t *testing.T) { VolumeType: "ssd", VolumeAZ: "us-central1-a", IOPS: "100", + Phase: SnapshotPhaseCompleted, }, }, }, @@ -274,6 +329,7 @@ func TestGenerateVolumeInfoForVeleroNativeSnapshot(t *testing.T) { func TestGenerateVolumeInfoForCSIVolumeSnapshot(t *testing.T) { resourceQuantity := resource.MustParse("100Gi") now := metav1.Now() + readyToUse := true tests := []struct { name string volumeSnapshot snapshotv1api.VolumeSnapshot @@ -381,6 +437,7 @@ func TestGenerateVolumeInfoForCSIVolumeSnapshot(t *testing.T) { BoundVolumeSnapshotContentName: stringPtr("testContent"), CreationTime: &now, RestoreSize: &resourceQuantity, + ReadyToUse: &readyToUse, }, }, volumeSnapshotClass: *builder.ForVolumeSnapshotClass("testClass").Driver("pd.csi.storage.gke.io").Result(), @@ -427,6 +484,7 @@ func TestGenerateVolumeInfoForCSIVolumeSnapshot(t *testing.T) { Size: 107374182400, VSCName: "testContent", OperationID: "testID", + ReadyToUse: &readyToUse, }, PVInfo: &PVInfo{ ReclaimPolicy: "Delete", @@ -506,6 +564,7 @@ func TestGenerateVolumeInfoFromPVB(t *testing.T) { PVCNamespace: "", PVName: "", BackupMethod: PodVolumeBackup, + Result: VolumeResultFailed, PVBInfo: &PodVolumeInfo{ PodName: "testPod", PodNamespace: "velero", @@ -537,7 +596,7 @@ func TestGenerateVolumeInfoFromPVB(t *testing.T) { expectedVolumeInfos: []*BackupVolumeInfo{}, }, { - name: "PVB's volume has a PVC", + name: "PVB's volume has a PVC with failed phase", pvMap: map[string]pvcPvInfo{ "testPV": { PVCName: "testPVC", @@ -553,7 +612,13 @@ func TestGenerateVolumeInfoFromPVB(t *testing.T) { }, }, }, - pvb: builder.ForPodVolumeBackup("velero", "testPVB").PodName("testPod").PodNamespace("velero").StartTimestamp(&now).CompletionTimestamp(&now).Result(), + pvb: builder.ForPodVolumeBackup("velero", "testPVB"). + PodName("testPod"). + PodNamespace("velero"). + StartTimestamp(&now). + CompletionTimestamp(&now). + Phase(velerov1api.PodVolumeBackupPhaseFailed). + Result(), pod: builder.ForPod("velero", "testPod").Containers(&corev1api.Container{ Name: "test", VolumeMounts: []corev1api.VolumeMount{ @@ -580,9 +645,74 @@ func TestGenerateVolumeInfoFromPVB(t *testing.T) { BackupMethod: PodVolumeBackup, StartTimestamp: &now, CompletionTimestamp: &now, + Result: VolumeResultFailed, PVBInfo: &PodVolumeInfo{ PodName: "testPod", PodNamespace: "velero", + Phase: velerov1api.PodVolumeBackupPhaseFailed, + }, + PVInfo: &PVInfo{ + ReclaimPolicy: string(corev1api.PersistentVolumeReclaimDelete), + Labels: map[string]string{"a": "b"}, + }, + }, + }, + }, + { + name: "PVB's volume has a PVC", + pvMap: map[string]pvcPvInfo{ + "testPV": { + PVCName: "testPVC", + PVCNamespace: "velero", + PV: corev1api.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: "testPV", + Labels: map[string]string{"a": "b"}, + }, + Spec: corev1api.PersistentVolumeSpec{ + PersistentVolumeReclaimPolicy: corev1api.PersistentVolumeReclaimDelete, + }, + }, + }, + }, + pvb: builder.ForPodVolumeBackup("velero", "testPVB"). + PodName("testPod"). + PodNamespace("velero"). + StartTimestamp(&now). + CompletionTimestamp(&now). + Phase(velerov1api.PodVolumeBackupPhaseCompleted). + Result(), + pod: builder.ForPod("velero", "testPod").Containers(&corev1api.Container{ + Name: "test", + VolumeMounts: []corev1api.VolumeMount{ + { + Name: "testVolume", + MountPath: "/data", + }, + }, + }).Volumes( + &corev1api.Volume{ + Name: "", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{ + ClaimName: "testPVC", + }, + }, + }, + ).Result(), + expectedVolumeInfos: []*BackupVolumeInfo{ + { + PVCName: "testPVC", + PVCNamespace: "velero", + PVName: "testPV", + BackupMethod: PodVolumeBackup, + StartTimestamp: &now, + CompletionTimestamp: &now, + Result: VolumeResultSucceeded, + PVBInfo: &PodVolumeInfo{ + PodName: "testPod", + PodNamespace: "velero", + Phase: velerov1api.PodVolumeBackupPhaseCompleted, }, PVInfo: &PVInfo{ ReclaimPolicy: string(corev1api.PersistentVolumeReclaimDelete), @@ -770,10 +900,15 @@ func TestGenerateVolumeInfoFromDataUpload(t *testing.T) { }, { name: "Normal DataUpload case", - dataUpload: builder.ForDataUpload("velero", "testDU").DataMover("velero").CSISnapshot(&velerov2alpha1.CSISnapshotSpec{ - VolumeSnapshot: "testVS", - SnapshotClass: "testClass", - }).SnapshotID("testSnapshotHandle").StartTimestamp(&now).Result(), + dataUpload: builder.ForDataUpload("velero", "testDU"). + DataMover("velero"). + CSISnapshot(&velerov2alpha1.CSISnapshotSpec{ + VolumeSnapshot: "testVS", + SnapshotClass: "testClass", + }).SnapshotID("testSnapshotHandle"). + StartTimestamp(&now). + Phase(velerov2alpha1.DataUploadPhaseCompleted). + Result(), volumeSnapshotClass: builder.ForVolumeSnapshotClass("testClass").Driver("pd.csi.storage.gke.io").Result(), operation: &itemoperation.BackupOperation{ Spec: itemoperation.BackupOperationSpec{ @@ -832,6 +967,7 @@ func TestGenerateVolumeInfoFromDataUpload(t *testing.T) { DataMover: "velero", UploaderType: "kopia", OperationID: "testOperation", + Phase: velerov2alpha1.DataUploadPhaseCompleted, }, PVInfo: &PVInfo{ ReclaimPolicy: string(corev1api.PersistentVolumeReclaimDelete), diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 10ef71f39..9e6da6fe7 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -863,6 +863,13 @@ func updateVolumeInfos( volumeInfos[index].SnapshotDataMovementInfo.SnapshotHandle = dataUpload.Status.SnapshotID volumeInfos[index].SnapshotDataMovementInfo.RetainedSnapshot = dataUpload.Spec.CSISnapshot.VolumeSnapshot volumeInfos[index].SnapshotDataMovementInfo.Size = dataUpload.Status.Progress.TotalBytes + volumeInfos[index].SnapshotDataMovementInfo.Phase = dataUpload.Status.Phase + + if dataUpload.Status.Phase == velerov2alpha1.DataUploadPhaseCompleted { + volumeInfos[index].Result = volume.VolumeResultSucceeded + } else { + volumeInfos[index].Result = volume.VolumeResultFailed + } } } } @@ -879,6 +886,13 @@ func updateVolumeInfos( // VSC and VS status. When the controller finds they reach to the ReadyToUse state, // The operation.Status.Updated is set as the found time. volumeInfos[volumeIndex].CompletionTimestamp = operations[opIndex].Status.Updated + + // Set Succeeded to true when the operation has no error. + if operations[opIndex].Status.Error == "" { + volumeInfos[volumeIndex].Result = volume.VolumeResultSucceeded + } else { + volumeInfos[volumeIndex].Result = volume.VolumeResultFailed + } } } } diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index e787827a9..0c46070a9 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -4548,6 +4548,39 @@ func TestUpdateVolumeInfos(t *testing.T) { volumeInfos []*volume.BackupVolumeInfo expectedVolumeInfos []*volume.BackupVolumeInfo }{ + { + name: "CSISnapshot VolumeInfo update with Operation fails", + operations: []*itemoperation.BackupOperation{ + { + Spec: itemoperation.BackupOperationSpec{ + OperationID: "test-operation", + }, + Status: itemoperation.OperationStatus{ + Error: "failed", + Updated: &now, + }, + }, + }, + volumeInfos: []*volume.BackupVolumeInfo{ + { + BackupMethod: volume.CSISnapshot, + CompletionTimestamp: &metav1.Time{}, + CSISnapshotInfo: &volume.CSISnapshotInfo{ + OperationID: "test-operation", + }, + }, + }, + expectedVolumeInfos: []*volume.BackupVolumeInfo{ + { + BackupMethod: volume.CSISnapshot, + CompletionTimestamp: &now, + Result: volume.VolumeResultFailed, + CSISnapshotInfo: &volume.CSISnapshotInfo{ + OperationID: "test-operation", + }, + }, + }, + }, { name: "CSISnapshot VolumeInfo update", operations: []*itemoperation.BackupOperation{ @@ -4573,6 +4606,7 @@ func TestUpdateVolumeInfos(t *testing.T) { { BackupMethod: volume.CSISnapshot, CompletionTimestamp: &now, + Result: volume.VolumeResultSucceeded, CSISnapshotInfo: &volume.CSISnapshotInfo{ OperationID: "test-operation", }, @@ -4580,13 +4614,14 @@ func TestUpdateVolumeInfos(t *testing.T) { }, }, { - name: "DataUpload VolumeInfo update", + name: "DataUpload VolumeInfo update with fail phase", operations: []*itemoperation.BackupOperation{}, dataUpload: builder.ForDataUpload("velero", "du-1"). CompletionTimestamp(&now). CSISnapshot(&velerov2alpha1.CSISnapshotSpec{VolumeSnapshot: "vs-1"}). SnapshotID("snapshot-id"). Progress(shared.DataMoveOperationProgress{TotalBytes: 1000}). + Phase(velerov2alpha1.DataUploadPhaseFailed). SourceNamespace("ns-1"). SourcePVC("pvc-1"). Result(), @@ -4605,11 +4640,51 @@ func TestUpdateVolumeInfos(t *testing.T) { PVCName: "pvc-1", PVCNamespace: "ns-1", CompletionTimestamp: &now, + Result: volume.VolumeResultFailed, SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{ DataMover: "velero", RetainedSnapshot: "vs-1", SnapshotHandle: "snapshot-id", Size: 1000, + Phase: velerov2alpha1.DataUploadPhaseFailed, + }, + }, + }, + }, + { + name: "DataUpload VolumeInfo update", + operations: []*itemoperation.BackupOperation{}, + dataUpload: builder.ForDataUpload("velero", "du-1"). + CompletionTimestamp(&now). + CSISnapshot(&velerov2alpha1.CSISnapshotSpec{VolumeSnapshot: "vs-1"}). + SnapshotID("snapshot-id"). + Progress(shared.DataMoveOperationProgress{TotalBytes: 1000}). + Phase(velerov2alpha1.DataUploadPhaseCompleted). + SourceNamespace("ns-1"). + SourcePVC("pvc-1"). + Result(), + volumeInfos: []*volume.BackupVolumeInfo{ + { + PVCName: "pvc-1", + PVCNamespace: "ns-1", + CompletionTimestamp: &metav1.Time{}, + SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{ + DataMover: "velero", + }, + }, + }, + expectedVolumeInfos: []*volume.BackupVolumeInfo{ + { + PVCName: "pvc-1", + PVCNamespace: "ns-1", + CompletionTimestamp: &now, + Result: volume.VolumeResultSucceeded, + SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{ + DataMover: "velero", + RetainedSnapshot: "vs-1", + SnapshotHandle: "snapshot-id", + Size: 1000, + Phase: velerov2alpha1.DataUploadPhaseCompleted, }, }, }, diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index 7bf86db3a..b81bad002 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -619,6 +619,7 @@ func describNativeSnapshot(d *Describer, details bool, info *volume.BackupVolume d.Printf("\t\t\tType:\t%s\n", info.NativeSnapshotInfo.VolumeType) d.Printf("\t\t\tAvailability Zone:\t%s\n", info.NativeSnapshotInfo.VolumeAZ) d.Printf("\t\t\tIOPS:\t%s\n", info.NativeSnapshotInfo.IOPS) + d.Printf("\t\t\tResult:\t%s\n", info.Result) } else { d.Printf("\t\t%s: specify --details for more information\n", info.PVName) } @@ -662,6 +663,7 @@ func describeLocalSnapshot(d *Describer, details bool, info *volume.BackupVolume d.Printf("\t\t\t\tStorage Snapshot ID: %s\n", info.CSISnapshotInfo.SnapshotHandle) d.Printf("\t\t\t\tSnapshot Size (bytes): %d\n", info.CSISnapshotInfo.Size) d.Printf("\t\t\t\tCSI Driver: %s\n", info.CSISnapshotInfo.Driver) + d.Printf("\t\t\t\tResult: %s\n", info.Result) } else { d.Printf("\t\t\tSnapshot: %s\n", "included, specify --details for more information") } @@ -683,6 +685,7 @@ func describeDataMovement(d *Describer, details bool, info *volume.BackupVolumeI d.Printf("\t\t\t\tData Mover: %s\n", dataMover) d.Printf("\t\t\t\tUploader Type: %s\n", info.SnapshotDataMovementInfo.UploaderType) d.Printf("\t\t\t\tMoved data Size (bytes): %d\n", info.SnapshotDataMovementInfo.Size) + d.Printf("\t\t\t\tResult: %s\n", info.Result) } else { d.Printf("\t\t\tData Movement: %s\n", "included, specify --details for more information") } diff --git a/pkg/cmd/util/output/backup_describer_test.go b/pkg/cmd/util/output/backup_describer_test.go index ac6ed05e0..8fc09fbe9 100644 --- a/pkg/cmd/util/output/backup_describer_test.go +++ b/pkg/cmd/util/output/backup_describer_test.go @@ -22,17 +22,15 @@ import ( "text/tabwriter" "time" - "github.com/vmware-tanzu/velero/internal/volume" - "github.com/vmware-tanzu/velero/pkg/itemoperation" - - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" - "github.com/vmware-tanzu/velero/pkg/builder" - + "github.com/vmware-tanzu/velero/internal/volume" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + "github.com/vmware-tanzu/velero/pkg/builder" + "github.com/vmware-tanzu/velero/pkg/itemoperation" ) func TestDescribeUploaderConfig(t *testing.T) { @@ -353,6 +351,7 @@ func TestDescribeNativeSnapshots(t *testing.T) { { BackupMethod: volume.NativeSnapshot, PVName: "pv-1", + Result: volume.VolumeResultSucceeded, NativeSnapshotInfo: &volume.NativeSnapshotInfo{ SnapshotHandle: "snapshot-1", VolumeType: "ebs", @@ -368,6 +367,7 @@ func TestDescribeNativeSnapshots(t *testing.T) { Type: ebs Availability Zone: us-east-2 IOPS: 1000 mbps + Result: succeeded `, }, } @@ -438,6 +438,7 @@ func TestCSISnapshots(t *testing.T) { PVCNamespace: "pvc-ns-2", PVCName: "pvc-2", PreserveLocalSnapshot: true, + Result: volume.VolumeResultSucceeded, CSISnapshotInfo: &volume.CSISnapshotInfo{ SnapshotHandle: "snapshot-2", Size: 1024, @@ -456,6 +457,7 @@ func TestCSISnapshots(t *testing.T) { Storage Snapshot ID: snapshot-2 Snapshot Size (bytes): 1024 CSI Driver: fake-driver + Result: succeeded `, }, { @@ -487,6 +489,7 @@ func TestCSISnapshots(t *testing.T) { PVCNamespace: "pvc-ns-4", PVCName: "pvc-4", SnapshotDataMoved: true, + Result: volume.VolumeResultSucceeded, SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{ DataMover: "velero", UploaderType: "fake-uploader", @@ -503,6 +506,7 @@ func TestCSISnapshots(t *testing.T) { Data Mover: velero Uploader Type: fake-uploader Moved data Size (bytes): 0 + Result: succeeded `, }, { @@ -512,12 +516,14 @@ func TestCSISnapshots(t *testing.T) { BackupMethod: volume.CSISnapshot, PVCNamespace: "pvc-ns-5", PVCName: "pvc-5", + Result: volume.VolumeResultFailed, SnapshotDataMoved: true, SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{ UploaderType: "fake-uploader", SnapshotHandle: "fake-repo-id-5", OperationID: "fake-operation-5", Size: 100, + Phase: velerov2alpha1.DataUploadPhaseFailed, }, }, }, @@ -529,6 +535,7 @@ func TestCSISnapshots(t *testing.T) { Data Mover: velero Uploader Type: fake-uploader Moved data Size (bytes): 100 + Result: failed `, }, } diff --git a/pkg/cmd/util/output/backup_structured_describer.go b/pkg/cmd/util/output/backup_structured_describer.go index f04c7c0e1..63bfbbd5b 100644 --- a/pkg/cmd/util/output/backup_structured_describer.go +++ b/pkg/cmd/util/output/backup_structured_describer.go @@ -371,6 +371,7 @@ func describNativeSnapshotInSF(details bool, info *volume.BackupVolumeInfo, snap snapshotInfo["type"] = info.NativeSnapshotInfo.VolumeType snapshotInfo["availabilityZone"] = info.NativeSnapshotInfo.VolumeAZ snapshotInfo["IOPS"] = info.NativeSnapshotInfo.IOPS + snapshotInfo["result"] = string(info.Result) snapshotDetails[info.PVName] = snapshotInfo } else { @@ -421,6 +422,7 @@ func describeLocalSnapshotInSF(details bool, info *volume.BackupVolumeInfo, snap localSnapshot["storageSnapshotID"] = info.CSISnapshotInfo.SnapshotHandle localSnapshot["snapshotSize(bytes)"] = info.CSISnapshotInfo.Size localSnapshot["csiDriver"] = info.CSISnapshotInfo.Driver + localSnapshot["result"] = string(info.Result) snapshotDetail["snapshot"] = localSnapshot } else { @@ -444,6 +446,7 @@ func describeDataMovementInSF(details bool, info *volume.BackupVolumeInfo, snaps dataMovement["dataMover"] = dataMover dataMovement["uploaderType"] = info.SnapshotDataMovementInfo.UploaderType + dataMovement["result"] = string(info.Result) snapshotDetail["dataMovement"] = dataMovement } else { diff --git a/pkg/cmd/util/output/backup_structured_describer_test.go b/pkg/cmd/util/output/backup_structured_describer_test.go index 29fe83cbe..bd1686a34 100644 --- a/pkg/cmd/util/output/backup_structured_describer_test.go +++ b/pkg/cmd/util/output/backup_structured_describer_test.go @@ -21,17 +21,14 @@ import ( "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - v1 "k8s.io/api/core/v1" "github.com/vmware-tanzu/velero/internal/volume" - "github.com/vmware-tanzu/velero/pkg/util/results" - - "github.com/stretchr/testify/assert" - velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" + "github.com/vmware-tanzu/velero/pkg/util/results" ) func TestDescribeBackupInSF(t *testing.T) { @@ -314,6 +311,7 @@ func TestDescribeNativeSnapshotsInSF(t *testing.T) { { BackupMethod: volume.NativeSnapshot, PVName: "pv-1", + Result: volume.VolumeResultSucceeded, NativeSnapshotInfo: &volume.NativeSnapshotInfo{ SnapshotHandle: "snapshot-1", VolumeType: "ebs", @@ -330,6 +328,7 @@ func TestDescribeNativeSnapshotsInSF(t *testing.T) { "type": "ebs", "availabilityZone": "us-east-2", "IOPS": "1000 mbps", + "result": "succeeded", }, }, }, @@ -401,6 +400,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) { PVCNamespace: "pvc-ns-2", PVCName: "pvc-2", PreserveLocalSnapshot: true, + Result: volume.VolumeResultSucceeded, CSISnapshotInfo: &volume.CSISnapshotInfo{ SnapshotHandle: "snapshot-2", Size: 1024, @@ -420,6 +420,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) { "storageSnapshotID": "snapshot-2", "snapshotSize(bytes)": int64(1024), "csiDriver": "fake-driver", + "result": "succeeded", }, }, }, @@ -457,6 +458,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) { PVCNamespace: "pvc-ns-4", PVCName: "pvc-4", SnapshotDataMoved: true, + Result: volume.VolumeResultSucceeded, SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{ DataMover: "velero", UploaderType: "fake-uploader", @@ -473,6 +475,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) { "operationID": "fake-operation-4", "dataMover": "velero", "uploaderType": "fake-uploader", + "result": "succeeded", }, }, }, @@ -484,6 +487,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) { { BackupMethod: volume.CSISnapshot, PVCNamespace: "pvc-ns-4", + Result: volume.VolumeResultFailed, PVCName: "pvc-4", SnapshotDataMoved: true, SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{ @@ -501,6 +505,7 @@ func TestDescribeCSISnapshotsInSF(t *testing.T) { "operationID": "fake-operation-4", "dataMover": "velero", "uploaderType": "fake-uploader", + "result": "failed", }, }, },