Add warning/error result to cmd velero backup describe

Signed-off-by: allenxu404 <qix2@vmware.com>
This commit is contained in:
allenxu404
2023-03-15 14:20:47 +08:00
parent 1d8ca4f2ef
commit a0dac73c95
8 changed files with 114 additions and 16 deletions

View File

@@ -0,0 +1 @@
Add warning/error result to cmd `velero backup describe`

View File

@@ -48,6 +48,7 @@ spec:
- BackupVolumeSnapshots
- BackupItemOperations
- BackupResourceList
- BackupResults
- RestoreLog
- RestoreResults
- RestoreResourceList

File diff suppressed because one or more lines are too long

View File

@@ -25,7 +25,7 @@ type DownloadRequestSpec struct {
}
// DownloadTargetKind represents what type of file to download.
// +kubebuilder:validation:Enum=BackupLog;BackupContents;BackupVolumeSnapshots;BackupItemOperations;BackupResourceList;RestoreLog;RestoreResults;RestoreResourceList;RestoreItemOperations;CSIBackupVolumeSnapshots;CSIBackupVolumeSnapshotContents
// +kubebuilder:validation:Enum=BackupLog;BackupContents;BackupVolumeSnapshots;BackupItemOperations;BackupResourceList;BackupResults;RestoreLog;RestoreResults;RestoreResourceList;RestoreItemOperations;CSIBackupVolumeSnapshots;CSIBackupVolumeSnapshotContents
type DownloadTargetKind string
const (
@@ -34,6 +34,7 @@ const (
DownloadTargetKindBackupVolumeSnapshots DownloadTargetKind = "BackupVolumeSnapshots"
DownloadTargetKindBackupItemOperations DownloadTargetKind = "BackupItemOperations"
DownloadTargetKindBackupResourceList DownloadTargetKind = "BackupResourceList"
DownloadTargetKindBackupResults DownloadTargetKind = "BackupResults"
DownloadTargetKindRestoreLog DownloadTargetKind = "RestoreLog"
DownloadTargetKindRestoreResults DownloadTargetKind = "RestoreResults"
DownloadTargetKindRestoreResourceList DownloadTargetKind = "RestoreResourceList"

View File

@@ -36,7 +36,9 @@ import (
"github.com/vmware-tanzu/velero/pkg/features"
clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned"
"github.com/vmware-tanzu/velero/pkg/itemoperation"
"github.com/vmware-tanzu/velero/pkg/util/collections"
"github.com/vmware-tanzu/velero/pkg/util/results"
"github.com/vmware-tanzu/velero/pkg/volume"
)
@@ -91,8 +93,7 @@ func DescribeBackup(
}
d.Println()
d.Printf("Errors:\t%d\n", status.Errors)
d.Printf("Warnings:\t%d\n", status.Warnings)
DescribeBackupResults(ctx, kbClient, d, backup, insecureSkipTLSVerify, caCertFile)
d.Println()
DescribeBackupSpec(d, backup.Spec)
@@ -660,3 +661,39 @@ func DescribeVSC(d *Describer, details bool, vsc snapshotv1api.VolumeSnapshotCon
d.Printf("\tReady to use: %t\n", *vsc.Status.ReadyToUse)
}
}
// DescribeBackupResults describes errors and warnings in human-readable format.
func DescribeBackupResults(ctx context.Context, kbClient kbclient.Client, d *Describer, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) {
if backup.Status.Warnings == 0 && backup.Status.Errors == 0 {
return
}
var buf bytes.Buffer
var resultMap map[string]results.Result
// If err 'ErrNotFound' occurs, it means the backup bundle in the bucket has already been there before the backup-result file is introduced.
// We only display the count of errors and warnings in this case.
err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResults, &buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath)
if err == downloadrequest.ErrNotFound {
d.Printf("Errors:\t%d\n", backup.Status.Errors)
d.Printf("Warnings:\t%d\n", backup.Status.Warnings)
return
} else if err != nil {
d.Printf("Warnings:\t<error getting warnings: %v>\n\nErrors:\t<error getting errors: %v>\n", err, err)
return
}
if err := json.NewDecoder(&buf).Decode(&resultMap); err != nil {
d.Printf("Warnings:\t<error decoding warnings: %v>\n\nErrors:\t<error decoding errors: %v>\n", err, err)
return
}
if backup.Status.Warnings > 0 {
d.Println()
describeResult(d, "Warnings", resultMap["warnings"])
}
if backup.Status.Errors > 0 {
d.Println()
describeResult(d, "Errors", resultMap["errors"])
}
}

View File

@@ -33,6 +33,7 @@ import (
"github.com/vmware-tanzu/velero/pkg/cmd/util/downloadrequest"
"github.com/vmware-tanzu/velero/pkg/features"
clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned"
"github.com/vmware-tanzu/velero/pkg/util/results"
"github.com/vmware-tanzu/velero/pkg/volume"
)
@@ -60,8 +61,7 @@ func DescribeBackupInSF(
d.Describe("validationErrors", status.ValidationErrors)
}
d.Describe("errors", status.Errors)
d.Describe("warnings", status.Warnings)
DescribeBackupResultsInSF(ctx, kbClient, d, backup, insecureSkipTLSVerify, caCertFile)
DescribeBackupSpecInSF(d, backup.Spec)
@@ -266,7 +266,7 @@ func DescribeBackupStatusInSF(ctx context.Context, kbClient kbclient.Client, d *
// In consideration of decoding structured output conveniently, the three separate fields were created here
// the field of "veleroNativeSnapshots" displays the brief snapshots info
// the field of "veleroNativeSnapshotsError" displays the error message if it fails to get snapshot info
// the field of "errorGettingSnapshots" displays the error message if it fails to get snapshot info
// the field of "veleroNativeSnapshotsDetail" displays the detailed snapshots info
if status.VolumeSnapshotsAttempted > 0 {
if !details {
@@ -276,13 +276,13 @@ func DescribeBackupStatusInSF(ctx context.Context, kbClient kbclient.Client, d *
buf := new(bytes.Buffer)
if err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupVolumeSnapshots, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
backupStatusInfo["veleroNativeSnapshotsError"] = fmt.Sprintf("<error getting snapshot info: %v>", err)
backupStatusInfo["errorGettingSnapshots"] = fmt.Sprintf("<error getting snapshot info: %v>", err)
return
}
var snapshots []*volume.Snapshot
if err := json.NewDecoder(buf).Decode(&snapshots); err != nil {
backupStatusInfo["veleroNativeSnapshotsError"] = fmt.Sprintf("<error reading snapshot info: %v>", err)
backupStatusInfo["errorGettingSnapshots"] = fmt.Sprintf("<error reading snapshot info: %v>", err)
return
}
@@ -298,7 +298,7 @@ func DescribeBackupStatusInSF(ctx context.Context, kbClient kbclient.Client, d *
func describeBackupResourceListInSF(ctx context.Context, kbClient kbclient.Client, backupStatusInfo map[string]interface{}, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) {
// In consideration of decoding structured output conveniently, the two separate fields were created here(in func describeBackupResourceList, there is only one field describing either error message or resource list)
// the field of 'resourceListError' gives specific error message when it fails to get resources list
// the field of 'errorGettingResourceList' gives specific error message when it fails to get resources list
// the field of 'resourceList' lists the rearranged resources
buf := new(bytes.Buffer)
if err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResourceList, buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath); err != nil {
@@ -308,16 +308,16 @@ func describeBackupResourceListInSF(ctx context.Context, kbClient kbclient.Clien
// - the backup hasn't completed yet; or
// - there was an error uploading the file; or
// - the file was manually deleted after upload
backupStatusInfo["resourceListError"] = "<backup resource list not found>"
backupStatusInfo["errorGettingResourceList"] = "<backup resource list not found>"
} else {
backupStatusInfo["resourceListError"] = fmt.Sprintf("<error getting backup resource list: %v>", err)
backupStatusInfo["errorGettingResourceList"] = fmt.Sprintf("<error getting backup resource list: %v>", err)
}
return
}
var resourceList map[string][]string
if err := json.NewDecoder(buf).Decode(&resourceList); err != nil {
backupStatusInfo["resourceListError"] = fmt.Sprintf("<error reading backup resource list: %v>\n", err)
backupStatusInfo["errorGettingResourceList"] = fmt.Sprintf("<error reading backup resource list: %v>\n", err)
return
}
backupStatusInfo["resourceList"] = resourceList
@@ -460,3 +460,59 @@ func DescribeVSCInSF(details bool, vsc snapshotv1api.VolumeSnapshotContent, vscD
}
vscDetails[vsc.Name] = content
}
// DescribeBackupResultsInSF describes errors and warnings in structured format.
func DescribeBackupResultsInSF(ctx context.Context, kbClient kbclient.Client, d *StructuredDescriber, backup *velerov1api.Backup, insecureSkipTLSVerify bool, caCertPath string) {
if backup.Status.Warnings == 0 && backup.Status.Errors == 0 {
return
}
var buf bytes.Buffer
var resultMap map[string]results.Result
errors, warnings := make(map[string]interface{}), make(map[string]interface{})
defer func() {
d.Describe("errors", errors)
d.Describe("warnings", warnings)
}()
// If 'ErrNotFound' occurs, it means the backup bundle in the bucket has already been there before the backup-result file is introduced.
// We only display the count of errors and warnings in this case.
err := downloadrequest.Stream(ctx, kbClient, backup.Namespace, backup.Name, velerov1api.DownloadTargetKindBackupResults, &buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertPath)
if err == downloadrequest.ErrNotFound {
errors["count"] = backup.Status.Errors
warnings["count"] = backup.Status.Warnings
return
} else if err != nil {
errors["errorGettingErrors"] = fmt.Errorf("<error getting errors: %v>", err)
warnings["errorGettingWarnings"] = fmt.Errorf("<error getting warnings: %v>", err)
return
}
if err := json.NewDecoder(&buf).Decode(&resultMap); err != nil {
errors["errorGettingErrors"] = fmt.Errorf("<error decoding errors: %v>", err)
warnings["errorGettingWarnings"] = fmt.Errorf("<error decoding warnings: %v>", err)
return
}
if backup.Status.Warnings > 0 {
describeResultInSF(warnings, resultMap["warnings"])
}
if backup.Status.Errors > 0 {
describeResultInSF(errors, resultMap["errors"])
}
}
func describeResultInSF(m map[string]interface{}, result results.Result) {
m["velero"], m["cluster"], m["namespace"] = []string{}, []string{}, []string{}
if len(result.Velero) > 0 {
m["velero"] = result.Velero
}
if len(result.Cluster) > 0 {
m["cluster"] = result.Cluster
}
if len(result.Namespaces) > 0 {
m["namespace"] = result.Namespaces
}
}

View File

@@ -186,15 +186,15 @@ func describeRestoreResults(ctx context.Context, kbClient kbclient.Client, d *De
if restore.Status.Warnings > 0 {
d.Println()
describeRestoreResult(d, "Warnings", resultMap["warnings"])
describeResult(d, "Warnings", resultMap["warnings"])
}
if restore.Status.Errors > 0 {
d.Println()
describeRestoreResult(d, "Errors", resultMap["errors"])
describeResult(d, "Errors", resultMap["errors"])
}
}
func describeRestoreResult(d *Describer, name string, result results.Result) {
func describeResult(d *Describer, name string, result results.Result) {
d.Printf("%s:\n", name)
d.DescribeSlice(1, "Velero", result.Velero)
d.DescribeSlice(1, "Cluster", result.Cluster)

View File

@@ -584,6 +584,8 @@ func (s *objectBackupStore) GetDownloadURL(target velerov1api.DownloadTarget) (s
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getCSIVolumeSnapshotKey(target.Name), DownloadURLTTL)
case velerov1api.DownloadTargetKindCSIBackupVolumeSnapshotContents:
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getCSIVolumeSnapshotContentsKey(target.Name), DownloadURLTTL)
case velerov1api.DownloadTargetKindBackupResults:
return s.objectStore.CreateSignedURL(s.bucket, s.layout.getBackupResultsKey(target.Name), DownloadURLTTL)
default:
return "", errors.Errorf("unsupported download target kind %q", target.Kind)
}