418 lines
11 KiB
Go
418 lines
11 KiB
Go
package output
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"testing"
|
|
"text/tabwriter"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
corev1api "k8s.io/api/core/v1"
|
|
|
|
"github.com/vmware-tanzu/velero/internal/volume"
|
|
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
|
"github.com/vmware-tanzu/velero/pkg/builder"
|
|
"github.com/vmware-tanzu/velero/pkg/itemoperation"
|
|
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
|
"github.com/vmware-tanzu/velero/pkg/util/results"
|
|
)
|
|
|
|
func TestDescribeResult(t *testing.T) {
|
|
testcases := []struct {
|
|
name string
|
|
inputName string
|
|
inputResult results.Result
|
|
expect string
|
|
}{
|
|
{
|
|
name: "result without ns warns",
|
|
inputName: "restore-1",
|
|
inputResult: results.Result{
|
|
Velero: []string{"velero-msg-1", "velero-msg-2"},
|
|
Cluster: []string{"cluster-msg-1", "cluster-msg-2"},
|
|
Namespaces: map[string][]string{},
|
|
},
|
|
expect: `restore-1:
|
|
Velero: velero-msg-1
|
|
velero-msg-2
|
|
Cluster: cluster-msg-1
|
|
cluster-msg-2
|
|
Namespaces: <none>
|
|
`,
|
|
},
|
|
{
|
|
name: "result with ns warns",
|
|
inputName: "restore-2",
|
|
inputResult: results.Result{
|
|
Velero: []string{"velero-msg-1", "velero-msg-2"},
|
|
Cluster: []string{"cluster-msg-1", "cluster-msg-2"},
|
|
Namespaces: map[string][]string{
|
|
"ns-1": {"ns-1-warn-1", "ns-1-warn-2"},
|
|
},
|
|
},
|
|
expect: `restore-2:
|
|
Velero: velero-msg-1
|
|
velero-msg-2
|
|
Cluster: cluster-msg-1
|
|
cluster-msg-2
|
|
Namespaces:
|
|
ns-1: ns-1-warn-1
|
|
ns-1-warn-2
|
|
`,
|
|
},
|
|
}
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(tt *testing.T) {
|
|
d := &Describer{
|
|
Prefix: "",
|
|
out: &tabwriter.Writer{},
|
|
buf: &bytes.Buffer{},
|
|
}
|
|
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
|
|
describeResult(d, tc.inputName, tc.inputResult)
|
|
d.out.Flush()
|
|
assert.Equal(tt, tc.expect, d.buf.String())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDescribeRestoreItemOperation(t *testing.T) {
|
|
t1, err1 := time.Parse("2006-Jan-02", "2023-Jun-26")
|
|
require.NoError(t, err1)
|
|
t2, err2 := time.Parse("2006-Jan-02", "2023-Jun-25")
|
|
require.NoError(t, err2)
|
|
t3, err3 := time.Parse("2006-Jan-02", "2023-Jun-24")
|
|
require.NoError(t, err3)
|
|
input := builder.ForRestoreOperation().
|
|
RestoreName("restore-1").
|
|
OperationID("op-1").
|
|
RestoreItemAction("action-1").
|
|
ResourceIdentifier("group", "rs-type", "ns", "rs-name").
|
|
Status(*builder.ForOperationStatus().
|
|
Phase(itemoperation.OperationPhaseFailed).
|
|
Error("operation error").
|
|
Progress(50, 100, "bytes").
|
|
Description("operation description").
|
|
Created(t3).
|
|
Started(t2).
|
|
Updated(t1).
|
|
Result()).Result()
|
|
expected := ` Operation for rs-type.group ns/rs-name:
|
|
Restore Item Action Plugin: action-1
|
|
Operation ID: op-1
|
|
Phase: Failed
|
|
Operation Error: operation error
|
|
Progress: 50 of 100 complete (bytes)
|
|
Progress description: operation description
|
|
Created: 2023-06-24 00:00:00 +0000 UTC
|
|
Started: 2023-06-25 00:00:00 +0000 UTC
|
|
Updated: 2023-06-26 00:00:00 +0000 UTC
|
|
`
|
|
d := &Describer{
|
|
Prefix: "",
|
|
out: &tabwriter.Writer{},
|
|
buf: &bytes.Buffer{},
|
|
}
|
|
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
|
|
describeRestoreItemOperation(d, input)
|
|
d.out.Flush()
|
|
assert.Equal(t, expected, d.buf.String())
|
|
}
|
|
|
|
func TestDescribePodVolumeRestores(t *testing.T) {
|
|
pvr1 := builder.ForPodVolumeRestore("velero", "pvr-1").
|
|
UploaderType("kopia").
|
|
Phase(velerov1api.PodVolumeRestorePhaseCompleted).
|
|
BackupStorageLocation("bsl-1").
|
|
Volume("vol-1").
|
|
PodName("pod-1").
|
|
PodNamespace("pod-ns-1").
|
|
SnapshotID("snap-1").Result()
|
|
pvr2 := builder.ForPodVolumeRestore("velero", "pvr-2").
|
|
UploaderType("kopia").
|
|
Phase(velerov1api.PodVolumeRestorePhaseCompleted).
|
|
BackupStorageLocation("bsl-1").
|
|
Volume("vol-2").
|
|
PodName("pod-2").
|
|
PodNamespace("pod-ns-1").
|
|
SnapshotID("snap-2").Result()
|
|
|
|
testcases := []struct {
|
|
name string
|
|
inputPVRList []velerov1api.PodVolumeRestore
|
|
inputDetails bool
|
|
expect string
|
|
}{
|
|
{
|
|
name: "empty list",
|
|
inputPVRList: []velerov1api.PodVolumeRestore{},
|
|
inputDetails: true,
|
|
expect: ``,
|
|
},
|
|
{
|
|
name: "2 completed pvrs no details",
|
|
inputPVRList: []velerov1api.PodVolumeRestore{*pvr1, *pvr2},
|
|
inputDetails: false,
|
|
expect: `kopia Restores (specify --details for more information):
|
|
Completed: 2
|
|
`,
|
|
},
|
|
{
|
|
name: "2 completed pvrs with details",
|
|
inputPVRList: []velerov1api.PodVolumeRestore{*pvr1, *pvr2},
|
|
inputDetails: true,
|
|
expect: `kopia Restores:
|
|
Completed:
|
|
pod-ns-1/pod-1: vol-1
|
|
pod-ns-1/pod-2: vol-2
|
|
`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testcases {
|
|
t.Run(tc.name, func(tt *testing.T) {
|
|
d := &Describer{
|
|
Prefix: "",
|
|
out: &tabwriter.Writer{},
|
|
buf: &bytes.Buffer{},
|
|
}
|
|
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
|
|
describePodVolumeRestores(d, tc.inputPVRList, tc.inputDetails)
|
|
d.out.Flush()
|
|
assert.Equal(tt, tc.expect, d.buf.String())
|
|
})
|
|
}
|
|
}
|
|
func TestDescribeUploaderConfigForRestore(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
spec velerov1api.RestoreSpec
|
|
expected string
|
|
}{
|
|
{
|
|
name: "UploaderConfigNil",
|
|
spec: velerov1api.RestoreSpec{}, // Create a RestoreSpec with nil UploaderConfig
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "test",
|
|
spec: velerov1api.RestoreSpec{
|
|
UploaderConfig: &velerov1api.UploaderConfigForRestore{
|
|
WriteSparseFiles: boolptr.True(),
|
|
ParallelFilesDownload: 4,
|
|
},
|
|
},
|
|
expected: "\nUploader config:\n Write Sparse Files: true\n Parallel Restore: 4\n",
|
|
},
|
|
{
|
|
name: "WriteSparseFiles test",
|
|
spec: velerov1api.RestoreSpec{
|
|
UploaderConfig: &velerov1api.UploaderConfigForRestore{
|
|
WriteSparseFiles: boolptr.True(),
|
|
},
|
|
},
|
|
expected: "\nUploader config:\n Write Sparse Files: true\n",
|
|
},
|
|
{
|
|
name: "ParallelFilesDownload test",
|
|
spec: velerov1api.RestoreSpec{
|
|
UploaderConfig: &velerov1api.UploaderConfigForRestore{
|
|
ParallelFilesDownload: 4,
|
|
},
|
|
},
|
|
expected: "\nUploader config:\n Parallel Restore: 4\n",
|
|
},
|
|
}
|
|
|
|
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)
|
|
describeUploaderConfigForRestore(d, tc.spec)
|
|
d.out.Flush()
|
|
assert.Equal(t, tc.expected, d.buf.String(), "Output should match expected")
|
|
})
|
|
}
|
|
}
|
|
|
|
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())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDescribeResourceModifier(t *testing.T) {
|
|
d := &Describer{
|
|
Prefix: "",
|
|
out: &tabwriter.Writer{},
|
|
buf: &bytes.Buffer{},
|
|
}
|
|
|
|
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
|
|
|
|
DescribeResourceModifier(d, &corev1api.TypedLocalObjectReference{
|
|
APIGroup: &corev1api.SchemeGroupVersion.Group,
|
|
Kind: "ConfigMap",
|
|
Name: "resourceModifier",
|
|
})
|
|
d.out.Flush()
|
|
|
|
expectOutput := `Resource modifier:
|
|
Type: ConfigMap
|
|
Name: resourceModifier
|
|
`
|
|
|
|
fmt.Println(d.buf.String())
|
|
require.Equal(t, expectOutput, d.buf.String())
|
|
}
|