From 434e073c67b19bfa904b96c87d57b4dd74acb950 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 5 Dec 2023 15:49:35 +0800 Subject: [PATCH] csi data mover backup describe, support legacy backups Signed-off-by: Lyndon-Li --- pkg/cmd/util/output/backup_describer.go | 161 ++++++++++++++++-- .../output/backup_structured_describer.go | 50 ++++-- 2 files changed, 175 insertions(+), 36 deletions(-) diff --git a/pkg/cmd/util/output/backup_describer.go b/pkg/cmd/util/output/backup_describer.go index f9fc5a61a..390a275f7 100644 --- a/pkg/cmd/util/output/backup_describer.go +++ b/pkg/cmd/util/output/backup_describer.go @@ -22,11 +22,15 @@ import ( "encoding/json" "fmt" "sort" + "strconv" "strings" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + "github.com/pkg/errors" + "github.com/fatih/color" kbclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -438,26 +442,40 @@ func describeBackupVolumes(ctx context.Context, kbClient kbclient.Client, d *Des d.Println("Backup Volumes:") - buf := new(bytes.Buffer) - if err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeInfos, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil { - d.Printf("\t\n", err) - return - } - - var volumeInfos *volume.VolumeInfos - if err := json.NewDecoder(buf).Decode(&volumeInfos); err != nil { - d.Printf("\t\n", err) - return - } - nativeSnapshots := []*volume.VolumeInfo{} csiSnapshots := []*volume.VolumeInfo{} - for i := range volumeInfos.VolumeInfos { - switch volumeInfos.VolumeInfos[i].BackupMethod { - case volume.NativeSnapshot: - nativeSnapshots = append(nativeSnapshots, &volumeInfos.VolumeInfos[i]) - case volume.CSISnapshot: - csiSnapshots = append(csiSnapshots, &volumeInfos.VolumeInfos[i]) + + buf := new(bytes.Buffer) + err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeInfos, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath) + if err == downloadrequest.ErrNotFound { + nativeSnapshots, err = retrieveNativeSnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath) + if err != nil { + d.Printf("\t\n", err) + return + } + + csiSnapshots, err = retrieveCSISnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath) + if err != nil { + d.Printf("\t\n", err) + return + } + } else if err != nil { + d.Printf("\t\n", err) + return + } else { + var volumeInfos *volume.VolumeInfos + if err := json.NewDecoder(buf).Decode(&volumeInfos); err != nil { + d.Printf("\t\n", err) + return + } + + for i := range volumeInfos.VolumeInfos { + switch volumeInfos.VolumeInfos[i].BackupMethod { + case volume.NativeSnapshot: + nativeSnapshots = append(nativeSnapshots, &volumeInfos.VolumeInfos[i]) + case volume.CSISnapshot: + csiSnapshots = append(csiSnapshots, &volumeInfos.VolumeInfos[i]) + } } } @@ -471,6 +489,113 @@ func describeBackupVolumes(ctx context.Context, kbClient kbclient.Client, d *Des d.Println() } +func retrieveNativeSnapshotLegacy(ctx context.Context, kbClient kbclient.Client, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) ([]*volume.VolumeInfo, error) { + status := backup.Status + nativeSnapshots := []*volume.VolumeInfo{} + + if status.VolumeSnapshotsAttempted == 0 { + return nativeSnapshots, nil + } + + buf := new(bytes.Buffer) + if err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeSnapshots, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil { + return nativeSnapshots, errors.Wrapf(err, "error to download native snapshot info") + } + + var snapshots []*volume.Snapshot + if err := json.NewDecoder(buf).Decode(&snapshots); err != nil { + return nativeSnapshots, errors.Wrapf(err, "error to decode native snapshot info") + } + + for _, snap := range snapshots { + volumeInfo := volume.VolumeInfo{ + PVName: snap.Spec.PersistentVolumeName, + NativeSnapshotInfo: volume.NativeSnapshotInfo{ + SnapshotHandle: snap.Status.ProviderSnapshotID, + VolumeType: snap.Spec.VolumeType, + VolumeAZ: snap.Spec.VolumeAZ, + }, + } + + if snap.Spec.VolumeIOPS != nil { + volumeInfo.NativeSnapshotInfo.IOPS = strconv.FormatInt(*snap.Spec.VolumeIOPS, 10) + } + + nativeSnapshots = append(nativeSnapshots, &volumeInfo) + } + + return nativeSnapshots, nil +} + +func retrieveCSISnapshotLegacy(ctx context.Context, kbClient kbclient.Client, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) ([]*volume.VolumeInfo, error) { + csiSnapshots := []*volume.VolumeInfo{} + + if !features.IsEnabled(velerov1api.CSIFeatureFlag) { + return csiSnapshots, nil + } + + vsBuf := new(bytes.Buffer) + err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindCSIBackupVolumeSnapshots, vsBuf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath) + if err != nil { + return csiSnapshots, errors.Wrapf(err, "error to download vs list") + } + + var vsList []snapshotv1api.VolumeSnapshot + if err := json.NewDecoder(vsBuf).Decode(&vsList); err != nil { + return csiSnapshots, errors.Wrapf(err, "error to decode vs list") + } + + vscBuf := new(bytes.Buffer) + err = downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindCSIBackupVolumeSnapshotContents, vscBuf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath) + if err != nil { + return csiSnapshots, errors.Wrapf(err, "error to download vsc list") + } + + var vscList []snapshotv1api.VolumeSnapshotContent + if err := json.NewDecoder(vsBuf).Decode(&vscList); err != nil { + return csiSnapshots, errors.Wrapf(err, "error to decode vsc list") + } + + for _, vsc := range vscList { + volInfo := volume.VolumeInfo{ + CSISnapshotInfo: volume.CSISnapshotInfo{ + VSCName: vsc.Name, + Driver: vsc.Spec.Driver, + }, + } + + if vsc.Status != nil && vsc.Status.SnapshotHandle != nil { + volInfo.CSISnapshotInfo.SnapshotHandle = *vsc.Status.SnapshotHandle + } + + if vsc.Status != nil && vsc.Status.RestoreSize != nil { + volInfo.CSISnapshotInfo.Size = *vsc.Status.RestoreSize + } + + for _, vs := range vsList { + if vs.Spec.Source.VolumeSnapshotContentName == nil { + continue + } + + if vs.Spec.Source.PersistentVolumeClaimName == nil { + continue + } + + if *vs.Spec.Source.VolumeSnapshotContentName == vsc.Name { + volInfo.PVCName = *vs.Spec.Source.PersistentVolumeClaimName + } + } + + if volInfo.PVCName == "" { + volInfo.PVCName = "" + } + + csiSnapshots = append(csiSnapshots, &volInfo) + } + + return csiSnapshots, nil +} + func describeNativeSnapshots(d *Describer, details bool, infos []*volume.VolumeInfo) { if len(infos) == 0 { d.Printf("\tVelero-Native Snapshots: \n") diff --git a/pkg/cmd/util/output/backup_structured_describer.go b/pkg/cmd/util/output/backup_structured_describer.go index f6ec14e74..740ed7d7e 100644 --- a/pkg/cmd/util/output/backup_structured_describer.go +++ b/pkg/cmd/util/output/backup_structured_describer.go @@ -309,26 +309,40 @@ func describeBackupVolumesInSF(ctx context.Context, kbClient kbclient.Client, ba backupVolumes := make(map[string]interface{}) - buf := new(bytes.Buffer) - if err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeInfos, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil { - backupVolumes["errorGetBackupVolumeInfo"] = fmt.Sprintf("error getting backup volume info: %v", err) - return - } - - var volumeInfos *volume.VolumeInfos - if err := json.NewDecoder(buf).Decode(&volumeInfos); err != nil { - backupVolumes["errorReadBackupVolumeInfo"] = fmt.Sprintf("error reading backup volume info: %v", err) - return - } - nativeSnapshots := []*volume.VolumeInfo{} csiSnapshots := []*volume.VolumeInfo{} - for i := range volumeInfos.VolumeInfos { - switch volumeInfos.VolumeInfos[i].BackupMethod { - case volume.NativeSnapshot: - nativeSnapshots = append(nativeSnapshots, &volumeInfos.VolumeInfos[i]) - case volume.CSISnapshot: - csiSnapshots = append(csiSnapshots, &volumeInfos.VolumeInfos[i]) + + buf := new(bytes.Buffer) + err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeInfos, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath) + if err == downloadrequest.ErrNotFound { + nativeSnapshots, err = retrieveNativeSnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath) + if err != nil { + backupVolumes["errorConcludeNativeSnapshot"] = fmt.Sprintf("error concluding native snapshot info: %v", err) + return + } + + csiSnapshots, err = retrieveCSISnapshotLegacy(ctx, kbClient, backup, insecureSkipTLSVerify, caCertPath) + if err != nil { + backupVolumes["errorConcludeCSISnapshot"] = fmt.Sprintf("error concluding CSI snapshot info: %v", err) + return + } + } else if err != nil { + backupVolumes["errorGetBackupVolumeInfo"] = fmt.Sprintf("error getting backup volume info: %v", err) + return + } else { + var volumeInfos *volume.VolumeInfos + if err := json.NewDecoder(buf).Decode(&volumeInfos); err != nil { + backupVolumes["errorReadBackupVolumeInfo"] = fmt.Sprintf("error reading backup volume info: %v", err) + return + } + + for i := range volumeInfos.VolumeInfos { + switch volumeInfos.VolumeInfos[i].BackupMethod { + case volume.NativeSnapshot: + nativeSnapshots = append(nativeSnapshots, &volumeInfos.VolumeInfos[i]) + case volume.CSISnapshot: + csiSnapshots = append(csiSnapshots, &volumeInfos.VolumeInfos[i]) + } } }