mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-27 10:24:57 +00:00
Restore from PodVolumeBackups (#1723)
* Restore from PodVolumeBackups Signed-off-by: Carlisia <carlisiac@vmware.com> * Partially address code reviews Signed-off-by: Carlisia <carlisiac@vmware.com> * Partially address code reviews #2 Signed-off-by: Carlisia <carlisiac@vmware.com> * Clean up struct Signed-off-by: Carlisia <carlisiac@vmware.com> * Fix log messages Signed-off-by: Carlisia <carlisiac@vmware.com> * Fix tests Signed-off-by: Carlisia <carlisiac@vmware.com> * Clean up Signed-off-by: Carlisia <carlisiac@vmware.com> * Add changelog Signed-off-by: Carlisia <carlisiac@vmware.com>
This commit is contained in:
committed by
Adnan Abdulhussein
parent
4e1b1f9457
commit
4accb8512a
@@ -28,8 +28,10 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/builder"
|
||||
"github.com/heptio/velero/pkg/buildinfo"
|
||||
velerov1client "github.com/heptio/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
"github.com/heptio/velero/pkg/plugin/framework"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
"github.com/heptio/velero/pkg/restic"
|
||||
@@ -43,14 +45,16 @@ const (
|
||||
)
|
||||
|
||||
type ResticRestoreAction struct {
|
||||
logger logrus.FieldLogger
|
||||
client corev1client.ConfigMapInterface
|
||||
logger logrus.FieldLogger
|
||||
client corev1client.ConfigMapInterface
|
||||
podVolumeBackupClient velerov1client.PodVolumeBackupInterface
|
||||
}
|
||||
|
||||
func NewResticRestoreAction(logger logrus.FieldLogger, client corev1client.ConfigMapInterface) *ResticRestoreAction {
|
||||
func NewResticRestoreAction(logger logrus.FieldLogger, client corev1client.ConfigMapInterface, podVolumeBackupClient velerov1client.PodVolumeBackupInterface) *ResticRestoreAction {
|
||||
return &ResticRestoreAction{
|
||||
logger: logger,
|
||||
client: client,
|
||||
logger: logger,
|
||||
client: client,
|
||||
podVolumeBackupClient: podVolumeBackupClient,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,13 +75,23 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu
|
||||
|
||||
log := a.logger.WithField("pod", kube.NamespaceAndName(&pod))
|
||||
|
||||
volumeSnapshots := restic.GetPodSnapshotAnnotations(&pod)
|
||||
opts := restic.NewPodVolumeBackupListOptions(input.Restore.Spec.BackupName)
|
||||
podVolumeBackupList, err := a.podVolumeBackupClient.List(opts)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
var podVolumeBackups []*velerov1api.PodVolumeBackup
|
||||
for i := range podVolumeBackupList.Items {
|
||||
podVolumeBackups = append(podVolumeBackups, &podVolumeBackupList.Items[i])
|
||||
}
|
||||
volumeSnapshots := restic.GetVolumeBackupsForPod(podVolumeBackups, &pod)
|
||||
if len(volumeSnapshots) == 0 {
|
||||
log.Debug("No restic snapshot ID annotations found")
|
||||
log.Debug("No restic backups found for pod")
|
||||
return velero.NewRestoreItemActionExecuteOutput(input.Item), nil
|
||||
}
|
||||
|
||||
log.Info("Restic snapshot ID annotations found")
|
||||
log.Info("Restic backups for pod found")
|
||||
|
||||
// TODO we might want/need to get plugin config at the top of this method at some point; for now, wait
|
||||
// until we know we're doing a restore before getting config.
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
api "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"
|
||||
"github.com/heptio/velero/pkg/plugin/velero"
|
||||
"github.com/heptio/velero/pkg/util/kube"
|
||||
velerotest "github.com/heptio/velero/pkg/util/test"
|
||||
@@ -149,9 +150,11 @@ func TestResticRestoreActionExecute(t *testing.T) {
|
||||
}
|
||||
|
||||
clientset := fake.NewSimpleClientset()
|
||||
clientsetVelero := velerofake.NewSimpleClientset()
|
||||
a := NewResticRestoreAction(
|
||||
logrus.StandardLogger(),
|
||||
clientset.CoreV1().ConfigMaps("velero"),
|
||||
clientsetVelero.VeleroV1().PodVolumeBackups("velero"),
|
||||
)
|
||||
|
||||
// method under test
|
||||
|
||||
+75
-59
@@ -43,7 +43,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
|
||||
api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/heptio/velero/pkg/apis/velero/v1"
|
||||
"github.com/heptio/velero/pkg/client"
|
||||
"github.com/heptio/velero/pkg/discovery"
|
||||
listers "github.com/heptio/velero/pkg/generated/listers/velero/v1"
|
||||
@@ -63,14 +63,20 @@ type VolumeSnapshotterGetter interface {
|
||||
GetVolumeSnapshotter(name string) (velero.VolumeSnapshotter, error)
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
*velerov1api.Restore
|
||||
|
||||
Log logrus.FieldLogger
|
||||
Backup *velerov1api.Backup
|
||||
PodVolumeBackups []*velerov1api.PodVolumeBackup
|
||||
VolumeSnapshots []*volume.Snapshot
|
||||
BackupReader io.Reader
|
||||
}
|
||||
|
||||
// Restorer knows how to restore a backup.
|
||||
type Restorer interface {
|
||||
// Restore restores the backup data from backupReader, returning warnings and errors.
|
||||
Restore(log logrus.FieldLogger,
|
||||
restore *api.Restore,
|
||||
backup *api.Backup,
|
||||
volumeSnapshots []*volume.Snapshot,
|
||||
backupReader io.Reader,
|
||||
Restore(req Request,
|
||||
actions []velero.RestoreItemAction,
|
||||
snapshotLocationLister listers.VolumeSnapshotLocationLister,
|
||||
volumeSnapshotterGetter VolumeSnapshotterGetter,
|
||||
@@ -176,11 +182,7 @@ func NewKubernetesRestorer(
|
||||
// and using data from the provided backup/backup reader. Returns a warnings and errors RestoreResult,
|
||||
// respectively, summarizing info about the restore.
|
||||
func (kr *kubernetesRestorer) Restore(
|
||||
log logrus.FieldLogger,
|
||||
restore *api.Restore,
|
||||
backup *api.Backup,
|
||||
volumeSnapshots []*volume.Snapshot,
|
||||
backupReader io.Reader,
|
||||
req Request,
|
||||
actions []velero.RestoreItemAction,
|
||||
snapshotLocationLister listers.VolumeSnapshotLocationLister,
|
||||
volumeSnapshotterGetter VolumeSnapshotterGetter,
|
||||
@@ -189,7 +191,7 @@ func (kr *kubernetesRestorer) Restore(
|
||||
// Nothing Selector, i.e. a selector that matches nothing. We want
|
||||
// a selector that matches everything. This can be accomplished by
|
||||
// passing a non-nil empty LabelSelector.
|
||||
ls := restore.Spec.LabelSelector
|
||||
ls := req.Restore.Spec.LabelSelector
|
||||
if ls == nil {
|
||||
ls = &metav1.LabelSelector{}
|
||||
}
|
||||
@@ -200,16 +202,16 @@ func (kr *kubernetesRestorer) Restore(
|
||||
}
|
||||
|
||||
// get resource includes-excludes
|
||||
resourceIncludesExcludes := getResourceIncludesExcludes(kr.discoveryHelper, restore.Spec.IncludedResources, restore.Spec.ExcludedResources)
|
||||
prioritizedResources, err := prioritizeResources(kr.discoveryHelper, kr.resourcePriorities, resourceIncludesExcludes, log)
|
||||
resourceIncludesExcludes := getResourceIncludesExcludes(kr.discoveryHelper, req.Restore.Spec.IncludedResources, req.Restore.Spec.ExcludedResources)
|
||||
prioritizedResources, err := prioritizeResources(kr.discoveryHelper, kr.resourcePriorities, resourceIncludesExcludes, req.Log)
|
||||
if err != nil {
|
||||
return Result{}, Result{Velero: []string{err.Error()}}
|
||||
}
|
||||
|
||||
// get namespace includes-excludes
|
||||
namespaceIncludesExcludes := collections.NewIncludesExcludes().
|
||||
Includes(restore.Spec.IncludedNamespaces...).
|
||||
Excludes(restore.Spec.ExcludedNamespaces...)
|
||||
Includes(req.Restore.Spec.IncludedNamespaces...).
|
||||
Excludes(req.Restore.Spec.ExcludedNamespaces...)
|
||||
|
||||
resolvedActions, err := resolveActions(actions, kr.discoveryHelper)
|
||||
if err != nil {
|
||||
@@ -217,10 +219,10 @@ func (kr *kubernetesRestorer) Restore(
|
||||
}
|
||||
|
||||
podVolumeTimeout := kr.resticTimeout
|
||||
if val := restore.Annotations[api.PodVolumeOperationTimeoutAnnotation]; val != "" {
|
||||
if val := req.Restore.Annotations[velerov1api.PodVolumeOperationTimeoutAnnotation]; val != "" {
|
||||
parsed, err := time.ParseDuration(val)
|
||||
if err != nil {
|
||||
log.WithError(errors.WithStack(err)).Errorf("Unable to parse pod volume timeout annotation %s, using server value.", val)
|
||||
req.Log.WithError(errors.WithStack(err)).Errorf("Unable to parse pod volume timeout annotation %s, using server value.", val)
|
||||
} else {
|
||||
podVolumeTimeout = parsed
|
||||
}
|
||||
@@ -231,31 +233,31 @@ func (kr *kubernetesRestorer) Restore(
|
||||
|
||||
var resticRestorer restic.Restorer
|
||||
if kr.resticRestorerFactory != nil {
|
||||
resticRestorer, err = kr.resticRestorerFactory.NewRestorer(ctx, restore)
|
||||
resticRestorer, err = kr.resticRestorerFactory.NewRestorer(ctx, req.Restore)
|
||||
if err != nil {
|
||||
return Result{}, Result{Velero: []string{err.Error()}}
|
||||
}
|
||||
}
|
||||
|
||||
pvRestorer := &pvRestorer{
|
||||
logger: log,
|
||||
backup: backup,
|
||||
snapshotVolumes: backup.Spec.SnapshotVolumes,
|
||||
restorePVs: restore.Spec.RestorePVs,
|
||||
volumeSnapshots: volumeSnapshots,
|
||||
logger: req.Log,
|
||||
backup: req.Backup,
|
||||
snapshotVolumes: req.Backup.Spec.SnapshotVolumes,
|
||||
restorePVs: req.Restore.Spec.RestorePVs,
|
||||
volumeSnapshots: req.VolumeSnapshots,
|
||||
volumeSnapshotterGetter: volumeSnapshotterGetter,
|
||||
snapshotLocationLister: snapshotLocationLister,
|
||||
}
|
||||
|
||||
restoreCtx := &context{
|
||||
backup: backup,
|
||||
backupReader: backupReader,
|
||||
restore: restore,
|
||||
backup: req.Backup,
|
||||
backupReader: req.BackupReader,
|
||||
restore: req.Restore,
|
||||
resourceIncludesExcludes: resourceIncludesExcludes,
|
||||
namespaceIncludesExcludes: namespaceIncludesExcludes,
|
||||
prioritizedResources: prioritizedResources,
|
||||
selector: selector,
|
||||
log: log,
|
||||
log: req.Log,
|
||||
dynamicFactory: kr.dynamicFactory,
|
||||
fileSystem: kr.fileSystem,
|
||||
namespaceClient: kr.namespaceClient,
|
||||
@@ -264,10 +266,11 @@ func (kr *kubernetesRestorer) Restore(
|
||||
resticRestorer: resticRestorer,
|
||||
pvsToProvision: sets.NewString(),
|
||||
pvRestorer: pvRestorer,
|
||||
volumeSnapshots: volumeSnapshots,
|
||||
volumeSnapshots: req.VolumeSnapshots,
|
||||
podVolumeBackups: req.PodVolumeBackups,
|
||||
resourceTerminatingTimeout: kr.resourceTerminatingTimeout,
|
||||
extractor: &backupExtractor{
|
||||
log: log,
|
||||
log: req.Log,
|
||||
fileSystem: kr.fileSystem,
|
||||
},
|
||||
resourceClients: make(map[resourceClientKey]client.Dynamic),
|
||||
@@ -339,9 +342,9 @@ func resolveActions(actions []velero.RestoreItemAction, helper discovery.Helper)
|
||||
}
|
||||
|
||||
type context struct {
|
||||
backup *api.Backup
|
||||
backup *velerov1api.Backup
|
||||
backupReader io.Reader
|
||||
restore *api.Restore
|
||||
restore *velerov1api.Restore
|
||||
restoreDir string
|
||||
resourceIncludesExcludes *collections.IncludesExcludes
|
||||
namespaceIncludesExcludes *collections.IncludesExcludes
|
||||
@@ -358,6 +361,7 @@ type context struct {
|
||||
pvsToProvision sets.String
|
||||
pvRestorer PVRestorer
|
||||
volumeSnapshots []*volume.Snapshot
|
||||
podVolumeBackups []*velerov1api.PodVolumeBackup
|
||||
resourceTerminatingTimeout time.Duration
|
||||
extractor *backupExtractor
|
||||
resourceClients map[resourceClientKey]client.Dynamic
|
||||
@@ -391,7 +395,7 @@ func (ctx *context) restoreFromDir() (Result, Result) {
|
||||
warnings, errs := Result{}, Result{}
|
||||
|
||||
// Make sure the top level "resources" dir exists:
|
||||
resourcesDir := filepath.Join(ctx.restoreDir, api.ResourcesDir)
|
||||
resourcesDir := filepath.Join(ctx.restoreDir, velerov1api.ResourcesDir)
|
||||
rde, err := ctx.fileSystem.DirExists(resourcesDir)
|
||||
if err != nil {
|
||||
addVeleroError(&errs, err)
|
||||
@@ -430,7 +434,7 @@ func (ctx *context) restoreFromDir() (Result, Result) {
|
||||
|
||||
resourcePath := filepath.Join(resourcesDir, rscDir.Name())
|
||||
|
||||
clusterSubDir := filepath.Join(resourcePath, api.ClusterScopedDir)
|
||||
clusterSubDir := filepath.Join(resourcePath, velerov1api.ClusterScopedDir)
|
||||
clusterSubDirExists, err := ctx.fileSystem.DirExists(clusterSubDir)
|
||||
if err != nil {
|
||||
addVeleroError(&errs, err)
|
||||
@@ -443,7 +447,7 @@ func (ctx *context) restoreFromDir() (Result, Result) {
|
||||
continue
|
||||
}
|
||||
|
||||
nsSubDir := filepath.Join(resourcePath, api.NamespaceScopedDir)
|
||||
nsSubDir := filepath.Join(resourcePath, velerov1api.NamespaceScopedDir)
|
||||
nsSubDirExists, err := ctx.fileSystem.DirExists(nsSubDir)
|
||||
if err != nil {
|
||||
addVeleroError(&errs, err)
|
||||
@@ -518,9 +522,9 @@ func (ctx *context) restoreFromDir() (Result, Result) {
|
||||
func getItemFilePath(rootDir, groupResource, namespace, name string) string {
|
||||
switch namespace {
|
||||
case "":
|
||||
return filepath.Join(rootDir, api.ResourcesDir, groupResource, api.ClusterScopedDir, name+".json")
|
||||
return filepath.Join(rootDir, velerov1api.ResourcesDir, groupResource, velerov1api.ClusterScopedDir, name+".json")
|
||||
default:
|
||||
return filepath.Join(rootDir, api.ResourcesDir, groupResource, api.NamespaceScopedDir, namespace, name+".json")
|
||||
return filepath.Join(rootDir, velerov1api.ResourcesDir, groupResource, velerov1api.NamespaceScopedDir, namespace, name+".json")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1037,7 +1041,7 @@ func (ctx *context) restoreItem(obj *unstructured.Unstructured, groupResource sc
|
||||
// We know the object from the cluster won't have the backup/restore name labels, so
|
||||
// copy them from the object we attempted to restore.
|
||||
labels := obj.GetLabels()
|
||||
addRestoreLabels(fromCluster, labels[api.RestoreNameLabel], labels[api.BackupNameLabel])
|
||||
addRestoreLabels(fromCluster, labels[velerov1api.RestoreNameLabel], labels[velerov1api.BackupNameLabel])
|
||||
|
||||
if !equality.Semantic.DeepEqual(fromCluster, obj) {
|
||||
switch groupResource {
|
||||
@@ -1085,30 +1089,42 @@ func (ctx *context) restoreItem(obj *unstructured.Unstructured, groupResource sc
|
||||
return warnings, errs
|
||||
}
|
||||
|
||||
if groupResource == kuberesource.Pods && len(restic.GetPodSnapshotAnnotations(obj)) > 0 {
|
||||
if ctx.resticRestorer == nil {
|
||||
ctx.log.Warn("No restic restorer, not restoring pod's volumes")
|
||||
} else {
|
||||
ctx.globalWaitGroup.GoErrorSlice(func() []error {
|
||||
pod := new(v1.Pod)
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(createdObj.UnstructuredContent(), &pod); err != nil {
|
||||
ctx.log.WithError(err).Error("error converting unstructured pod")
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
if errs := ctx.resticRestorer.RestorePodVolumes(ctx.restore, pod, originalNamespace, ctx.backup.Spec.StorageLocation, ctx.log); errs != nil {
|
||||
ctx.log.WithError(kubeerrs.NewAggregate(errs)).Error("unable to successfully complete restic restores of pod's volumes")
|
||||
return errs
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
if groupResource == kuberesource.Pods && len(restic.GetVolumeBackupsForPod(ctx.podVolumeBackups, obj)) > 0 {
|
||||
restorePodVolumeBackups(ctx, createdObj, originalNamespace)
|
||||
}
|
||||
|
||||
return warnings, errs
|
||||
}
|
||||
|
||||
// restorePodVolumeBackups restores the PodVolumeBackups for the given restored pod
|
||||
func restorePodVolumeBackups(ctx *context, createdObj *unstructured.Unstructured, originalNamespace string) {
|
||||
if ctx.resticRestorer == nil {
|
||||
ctx.log.Warn("No restic restorer, not restoring pod's volumes")
|
||||
} else {
|
||||
ctx.globalWaitGroup.GoErrorSlice(func() []error {
|
||||
pod := new(v1.Pod)
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(createdObj.UnstructuredContent(), &pod); err != nil {
|
||||
ctx.log.WithError(err).Error("error converting unstructured pod")
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
data := restic.RestoreData{
|
||||
Restore: ctx.restore,
|
||||
Pod: pod,
|
||||
PodVolumeBackups: ctx.podVolumeBackups,
|
||||
SourceNamespace: originalNamespace,
|
||||
BackupLocation: ctx.backup.Spec.StorageLocation,
|
||||
}
|
||||
if errs := ctx.resticRestorer.RestorePodVolumes(data); errs != nil {
|
||||
ctx.log.WithError(kubeerrs.NewAggregate(errs)).Error("unable to successfully complete restic restores of pod's volumes")
|
||||
return errs
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func hasDeleteReclaimPolicy(obj map[string]interface{}) bool {
|
||||
policy, _, _ := unstructured.NestedString(obj, "spec", "persistentVolumeReclaimPolicy")
|
||||
return policy == string(v1.PersistentVolumeReclaimDelete)
|
||||
@@ -1147,8 +1163,8 @@ func addRestoreLabels(obj metav1.Object, restoreName, backupName string) {
|
||||
labels = make(map[string]string)
|
||||
}
|
||||
|
||||
labels[api.BackupNameLabel] = label.GetValidName(backupName)
|
||||
labels[api.RestoreNameLabel] = label.GetValidName(restoreName)
|
||||
labels[velerov1api.BackupNameLabel] = label.GetValidName(backupName)
|
||||
labels[velerov1api.RestoreNameLabel] = label.GetValidName(restoreName)
|
||||
|
||||
obj.SetLabels(labels)
|
||||
}
|
||||
|
||||
+147
-45
@@ -507,12 +507,16 @@ func TestRestoreResourceFiltering(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, h.restorer.discoveryHelper.Refresh())
|
||||
|
||||
data := Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: nil,
|
||||
VolumeSnapshots: nil,
|
||||
BackupReader: tc.tarball,
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
h.log,
|
||||
tc.restore,
|
||||
tc.backup,
|
||||
nil, // volume snapshots
|
||||
tc.tarball,
|
||||
data,
|
||||
nil, // actions
|
||||
nil, // snapshot location lister
|
||||
nil, // volume snapshotter getter
|
||||
@@ -566,12 +570,16 @@ func TestRestoreNamespaceMapping(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, h.restorer.discoveryHelper.Refresh())
|
||||
|
||||
data := Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: nil,
|
||||
VolumeSnapshots: nil,
|
||||
BackupReader: tc.tarball,
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
h.log,
|
||||
tc.restore,
|
||||
tc.backup,
|
||||
nil, // volume snapshots
|
||||
tc.tarball,
|
||||
data,
|
||||
nil, // actions
|
||||
nil, // snapshot location lister
|
||||
nil, // volume snapshotter getter
|
||||
@@ -644,12 +652,16 @@ func TestRestoreResourcePriorities(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, h.restorer.discoveryHelper.Refresh())
|
||||
|
||||
data := Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: nil,
|
||||
VolumeSnapshots: nil,
|
||||
BackupReader: tc.tarball,
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
h.log,
|
||||
tc.restore,
|
||||
tc.backup,
|
||||
nil, // volume snapshots
|
||||
tc.tarball,
|
||||
data,
|
||||
nil, // actions
|
||||
nil, // snapshot location lister
|
||||
nil, // volume snapshotter getter
|
||||
@@ -717,12 +729,16 @@ func TestInvalidTarballContents(t *testing.T) {
|
||||
}
|
||||
require.NoError(t, h.restorer.discoveryHelper.Refresh())
|
||||
|
||||
data := Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: nil,
|
||||
VolumeSnapshots: nil,
|
||||
BackupReader: tc.tarball,
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
h.log,
|
||||
tc.restore,
|
||||
tc.backup,
|
||||
nil, // volume snapshots
|
||||
tc.tarball,
|
||||
data,
|
||||
nil, // actions
|
||||
nil, // snapshot location lister
|
||||
nil, // volume snapshotter getter
|
||||
@@ -928,12 +944,16 @@ func TestRestoreItems(t *testing.T) {
|
||||
h.addItems(t, r)
|
||||
}
|
||||
|
||||
data := Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: nil,
|
||||
VolumeSnapshots: nil,
|
||||
BackupReader: tc.tarball,
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
h.log,
|
||||
tc.restore,
|
||||
tc.backup,
|
||||
nil, // volume snapshots
|
||||
tc.tarball,
|
||||
data,
|
||||
nil, // actions
|
||||
nil, // snapshot location lister
|
||||
nil, // volume snapshotter getter
|
||||
@@ -1118,12 +1138,16 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) {
|
||||
actions = append(actions, action)
|
||||
}
|
||||
|
||||
data := Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: nil,
|
||||
VolumeSnapshots: nil,
|
||||
BackupReader: tc.tarball,
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
h.log,
|
||||
tc.restore,
|
||||
tc.backup,
|
||||
nil, // volume snapshots
|
||||
tc.tarball,
|
||||
data,
|
||||
actions,
|
||||
nil, // snapshot location lister
|
||||
nil, // volume snapshotter getter
|
||||
@@ -1253,12 +1277,16 @@ func TestRestoreActionModifications(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
data := Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: nil,
|
||||
VolumeSnapshots: nil,
|
||||
BackupReader: tc.tarball,
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
h.log,
|
||||
tc.restore,
|
||||
tc.backup,
|
||||
nil, // volume snapshots
|
||||
tc.tarball,
|
||||
data,
|
||||
tc.actions,
|
||||
nil, // snapshot location lister
|
||||
nil, // volume snapshotter getter
|
||||
@@ -1416,12 +1444,16 @@ func TestRestoreActionAdditionalItems(t *testing.T) {
|
||||
h.addItems(t, r)
|
||||
}
|
||||
|
||||
data := Request{
|
||||
Log: h.log,
|
||||
Restore: tc.restore,
|
||||
Backup: tc.backup,
|
||||
PodVolumeBackups: nil,
|
||||
VolumeSnapshots: nil,
|
||||
BackupReader: tc.tarball,
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
h.log,
|
||||
tc.restore,
|
||||
tc.backup,
|
||||
nil, // volume snapshots
|
||||
tc.tarball,
|
||||
data,
|
||||
tc.actions,
|
||||
nil, // snapshot location lister
|
||||
nil, // volume snapshotter getter
|
||||
@@ -1707,6 +1739,7 @@ func TestRestorePersistentVolumes(t *testing.T) {
|
||||
volumeSnapshots []*volume.Snapshot
|
||||
volumeSnapshotLocations []*velerov1api.VolumeSnapshotLocation
|
||||
volumeSnapshotterGetter volumeSnapshotterGetter
|
||||
podVolumeBackups []*velerov1api.PodVolumeBackup
|
||||
want []*test.APIResource
|
||||
}{
|
||||
{
|
||||
@@ -1997,6 +2030,71 @@ 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",
|
||||
restore: defaultRestore().Result(),
|
||||
backup: defaultBackup().Result(),
|
||||
tarball: newTarWriter(t).
|
||||
addItems("persistentvolumes",
|
||||
builder.ForPersistentVolume("pv-1").
|
||||
ReclaimPolicy(corev1api.PersistentVolumeReclaimRetain).
|
||||
AWSEBSVolumeID("old-volume").
|
||||
Result(),
|
||||
).
|
||||
done(),
|
||||
apiResources: []*test.APIResource{
|
||||
test.PVs(
|
||||
builder.ForPersistentVolume("pv-1").
|
||||
ReclaimPolicy(corev1api.PersistentVolumeReclaimRetain).
|
||||
AWSEBSVolumeID("old-volume").
|
||||
Result(),
|
||||
),
|
||||
test.PVCs(),
|
||||
},
|
||||
volumeSnapshots: []*volume.Snapshot{
|
||||
{
|
||||
Spec: volume.SnapshotSpec{
|
||||
BackupName: "backup-1",
|
||||
Location: "default",
|
||||
PersistentVolumeName: "pv-1",
|
||||
},
|
||||
Status: volume.SnapshotStatus{
|
||||
Phase: volume.SnapshotPhaseCompleted,
|
||||
ProviderSnapshotID: "snapshot-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
volumeSnapshotLocations: []*velerov1api.VolumeSnapshotLocation{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "default",
|
||||
},
|
||||
Spec: velerov1api.VolumeSnapshotLocationSpec{
|
||||
Provider: "provider-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
volumeSnapshotterGetter: map[string]velero.VolumeSnapshotter{
|
||||
// the volume snapshotter fake is not configured with any snapshotID -> volumeID
|
||||
// mappings as a way to verify that the snapshot is not restored, since if it were
|
||||
// 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").
|
||||
ReclaimPolicy(corev1api.PersistentVolumeReclaimRetain).
|
||||
AWSEBSVolumeID("old-volume").
|
||||
Result(),
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -2025,12 +2123,16 @@ 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,
|
||||
}
|
||||
warnings, errs := h.restorer.Restore(
|
||||
h.log,
|
||||
tc.restore,
|
||||
tc.backup,
|
||||
tc.volumeSnapshots,
|
||||
tc.tarball,
|
||||
data,
|
||||
nil, // actions
|
||||
vslInformer.Lister(),
|
||||
tc.volumeSnapshotterGetter,
|
||||
|
||||
Reference in New Issue
Block a user