diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index b29c8545c..902d22fcd 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -217,6 +217,9 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim return kubeerrs.NewAggregate(backupErrs) } obj = updatedObj + if metadata, err = meta.Accessor(obj); err != nil { + return errors.WithStack(err) + } if groupResource == kuberesource.PersistentVolumes { if ib.blockStore == nil { diff --git a/pkg/backup/item_backupper_test.go b/pkg/backup/item_backupper_test.go index 2e2c16900..67eadc5c0 100644 --- a/pkg/backup/item_backupper_test.go +++ b/pkg/backup/item_backupper_test.go @@ -26,6 +26,7 @@ import ( "github.com/heptio/ark/pkg/apis/ark/v1" api "github.com/heptio/ark/pkg/apis/ark/v1" + resticmocks "github.com/heptio/ark/pkg/restic/mocks" "github.com/heptio/ark/pkg/util/collections" arktest "github.com/heptio/ark/pkg/util/test" "github.com/pkg/errors" @@ -618,6 +619,74 @@ func TestItemActionModificationsToItemPersist(t *testing.T) { assert.EqualValues(t, expected.Object, actual) } +func TestResticAnnotationsPersist(t *testing.T) { + var ( + w = &fakeTarWriter{} + obj = &unstructured.Unstructured{ + Object: map[string]interface{}{ + "metadata": map[string]interface{}{ + "namespace": "myns", + "name": "bar", + "annotations": map[string]interface{}{ + "backup.ark.heptio.com/backup-volumes": "volume-1,volume-2", + }, + }, + }, + } + actions = []resolvedAction{ + { + ItemAction: &addAnnotationAction{}, + namespaceIncludesExcludes: collections.NewIncludesExcludes(), + resourceIncludesExcludes: collections.NewIncludesExcludes(), + selector: labels.Everything(), + }, + } + resticBackupper = &resticmocks.Backupper{} + b = (&defaultItemBackupperFactory{}).newItemBackupper( + &v1.Backup{}, + collections.NewIncludesExcludes(), + collections.NewIncludesExcludes(), + make(map[itemKey]struct{}), + actions, + nil, + w, + nil, + &arktest.FakeDynamicFactory{}, + arktest.NewFakeDiscoveryHelper(true, nil), + nil, + resticBackupper, + newPVCSnapshotTracker(), + ).(*defaultItemBackupper) + ) + + resticBackupper. + On("BackupPodVolumes", mock.Anything, mock.Anything, mock.Anything). + Return(map[string]string{"volume-1": "snapshot-1", "volume-2": "snapshot-2"}, nil) + + // our expected backed-up object is the passed-in object, plus the annotation + // that the backup item action adds, plus the annotations that the restic + // backupper adds + expected := obj.DeepCopy() + annotations := expected.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + annotations["foo"] = "bar" + annotations["snapshot.ark.heptio.com/volume-1"] = "snapshot-1" + annotations["snapshot.ark.heptio.com/volume-2"] = "snapshot-2" + expected.SetAnnotations(annotations) + + // method under test + require.NoError(t, b.backupItem(arktest.NewLogger(), obj, schema.ParseGroupResource("pods"))) + + // get the actual backed-up item + require.Len(t, w.data, 1) + actual, err := arktest.GetAsMap(string(w.data[0])) + require.NoError(t, err) + + assert.EqualValues(t, expected.Object, actual) +} + func TestTakePVSnapshot(t *testing.T) { iops := int64(1000) diff --git a/pkg/restic/mocks/backupper.go b/pkg/restic/mocks/backupper.go new file mode 100644 index 000000000..feb716740 --- /dev/null +++ b/pkg/restic/mocks/backupper.go @@ -0,0 +1,38 @@ +// Code generated by mockery v1.0.0 +package mocks + +import corev1 "k8s.io/api/core/v1" +import logrus "github.com/sirupsen/logrus" +import mock "github.com/stretchr/testify/mock" + +import v1 "github.com/heptio/ark/pkg/apis/ark/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 *v1.Backup, pod *corev1.Pod, log logrus.FieldLogger) (map[string]string, []error) { + ret := _m.Called(backup, pod, log) + + var r0 map[string]string + if rf, ok := ret.Get(0).(func(*v1.Backup, *corev1.Pod, logrus.FieldLogger) map[string]string); ok { + r0 = rf(backup, pod, log) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string]string) + } + } + + var r1 []error + if rf, ok := ret.Get(1).(func(*v1.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 +}