mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-05 21:14:56 +00:00
Unit tests for restic restore (#1747)
* Add unit tests for PVB restore functionality Signed-off-by: Carlisia <carlisiac@vmware.com> * Add tests for restore action Signed-off-by: Carlisia <carlisiac@vmware.com> * TestRestoreWithRestic wip Signed-off-by: Carlisia <carlisiac@vmware.com> * Fix build Signed-off-by: Carlisia <carlisiac@vmware.com> * Mockery Signed-off-by: Carlisia <carlisiac@vmware.com> * Cleanup mocks Signed-off-by: Carlisia <carlisiac@vmware.com> * Remove unused mock Signed-off-by: Carlisia <carlisiac@vmware.com> * Use consistent pattern for test building Signed-off-by: Carlisia <carlisia@vmware.com> * Test cleanup Signed-off-by: Carlisia <carlisia@vmware.com> * Better godoc Signed-off-by: Carlisia <carlisia@vmware.com> * Improve test cases Signed-off-by: Carlisia <carlisia@vmware.com> * Fix build Signed-off-by: Carlisia <carlisia@vmware.com> * Minor test cleanup Signed-off-by: Carlisia <carlisia@vmware.com> * New pvb test input names Signed-off-by: Carlisia <carlisia@vmware.com>
This commit is contained in:
committed by
Adnan Abdulhussein
parent
7ea065a94f
commit
6b66a49a21
@@ -62,3 +62,21 @@ func (b *PodVolumeBackupBuilder) Phase(phase velerov1api.PodVolumeBackupPhase) *
|
||||
b.object.Status.Phase = phase
|
||||
return b
|
||||
}
|
||||
|
||||
// SnapshotID sets the PodVolumeBackup's snapshot ID.
|
||||
func (b *PodVolumeBackupBuilder) SnapshotID(snapshotID string) *PodVolumeBackupBuilder {
|
||||
b.object.Status.SnapshotID = snapshotID
|
||||
return b
|
||||
}
|
||||
|
||||
// PodName sets the name of the pod associated with this PodVolumeBackup.
|
||||
func (b *PodVolumeBackupBuilder) PodName(name string) *PodVolumeBackupBuilder {
|
||||
b.object.Spec.Pod.Name = name
|
||||
return b
|
||||
}
|
||||
|
||||
// Volume sets the name of the volume associated with this PodVolumeBackup.
|
||||
func (b *PodVolumeBackupBuilder) Volume(volume string) *PodVolumeBackupBuilder {
|
||||
b.object.Spec.Volume = volume
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -28,54 +28,90 @@ import (
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/builder"
|
||||
"github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
|
||||
informers "github.com/heptio/velero/pkg/generated/informers/externalversions"
|
||||
velerotest "github.com/heptio/velero/pkg/test"
|
||||
)
|
||||
|
||||
func TestGetPodSnapshotAnnotations(t *testing.T) {
|
||||
func TestGetVolumeBackupsForPod(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
annotations map[string]string
|
||||
expected map[string]string
|
||||
name string
|
||||
podVolumeBackups []*velerov1api.PodVolumeBackup
|
||||
podAnnotations map[string]string
|
||||
podName string
|
||||
expected map[string]string
|
||||
}{
|
||||
{
|
||||
name: "nil annotations",
|
||||
annotations: nil,
|
||||
expected: nil,
|
||||
name: "nil annotations",
|
||||
podAnnotations: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "empty annotations",
|
||||
annotations: make(map[string]string),
|
||||
expected: nil,
|
||||
name: "empty annotations",
|
||||
podAnnotations: make(map[string]string),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "non-empty map, no snapshot annotation",
|
||||
annotations: map[string]string{"foo": "bar"},
|
||||
expected: nil,
|
||||
name: "non-empty map, no snapshot annotation",
|
||||
podAnnotations: map[string]string{"foo": "bar"},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "has snapshot annotation only, no suffix",
|
||||
annotations: map[string]string{podAnnotationPrefix: "bar"},
|
||||
expected: map[string]string{"": "bar"},
|
||||
name: "has snapshot annotation only, no suffix",
|
||||
podAnnotations: map[string]string{podAnnotationPrefix: "bar"},
|
||||
expected: map[string]string{"": "bar"},
|
||||
},
|
||||
{
|
||||
name: "has snapshot annotation only, with suffix",
|
||||
annotations: map[string]string{podAnnotationPrefix + "foo": "bar"},
|
||||
expected: map[string]string{"foo": "bar"},
|
||||
name: "has snapshot annotation only, with suffix",
|
||||
podAnnotations: map[string]string{podAnnotationPrefix + "foo": "bar"},
|
||||
expected: map[string]string{"foo": "bar"},
|
||||
},
|
||||
{
|
||||
name: "has snapshot annotation, with suffix",
|
||||
annotations: map[string]string{"x": "y", podAnnotationPrefix + "foo": "bar", podAnnotationPrefix + "abc": "123"},
|
||||
expected: map[string]string{"foo": "bar", "abc": "123"},
|
||||
name: "has snapshot annotation, with suffix",
|
||||
podAnnotations: map[string]string{"x": "y", podAnnotationPrefix + "foo": "bar", podAnnotationPrefix + "abc": "123"},
|
||||
expected: map[string]string{"foo": "bar", "abc": "123"},
|
||||
},
|
||||
{
|
||||
name: "has snapshot annotation, with suffix, and also PVBs",
|
||||
podVolumeBackups: []*velerov1api.PodVolumeBackup{
|
||||
builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").SnapshotID("bar").Volume("pvbtest1-foo").Result(),
|
||||
builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").SnapshotID("123").Volume("pvbtest2-abc").Result(),
|
||||
},
|
||||
podName: "TestPod",
|
||||
podAnnotations: map[string]string{"x": "y", podAnnotationPrefix + "foo": "bar", podAnnotationPrefix + "abc": "123"},
|
||||
expected: map[string]string{"pvbtest1-foo": "bar", "pvbtest2-abc": "123"},
|
||||
},
|
||||
{
|
||||
name: "no snapshot annotation, no suffix, but with PVBs",
|
||||
podVolumeBackups: []*velerov1api.PodVolumeBackup{
|
||||
builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").SnapshotID("bar").Volume("pvbtest1-foo").Result(),
|
||||
builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").SnapshotID("123").Volume("pvbtest2-abc").Result(),
|
||||
},
|
||||
podName: "TestPod",
|
||||
expected: map[string]string{"pvbtest1-foo": "bar", "pvbtest2-abc": "123"},
|
||||
},
|
||||
{
|
||||
name: "has snapshot annotation, with suffix, and with PVBs from current pod and a PVB from another pod",
|
||||
podVolumeBackups: []*velerov1api.PodVolumeBackup{
|
||||
builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").SnapshotID("bar").Volume("pvbtest1-foo").Result(),
|
||||
builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").SnapshotID("123").Volume("pvbtest2-abc").Result(),
|
||||
builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestAnotherPod").SnapshotID("xyz").Volume("pvbtest3-xyz").Result(),
|
||||
},
|
||||
podAnnotations: map[string]string{"x": "y", podAnnotationPrefix + "foo": "bar", podAnnotationPrefix + "abc": "123"},
|
||||
podName: "TestPod",
|
||||
expected: map[string]string{"pvbtest1-foo": "bar", "pvbtest2-abc": "123"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
pod := &corev1api.Pod{}
|
||||
pod.Annotations = test.annotations
|
||||
assert.Equal(t, test.expected, getPodSnapshotAnnotations(pod))
|
||||
pod.Annotations = test.podAnnotations
|
||||
pod.Name = test.podName
|
||||
|
||||
res := GetVolumeBackupsForPod(test.podVolumeBackups, pod)
|
||||
assert.Equal(t, test.expected, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
logrus "github.com/sirupsen/logrus"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
// Backupper is an autogenerated mock type for the Backupper type
|
||||
type Backupper struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// BackupPodVolumes provides a mock function with given fields: backup, pod, log
|
||||
func (_m *Backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1.Pod, log logrus.FieldLogger) ([]*velerov1api.PodVolumeBackup, []error) {
|
||||
ret := _m.Called(backup, pod, log)
|
||||
|
||||
var r0 []*velerov1api.PodVolumeBackup
|
||||
if rf, ok := ret.Get(0).(func(*velerov1api.Backup, *corev1.Pod, logrus.FieldLogger) []*velerov1api.PodVolumeBackup); ok {
|
||||
r0 = rf(backup, pod, log)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*velerov1api.PodVolumeBackup)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 []error
|
||||
if rf, ok := ret.Get(1).(func(*velerov1api.Backup, *corev1.Pod, logrus.FieldLogger) []error); ok {
|
||||
r1 = rf(backup, pod, log)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).([]error)
|
||||
}
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
27
pkg/restic/mocks/restorer.go
Normal file
27
pkg/restic/mocks/restorer.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
import restic "github.com/heptio/velero/pkg/restic"
|
||||
|
||||
// Restorer is an autogenerated mock type for the Restorer type
|
||||
type Restorer struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// RestorePodVolumes provides a mock function with given fields: _a0
|
||||
func (_m *Restorer) RestorePodVolumes(_a0 restic.RestoreData) []error {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 []error
|
||||
if rf, ok := ret.Get(0).(func(restic.RestoreData) []error); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]error)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
|
||||
api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/builder"
|
||||
"github.com/heptio/velero/pkg/buildinfo"
|
||||
velerofake "github.com/heptio/velero/pkg/generated/clientset/versioned/fake"
|
||||
@@ -98,17 +98,24 @@ func TestResticRestoreActionExecute(t *testing.T) {
|
||||
defaultCPURequestLimit, defaultMemRequestLimit, // limits
|
||||
)
|
||||
|
||||
var (
|
||||
restoreName = "my-restore"
|
||||
backupName = "test-backup"
|
||||
veleroNs = "velero"
|
||||
)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *corev1api.Pod
|
||||
want *corev1api.Pod
|
||||
name string
|
||||
pod *corev1api.Pod
|
||||
podVolumeBackups []*velerov1api.PodVolumeBackup
|
||||
want *corev1api.Pod
|
||||
}{
|
||||
{
|
||||
name: "Restoring pod with no other initContainers adds the restic initContainer",
|
||||
pod: builder.ForPod("ns-1", "pod").ObjectMeta(
|
||||
pod: builder.ForPod("ns-1", "my-pod").ObjectMeta(
|
||||
builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
Result(),
|
||||
want: builder.ForPod("ns-1", "pod").
|
||||
want: builder.ForPod("ns-1", "my-pod").
|
||||
ObjectMeta(
|
||||
builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
InitContainers(
|
||||
@@ -119,11 +126,12 @@ func TestResticRestoreActionExecute(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "Restoring pod with other initContainers adds the restic initContainer as the first one",
|
||||
pod: builder.ForPod("ns-1", "pod").ObjectMeta(
|
||||
builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
pod: builder.ForPod("ns-1", "my-pod").
|
||||
ObjectMeta(
|
||||
builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
InitContainers(builder.ForContainer("first-container", "").Result()).
|
||||
Result(),
|
||||
want: builder.ForPod("ns-1", "pod").
|
||||
want: builder.ForPod("ns-1", "my-pod").
|
||||
ObjectMeta(
|
||||
builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
InitContainers(
|
||||
@@ -133,10 +141,55 @@ func TestResticRestoreActionExecute(t *testing.T) {
|
||||
builder.ForContainer("first-container", "").Result()).
|
||||
Result(),
|
||||
},
|
||||
{
|
||||
name: "Restoring pod with other initContainers adds the restic initContainer as the first one using PVB to identify the volumes and not annotations",
|
||||
pod: builder.ForPod("ns-1", "my-pod").
|
||||
Volumes(
|
||||
builder.ForVolume("vol-1").PersistentVolumeClaimSource("pvc-1").Result(),
|
||||
builder.ForVolume("vol-2").PersistentVolumeClaimSource("pvc-2").Result(),
|
||||
).
|
||||
ObjectMeta(
|
||||
builder.WithAnnotations("snapshot.velero.io/not-used", "")).
|
||||
InitContainers(builder.ForContainer("first-container", "").Result()).
|
||||
Result(),
|
||||
podVolumeBackups: []*velerov1api.PodVolumeBackup{
|
||||
builder.ForPodVolumeBackup(veleroNs, "pvb-1").
|
||||
PodName("my-pod").
|
||||
Volume("vol-1").
|
||||
ObjectMeta(builder.WithLabels(velerov1api.BackupNameLabel, backupName)).
|
||||
Result(),
|
||||
builder.ForPodVolumeBackup(veleroNs, "pvb-2").
|
||||
PodName("my-pod").
|
||||
Volume("vol-2").
|
||||
ObjectMeta(builder.WithLabels(velerov1api.BackupNameLabel, backupName)).
|
||||
Result(),
|
||||
},
|
||||
want: builder.ForPod("ns-1", "my-pod").
|
||||
Volumes(
|
||||
builder.ForVolume("vol-1").PersistentVolumeClaimSource("pvc-1").Result(),
|
||||
builder.ForVolume("vol-2").PersistentVolumeClaimSource("pvc-2").Result(),
|
||||
).
|
||||
ObjectMeta(
|
||||
builder.WithAnnotations("snapshot.velero.io/not-used", "")).
|
||||
InitContainers(
|
||||
newResticInitContainerBuilder(initContainerImage(defaultImageBase), "").
|
||||
Resources(&resourceReqs).
|
||||
VolumeMounts(builder.ForVolumeMount("vol-1", "/restores/vol-1").Result(), builder.ForVolumeMount("vol-2", "/restores/vol-2").Result()).Result(),
|
||||
builder.ForContainer("first-container", "").Result()).
|
||||
Result(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
clientset := fake.NewSimpleClientset()
|
||||
clientsetVelero := velerofake.NewSimpleClientset()
|
||||
|
||||
for _, podVolumeBackup := range tc.podVolumeBackups {
|
||||
_, err := clientsetVelero.VeleroV1().PodVolumeBackups(veleroNs).Create(podVolumeBackup)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tc.pod)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -144,22 +197,20 @@ func TestResticRestoreActionExecute(t *testing.T) {
|
||||
Item: &unstructured.Unstructured{
|
||||
Object: unstructuredMap,
|
||||
},
|
||||
Restore: builder.ForRestore("velero", "my-restore").
|
||||
Phase(api.RestorePhaseInProgress).
|
||||
Restore: builder.ForRestore(veleroNs, restoreName).
|
||||
Backup(backupName).
|
||||
Phase(velerov1api.RestorePhaseInProgress).
|
||||
Result(),
|
||||
}
|
||||
|
||||
clientset := fake.NewSimpleClientset()
|
||||
clientsetVelero := velerofake.NewSimpleClientset()
|
||||
a := NewResticRestoreAction(
|
||||
logrus.StandardLogger(),
|
||||
clientset.CoreV1().ConfigMaps("velero"),
|
||||
clientsetVelero.VeleroV1().PodVolumeBackups("velero"),
|
||||
clientset.CoreV1().ConfigMaps(veleroNs),
|
||||
clientsetVelero.VeleroV1().PodVolumeBackups(veleroNs),
|
||||
)
|
||||
|
||||
// method under test
|
||||
res, err := a.Execute(input)
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
wantUnstructured, err := runtime.DefaultUnstructuredConverter.ToUnstructured(tc.want)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
ctx "context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -50,6 +51,8 @@ import (
|
||||
velerov1informers "github.com/heptio/velero/pkg/generated/informers/externalversions"
|
||||
"github.com/heptio/velero/pkg/kuberesource"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
"github.com/heptio/velero/pkg/restic"
|
||||
resticmocks "github.com/heptio/velero/pkg/restic/mocks"
|
||||
"github.com/heptio/velero/pkg/test"
|
||||
testutil "github.com/heptio/velero/pkg/test"
|
||||
"github.com/heptio/velero/pkg/util/collections"
|
||||
@@ -1739,7 +1742,6 @@ func TestRestorePersistentVolumes(t *testing.T) {
|
||||
volumeSnapshots []*volume.Snapshot
|
||||
volumeSnapshotLocations []*velerov1api.VolumeSnapshotLocation
|
||||
volumeSnapshotterGetter volumeSnapshotterGetter
|
||||
podVolumeBackups []*velerov1api.PodVolumeBackup
|
||||
want []*test.APIResource
|
||||
}{
|
||||
{
|
||||
@@ -2000,7 +2002,7 @@ func TestRestorePersistentVolumes(t *testing.T) {
|
||||
},
|
||||
|
||||
{
|
||||
name: "include podvolumebackups, and when a PV with a reclaim policy of retain has a snapshot and exists in-cluster, neither the snapshot nor the PV are restored",
|
||||
name: "when a PV with a reclaim policy of retain has a snapshot and exists in-cluster, neither the snapshot nor the PV are restored",
|
||||
restore: defaultRestore().Result(),
|
||||
backup: defaultBackup().Result(),
|
||||
tarball: newTarWriter(t).
|
||||
@@ -2050,10 +2052,7 @@ func TestRestorePersistentVolumes(t *testing.T) {
|
||||
// restored, we'd get an error of "snapshot not found".
|
||||
"provider-1": &volumeSnapshotter{},
|
||||
},
|
||||
podVolumeBackups: []*velerov1api.PodVolumeBackup{
|
||||
builder.ForPodVolumeBackup("velero", "pvb-1").Result(),
|
||||
builder.ForPodVolumeBackup("velero", "pvb-2").Result(),
|
||||
},
|
||||
|
||||
want: []*test.APIResource{
|
||||
test.PVs(
|
||||
builder.ForPersistentVolume("pv-1").
|
||||
@@ -2092,12 +2091,11 @@ func TestRestorePersistentVolumes(t *testing.T) {
|
||||
}
|
||||
|
||||
data := Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: tc.podVolumeBackups,
|
||||
VolumeSnapshots: tc.volumeSnapshots,
|
||||
BackupReader: tc.tarball,
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
VolumeSnapshots: tc.volumeSnapshots,
|
||||
BackupReader: tc.tarball,
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
data,
|
||||
@@ -2113,6 +2111,132 @@ func TestRestorePersistentVolumes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type fakeResticRestorerFactory struct {
|
||||
restorer *resticmocks.Restorer
|
||||
}
|
||||
|
||||
func (f *fakeResticRestorerFactory) NewRestorer(ctx.Context, *velerov1api.Restore) (restic.Restorer, error) {
|
||||
return f.restorer, nil
|
||||
}
|
||||
|
||||
// TestRestoreWithRestic verifies that a call to RestorePodVolumes was made as and when
|
||||
// expected for the given pods by using a mock for the restic restorer.
|
||||
func TestRestoreWithRestic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
restore *velerov1api.Restore
|
||||
backup *velerov1api.Backup
|
||||
apiResources []*test.APIResource
|
||||
podVolumeBackups []*velerov1api.PodVolumeBackup
|
||||
podWithPVBs, podWithoutPVBs []*corev1api.Pod
|
||||
want map[*test.APIResource][]string
|
||||
}{
|
||||
{
|
||||
name: "a pod that exists in given backup and contains associated PVBs should have should have RestorePodVolumes called",
|
||||
restore: defaultRestore().Result(),
|
||||
backup: defaultBackup().Result(),
|
||||
apiResources: []*test.APIResource{test.Pods()},
|
||||
podVolumeBackups: []*velerov1api.PodVolumeBackup{
|
||||
builder.ForPodVolumeBackup("velero", "pvb-1").PodName("pod-1").Result(),
|
||||
builder.ForPodVolumeBackup("velero", "pvb-2").PodName("pod-2").Result(),
|
||||
builder.ForPodVolumeBackup("velero", "pvb-3").PodName("pod-4").Result(),
|
||||
},
|
||||
podWithPVBs: []*corev1api.Pod{
|
||||
builder.ForPod("ns-1", "pod-2").
|
||||
Result(),
|
||||
builder.ForPod("ns-2", "pod-4").
|
||||
Result(),
|
||||
},
|
||||
podWithoutPVBs: []*corev1api.Pod{
|
||||
builder.ForPod("ns-2", "pod-3").
|
||||
Result(),
|
||||
},
|
||||
want: map[*test.APIResource][]string{
|
||||
test.Pods(): {"ns-1/pod-2", "ns-2/pod-3", "ns-2/pod-4"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "a pod that exists in given backup but does not contain associated PVBs should not have should have RestorePodVolumes called",
|
||||
restore: defaultRestore().Result(),
|
||||
backup: defaultBackup().Result(),
|
||||
apiResources: []*test.APIResource{test.Pods()},
|
||||
podVolumeBackups: []*velerov1api.PodVolumeBackup{
|
||||
builder.ForPodVolumeBackup("velero", "pvb-1").PodName("pod-1").Result(),
|
||||
builder.ForPodVolumeBackup("velero", "pvb-2").PodName("pod-2").Result(),
|
||||
},
|
||||
podWithPVBs: []*corev1api.Pod{},
|
||||
podWithoutPVBs: []*corev1api.Pod{
|
||||
builder.ForPod("ns-1", "pod-3").
|
||||
Result(),
|
||||
builder.ForPod("ns-2", "pod-4").
|
||||
Result(),
|
||||
},
|
||||
want: map[*test.APIResource][]string{
|
||||
test.Pods(): {"ns-1/pod-3", "ns-2/pod-4"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h := newHarness(t)
|
||||
restorer := new(resticmocks.Restorer)
|
||||
defer restorer.AssertExpectations(t)
|
||||
h.restorer.resticRestorerFactory = &fakeResticRestorerFactory{
|
||||
restorer: restorer,
|
||||
}
|
||||
|
||||
// needed only to indicate resource types that can be restored, in this case, pods
|
||||
for _, resource := range tc.apiResources {
|
||||
h.addItems(t, resource)
|
||||
}
|
||||
|
||||
tarball := newTarWriter(t)
|
||||
|
||||
// these backed up pods don't have any PVBs associated with them, so a call to RestorePodVolumes is not expected to be made for them
|
||||
for _, pod := range tc.podWithoutPVBs {
|
||||
tarball.addItems("pods", pod)
|
||||
}
|
||||
|
||||
// these backed up pods have PVBs associated with them, so a call to RestorePodVolumes will be made for each of them
|
||||
for _, pod := range tc.podWithPVBs {
|
||||
tarball.addItems("pods", pod)
|
||||
|
||||
// the restore process adds these labels before restoring, so we must add them here too otherwise they won't match
|
||||
pod.Labels = map[string]string{"velero.io/backup-name": tc.backup.Name, "velero.io/restore-name": tc.restore.Name}
|
||||
expectedArgs := restic.RestoreData{
|
||||
Restore: tc.restore,
|
||||
Pod: pod,
|
||||
PodVolumeBackups: tc.podVolumeBackups,
|
||||
SourceNamespace: pod.Namespace,
|
||||
BackupLocation: "",
|
||||
}
|
||||
restorer.
|
||||
On("RestorePodVolumes", expectedArgs).
|
||||
Return(nil)
|
||||
}
|
||||
|
||||
data := Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: tc.podVolumeBackups,
|
||||
BackupReader: tarball.done(),
|
||||
}
|
||||
|
||||
warnings, errs := h.restorer.Restore(
|
||||
data,
|
||||
nil, // actions
|
||||
nil, // snapshot location lister
|
||||
nil, // volume snapshotter getter
|
||||
)
|
||||
|
||||
assertEmptyResults(t, warnings, errs)
|
||||
assertAPIContents(t, h, tc.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrioritizeResources(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
Reference in New Issue
Block a user