Repository: vmware-tanzu/velero (Go)

Issue:
Reflect schedule annotations to backups for more flexibility storing metadata
**Describe the problem/challenge you have**
We need to store some metadata along with the backup like some spec indicating how we actually creating/managing that cluster. This spec includes Kubernetes version as well as versions/configs of components that we installed on the cluster.

Currently, it is possible to store some metadata with labels and labels on schedule is[ reflected to backups](https://github.com/vmware-tanzu/velero/blob/master/pkg/builder/backup_builder.go#L78-L85) and velero stores whole backup resource including the labels. This is good for storing some metadata but label length is limited to 63 chars.

**Describe the solution you'd like**
Annotations on velero schedule should also be reflected to backups created from them. This way we would have more flexibility on keeping metadata of backups.


**Anything else you would like to add:**
I would expect this to be a simple change without breaking any existing flow, hope we can have this soon :)


**Environment:**

- Velero version (use `velero version`): v1.2.0
- Kubernetes version (use `kubectl version`): v1.17.3
- Kubernetes installer & version: N/A
- Cloud provider or hardware configuration: N/A
- OS (e.g. from `/etc/os-release`): N/A



Discussion:

Patch:
diff --git a/changelogs/unreleased/3067-funkycode b/changelogs/unreleased/3067-funkycode
new file mode 100644
index 0000000000..62d9891ca6
--- /dev/null
+++ b/changelogs/unreleased/3067-funkycode
@@ -0,0 +1,2 @@
+Pass annotations from schedule to backup it creates the same way it is done for labels
+Add WithannotationsMap function to builder to be able to pass map instead of key/val list
\ No newline at end of file
diff --git a/pkg/builder/backup_builder.go b/pkg/builder/backup_builder.go
index 0c88cd9a69..5090700bc1 100644
--- a/pkg/builder/backup_builder.go
+++ b/pkg/builder/backup_builder.go
@@ -83,6 +83,11 @@ func (b *BackupBuilder) FromSchedule(schedule *velerov1api.Schedule) *BackupBuil
 
 	b.object.Spec = schedule.Spec.Template
 	b.ObjectMeta(WithLabelsMap(labels))
+
+	if schedule.Annotations != nil {
+		b.ObjectMeta(WithAnnotationsMap(schedule.Annotations))
+	}
+
 	return b
 }
 
diff --git a/pkg/builder/object_meta.go b/pkg/builder/object_meta.go
index c404013cb2..92cfb29ef9 100644
--- a/pkg/builder/object_meta.go
+++ b/pkg/builder/object_meta.go
@@ -76,6 +76,24 @@ func WithAnnotations(vals ...string) func(obj metav1.Object) {
 	}
 }
 
+// WithAnnotationsMap is a functional option that applies the specified annotations map to
+// an object.
+func WithAnnotationsMap(annotations map[string]string) func(obj metav1.Object) {
+	return func(obj metav1.Object) {
+		objAnnotations := obj.GetAnnotations()
+		if objAnnotations == nil {
+			objAnnotations = make(map[string]string)
+		}
+
+		// If the label already exists in the object, it will be overwritten
+		for k, v := range annotations {
+			objAnnotations[k] = v
+		}
+
+		obj.SetAnnotations(objAnnotations)
+	}
+}
+
 func setMapEntries(m map[string]string, vals ...string) map[string]string {
 	if m == nil {
 		m = make(map[string]string)


Test patch:
diff --git a/pkg/cmd/cli/backup/create_test.go b/pkg/cmd/cli/backup/create_test.go
index 43cffe619c..09bffd81e8 100644
--- a/pkg/cmd/cli/backup/create_test.go
+++ b/pkg/cmd/cli/backup/create_test.go
@@ -68,7 +68,7 @@ func TestCreateOptions_BuildBackupFromSchedule(t *testing.T) {
 	})
 
 	expectedBackupSpec := builder.ForBackup("test", testNamespace).IncludedNamespaces("test").Result().Spec
-	schedule := builder.ForSchedule(testNamespace, "test").Template(expectedBackupSpec).ObjectMeta(builder.WithLabels("velero.io/test", "true")).Result()
+	schedule := builder.ForSchedule(testNamespace, "test").Template(expectedBackupSpec).ObjectMeta(builder.WithLabels("velero.io/test", "true"), builder.WithAnnotations("velero.io/test", "true")).Result()
 	o.client.VeleroV1().Schedules(testNamespace).Create(context.TODO(), schedule, metav1.CreateOptions{})
 
 	t.Run("existing schedule", func(t *testing.T) {
@@ -80,6 +80,9 @@ func TestCreateOptions_BuildBackupFromSchedule(t *testing.T) {
 			"velero.io/test":              "true",
 			velerov1api.ScheduleNameLabel: "test",
 		}, backup.GetLabels())
+		assert.Equal(t, map[string]string{
+			"velero.io/test": "true",
+		}, backup.GetAnnotations())
 	})
 
 	t.Run("command line labels take precedence over schedule labels", func(t *testing.T) {
diff --git a/pkg/controller/schedule_controller_test.go b/pkg/controller/schedule_controller_test.go
index b4ea043044..2dfff7c064 100644
--- a/pkg/controller/schedule_controller_test.go
+++ b/pkg/controller/schedule_controller_test.go
@@ -431,11 +431,17 @@ func TestGetBackup(t *testing.T) {
 				Result(),
 		},
 		{
-			name:           "ensure schedule labels is copied",
+			name:           "ensure schedule labels are copied",
 			schedule:       builder.ForSchedule("foo", "bar").ObjectMeta(builder.WithLabels("foo", "bar", "bar", "baz")).Result(),
 			testClockTime:  "2017-07-25 14:15:00",
 			expectedBackup: builder.ForBackup("foo", "bar-20170725141500").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar", "bar", "baz", "foo", "bar")).Result(),
 		},
+		{
+			name:           "ensure schedule annotations are copied",
+			schedule:       builder.ForSchedule("foo", "bar").ObjectMeta(builder.WithAnnotations("foo", "bar", "bar", "baz")).Result(),
+			testClockTime:  "2017-07-25 14:15:00",
+			expectedBackup: builder.ForBackup("foo", "bar-20170725141500").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar"), builder.WithAnnotations("bar", "baz", "foo", "bar")).Result(),
+		},
 	}
 
 	for _, test := range tests {
@@ -448,6 +454,7 @@ func TestGetBackup(t *testing.T) {
 			assert.Equal(t, test.expectedBackup.Namespace, backup.Namespace)
 			assert.Equal(t, test.expectedBackup.Name, backup.Name)
 			assert.Equal(t, test.expectedBackup.Labels, backup.Labels)
+			assert.Equal(t, test.expectedBackup.Annotations, backup.Annotations)
 			assert.Equal(t, test.expectedBackup.Spec, backup.Spec)
 		})
 	}