mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-03 22:57:04 +00:00
Merge branch 'main' into update_enabled_runtime_controllers
This commit is contained in:
@@ -951,6 +951,10 @@ func (c *backupController) deleteVolumeSnapshot(volumeSnapshots []*snapshotv1api
|
||||
if vs.Status.BoundVolumeSnapshotContentName != nil &&
|
||||
len(*vs.Status.BoundVolumeSnapshotContentName) > 0 {
|
||||
vsc = vscMap[*vs.Status.BoundVolumeSnapshotContentName]
|
||||
if nil == vsc {
|
||||
logger.Errorf("Not find %s from the vscMap", vs.Status.BoundVolumeSnapshotContentName)
|
||||
return
|
||||
}
|
||||
if vsc.Spec.DeletionPolicy == snapshotv1api.VolumeSnapshotContentDelete {
|
||||
modifyVSCFlag = true
|
||||
}
|
||||
|
||||
@@ -41,7 +41,6 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/repository"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
|
||||
@@ -440,7 +439,7 @@ func (r *backupDeletionReconciler) deleteResticSnapshots(ctx context.Context, ba
|
||||
return nil
|
||||
}
|
||||
|
||||
snapshots, err := restic.GetSnapshotsInBackup(ctx, backup, r.Client)
|
||||
snapshots, err := getSnapshotsInBackup(ctx, backup, r.Client)
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
@@ -491,3 +490,33 @@ func (r *backupDeletionReconciler) patchBackup(ctx context.Context, backup *vele
|
||||
}
|
||||
return backup, nil
|
||||
}
|
||||
|
||||
// getSnapshotsInBackup returns a list of all restic snapshot ids associated with
|
||||
// a given Velero backup.
|
||||
func getSnapshotsInBackup(ctx context.Context, backup *velerov1api.Backup, kbClient client.Client) ([]repository.SnapshotIdentifier, error) {
|
||||
podVolumeBackups := &velerov1api.PodVolumeBackupList{}
|
||||
options := &client.ListOptions{
|
||||
LabelSelector: labels.Set(map[string]string{
|
||||
velerov1api.BackupNameLabel: label.GetValidName(backup.Name),
|
||||
}).AsSelector(),
|
||||
}
|
||||
|
||||
err := kbClient.List(ctx, podVolumeBackups, options)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
var res []repository.SnapshotIdentifier
|
||||
for _, item := range podVolumeBackups.Items {
|
||||
if item.Status.SnapshotID == "" {
|
||||
continue
|
||||
}
|
||||
res = append(res, repository.SnapshotIdentifier{
|
||||
VolumeNamespace: item.Spec.Pod.Namespace,
|
||||
BackupStorageLocation: backup.Spec.StorageLocation,
|
||||
SnapshotID: item.Status.SnapshotID,
|
||||
})
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package controller
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
@@ -32,6 +33,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -52,6 +54,7 @@ import (
|
||||
persistencemocks "github.com/vmware-tanzu/velero/pkg/persistence/mocks"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
|
||||
pluginmocks "github.com/vmware-tanzu/velero/pkg/plugin/mocks"
|
||||
"github.com/vmware-tanzu/velero/pkg/repository"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
|
||||
@@ -692,3 +695,172 @@ func TestBackupDeletionControllerReconcile(t *testing.T) {
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetSnapshotsInBackup(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
podVolumeBackups []velerov1api.PodVolumeBackup
|
||||
expected []repository.SnapshotIdentifier
|
||||
longBackupNameEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "no pod volume backups",
|
||||
podVolumeBackups: nil,
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "no pod volume backups with matching label",
|
||||
podVolumeBackups: []velerov1api.PodVolumeBackup{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"},
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "some pod volume backups with matching label",
|
||||
podVolumeBackups: []velerov1api.PodVolumeBackup{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-3"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb-2", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-4"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "incomplete-or-failed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-2"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: ""},
|
||||
},
|
||||
},
|
||||
expected: []repository.SnapshotIdentifier{
|
||||
{
|
||||
VolumeNamespace: "ns-1",
|
||||
SnapshotID: "snap-3",
|
||||
},
|
||||
{
|
||||
VolumeNamespace: "ns-1",
|
||||
SnapshotID: "snap-4",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "some pod volume backups with matching label and backup name greater than 63 chars",
|
||||
longBackupNameEnabled: true,
|
||||
podVolumeBackups: []velerov1api.PodVolumeBackup{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-1"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{velerov1api.BackupNameLabel: "non-matching-backup-2"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-2", Namespace: "ns-2"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-2"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "the-really-long-backup-name-that-is-much-more-than-63-cha6ca4bc"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-3"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "completed-pvb-2", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-1"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: "snap-4"},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "incomplete-or-failed-pvb", Labels: map[string]string{velerov1api.BackupNameLabel: "backup-1"}},
|
||||
Spec: velerov1api.PodVolumeBackupSpec{
|
||||
Pod: corev1api.ObjectReference{Name: "pod-1", Namespace: "ns-2"},
|
||||
},
|
||||
Status: velerov1api.PodVolumeBackupStatus{SnapshotID: ""},
|
||||
},
|
||||
},
|
||||
expected: []repository.SnapshotIdentifier{
|
||||
{
|
||||
VolumeNamespace: "ns-1",
|
||||
SnapshotID: "snap-3",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
clientBuilder = velerotest.NewFakeControllerRuntimeClientBuilder(t)
|
||||
veleroBackup = &velerov1api.Backup{}
|
||||
)
|
||||
|
||||
veleroBackup.Name = "backup-1"
|
||||
|
||||
if test.longBackupNameEnabled {
|
||||
veleroBackup.Name = "the-really-long-backup-name-that-is-much-more-than-63-characters"
|
||||
}
|
||||
clientBuilder.WithLists(&velerov1api.PodVolumeBackupList{
|
||||
Items: test.podVolumeBackups,
|
||||
})
|
||||
|
||||
res, err := getSnapshotsInBackup(context.TODO(), veleroBackup, clientBuilder.Build())
|
||||
assert.NoError(t, err)
|
||||
|
||||
// sort to ensure good compare of slices
|
||||
less := func(snapshots []repository.SnapshotIdentifier) func(i, j int) bool {
|
||||
return func(i, j int) bool {
|
||||
if snapshots[i].VolumeNamespace == snapshots[j].VolumeNamespace {
|
||||
return snapshots[i].SnapshotID < snapshots[j].SnapshotID
|
||||
}
|
||||
return snapshots[i].VolumeNamespace < snapshots[j].VolumeNamespace
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sort.Slice(test.expected, less(test.expected))
|
||||
sort.Slice(res, less(res))
|
||||
|
||||
assert.Equal(t, test.expected, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+79
-102
@@ -23,19 +23,16 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
pkgbackup "github.com/vmware-tanzu/velero/pkg/backup"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1"
|
||||
velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/label"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -46,100 +43,77 @@ const (
|
||||
gcFailureBSLReadOnly = "BSLReadOnly"
|
||||
)
|
||||
|
||||
// gcController creates DeleteBackupRequests for expired backups.
|
||||
type gcController struct {
|
||||
*genericController
|
||||
|
||||
backupLister velerov1listers.BackupLister
|
||||
deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister
|
||||
deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter
|
||||
kbClient client.Client
|
||||
frequency time.Duration
|
||||
|
||||
clock clock.Clock
|
||||
// gcReconciler creates DeleteBackupRequests for expired backups.
|
||||
type gcReconciler struct {
|
||||
client.Client
|
||||
logger logrus.FieldLogger
|
||||
clock clock.Clock
|
||||
}
|
||||
|
||||
// NewGCController constructs a new gcController.
|
||||
func NewGCController(
|
||||
// NewGCReconciler constructs a new gcReconciler.
|
||||
func NewGCReconciler(
|
||||
logger logrus.FieldLogger,
|
||||
backupInformer velerov1informers.BackupInformer,
|
||||
deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister,
|
||||
deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter,
|
||||
kbClient client.Client,
|
||||
frequency time.Duration,
|
||||
) Interface {
|
||||
c := &gcController{
|
||||
genericController: newGenericController(GarbageCollection, logger),
|
||||
clock: clock.RealClock{},
|
||||
backupLister: backupInformer.Lister(),
|
||||
deleteBackupRequestLister: deleteBackupRequestLister,
|
||||
deleteBackupRequestClient: deleteBackupRequestClient,
|
||||
kbClient: kbClient,
|
||||
}
|
||||
|
||||
c.syncHandler = c.processQueueItem
|
||||
c.resyncPeriod = frequency
|
||||
if c.resyncPeriod <= 0 {
|
||||
c.resyncPeriod = defaultGCFrequency
|
||||
}
|
||||
logger.Infof("Garbage collection frequency: %s", c.resyncPeriod.String())
|
||||
c.resyncFunc = c.enqueueAllBackups
|
||||
|
||||
backupInformer.Informer().AddEventHandler(
|
||||
cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: c.enqueue,
|
||||
UpdateFunc: func(_, obj interface{}) { c.enqueue(obj) },
|
||||
},
|
||||
)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// enqueueAllBackups lists all backups from cache and enqueues all of them so we can check each one
|
||||
// for expiration.
|
||||
func (c *gcController) enqueueAllBackups() {
|
||||
c.logger.Debug("gcController.enqueueAllBackups")
|
||||
|
||||
backups, err := c.backupLister.List(labels.Everything())
|
||||
if err != nil {
|
||||
c.logger.WithError(errors.WithStack(err)).Error("error listing backups")
|
||||
return
|
||||
}
|
||||
|
||||
for _, backup := range backups {
|
||||
c.enqueue(backup)
|
||||
client client.Client,
|
||||
) *gcReconciler {
|
||||
return &gcReconciler{
|
||||
Client: client,
|
||||
logger: logger,
|
||||
clock: clock.RealClock{},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *gcController) processQueueItem(key string) error {
|
||||
log := c.logger.WithField("backup", key)
|
||||
// GCController only watches on CreateEvent for ensuring every new backup will be taken care of.
|
||||
// Other Events will be filtered to decrease the number of reconcile call. Especially UpdateEvent must be filtered since we removed
|
||||
// the backup status as the sub-resource of backup in v1.9, every change on it will be treated as UpdateEvent and trigger reconcile call.
|
||||
func (c *gcReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
s := kube.NewPeriodicalEnqueueSource(c.logger, mgr.GetClient(), &velerov1api.BackupList{}, defaultGCFrequency)
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&velerov1api.Backup{}).
|
||||
WithEventFilter(predicate.Funcs{
|
||||
UpdateFunc: func(ue event.UpdateEvent) bool {
|
||||
return false
|
||||
},
|
||||
DeleteFunc: func(de event.DeleteEvent) bool {
|
||||
return false
|
||||
},
|
||||
GenericFunc: func(ge event.GenericEvent) bool {
|
||||
return false
|
||||
},
|
||||
}).
|
||||
Watches(s, nil).
|
||||
Complete(c)
|
||||
}
|
||||
|
||||
ns, name, err := cache.SplitMetaNamespaceKey(key)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error splitting queue key")
|
||||
}
|
||||
// +kubebuilder:rbac:groups=velero.io,resources=backups,verbs=get;list;watch;update
|
||||
// +kubebuilder:rbac:groups=velero.io,resources=backups/status,verbs=get
|
||||
// +kubebuilder:rbac:groups=velero.io,resources=deletebackuprequests,verbs=get;list;watch;create;
|
||||
// +kubebuilder:rbac:groups=velero.io,resources=deletebackuprequests/status,verbs=get
|
||||
// +kubebuilder:rbac:groups=velero.io,resources=backupstoragelocations,verbs=get
|
||||
func (c *gcReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
log := c.logger.WithField("gc backup", req.String())
|
||||
log.Debug("gcController getting backup")
|
||||
|
||||
backup, err := c.backupLister.Backups(ns).Get(name)
|
||||
if apierrors.IsNotFound(err) {
|
||||
log.Debug("Unable to find backup")
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting backup")
|
||||
backup := &velerov1api.Backup{}
|
||||
if err := c.Get(ctx, req.NamespacedName, backup); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
log.WithError(err).Error("backup not found")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
return ctrl.Result{}, errors.Wrapf(err, "error getting backup %s", req.String())
|
||||
}
|
||||
log.Debugf("backup: %v", backup)
|
||||
|
||||
log = c.logger.WithFields(
|
||||
logrus.Fields{
|
||||
"backup": key,
|
||||
"backup": req.String(),
|
||||
"expiration": backup.Status.Expiration,
|
||||
},
|
||||
)
|
||||
|
||||
now := c.clock.Now()
|
||||
|
||||
if backup.Status.Expiration == nil || backup.Status.Expiration.After(now) {
|
||||
log.Debug("Backup has not expired yet, skipping")
|
||||
return nil
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
log.Info("Backup has expired")
|
||||
@@ -149,8 +123,8 @@ func (c *gcController) processQueueItem(key string) error {
|
||||
}
|
||||
|
||||
loc := &velerov1api.BackupStorageLocation{}
|
||||
if err := c.kbClient.Get(context.Background(), client.ObjectKey{
|
||||
Namespace: ns,
|
||||
if err := c.Get(ctx, client.ObjectKey{
|
||||
Namespace: req.Namespace,
|
||||
Name: backup.Spec.StorageLocation,
|
||||
}, loc); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
@@ -159,53 +133,56 @@ func (c *gcController) processQueueItem(key string) error {
|
||||
} else {
|
||||
backup.Labels[garbageCollectionFailure] = gcFailureBSLCannotGet
|
||||
}
|
||||
if err := c.kbClient.Update(context.Background(), backup); err != nil {
|
||||
if err := c.Update(ctx, backup); err != nil {
|
||||
log.WithError(err).Error("error updating backup labels")
|
||||
}
|
||||
return errors.Wrap(err, "error getting backup storage location")
|
||||
return ctrl.Result{}, errors.Wrap(err, "error getting backup storage location")
|
||||
}
|
||||
|
||||
if loc.Spec.AccessMode == velerov1api.BackupStorageLocationAccessModeReadOnly {
|
||||
log.Infof("Backup cannot be garbage-collected because backup storage location %s is currently in read-only mode", loc.Name)
|
||||
backup.Labels[garbageCollectionFailure] = gcFailureBSLReadOnly
|
||||
if err := c.kbClient.Update(context.Background(), backup); err != nil {
|
||||
if err := c.Update(ctx, backup); err != nil {
|
||||
log.WithError(err).Error("error updating backup labels")
|
||||
}
|
||||
return nil
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// remove gc fail error label after this point
|
||||
delete(backup.Labels, garbageCollectionFailure)
|
||||
if err := c.kbClient.Update(context.Background(), backup); err != nil {
|
||||
if err := c.Update(ctx, backup); err != nil {
|
||||
log.WithError(err).Error("error updating backup labels")
|
||||
}
|
||||
|
||||
selector := labels.SelectorFromSet(labels.Set(map[string]string{
|
||||
selector := client.MatchingLabels{
|
||||
velerov1api.BackupNameLabel: label.GetValidName(backup.Name),
|
||||
velerov1api.BackupUIDLabel: string(backup.UID),
|
||||
}))
|
||||
|
||||
dbrs, err := c.deleteBackupRequestLister.DeleteBackupRequests(ns).List(selector)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error listing existing DeleteBackupRequests for backup")
|
||||
}
|
||||
|
||||
dbrs := &velerov1api.DeleteBackupRequestList{}
|
||||
if err := c.List(ctx, dbrs, selector); err != nil {
|
||||
log.WithError(err).Error("error listing DeleteBackupRequests")
|
||||
return ctrl.Result{}, errors.Wrap(err, "error listing existing DeleteBackupRequests for backup")
|
||||
}
|
||||
log.Debugf("length of dbrs:%d", len(dbrs.Items))
|
||||
|
||||
// if there's an existing unprocessed deletion request for this backup, don't create
|
||||
// another one
|
||||
for _, dbr := range dbrs {
|
||||
for _, dbr := range dbrs.Items {
|
||||
switch dbr.Status.Phase {
|
||||
case "", velerov1api.DeleteBackupRequestPhaseNew, velerov1api.DeleteBackupRequestPhaseInProgress:
|
||||
log.Info("Backup already has a pending deletion request")
|
||||
return nil
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Creating a new deletion request")
|
||||
req := pkgbackup.NewDeleteBackupRequest(backup.Name, string(backup.UID))
|
||||
|
||||
if _, err = c.deleteBackupRequestClient.DeleteBackupRequests(ns).Create(context.TODO(), req, metav1.CreateOptions{}); err != nil {
|
||||
return errors.Wrap(err, "error creating DeleteBackupRequest")
|
||||
ndbr := pkgbackup.NewDeleteBackupRequest(backup.Name, string(backup.UID))
|
||||
ndbr.SetNamespace(backup.Namespace)
|
||||
if err := c.Create(ctx, ndbr); err != nil {
|
||||
log.WithError(err).Error("error creating DeleteBackupRequests")
|
||||
return ctrl.Result{}, errors.Wrap(err, "error creating DeleteBackupRequest")
|
||||
}
|
||||
|
||||
return nil
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
@@ -18,152 +18,41 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
core "k8s.io/client-go/testing"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake"
|
||||
informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
|
||||
func TestGCControllerEnqueueAllBackups(t *testing.T) {
|
||||
var (
|
||||
client = fake.NewSimpleClientset()
|
||||
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
||||
|
||||
controller = NewGCController(
|
||||
velerotest.NewLogger(),
|
||||
sharedInformers.Velero().V1().Backups(),
|
||||
sharedInformers.Velero().V1().DeleteBackupRequests().Lister(),
|
||||
client.VeleroV1(),
|
||||
nil,
|
||||
defaultGCFrequency,
|
||||
).(*gcController)
|
||||
)
|
||||
|
||||
keys := make(chan string)
|
||||
|
||||
controller.syncHandler = func(key string) error {
|
||||
keys <- key
|
||||
return nil
|
||||
}
|
||||
|
||||
var expected []string
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
backup := builder.ForBackup(velerov1api.DefaultNamespace, fmt.Sprintf("backup-%d", i)).Result()
|
||||
sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(backup)
|
||||
expected = append(expected, kube.NamespaceAndName(backup))
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
go controller.Run(ctx, 1)
|
||||
|
||||
var received []string
|
||||
|
||||
Loop:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatal("test timed out")
|
||||
case key := <-keys:
|
||||
received = append(received, key)
|
||||
if len(received) == len(expected) {
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(expected)
|
||||
sort.Strings(received)
|
||||
assert.Equal(t, expected, received)
|
||||
}
|
||||
|
||||
func TestGCControllerHasUpdateFunc(t *testing.T) {
|
||||
backup := defaultBackup().Result()
|
||||
expected := kube.NamespaceAndName(backup)
|
||||
|
||||
client := fake.NewSimpleClientset(backup)
|
||||
|
||||
fakeWatch := watch.NewFake()
|
||||
defer fakeWatch.Stop()
|
||||
client.PrependWatchReactor("backups", core.DefaultWatchReactor(fakeWatch, nil))
|
||||
|
||||
sharedInformers := informers.NewSharedInformerFactory(client, 0)
|
||||
|
||||
controller := NewGCController(
|
||||
func mockGCReconciler(fakeClient kbclient.Client, fakeClock *clock.FakeClock) *gcReconciler {
|
||||
gcr := NewGCReconciler(
|
||||
velerotest.NewLogger(),
|
||||
sharedInformers.Velero().V1().Backups(),
|
||||
sharedInformers.Velero().V1().DeleteBackupRequests().Lister(),
|
||||
client.VeleroV1(),
|
||||
nil,
|
||||
defaultGCFrequency,
|
||||
).(*gcController)
|
||||
|
||||
keys := make(chan string)
|
||||
|
||||
controller.syncHandler = func(key string) error {
|
||||
keys <- key
|
||||
return nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
go sharedInformers.Start(ctx.Done())
|
||||
go controller.Run(ctx, 1)
|
||||
|
||||
// wait for the AddFunc
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatal("test timed out waiting for AddFunc")
|
||||
case key := <-keys:
|
||||
assert.Equal(t, expected, key)
|
||||
}
|
||||
|
||||
backup.Status.Version = 1234
|
||||
fakeWatch.Add(backup)
|
||||
|
||||
// wait for the UpdateFunc
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Fatal("test timed out waiting for UpdateFunc")
|
||||
case key := <-keys:
|
||||
assert.Equal(t, expected, key)
|
||||
}
|
||||
fakeClient,
|
||||
)
|
||||
gcr.clock = fakeClock
|
||||
return gcr
|
||||
}
|
||||
|
||||
func TestGCControllerProcessQueueItem(t *testing.T) {
|
||||
|
||||
func TestGCReconcile(t *testing.T) {
|
||||
fakeClock := clock.NewFakeClock(time.Now())
|
||||
defaultBackupLocation := builder.ForBackupStorageLocation("velero", "default").Result()
|
||||
defaultBackupLocation := builder.ForBackupStorageLocation(velerov1api.DefaultNamespace, "default").Result()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
backup *velerov1api.Backup
|
||||
deleteBackupRequests []*velerov1api.DeleteBackupRequest
|
||||
backupLocation *velerov1api.BackupStorageLocation
|
||||
expectDeletion bool
|
||||
createDeleteBackupRequestError bool
|
||||
expectError bool
|
||||
name string
|
||||
backup *velerov1api.Backup
|
||||
deleteBackupRequests []*velerov1api.DeleteBackupRequest
|
||||
backupLocation *velerov1api.BackupStorageLocation
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "can't find backup - no error",
|
||||
@@ -172,25 +61,21 @@ func TestGCControllerProcessQueueItem(t *testing.T) {
|
||||
name: "unexpired backup is not deleted",
|
||||
backup: defaultBackup().Expiration(fakeClock.Now().Add(time.Minute)).StorageLocation("default").Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
expectDeletion: false,
|
||||
},
|
||||
{
|
||||
name: "expired backup in read-only storage location is not deleted",
|
||||
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-only").Result(),
|
||||
backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result(),
|
||||
expectDeletion: false,
|
||||
},
|
||||
{
|
||||
name: "expired backup in read-write storage location is deleted",
|
||||
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-write").Result(),
|
||||
backupLocation: builder.ForBackupStorageLocation("velero", "read-write").AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite).Result(),
|
||||
expectDeletion: true,
|
||||
},
|
||||
{
|
||||
name: "expired backup with no pending deletion requests is deleted",
|
||||
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
expectDeletion: true,
|
||||
},
|
||||
{
|
||||
name: "expired backup with a pending deletion request is not deleted",
|
||||
@@ -211,7 +96,6 @@ func TestGCControllerProcessQueueItem(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectDeletion: false,
|
||||
},
|
||||
{
|
||||
name: "expired backup with only processed deletion requests is deleted",
|
||||
@@ -232,72 +116,31 @@ func TestGCControllerProcessQueueItem(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
expectDeletion: true,
|
||||
},
|
||||
{
|
||||
name: "create DeleteBackupRequest error returns an error",
|
||||
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
expectDeletion: true,
|
||||
createDeleteBackupRequestError: true,
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
client = fake.NewSimpleClientset()
|
||||
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
||||
)
|
||||
|
||||
var fakeClient kbclient.Client
|
||||
if test.backupLocation != nil {
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClient(t, test.backupLocation)
|
||||
} else {
|
||||
fakeClient = velerotest.NewFakeControllerRuntimeClient(t)
|
||||
if test.backup == nil {
|
||||
return
|
||||
}
|
||||
|
||||
controller := NewGCController(
|
||||
velerotest.NewLogger(),
|
||||
sharedInformers.Velero().V1().Backups(),
|
||||
sharedInformers.Velero().V1().DeleteBackupRequests().Lister(),
|
||||
client.VeleroV1(),
|
||||
fakeClient,
|
||||
defaultGCFrequency,
|
||||
).(*gcController)
|
||||
controller.clock = fakeClock
|
||||
initObjs := []runtime.Object{}
|
||||
initObjs = append(initObjs, test.backup)
|
||||
|
||||
var key string
|
||||
if test.backup != nil {
|
||||
key = kube.NamespaceAndName(test.backup)
|
||||
sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(test.backup)
|
||||
if test.backupLocation != nil {
|
||||
initObjs = append(initObjs, test.backupLocation)
|
||||
}
|
||||
|
||||
for _, dbr := range test.deleteBackupRequests {
|
||||
sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(dbr)
|
||||
initObjs = append(initObjs, dbr)
|
||||
}
|
||||
|
||||
if test.createDeleteBackupRequestError {
|
||||
client.PrependReactor("create", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, errors.New("foo")
|
||||
})
|
||||
}
|
||||
|
||||
err := controller.processQueueItem(key)
|
||||
fakeClient := velerotest.NewFakeControllerRuntimeClient(t, initObjs...)
|
||||
reconciler := mockGCReconciler(fakeClient, fakeClock)
|
||||
_, err := reconciler.Reconcile(context.TODO(), ctrl.Request{NamespacedName: types.NamespacedName{Namespace: test.backup.Namespace, Name: test.backup.Name}})
|
||||
gotErr := err != nil
|
||||
assert.Equal(t, test.expectError, gotErr)
|
||||
|
||||
if test.expectDeletion {
|
||||
require.Len(t, client.Actions(), 1)
|
||||
|
||||
createAction, ok := client.Actions()[0].(core.CreateAction)
|
||||
require.True(t, ok)
|
||||
|
||||
assert.Equal(t, "deletebackuprequests", createAction.GetResource().Resource)
|
||||
} else {
|
||||
assert.Len(t, client.Actions(), 0)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/metrics"
|
||||
repokey "github.com/vmware-tanzu/velero/pkg/repository/keys"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/uploader"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
@@ -61,6 +62,13 @@ type PodVolumeBackupReconciler struct {
|
||||
Log logrus.FieldLogger
|
||||
}
|
||||
|
||||
type BackupProgressUpdater struct {
|
||||
PodVolumeBackup *velerov1api.PodVolumeBackup
|
||||
Log logrus.FieldLogger
|
||||
Ctx context.Context
|
||||
Cli client.Client
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=velero.io,resources=podvolumebackups,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=velero.io,resources=podvolumebackups/status,verbs=get;update;patch
|
||||
func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
@@ -364,3 +372,20 @@ func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (r *PodVolumeBackupReconciler) NewBackupProgressUpdater(pvb *velerov1api.PodVolumeBackup, log logrus.FieldLogger, ctx context.Context) *BackupProgressUpdater {
|
||||
return &BackupProgressUpdater{pvb, log, ctx, r.Client}
|
||||
}
|
||||
|
||||
//UpdateProgress which implement ProgressUpdater interface to update pvb progress status
|
||||
func (b *BackupProgressUpdater) UpdateProgress(p *uploader.UploaderProgress) {
|
||||
original := b.PodVolumeBackup.DeepCopy()
|
||||
b.PodVolumeBackup.Status.Progress = velerov1api.PodVolumeOperationProgress{TotalBytes: p.TotalBytes, BytesDone: p.BytesDone}
|
||||
if b.Cli == nil {
|
||||
b.Log.Errorf("failed to update backup pod %s volume %s progress with uninitailize client", b.PodVolumeBackup.Spec.Pod.Name, b.PodVolumeBackup.Spec.Volume)
|
||||
return
|
||||
}
|
||||
if err := b.Cli.Patch(b.Ctx, b.PodVolumeBackup, client.MergeFrom(original)); err != nil {
|
||||
b.Log.Errorf("update backup pod %s volume %s progress with %v", b.PodVolumeBackup.Spec.Pod.Name, b.PodVolumeBackup.Spec.Volume, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,8 +39,10 @@ import (
|
||||
|
||||
"github.com/vmware-tanzu/velero/internal/credentials"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/podvolume"
|
||||
repokey "github.com/vmware-tanzu/velero/pkg/repository/keys"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/uploader"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/boolptr"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
@@ -64,6 +66,13 @@ type PodVolumeRestoreReconciler struct {
|
||||
clock clock.Clock
|
||||
}
|
||||
|
||||
type RestoreProgressUpdater struct {
|
||||
PodVolumeRestore *velerov1api.PodVolumeRestore
|
||||
Log logrus.FieldLogger
|
||||
Ctx context.Context
|
||||
Cli client.Client
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=velero.io,resources=podvolumerestores,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=velero.io,resources=podvolumerestores/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups="",resources=pods,verbs=get
|
||||
@@ -98,7 +107,7 @@ func (c *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req
|
||||
resticInitContainerIndex := getResticInitContainerIndex(pod)
|
||||
if resticInitContainerIndex > 0 {
|
||||
log.Warnf(`Init containers before the %s container may cause issues
|
||||
if they interfere with volumes being restored: %s index %d`, restic.InitContainer, restic.InitContainer, resticInitContainerIndex)
|
||||
if they interfere with volumes being restored: %s index %d`, podvolume.InitContainer, podvolume.InitContainer, resticInitContainerIndex)
|
||||
}
|
||||
|
||||
log.Info("Restore starting")
|
||||
@@ -208,7 +217,7 @@ func isResticInitContainerRunning(pod *corev1api.Pod) bool {
|
||||
func getResticInitContainerIndex(pod *corev1api.Pod) int {
|
||||
// Restic wait container can be anywhere in the list of init containers so locate it.
|
||||
for i, initContainer := range pod.Spec.InitContainers {
|
||||
if initContainer.Name == restic.InitContainer {
|
||||
if initContainer.Name == podvolume.InitContainer {
|
||||
return i
|
||||
}
|
||||
}
|
||||
@@ -329,3 +338,20 @@ func (c *PodVolumeRestoreReconciler) updateRestoreProgressFunc(req *velerov1api.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *PodVolumeRestoreReconciler) NewRestoreProgressUpdater(pvr *velerov1api.PodVolumeRestore, log logrus.FieldLogger, ctx context.Context) *RestoreProgressUpdater {
|
||||
return &RestoreProgressUpdater{pvr, log, ctx, r.Client}
|
||||
}
|
||||
|
||||
//UpdateProgress which implement ProgressUpdater interface to update pvr progress status
|
||||
func (r *RestoreProgressUpdater) UpdateProgress(p *uploader.UploaderProgress) {
|
||||
original := r.PodVolumeRestore.DeepCopy()
|
||||
r.PodVolumeRestore.Status.Progress = velerov1api.PodVolumeOperationProgress{TotalBytes: p.TotalBytes, BytesDone: p.BytesDone}
|
||||
if r.Cli == nil {
|
||||
r.Log.Errorf("failed to update restore pod %s volume %s progress with uninitailize client", r.PodVolumeRestore.Spec.Pod.Name, r.PodVolumeRestore.Spec.Volume)
|
||||
return
|
||||
}
|
||||
if err := r.Cli.Patch(r.Ctx, r.PodVolumeRestore, client.MergeFrom(original)); err != nil {
|
||||
r.Log.Errorf("update restore pod %s volume %s progress with %v", r.PodVolumeRestore.Spec.Pod.Name, r.PodVolumeRestore.Spec.Volume, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/podvolume"
|
||||
"github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
|
||||
@@ -120,7 +120,7 @@ func TestShouldProcess(t *testing.T) {
|
||||
NodeName: controllerNode,
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -160,7 +160,7 @@ func TestShouldProcess(t *testing.T) {
|
||||
NodeName: controllerNode,
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -260,7 +260,7 @@ func TestIsResticContainerRunning(t *testing.T) {
|
||||
Name: "non-restic-init",
|
||||
},
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -291,7 +291,7 @@ func TestIsResticContainerRunning(t *testing.T) {
|
||||
Spec: corev1api.PodSpec{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
{
|
||||
Name: "non-restic-init",
|
||||
@@ -323,7 +323,7 @@ func TestIsResticContainerRunning(t *testing.T) {
|
||||
Spec: corev1api.PodSpec{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
{
|
||||
Name: "non-restic-init",
|
||||
@@ -357,7 +357,7 @@ func TestIsResticContainerRunning(t *testing.T) {
|
||||
Spec: corev1api.PodSpec{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -422,7 +422,7 @@ func TestGetResticInitContainerIndex(t *testing.T) {
|
||||
Name: "non-restic-init",
|
||||
},
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -439,7 +439,7 @@ func TestGetResticInitContainerIndex(t *testing.T) {
|
||||
Spec: corev1api.PodSpec{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
{
|
||||
Name: "non-restic-init",
|
||||
@@ -459,7 +459,7 @@ func TestGetResticInitContainerIndex(t *testing.T) {
|
||||
Spec: corev1api.PodSpec{
|
||||
InitContainers: []corev1api.Container{
|
||||
{
|
||||
Name: restic.InitContainer,
|
||||
Name: podvolume.InitContainer,
|
||||
},
|
||||
{
|
||||
Name: "non-restic-init",
|
||||
|
||||
Reference in New Issue
Block a user