diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index b1777c77b9..c2812656e6 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -286,6 +286,7 @@ func (kb *kubernetesBackupper) Backup(backup *api.Backup, backupFile, logFile io resourceHooks, kb.snapshotService, resticBackupper, + newPVCSnapshotTracker(), ) for _, group := range kb.discoveryHelper.Resources() { diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index c30edc6e63..f52cbbc0d1 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -542,6 +542,7 @@ func TestBackup(t *testing.T) { test.expectedHooks, mock.Anything, mock.Anything, // restic backupper + mock.Anything, // pvc snapshot tracker ).Return(groupBackupper) for group, err := range test.backupGroupErrors { @@ -606,6 +607,7 @@ func TestBackupUsesNewCohabitatingResourcesForEachBackup(t *testing.T) { mock.Anything, mock.Anything, mock.Anything, + mock.Anything, ).Return(&mockGroupBackupper{}) assert.NoError(t, b.Backup(&v1.Backup{}, &bytes.Buffer{}, &bytes.Buffer{}, nil)) @@ -637,6 +639,7 @@ func TestBackupUsesNewCohabitatingResourcesForEachBackup(t *testing.T) { mock.Anything, mock.Anything, mock.Anything, + mock.Anything, ).Return(&mockGroupBackupper{}) assert.NoError(t, b.Backup(&v1.Backup{}, &bytes.Buffer{}, &bytes.Buffer{}, nil)) @@ -665,6 +668,7 @@ func (f *mockGroupBackupperFactory) newGroupBackupper( resourceHooks []resourceHook, snapshotService cloudprovider.SnapshotService, resticBackupper restic.Backupper, + resticSnapshotTracker *pvcSnapshotTracker, ) groupBackupper { args := f.Called( log, @@ -681,6 +685,7 @@ func (f *mockGroupBackupperFactory) newGroupBackupper( resourceHooks, snapshotService, resticBackupper, + resticSnapshotTracker, ) return args.Get(0).(groupBackupper) } diff --git a/pkg/backup/group_backupper.go b/pkg/backup/group_backupper.go index 4cad7cba66..481463fbb3 100644 --- a/pkg/backup/group_backupper.go +++ b/pkg/backup/group_backupper.go @@ -51,6 +51,7 @@ type groupBackupperFactory interface { resourceHooks []resourceHook, snapshotService cloudprovider.SnapshotService, resticBackupper restic.Backupper, + resticSnapshotTracker *pvcSnapshotTracker, ) groupBackupper } @@ -70,6 +71,7 @@ func (f *defaultGroupBackupperFactory) newGroupBackupper( resourceHooks []resourceHook, snapshotService cloudprovider.SnapshotService, resticBackupper restic.Backupper, + resticSnapshotTracker *pvcSnapshotTracker, ) groupBackupper { return &defaultGroupBackupper{ log: log, @@ -86,6 +88,7 @@ func (f *defaultGroupBackupperFactory) newGroupBackupper( resourceHooks: resourceHooks, snapshotService: snapshotService, resticBackupper: resticBackupper, + resticSnapshotTracker: resticSnapshotTracker, resourceBackupperFactory: &defaultResourceBackupperFactory{}, } } @@ -108,6 +111,7 @@ type defaultGroupBackupper struct { resourceHooks []resourceHook snapshotService cloudprovider.SnapshotService resticBackupper restic.Backupper + resticSnapshotTracker *pvcSnapshotTracker resourceBackupperFactory resourceBackupperFactory } @@ -131,6 +135,7 @@ func (gb *defaultGroupBackupper) backupGroup(group *metav1.APIResourceList) erro gb.resourceHooks, gb.snapshotService, gb.resticBackupper, + gb.resticSnapshotTracker, ) ) diff --git a/pkg/backup/group_backupper_test.go b/pkg/backup/group_backupper_test.go index 8276ee08c1..1be6abb4ce 100644 --- a/pkg/backup/group_backupper_test.go +++ b/pkg/backup/group_backupper_test.go @@ -89,6 +89,7 @@ func TestBackupGroup(t *testing.T) { resourceHooks, nil, // snapshot service nil, // restic backupper + newPVCSnapshotTracker(), ).(*defaultGroupBackupper) resourceBackupperFactory := &mockResourceBackupperFactory{} @@ -113,6 +114,7 @@ func TestBackupGroup(t *testing.T) { resourceHooks, nil, mock.Anything, // restic backupper + mock.Anything, // pvc snapshot tracker ).Return(resourceBackupper) group := &metav1.APIResourceList{ @@ -161,6 +163,7 @@ func (rbf *mockResourceBackupperFactory) newResourceBackupper( resourceHooks []resourceHook, snapshotService cloudprovider.SnapshotService, resticBackupper restic.Backupper, + resticSnapshotTracker *pvcSnapshotTracker, ) resourceBackupper { args := rbf.Called( log, @@ -176,6 +179,8 @@ func (rbf *mockResourceBackupperFactory) newResourceBackupper( tarWriter, resourceHooks, snapshotService, + resticBackupper, + resticSnapshotTracker, ) return args.Get(0).(resourceBackupper) } diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index d821ed1cfb..9f0788b09b 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -28,7 +28,6 @@ import ( corev1api "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -58,6 +57,7 @@ type itemBackupperFactory interface { discoveryHelper discovery.Helper, snapshotService cloudprovider.SnapshotService, resticBackupper restic.Backupper, + resticSnapshotTracker *pvcSnapshotTracker, ) ItemBackupper } @@ -75,6 +75,7 @@ func (f *defaultItemBackupperFactory) newItemBackupper( discoveryHelper discovery.Helper, snapshotService cloudprovider.SnapshotService, resticBackupper restic.Backupper, + resticSnapshotTracker *pvcSnapshotTracker, ) ItemBackupper { ib := &defaultItemBackupper{ backup: backup, @@ -90,7 +91,8 @@ func (f *defaultItemBackupperFactory) newItemBackupper( itemHookHandler: &defaultItemHookHandler{ podCommandExecutor: podCommandExecutor, }, - resticBackupper: resticBackupper, + resticBackupper: resticBackupper, + resticSnapshotTracker: resticSnapshotTracker, } // this is for testing purposes @@ -104,17 +106,18 @@ type ItemBackupper interface { } type defaultItemBackupper struct { - backup *api.Backup - namespaces *collections.IncludesExcludes - resources *collections.IncludesExcludes - backedUpItems map[itemKey]struct{} - actions []resolvedAction - tarWriter tarWriter - resourceHooks []resourceHook - dynamicFactory client.DynamicFactory - discoveryHelper discovery.Helper - snapshotService cloudprovider.SnapshotService - resticBackupper restic.Backupper + backup *api.Backup + namespaces *collections.IncludesExcludes + resources *collections.IncludesExcludes + backedUpItems map[itemKey]struct{} + actions []resolvedAction + tarWriter tarWriter + resourceHooks []resourceHook + dynamicFactory client.DynamicFactory + discoveryHelper discovery.Helper + snapshotService cloudprovider.SnapshotService + resticBackupper restic.Backupper + resticSnapshotTracker *pvcSnapshotTracker itemHookHandler itemHookHandler additionalItemBackupper ItemBackupper @@ -178,9 +181,30 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim return err } - backupErrs := make([]error, 0) - err = ib.executeActions(log, obj, groupResource, name, namespace, metadata) - if err != nil { + var ( + backupErrs []error + pod *corev1api.Pod + resticVolumesToBackup []string + ) + + if groupResource == kuberesource.Pods { + // pod needs to be initialized for the unstructured converter + pod = new(corev1api.Pod) + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), pod); err != nil { + backupErrs = append(backupErrs, errors.WithStack(err)) + // nil it on error since it's not valid + pod = nil + } else { + // get the volumes to backup using restic, and add any of them that are PVCs to the pvc snapshot + // tracker, so that when we backup PVCs/PVs via an item action in the next step, we don't snapshot + // PVs that will have their data backed up with restic. + resticVolumesToBackup = restic.GetVolumesToBackup(pod) + + ib.resticSnapshotTracker.Track(pod, resticVolumesToBackup) + } + } + + if err := ib.executeActions(log, obj, groupResource, name, namespace, metadata); err != nil { log.WithError(err).Error("Error executing item actions") backupErrs = append(backupErrs, err) } @@ -188,24 +212,22 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim if groupResource == kuberesource.PersistentVolumes { if ib.snapshotService == nil { log.Debug("Skipping Persistent Volume snapshot because they're not enabled.") - } else { - if err := ib.takePVSnapshot(obj, ib.backup, log); err != nil { - backupErrs = append(backupErrs, err) - } + } else if err := ib.takePVSnapshot(obj, ib.backup, log); err != nil { + backupErrs = append(backupErrs, err) } } - if groupResource == kuberesource.Pods && len(restic.GetVolumesToBackup(metadata)) > 0 { - var ( - updatedObj runtime.Unstructured - errs []error - ) + if groupResource == kuberesource.Pods && pod != nil { + // this function will return partial results, so process volumeSnapshots + // even if there are errors. + volumeSnapshots, errs := ib.backupPodVolumes(log, pod, resticVolumesToBackup) - if updatedObj, errs = backupPodVolumes(log, ib.backup, obj, ib.resticBackupper); len(errs) > 0 { - backupErrs = append(backupErrs, errs...) - } else { - obj = updatedObj + // annotate the pod with the successful volume snapshots + for volume, snapshot := range volumeSnapshots { + restic.SetPodSnapshotAnnotation(metadata, volume, snapshot) } + + backupErrs = append(backupErrs, errs...) } log.Debug("Executing post hooks") @@ -248,37 +270,19 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim return nil } -func backupPodVolumes(log logrus.FieldLogger, backup *api.Backup, obj runtime.Unstructured, backupper restic.Backupper) (runtime.Unstructured, []error) { - if backupper == nil { - log.Warn("No restic backupper, not backing up pod's volumes") - return obj, nil - } - - pod := new(corev1api.Pod) - if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), pod); err != nil { - return nil, []error{errors.WithStack(err)} +// backupPodVolumes triggers restic backups of the specified pod volumes, and returns a map of volume name -> snapshot ID +// for volumes that were successfully backed up, and a slice of any errors that were encountered. +func (ib *defaultItemBackupper) backupPodVolumes(log logrus.FieldLogger, pod *corev1api.Pod, volumes []string) (map[string]string, []error) { + if len(volumes) == 0 { + return nil, nil } - volumeSnapshots, errs := backupper.BackupPodVolumes(backup, pod, log) - if len(errs) > 0 { - return nil, errs - } - if len(volumeSnapshots) == 0 { - return obj, nil - } - - // annotate the pod with the successful volume snapshots - for volume, snapshot := range volumeSnapshots { - restic.SetPodSnapshotAnnotation(pod, volume, snapshot) - } - - // convert annotated pod back to unstructured to return - unstructuredObj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(pod) - if err != nil { - return nil, []error{errors.WithStack(err)} + if ib.resticBackupper == nil { + log.Warn("No restic backupper, not backing up pod's volumes") + return nil, nil } - return &unstructured.Unstructured{Object: unstructuredObj}, nil + return ib.resticBackupper.BackupPodVolumes(ib.backup, pod, log) } func (ib *defaultItemBackupper) executeActions(log logrus.FieldLogger, obj runtime.Unstructured, groupResource schema.GroupResource, name, namespace string, metadata metav1.Object) error { @@ -347,7 +351,7 @@ const zoneLabel = "failure-domain.beta.kubernetes.io/zone" // takePVSnapshot triggers a snapshot for the volume/disk underlying a PersistentVolume if the provided // backup has volume snapshots enabled and the PV is of a compatible type. Also records cloud // disk type and IOPS (if applicable) to be able to restore to current state later. -func (ib *defaultItemBackupper) takePVSnapshot(pv runtime.Unstructured, backup *api.Backup, log logrus.FieldLogger) error { +func (ib *defaultItemBackupper) takePVSnapshot(obj runtime.Unstructured, backup *api.Backup, log logrus.FieldLogger) error { log.Info("Executing takePVSnapshot") if backup.Spec.SnapshotVolumes != nil && !*backup.Spec.SnapshotVolumes { @@ -355,7 +359,21 @@ func (ib *defaultItemBackupper) takePVSnapshot(pv runtime.Unstructured, backup * return nil } - metadata, err := meta.Accessor(pv) + pv := new(corev1api.PersistentVolume) + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), pv); err != nil { + return errors.WithStack(err) + } + + // If this PV is claimed, see if we've already taken a (restic) snapshot of the contents + // of this PV. If so, don't take a snapshot. + if pv.Spec.ClaimRef != nil { + if ib.resticSnapshotTracker.Has(pv.Spec.ClaimRef.Namespace, pv.Spec.ClaimRef.Name) { + log.Info("Skipping Persistent Volume snapshot because volume has already been backed up.") + return nil + } + } + + metadata, err := meta.Accessor(obj) if err != nil { return errors.WithStack(err) } @@ -370,7 +388,7 @@ func (ib *defaultItemBackupper) takePVSnapshot(pv runtime.Unstructured, backup * log.Infof("label %q is not present on PersistentVolume", zoneLabel) } - volumeID, err := ib.snapshotService.GetVolumeID(pv) + volumeID, err := ib.snapshotService.GetVolumeID(obj) if err != nil { return errors.Wrapf(err, "error getting volume ID for PersistentVolume") } diff --git a/pkg/backup/item_backupper_test.go b/pkg/backup/item_backupper_test.go index 680f3fc4e6..2d17e5209b 100644 --- a/pkg/backup/item_backupper_test.go +++ b/pkg/backup/item_backupper_test.go @@ -39,6 +39,7 @@ import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/sets" ) func TestBackupItemSkips(t *testing.T) { @@ -163,6 +164,8 @@ func TestBackupItemNoSkips(t *testing.T) { snapshottableVolumes map[string]api.VolumeBackupInfo snapshotError error additionalItemError error + trackedPVCs sets.String + expectedTrackedPVCs sets.String }{ { name: "explicit namespace include", @@ -293,6 +296,32 @@ func TestBackupItemNoSkips(t *testing.T) { "vol-abc123": {SnapshotID: "snapshot-1", AvailabilityZone: "us-east-1c"}, }, }, + { + name: "takePVSnapshot is not invoked for PVs when their claim is tracked in the restic PVC tracker", + namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"), + item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"claimRef": {"namespace": "pvc-ns", "name": "pvc"}, "awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`, + expectError: false, + expectExcluded: false, + expectedTarHeaderName: "resources/persistentvolumes/cluster/mypv.json", + groupResource: "persistentvolumes", + // empty snapshottableVolumes causes a snapshotService to be created, but no + // snapshots are expected to be taken. + snapshottableVolumes: map[string]api.VolumeBackupInfo{}, + trackedPVCs: sets.NewString(key("pvc-ns", "pvc"), key("another-pvc-ns", "another-pvc")), + }, + { + name: "takePVSnapshot is invoked for PVs when their claim is not tracked in the restic PVC tracker", + namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"), + item: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv", "labels": {"failure-domain.beta.kubernetes.io/zone": "us-east-1c"}}, "spec": {"claimRef": {"namespace": "pvc-ns", "name": "pvc"}, "awsElasticBlockStore": {"volumeID": "aws://us-east-1c/vol-abc123"}}}`, + expectError: false, + expectExcluded: false, + expectedTarHeaderName: "resources/persistentvolumes/cluster/mypv.json", + groupResource: "persistentvolumes", + snapshottableVolumes: map[string]api.VolumeBackupInfo{ + "vol-abc123": {SnapshotID: "snapshot-1", AvailabilityZone: "us-east-1c"}, + }, + trackedPVCs: sets.NewString(key("another-pvc-ns", "another-pvc")), + }, { name: "backup fails when takePVSnapshot fails", namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"), @@ -304,6 +333,16 @@ func TestBackupItemNoSkips(t *testing.T) { }, snapshotError: fmt.Errorf("failure"), }, + { + name: "pod's restic PVC volume backups (only) are tracked", + item: `{"apiVersion": "v1", "kind": "Pod", "spec": {"volumes": [{"name": "volume-1", "persistentVolumeClaim": {"claimName": "bar"}},{"name": "volume-2", "persistentVolumeClaim": {"claimName": "baz"}},{"name": "volume-1", "emptyDir": {}}]}, "metadata":{"namespace":"foo","name":"bar", "annotations": {"backup.ark.heptio.com/backup-volumes": "volume-1,volume-2"}}}`, + namespaceIncludesExcludes: collections.NewIncludesExcludes().Includes("*"), + groupResource: "pods", + expectError: false, + expectExcluded: false, + expectedTarHeaderName: "resources/pods/namespaces/foo/bar.json", + expectedTrackedPVCs: sets.NewString(key("foo", "bar"), key("foo", "baz")), + }, } for _, test := range tests { @@ -376,6 +415,7 @@ func TestBackupItemNoSkips(t *testing.T) { discoveryHelper, nil, // snapshot service nil, // restic backupper + newPVCSnapshotTracker(), ).(*defaultItemBackupper) var snapshotService *arktest.FakeSnapshotService @@ -388,6 +428,10 @@ func TestBackupItemNoSkips(t *testing.T) { b.snapshotService = snapshotService } + if test.trackedPVCs != nil { + b.resticSnapshotTracker.pvcs = test.trackedPVCs + } + // make sure the podCommandExecutor was set correctly in the real hook handler assert.Equal(t, podCommandExecutor, b.itemHookHandler.(*defaultItemHookHandler).podCommandExecutor) @@ -469,7 +513,7 @@ func TestBackupItemNoSkips(t *testing.T) { } if test.snapshottableVolumes != nil { - require.Equal(t, 1, len(snapshotService.SnapshotsTaken)) + require.Equal(t, len(test.snapshottableVolumes), len(snapshotService.SnapshotsTaken)) var expectedBackups []api.VolumeBackupInfo for _, vbi := range test.snapshottableVolumes { @@ -483,6 +527,14 @@ func TestBackupItemNoSkips(t *testing.T) { assert.Equal(t, expectedBackups, actualBackups) } + + if test.expectedTrackedPVCs != nil { + require.Equal(t, len(test.expectedTrackedPVCs), len(b.resticSnapshotTracker.pvcs)) + + for key := range test.expectedTrackedPVCs { + assert.True(t, b.resticSnapshotTracker.pvcs.Has(key)) + } + } }) } } diff --git a/pkg/backup/pvc_snapshot_tracker.go b/pkg/backup/pvc_snapshot_tracker.go new file mode 100644 index 0000000000..5221d2ce2f --- /dev/null +++ b/pkg/backup/pvc_snapshot_tracker.go @@ -0,0 +1,61 @@ +/* +Copyright 2018 the Heptio Ark contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package backup + +import ( + "fmt" + + corev1api "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/sets" +) + +// pvcSnapshotTracker keeps track of persistent volume claims that have been snapshotted +// with restic. +type pvcSnapshotTracker struct { + pvcs sets.String +} + +func newPVCSnapshotTracker() *pvcSnapshotTracker { + return &pvcSnapshotTracker{ + pvcs: sets.NewString(), + } +} + +// Track takes a pod and a list of volumes from that pod that were snapshotted, and +// tracks each snapshotted volume that's a PVC. +func (t *pvcSnapshotTracker) Track(pod *corev1api.Pod, snapshottedVolumes []string) { + for _, volumeName := range snapshottedVolumes { + // if the volume is a PVC, track it + for _, volume := range pod.Spec.Volumes { + if volume.Name == volumeName { + if volume.PersistentVolumeClaim != nil { + t.pvcs.Insert(key(pod.Namespace, volume.PersistentVolumeClaim.ClaimName)) + } + break + } + } + } +} + +// Has returns true if the PVC with the specified namespace and name has been tracked. +func (t *pvcSnapshotTracker) Has(namespace, name string) bool { + return t.pvcs.Has(key(namespace, name)) +} + +func key(namespace, name string) string { + return fmt.Sprintf("%s/%s", namespace, name) +} diff --git a/pkg/backup/resource_backupper.go b/pkg/backup/resource_backupper.go index 38d97b32a9..52bdcc0d08 100644 --- a/pkg/backup/resource_backupper.go +++ b/pkg/backup/resource_backupper.go @@ -53,6 +53,7 @@ type resourceBackupperFactory interface { resourceHooks []resourceHook, snapshotService cloudprovider.SnapshotService, resticBackupper restic.Backupper, + resticSnapshotTracker *pvcSnapshotTracker, ) resourceBackupper } @@ -73,6 +74,7 @@ func (f *defaultResourceBackupperFactory) newResourceBackupper( resourceHooks []resourceHook, snapshotService cloudprovider.SnapshotService, resticBackupper restic.Backupper, + resticSnapshotTracker *pvcSnapshotTracker, ) resourceBackupper { return &defaultResourceBackupper{ log: log, @@ -89,6 +91,7 @@ func (f *defaultResourceBackupperFactory) newResourceBackupper( resourceHooks: resourceHooks, snapshotService: snapshotService, resticBackupper: resticBackupper, + resticSnapshotTracker: resticSnapshotTracker, itemBackupperFactory: &defaultItemBackupperFactory{}, } } @@ -112,6 +115,7 @@ type defaultResourceBackupper struct { resourceHooks []resourceHook snapshotService cloudprovider.SnapshotService resticBackupper restic.Backupper + resticSnapshotTracker *pvcSnapshotTracker itemBackupperFactory itemBackupperFactory } @@ -187,6 +191,7 @@ func (rb *defaultResourceBackupper) backupResource( rb.discoveryHelper, rb.snapshotService, rb.resticBackupper, + rb.resticSnapshotTracker, ) namespacesToList := getNamespacesToList(rb.namespaces) diff --git a/pkg/backup/resource_backupper_test.go b/pkg/backup/resource_backupper_test.go index ffa844f6b3..995afd5eef 100644 --- a/pkg/backup/resource_backupper_test.go +++ b/pkg/backup/resource_backupper_test.go @@ -272,6 +272,7 @@ func TestBackupResource(t *testing.T) { resourceHooks, nil, // snapshot service nil, // restic backupper + newPVCSnapshotTracker(), ).(*defaultResourceBackupper) itemBackupperFactory := &mockItemBackupperFactory{} @@ -295,6 +296,7 @@ func TestBackupResource(t *testing.T) { discoveryHelper, mock.Anything, mock.Anything, + mock.Anything, ).Return(itemBackupper) if len(test.listResponses) > 0 { @@ -438,6 +440,7 @@ func TestBackupResourceCohabitation(t *testing.T) { resourceHooks, nil, // snapshot service nil, // restic backupper + newPVCSnapshotTracker(), ).(*defaultResourceBackupper) itemBackupperFactory := &mockItemBackupperFactory{} @@ -460,6 +463,7 @@ func TestBackupResourceCohabitation(t *testing.T) { discoveryHelper, mock.Anything, // snapshot service mock.Anything, // restic backupper + mock.Anything, // pvc snapshot tracker ).Return(itemBackupper) client := &arktest.FakeDynamicClient{} @@ -519,6 +523,7 @@ func TestBackupResourceOnlyIncludesSpecifiedNamespaces(t *testing.T) { resourceHooks, nil, // snapshot service nil, // restic backupper + newPVCSnapshotTracker(), ).(*defaultResourceBackupper) itemBackupperFactory := &mockItemBackupperFactory{} @@ -555,6 +560,7 @@ func TestBackupResourceOnlyIncludesSpecifiedNamespaces(t *testing.T) { discoveryHelper, mock.Anything, mock.Anything, + mock.Anything, ).Return(itemBackupper) client := &arktest.FakeDynamicClient{} @@ -622,6 +628,7 @@ func TestBackupResourceListAllNamespacesExcludesCorrectly(t *testing.T) { resourceHooks, nil, // snapshot service nil, // restic backupper + newPVCSnapshotTracker(), ).(*defaultResourceBackupper) itemBackupperFactory := &mockItemBackupperFactory{} @@ -647,6 +654,7 @@ func TestBackupResourceListAllNamespacesExcludesCorrectly(t *testing.T) { discoveryHelper, mock.Anything, mock.Anything, + mock.Anything, ).Return(itemBackupper) client := &arktest.FakeDynamicClient{} @@ -684,6 +692,7 @@ func (ibf *mockItemBackupperFactory) newItemBackupper( discoveryHelper discovery.Helper, snapshotService cloudprovider.SnapshotService, resticBackupper restic.Backupper, + resticSnapshotTracker *pvcSnapshotTracker, ) ItemBackupper { args := ibf.Called( backup, @@ -698,6 +707,7 @@ func (ibf *mockItemBackupperFactory) newItemBackupper( discoveryHelper, snapshotService, resticBackupper, + resticSnapshotTracker, ) return args.Get(0).(ItemBackupper) }