Merge pull request #7687 from reasonerjt/restore-desc-vol-info
Display CSI snapshot restores in restore describepull/7701/head
commit
f04fbbcc41
|
@ -0,0 +1 @@
|
|||
Display CSI snapshot restores in restore describe
|
|
@ -60,6 +60,7 @@ spec:
|
|||
- CSIBackupVolumeSnapshots
|
||||
- CSIBackupVolumeSnapshotContents
|
||||
- BackupVolumeInfos
|
||||
- RestoreVolumeInfo
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the Kubernetes resource with
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -25,7 +25,7 @@ type DownloadRequestSpec struct {
|
|||
}
|
||||
|
||||
// DownloadTargetKind represents what type of file to download.
|
||||
// +kubebuilder:validation:Enum=BackupLog;BackupContents;BackupVolumeSnapshots;BackupItemOperations;BackupResourceList;BackupResults;RestoreLog;RestoreResults;RestoreResourceList;RestoreItemOperations;CSIBackupVolumeSnapshots;CSIBackupVolumeSnapshotContents;BackupVolumeInfos
|
||||
// +kubebuilder:validation:Enum=BackupLog;BackupContents;BackupVolumeSnapshots;BackupItemOperations;BackupResourceList;BackupResults;RestoreLog;RestoreResults;RestoreResourceList;RestoreItemOperations;CSIBackupVolumeSnapshots;CSIBackupVolumeSnapshotContents;BackupVolumeInfos;RestoreVolumeInfo
|
||||
type DownloadTargetKind string
|
||||
|
||||
const (
|
||||
|
|
|
@ -20,10 +20,13 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/vmware-tanzu/velero/internal/volume"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
|
@ -167,6 +170,21 @@ func DescribeRestore(ctx context.Context, kbClient kbclient.Client, restore *vel
|
|||
describePodVolumeRestores(d, podVolumeRestores, details)
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if err := downloadrequest.Stream(ctx, kbClient, restore.Namespace, restore.Name, velerov1api.DownloadTargetKindRestoreVolumeInfo,
|
||||
buf, downloadRequestTimeout, insecureSkipTLSVerify, caCertFile); err == nil {
|
||||
var restoreVolInfo []volume.RestoreVolumeInfo
|
||||
if err := json.NewDecoder(buf).Decode(&restoreVolInfo); err != nil {
|
||||
d.Printf("\t<error reading restore volume info: %v>\n", err)
|
||||
} else {
|
||||
describeCSISnapshotsRestores(d, restoreVolInfo, details)
|
||||
}
|
||||
} else if err != nil && !errors.Is(err, downloadrequest.ErrNotFound) {
|
||||
// For the restores by older versions of velero, it will see NotFound Error when downloading the volume info.
|
||||
// In that case, no errors will be printed.
|
||||
d.Printf("\t<error getting restore volume info: %v>\n", err)
|
||||
}
|
||||
|
||||
d.Println()
|
||||
s = emptyDisplay
|
||||
if restore.Spec.ExistingResourcePolicy != "" {
|
||||
|
@ -361,6 +379,50 @@ func describePodVolumeRestores(d *Describer, restores []velerov1api.PodVolumeRes
|
|||
}
|
||||
}
|
||||
|
||||
// describeCSISnapshotsRestores describes PVC restored via CSISnapshots, incl. data-movement, in human-readable format.
|
||||
func describeCSISnapshotsRestores(d *Describer, restoreVolInfo []volume.RestoreVolumeInfo, details bool) {
|
||||
d.Println()
|
||||
var nonDMInfoList, dmInfoList []volume.RestoreVolumeInfo
|
||||
for _, info := range restoreVolInfo {
|
||||
if info.RestoreMethod != volume.CSISnapshot {
|
||||
continue
|
||||
}
|
||||
if info.SnapshotDataMoved {
|
||||
dmInfoList = append(dmInfoList, info)
|
||||
} else {
|
||||
nonDMInfoList = append(nonDMInfoList, info)
|
||||
}
|
||||
}
|
||||
if len(nonDMInfoList) == 0 && len(dmInfoList) == 0 {
|
||||
d.Printf("CSI Snapshot Restores: <none included>\n")
|
||||
return
|
||||
}
|
||||
d.Printf("CSI Snapshot Restores:\n")
|
||||
for _, info := range nonDMInfoList {
|
||||
// All CSI snapshots are restored via PVC
|
||||
d.Printf("\t%s/%s:\n", info.PVCNamespace, info.PVCName)
|
||||
if details {
|
||||
d.Printf("\t\tSnapshot:\n")
|
||||
d.Printf("\t\t\tSnapshot Content Name: %s\n", info.CSISnapshotInfo.VSCName)
|
||||
d.Printf("\t\t\tStorage Snapshot ID: %s\n", info.CSISnapshotInfo.SnapshotHandle)
|
||||
d.Printf("\t\t\tCSI Driver: %s\n", info.CSISnapshotInfo.Driver)
|
||||
} else {
|
||||
d.Printf("\t\tSnapshot: specify --details for more information\n")
|
||||
}
|
||||
}
|
||||
for _, info := range dmInfoList {
|
||||
d.Printf("\t%s/%s:\n", info.PVCNamespace, info.PVCName)
|
||||
if details {
|
||||
d.Printf("\t\tData Movement:\n")
|
||||
d.Printf("\t\t\tOperation ID: %s\n", info.SnapshotDataMovementInfo.OperationID)
|
||||
d.Printf("\t\t\tData Mover: %s\n", info.SnapshotDataMovementInfo.DataMover)
|
||||
d.Printf("\t\t\tUploader Type: %s\n", info.SnapshotDataMovementInfo.UploaderType)
|
||||
} else {
|
||||
d.Printf("\t\tData Movement: specify --details for more information\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func groupRestoresByPhase(restores []velerov1api.PodVolumeRestore) map[string][]velerov1api.PodVolumeRestore {
|
||||
restoresByPhase := make(map[string][]velerov1api.PodVolumeRestore)
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ import (
|
|||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
"github.com/vmware-tanzu/velero/internal/volume"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
|
@ -237,3 +239,153 @@ func TestDescribeUploaderConfigForRestore(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeCSISnapshotsRestore(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
inputVolInfoList []volume.RestoreVolumeInfo
|
||||
inputDetail bool
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "empty list",
|
||||
inputVolInfoList: []volume.RestoreVolumeInfo{},
|
||||
inputDetail: true,
|
||||
expect: `
|
||||
CSI Snapshot Restores: <none included>
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "list with non CSI snapshot",
|
||||
inputVolInfoList: []volume.RestoreVolumeInfo{
|
||||
{
|
||||
PVCName: "pvc-2",
|
||||
PVCNamespace: "ns-2",
|
||||
PVName: "pv-2",
|
||||
RestoreMethod: volume.NativeSnapshot,
|
||||
NativeSnapshotInfo: &volume.NativeSnapshotInfo{
|
||||
SnapshotHandle: "snap-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
inputDetail: true,
|
||||
expect: `
|
||||
CSI Snapshot Restores: <none included>
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "CSI restore without data movement, detailed",
|
||||
inputVolInfoList: []volume.RestoreVolumeInfo{
|
||||
{
|
||||
PVCName: "pvc-1",
|
||||
PVCNamespace: "ns-1",
|
||||
PVName: "pv-1",
|
||||
RestoreMethod: volume.CSISnapshot,
|
||||
CSISnapshotInfo: &volume.CSISnapshotInfo{
|
||||
SnapshotHandle: "snapshot-handle-1",
|
||||
Size: 1234,
|
||||
Driver: "csi.test.driver",
|
||||
VSCName: "content-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
inputDetail: true,
|
||||
expect: `
|
||||
CSI Snapshot Restores:
|
||||
ns-1/pvc-1:
|
||||
Snapshot:
|
||||
Snapshot Content Name: content-1
|
||||
Storage Snapshot ID: snapshot-handle-1
|
||||
CSI Driver: csi.test.driver
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "CSI restore with data movement, detailed",
|
||||
inputVolInfoList: []volume.RestoreVolumeInfo{
|
||||
{
|
||||
PVCName: "pvc-3",
|
||||
PVCNamespace: "ns-3",
|
||||
PVName: "pv-3",
|
||||
RestoreMethod: volume.CSISnapshot,
|
||||
SnapshotDataMoved: true,
|
||||
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
|
||||
OperationID: "op-3",
|
||||
DataMover: "velero",
|
||||
UploaderType: "kopia",
|
||||
Size: 1234,
|
||||
},
|
||||
},
|
||||
},
|
||||
inputDetail: true,
|
||||
expect: `
|
||||
CSI Snapshot Restores:
|
||||
ns-3/pvc-3:
|
||||
Data Movement:
|
||||
Operation ID: op-3
|
||||
Data Mover: velero
|
||||
Uploader Type: kopia
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "vol info with different entries, without details",
|
||||
inputVolInfoList: []volume.RestoreVolumeInfo{
|
||||
{
|
||||
PVCName: "pvc-3",
|
||||
PVCNamespace: "ns-3",
|
||||
PVName: "pv-3",
|
||||
RestoreMethod: volume.CSISnapshot,
|
||||
SnapshotDataMoved: true,
|
||||
SnapshotDataMovementInfo: &volume.SnapshotDataMovementInfo{
|
||||
OperationID: "op-3",
|
||||
DataMover: "velero",
|
||||
UploaderType: "kopia",
|
||||
Size: 1234,
|
||||
},
|
||||
},
|
||||
{
|
||||
PVCName: "pvc-2",
|
||||
PVCNamespace: "ns-2",
|
||||
PVName: "pv-2",
|
||||
RestoreMethod: volume.NativeSnapshot,
|
||||
NativeSnapshotInfo: &volume.NativeSnapshotInfo{
|
||||
SnapshotHandle: "snap-1",
|
||||
},
|
||||
},
|
||||
{
|
||||
PVCName: "pvc-1",
|
||||
PVCNamespace: "ns-1",
|
||||
PVName: "pv-1",
|
||||
RestoreMethod: volume.CSISnapshot,
|
||||
CSISnapshotInfo: &volume.CSISnapshotInfo{
|
||||
SnapshotHandle: "snapshot-handle-1",
|
||||
Size: 1234,
|
||||
Driver: "csi.test.driver",
|
||||
VSCName: "content-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
inputDetail: false,
|
||||
expect: `
|
||||
CSI Snapshot Restores:
|
||||
ns-1/pvc-1:
|
||||
Snapshot: specify --details for more information
|
||||
ns-3/pvc-3:
|
||||
Data Movement: specify --details for more information
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
d := &Describer{
|
||||
Prefix: "",
|
||||
out: &tabwriter.Writer{},
|
||||
buf: &bytes.Buffer{},
|
||||
}
|
||||
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
|
||||
describeCSISnapshotsRestores(d, tc.inputVolInfoList, tc.inputDetail)
|
||||
d.out.Flush()
|
||||
assert.Equal(t, tc.expect, d.buf.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,7 +134,8 @@ func (r *downloadRequestReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
|||
if downloadRequest.Spec.Target.Kind == velerov1api.DownloadTargetKindRestoreLog ||
|
||||
downloadRequest.Spec.Target.Kind == velerov1api.DownloadTargetKindRestoreResults ||
|
||||
downloadRequest.Spec.Target.Kind == velerov1api.DownloadTargetKindRestoreResourceList ||
|
||||
downloadRequest.Spec.Target.Kind == velerov1api.DownloadTargetKindRestoreItemOperations {
|
||||
downloadRequest.Spec.Target.Kind == velerov1api.DownloadTargetKindRestoreItemOperations ||
|
||||
downloadRequest.Spec.Target.Kind == velerov1api.DownloadTargetKindRestoreVolumeInfo {
|
||||
restore := &velerov1api.Restore{}
|
||||
if err := r.client.Get(ctx, kbclient.ObjectKey{
|
||||
Namespace: downloadRequest.Namespace,
|
||||
|
|
Loading…
Reference in New Issue