Skip to content

Commit

Permalink
Merge pull request #7687 from reasonerjt/restore-desc-vol-info
Browse files Browse the repository at this point in the history
Display CSI snapshot restores in restore describe
  • Loading branch information
reasonerjt authored Apr 17, 2024
2 parents b46fc6b + 2197cab commit f04fbbc
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 3 deletions.
1 change: 1 addition & 0 deletions changelogs/unreleased/7687-reasonerjt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Display CSI snapshot restores in restore describe
1 change: 1 addition & 0 deletions config/crd/v1/bases/velero.io_downloadrequests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ spec:
- CSIBackupVolumeSnapshots
- CSIBackupVolumeSnapshotContents
- BackupVolumeInfos
- RestoreVolumeInfo
type: string
name:
description: Name is the name of the Kubernetes resource with
Expand Down
2 changes: 1 addition & 1 deletion config/crd/v1/crds/crds.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/apis/velero/v1/download_request_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
62 changes: 62 additions & 0 deletions pkg/cmd/util/output/restore_describer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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 != "" {
Expand Down Expand Up @@ -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)

Expand Down
152 changes: 152 additions & 0 deletions pkg/cmd/util/output/restore_describer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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())
})
}
}
3 changes: 2 additions & 1 deletion pkg/controller/download_request_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit f04fbbc

Please sign in to comment.