diff --git a/pkg/backup/backup_builder.go b/pkg/backup/backup_builder.go deleted file mode 100644 index 293641983..000000000 --- a/pkg/backup/backup_builder.go +++ /dev/null @@ -1,178 +0,0 @@ -/* -Copyright 2019 the Velero 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 ( - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" -) - -// Builder is a helper for concisely constructing Backup API objects. -type Builder struct { - backup velerov1api.Backup -} - -// NewBuilder returns a Builder for a Backup with no namespace/name. -func NewBuilder() *Builder { - return NewNamedBackupBuilder("", "") -} - -// NewNamedBackupBuilder returns a Builder for a Backup with the specified namespace -// and name. -func NewNamedBackupBuilder(namespace, name string) *Builder { - return &Builder{ - backup: velerov1api.Backup{ - TypeMeta: metav1.TypeMeta{ - APIVersion: velerov1api.SchemeGroupVersion.String(), - Kind: "Backup", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - }, - } -} - -// Backup returns the built Backup API object. -func (b *Builder) Backup() *velerov1api.Backup { - return &b.backup -} - -// Namespace sets the Backup's namespace. -func (b *Builder) Namespace(namespace string) *Builder { - b.backup.Namespace = namespace - return b -} - -// Name sets the Backup's name. -func (b *Builder) Name(name string) *Builder { - b.backup.Name = name - return b -} - -// Labels sets the Backup's labels. -func (b *Builder) Labels(vals ...string) *Builder { - if b.backup.Labels == nil { - b.backup.Labels = map[string]string{} - } - - // if we don't have an even number of values, e.g. a key and a value - // for each pair, add an empty-string value at the end to serve as - // the default value for the last key. - if len(vals)%2 != 0 { - vals = append(vals, "") - } - - for i := 0; i < len(vals); i += 2 { - b.backup.Labels[vals[i]] = vals[i+1] - } - return b -} - -// IncludedNamespaces sets the Backup's included namespaces. -func (b *Builder) IncludedNamespaces(namespaces ...string) *Builder { - b.backup.Spec.IncludedNamespaces = namespaces - return b -} - -// ExcludedNamespaces sets the Backup's excluded namespaces. -func (b *Builder) ExcludedNamespaces(namespaces ...string) *Builder { - b.backup.Spec.ExcludedNamespaces = namespaces - return b -} - -// IncludedResources sets the Backup's included resources. -func (b *Builder) IncludedResources(resources ...string) *Builder { - b.backup.Spec.IncludedResources = resources - return b -} - -// ExcludedResources sets the Backup's excluded resources. -func (b *Builder) ExcludedResources(resources ...string) *Builder { - b.backup.Spec.ExcludedResources = resources - return b -} - -// IncludeClusterResources sets the Backup's "include cluster resources" flag. -func (b *Builder) IncludeClusterResources(val bool) *Builder { - b.backup.Spec.IncludeClusterResources = &val - return b -} - -// LabelSelector sets the Backup's label selector. -func (b *Builder) LabelSelector(selector *metav1.LabelSelector) *Builder { - b.backup.Spec.LabelSelector = selector - return b -} - -// SnapshotVolumes sets the Backup's "snapshot volumes" flag. -func (b *Builder) SnapshotVolumes(val bool) *Builder { - b.backup.Spec.SnapshotVolumes = &val - return b -} - -// Phase sets the Backup's phase. -func (b *Builder) Phase(phase velerov1api.BackupPhase) *Builder { - b.backup.Status.Phase = phase - return b -} - -// StorageLocation sets the Backup's storage location. -func (b *Builder) StorageLocation(location string) *Builder { - b.backup.Spec.StorageLocation = location - return b -} - -// VolumeSnapshotLocations sets the Backup's volume snapshot locations. -func (b *Builder) VolumeSnapshotLocations(locations ...string) *Builder { - b.backup.Spec.VolumeSnapshotLocations = locations - return b -} - -// TTL sets the Backup's TTL. -func (b *Builder) TTL(ttl time.Duration) *Builder { - b.backup.Spec.TTL.Duration = ttl - return b -} - -// Expiration sets the Backup's expiration. -func (b *Builder) Expiration(val time.Time) *Builder { - b.backup.Status.Expiration.Time = val - return b -} - -// StartTimestamp sets the Backup's start timestamp. -func (b *Builder) StartTimestamp(val time.Time) *Builder { - b.backup.Status.StartTimestamp.Time = val - return b -} - -// NoTypeMeta removes the type meta from the Backup. -func (b *Builder) NoTypeMeta() *Builder { - b.backup.TypeMeta = metav1.TypeMeta{} - return b -} - -// Hooks sets the Backup's hooks. -func (b *Builder) Hooks(hooks velerov1api.BackupHooks) *Builder { - b.backup.Spec.Hooks = hooks - return b -} diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 630506e5e..02a22ced5 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -41,6 +41,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" velerov1 "github.com/heptio/velero/pkg/apis/velero/v1" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/client" "github.com/heptio/velero/pkg/discovery" "github.com/heptio/velero/pkg/kuberesource" @@ -67,15 +68,15 @@ func TestBackupResourceFiltering(t *testing.T) { }{ { name: "no filters backs up everything", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), }, want: []string{ @@ -89,15 +90,15 @@ func TestBackupResourceFiltering(t *testing.T) { name: "included resources filter only backs up resources of those types", backup: defaultBackup(). IncludedResources("pods"). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), }, want: []string{ @@ -109,15 +110,15 @@ func TestBackupResourceFiltering(t *testing.T) { name: "excluded resources filter only backs up resources not of those types", backup: defaultBackup(). ExcludedResources("deployments"). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), }, want: []string{ @@ -129,15 +130,15 @@ func TestBackupResourceFiltering(t *testing.T) { name: "included namespaces filter only backs up resources in those namespaces", backup: defaultBackup(). IncludedNamespaces("foo"). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), }, want: []string{ @@ -149,15 +150,15 @@ func TestBackupResourceFiltering(t *testing.T) { name: "excluded namespaces filter only backs up resources not in those namespaces", backup: defaultBackup(). ExcludedNamespaces("zoo"). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), }, want: []string{ @@ -169,19 +170,19 @@ func TestBackupResourceFiltering(t *testing.T) { name: "IncludeClusterResources=false only backs up namespaced resources", backup: defaultBackup(). IncludeClusterResources(false). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), test.PVs( - test.NewPV("bar"), - test.NewPV("baz"), + builder.ForPersistentVolume("bar").Result(), + builder.ForPersistentVolume("baz").Result(), ), }, want: []string{ @@ -195,19 +196,19 @@ func TestBackupResourceFiltering(t *testing.T) { name: "label selector only backs up matching resources", backup: defaultBackup(). LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}}). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar", test.WithLabels("a", "b")), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz", test.WithLabels("a", "b")), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels("a", "b")).Result(), ), test.PVs( - test.NewPV("bar", test.WithLabels("a", "b")), - test.NewPV("baz", test.WithLabels("a", "c")), + builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels("a", "c")).Result(), ), }, want: []string{ @@ -219,19 +220,19 @@ func TestBackupResourceFiltering(t *testing.T) { { name: "resources with velero.io/exclude-from-backup=true label are not included", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar", test.WithLabels("velero.io/exclude-from-backup", "true")), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels("velero.io/exclude-from-backup", "true")).Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz", test.WithLabels("velero.io/exclude-from-backup", "true")), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels("velero.io/exclude-from-backup", "true")).Result(), ), test.PVs( - test.NewPV("bar", test.WithLabels("a", "b")), - test.NewPV("baz", test.WithLabels("velero.io/exclude-from-backup", "true")), + builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels("velero.io/exclude-from-backup", "true")).Result(), ), }, want: []string{ @@ -244,19 +245,19 @@ func TestBackupResourceFiltering(t *testing.T) { name: "resources with velero.io/exclude-from-backup=true label are not included even if matching label selector", backup: defaultBackup(). LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}}). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar", test.WithLabels("velero.io/exclude-from-backup", "true", "a", "b")), - test.NewPod("zoo", "raz", test.WithLabels("a", "b")), + builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels("velero.io/exclude-from-backup", "true", "a", "b")).Result(), + builder.ForPod("zoo", "raz").ObjectMeta(builder.WithLabels("a", "b")).Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz", test.WithLabels("velero.io/exclude-from-backup", "true", "a", "b")), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels("velero.io/exclude-from-backup", "true", "a", "b")).Result(), ), test.PVs( - test.NewPV("bar", test.WithLabels("a", "b")), - test.NewPV("baz", test.WithLabels("a", "b", "velero.io/exclude-from-backup", "true")), + builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels("a", "b", "velero.io/exclude-from-backup", "true")).Result(), ), }, want: []string{ @@ -267,19 +268,19 @@ func TestBackupResourceFiltering(t *testing.T) { { name: "resources with velero.io/exclude-from-backup label specified but not 'true' are included", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar", test.WithLabels("velero.io/exclude-from-backup", "false")), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels("velero.io/exclude-from-backup", "false")).Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz", test.WithLabels("velero.io/exclude-from-backup", "1")), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels("velero.io/exclude-from-backup", "1")).Result(), ), test.PVs( - test.NewPV("bar", test.WithLabels("a", "b")), - test.NewPV("baz", test.WithLabels("velero.io/exclude-from-backup", "")), + builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels("velero.io/exclude-from-backup", "")).Result(), ), }, want: []string{ @@ -296,16 +297,16 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). IncludedNamespaces("ns-1", "ns-2"). IncludeClusterResources(true). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-1"), - test.NewPod("ns-3", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, want: []string{ @@ -320,16 +321,16 @@ func TestBackupResourceFiltering(t *testing.T) { backup: defaultBackup(). IncludedNamespaces("ns-1", "ns-2"). IncludeClusterResources(false). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-1"), - test.NewPod("ns-3", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, want: []string{ @@ -341,16 +342,16 @@ func TestBackupResourceFiltering(t *testing.T) { name: "should not include cluster-scoped resource if backing up subset of namespaces and IncludeClusterResources=nil", backup: defaultBackup(). IncludedNamespaces("ns-1", "ns-2"). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-1"), - test.NewPod("ns-3", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, want: []string{ @@ -362,16 +363,16 @@ func TestBackupResourceFiltering(t *testing.T) { name: "should include cluster-scoped resources if backing up all namespaces and IncludeClusterResources=true", backup: defaultBackup(). IncludeClusterResources(true). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-1"), - test.NewPod("ns-3", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, want: []string{ @@ -386,16 +387,16 @@ func TestBackupResourceFiltering(t *testing.T) { name: "should not include cluster-scoped resources if backing up all namespaces and IncludeClusterResources=false", backup: defaultBackup(). IncludeClusterResources(false). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-1"), - test.NewPod("ns-3", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, want: []string{ @@ -407,16 +408,16 @@ func TestBackupResourceFiltering(t *testing.T) { { name: "should include cluster-scoped resources if backing up all namespaces and IncludeClusterResources=nil", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-1"), - test.NewPod("ns-3", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, want: []string{ @@ -431,15 +432,15 @@ func TestBackupResourceFiltering(t *testing.T) { name: "when a wildcard and a specific resource are included, the wildcard takes precedence", backup: defaultBackup(). IncludedResources("*", "pods"). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), }, want: []string{ @@ -453,15 +454,15 @@ func TestBackupResourceFiltering(t *testing.T) { name: "wildcard excludes are ignored", backup: defaultBackup(). ExcludedResources("*"). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), }, want: []string{ @@ -475,15 +476,15 @@ func TestBackupResourceFiltering(t *testing.T) { name: "unresolvable included resources are ignored", backup: defaultBackup(). IncludedResources("pods", "unresolvable"). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), }, want: []string{ @@ -495,15 +496,15 @@ func TestBackupResourceFiltering(t *testing.T) { name: "unresolvable excluded resources are ignored", backup: defaultBackup(). ExcludedResources("deployments", "unresolvable"). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), }, want: []string{ @@ -513,11 +514,11 @@ func TestBackupResourceFiltering(t *testing.T) { }, { name: "terminating resources are not backed up", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "ns-2", Name: "pod-2", DeletionTimestamp: &metav1.Time{Time: time.Now()}}}, + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").ObjectMeta(builder.WithDeletionTimestamp(time.Now())).Result(), ), }, want: []string{ @@ -558,11 +559,11 @@ func TestBackupResourceCohabitation(t *testing.T) { }{ { name: "when deployments exist only in extensions, they're backed up", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.ExtensionsDeployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), }, want: []string{ @@ -572,15 +573,15 @@ func TestBackupResourceCohabitation(t *testing.T) { }, { name: "when deployments exist in both apps and extensions, only apps/deployments are backed up", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.ExtensionsDeployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), test.Deployments( - test.NewDeployment("foo", "bar"), - test.NewDeployment("zoo", "raz"), + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), ), }, want: []string{ @@ -618,12 +619,12 @@ func TestBackupUsesNewCohabitatingResourcesForEachBackup(t *testing.T) { // run and verify backup 1 backup1 := &Request{ - Backup: defaultBackup().Backup(), + Backup: defaultBackup().Result(), } backup1File := bytes.NewBuffer([]byte{}) - h.addItems(t, test.Deployments(test.NewDeployment("ns-1", "deploy-1"))) - h.addItems(t, test.ExtensionsDeployments(test.NewDeployment("ns-1", "deploy-1"))) + h.addItems(t, test.Deployments(builder.ForDeployment("ns-1", "deploy-1").Result())) + h.addItems(t, test.ExtensionsDeployments(builder.ForDeployment("ns-1", "deploy-1").Result())) h.backupper.Backup(h.log, backup1, backup1File, nil, nil) @@ -631,7 +632,7 @@ func TestBackupUsesNewCohabitatingResourcesForEachBackup(t *testing.T) { // run and verify backup 2 backup2 := &Request{ - Backup: defaultBackup().Backup(), + Backup: defaultBackup().Result(), } backup2File := bytes.NewBuffer([]byte{}) @@ -653,23 +654,23 @@ func TestBackupResourceOrdering(t *testing.T) { name: "core API group: pods come before pvcs, pvcs come before pvs, pvs come before anything else", backup: defaultBackup(). SnapshotVolumes(false). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.PVCs( - test.NewPVC("foo", "bar"), - test.NewPVC("zoo", "raz"), + builder.ForPersistentVolumeClaim("foo", "bar").Result(), + builder.ForPersistentVolumeClaim("zoo", "raz").Result(), ), test.PVs( - test.NewPV("bar"), - test.NewPV("baz"), + builder.ForPersistentVolume("bar").Result(), + builder.ForPersistentVolume("baz").Result(), ), test.Secrets( - test.NewSecret("foo", "bar"), - test.NewSecret("zoo", "raz"), + builder.ForSecret("foo", "bar").Result(), + builder.ForSecret("zoo", "raz").Result(), ), }, }, @@ -756,15 +757,15 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { { name: "single action with no selector runs for all items", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, actions: map[*recordResourcesAction][]string{ @@ -774,15 +775,15 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { { name: "single action with a resource selector for namespaced resources runs only for matching resources", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, actions: map[*recordResourcesAction][]string{ @@ -792,15 +793,15 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { { name: "single action with a resource selector for cluster-scoped resources runs only for matching resources", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, actions: map[*recordResourcesAction][]string{ @@ -810,23 +811,23 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { { name: "single action with a namespace selector runs only for resources in that namespace", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ), test.PVCs( - test.NewPVC("ns-1", "pvc-1"), - test.NewPVC("ns-2", "pvc-2"), + builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result(), + builder.ForPersistentVolumeClaim("ns-2", "pvc-2").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), test.Namespaces( - test.NewNamespace("ns-1"), - test.NewNamespace("ns-2"), + builder.ForNamespace("ns-1").Result(), + builder.ForNamespace("ns-2").Result(), ), }, actions: map[*recordResourcesAction][]string{ @@ -836,15 +837,15 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { { name: "single action with a resource and namespace selector runs only for matching resources", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, actions: map[*recordResourcesAction][]string{ @@ -854,15 +855,15 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { { name: "multiple actions, each with a different resource selector using short name, run for matching resources", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, actions: map[*recordResourcesAction][]string{ @@ -873,17 +874,17 @@ func TestBackupActionsRunForCorrectItems(t *testing.T) { { name: "actions with selectors that don't match anything don't run for any resources", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), ), test.PVCs( - test.NewPVC("ns-2", "pvc-2"), + builder.ForPersistentVolumeClaim("ns-2", "pvc-2").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, actions: map[*recordResourcesAction][]string{ @@ -936,15 +937,15 @@ func TestBackupWithInvalidActions(t *testing.T) { { name: "action with invalid label selector results in an error", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.PVs( - test.NewPV("bar"), - test.NewPV("baz"), + builder.ForPersistentVolume("bar").Result(), + builder.ForPersistentVolume("baz").Result(), ), }, actions: []velero.BackupItemAction{ @@ -954,15 +955,15 @@ func TestBackupWithInvalidActions(t *testing.T) { { name: "action returning an error from AppliesTo results in an error", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), - test.NewPod("zoo", "raz"), + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), ), test.PVs( - test.NewPV("bar"), - test.NewPV("baz"), + builder.ForPersistentVolume("bar").Result(), + builder.ForPersistentVolume("baz").Result(), ), }, actions: []velero.BackupItemAction{ @@ -1032,10 +1033,10 @@ func TestBackupActionModifications(t *testing.T) { }{ { name: "action that adds a label to item gets persisted", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), ), }, actions: []velero.BackupItemAction{ @@ -1044,15 +1045,15 @@ func TestBackupActionModifications(t *testing.T) { }), }, want: map[string]unstructuredObject{ - "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, test.NewPod("ns-1", "pod-1", test.WithLabels("updated", "true"))), + "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("updated", "true")).Result()), }, }, { name: "action that removes labels from item gets persisted", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1", test.WithLabels("should-be-removed", "true")), + builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("should-be-removed", "true")).Result(), ), }, actions: []velero.BackupItemAction{ @@ -1061,15 +1062,15 @@ func TestBackupActionModifications(t *testing.T) { }), }, want: map[string]unstructuredObject{ - "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, test.NewPod("ns-1", "pod-1")), + "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, builder.ForPod("ns-1", "pod-1").Result()), }, }, { name: "action that sets a spec field on item gets persisted", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), ), }, actions: []velero.BackupItemAction{ @@ -1078,16 +1079,16 @@ func TestBackupActionModifications(t *testing.T) { }), }, want: map[string]unstructuredObject{ - "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, &corev1.Pod{TypeMeta: metav1.TypeMeta{Kind: "Pod", APIVersion: "v1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "ns-1", Name: "pod-1"}, Spec: corev1.PodSpec{NodeName: "foo"}}), + "resources/pods/namespaces/ns-1/pod-1.json": toUnstructuredOrFail(t, builder.ForPod("ns-1", "pod-1").NodeName("foo").Result()), }, }, { name: "modifications to name and namespace in an action are persisted in JSON and in filename", backup: defaultBackup(). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), ), }, actions: []velero.BackupItemAction{ @@ -1097,7 +1098,7 @@ func TestBackupActionModifications(t *testing.T) { }), }, want: map[string]unstructuredObject{ - "resources/pods/namespaces/ns-1-updated/pod-1-updated.json": toUnstructuredOrFail(t, test.NewPod("ns-1-updated", "pod-1-updated")), + "resources/pods/namespaces/ns-1-updated/pod-1-updated.json": toUnstructuredOrFail(t, builder.ForPod("ns-1-updated", "pod-1-updated").Result()), }, }, } @@ -1120,7 +1121,6 @@ func TestBackupActionModifications(t *testing.T) { assertTarballFileContents(t, backupFile, tc.want) }) } - } // TestBackupActionAdditionalItems runs backups with backup item actions that return @@ -1137,12 +1137,12 @@ func TestBackupActionAdditionalItems(t *testing.T) { }{ { name: "additional items that are already being backed up are not backed up twice", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), - test.NewPod("ns-3", "pod-3"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), + builder.ForPod("ns-3", "pod-3").Result(), ), }, actions: []velero.BackupItemAction{ @@ -1166,12 +1166,12 @@ func TestBackupActionAdditionalItems(t *testing.T) { }, { name: "when using a backup namespace filter, additional items that are in a non-included namespace are not backed up", - backup: defaultBackup().IncludedNamespaces("ns-1").Backup(), + backup: defaultBackup().IncludedNamespaces("ns-1").Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), - test.NewPod("ns-3", "pod-3"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), + builder.ForPod("ns-3", "pod-3").Result(), ), }, actions: []velero.BackupItemAction{ @@ -1192,15 +1192,15 @@ func TestBackupActionAdditionalItems(t *testing.T) { }, { name: "when using a backup namespace filter, additional items that are cluster-scoped are backed up", - backup: defaultBackup().IncludedNamespaces("ns-1").Backup(), + backup: defaultBackup().IncludedNamespaces("ns-1").Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, actions: []velero.BackupItemAction{ @@ -1223,14 +1223,14 @@ func TestBackupActionAdditionalItems(t *testing.T) { }, { name: "when using a backup resource filter, additional items that are non-included resources are not backed up", - backup: defaultBackup().IncludedResources("pods").Backup(), + backup: defaultBackup().IncludedResources("pods").Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, actions: []velero.BackupItemAction{ @@ -1251,15 +1251,15 @@ func TestBackupActionAdditionalItems(t *testing.T) { }, { name: "when IncludeClusterResources=false, additional items that are cluster-scoped are not backed up", - backup: defaultBackup().IncludeClusterResources(false).Backup(), + backup: defaultBackup().IncludeClusterResources(false).Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ), test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, actions: []velero.BackupItemAction{ @@ -1281,12 +1281,12 @@ func TestBackupActionAdditionalItems(t *testing.T) { }, { name: "if there's an error backing up additional items, the item the action was run for isn't backed up", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), - test.NewPod("ns-3", "pod-3"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), + builder.ForPod("ns-3", "pod-3").Result(), ), }, actions: []velero.BackupItemAction{ @@ -1471,14 +1471,14 @@ func TestBackupWithSnapshots(t *testing.T) { { name: "persistent volume with no zone annotation creates a snapshot", req: &Request{ - Backup: defaultBackup().Backup(), + Backup: defaultBackup().Result(), SnapshotLocations: []*velerov1.VolumeSnapshotLocation{ newSnapshotLocation("velero", "default", "default"), }, }, apiResources: []*test.APIResource{ test.PVs( - test.NewPV("pv-1"), + builder.ForPersistentVolume("pv-1").Result(), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1504,14 +1504,14 @@ func TestBackupWithSnapshots(t *testing.T) { { name: "persistent volume with zone annotation creates a snapshot", req: &Request{ - Backup: defaultBackup().Backup(), + Backup: defaultBackup().Result(), SnapshotLocations: []*velerov1.VolumeSnapshotLocation{ newSnapshotLocation("velero", "default", "default"), }, }, apiResources: []*test.APIResource{ test.PVs( - test.NewPV("pv-1", test.WithLabels("failure-domain.beta.kubernetes.io/zone", "zone-1")), + builder.ForPersistentVolume("pv-1").ObjectMeta(builder.WithLabels("failure-domain.beta.kubernetes.io/zone", "zone-1")).Result(), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1538,14 +1538,14 @@ func TestBackupWithSnapshots(t *testing.T) { { name: "error returned from CreateSnapshot results in a failed snapshot", req: &Request{ - Backup: defaultBackup().Backup(), + Backup: defaultBackup().Result(), SnapshotLocations: []*velerov1.VolumeSnapshotLocation{ newSnapshotLocation("velero", "default", "default"), }, }, apiResources: []*test.APIResource{ test.PVs( - test.NewPV("pv-1"), + builder.ForPersistentVolume("pv-1").Result(), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1570,14 +1570,14 @@ func TestBackupWithSnapshots(t *testing.T) { { name: "backup with SnapshotVolumes=false does not create any snapshots", req: &Request{ - Backup: defaultBackup().SnapshotVolumes(false).Backup(), + Backup: defaultBackup().SnapshotVolumes(false).Result(), SnapshotLocations: []*velerov1.VolumeSnapshotLocation{ newSnapshotLocation("velero", "default", "default"), }, }, apiResources: []*test.APIResource{ test.PVs( - test.NewPV("pv-1"), + builder.ForPersistentVolume("pv-1").Result(), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1588,11 +1588,11 @@ func TestBackupWithSnapshots(t *testing.T) { { name: "backup with no volume snapshot locations does not create any snapshots", req: &Request{ - Backup: defaultBackup().Backup(), + Backup: defaultBackup().Result(), }, apiResources: []*test.APIResource{ test.PVs( - test.NewPV("pv-1"), + builder.ForPersistentVolume("pv-1").Result(), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1603,14 +1603,14 @@ func TestBackupWithSnapshots(t *testing.T) { { name: "backup with no volume snapshotters does not create any snapshots", req: &Request{ - Backup: defaultBackup().Backup(), + Backup: defaultBackup().Result(), SnapshotLocations: []*velerov1.VolumeSnapshotLocation{ newSnapshotLocation("velero", "default", "default"), }, }, apiResources: []*test.APIResource{ test.PVs( - test.NewPV("pv-1"), + builder.ForPersistentVolume("pv-1").Result(), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{}, @@ -1619,14 +1619,14 @@ func TestBackupWithSnapshots(t *testing.T) { { name: "unsupported persistent volume type does not create any snapshots", req: &Request{ - Backup: defaultBackup().Backup(), + Backup: defaultBackup().Result(), SnapshotLocations: []*velerov1.VolumeSnapshotLocation{ newSnapshotLocation("velero", "default", "default"), }, }, apiResources: []*test.APIResource{ test.PVs( - test.NewPV("pv-1"), + builder.ForPersistentVolume("pv-1").Result(), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1637,7 +1637,7 @@ func TestBackupWithSnapshots(t *testing.T) { { name: "when there are multiple volumes, snapshot locations, and snapshotters, volumes are matched to the right snapshotters", req: &Request{ - Backup: defaultBackup().Backup(), + Backup: defaultBackup().Result(), SnapshotLocations: []*velerov1.VolumeSnapshotLocation{ newSnapshotLocation("velero", "default", "default"), newSnapshotLocation("velero", "another", "another"), @@ -1645,8 +1645,8 @@ func TestBackupWithSnapshots(t *testing.T) { }, apiResources: []*test.APIResource{ test.PVs( - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ), }, snapshotterGetter: map[string]velero.VolumeSnapshotter{ @@ -1733,10 +1733,10 @@ func TestBackupWithInvalidHooks(t *testing.T) { }, }, }). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("foo", "bar"), + builder.ForPod("foo", "bar").Result(), ), }, want: errors.New("\"nonexistent-operator\" is not a valid pod selector operator"), @@ -1797,11 +1797,11 @@ func TestBackupWithHooks(t *testing.T) { }, }, }). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ), }, wantExecutePodCommandCalls: []*expectedCall{ @@ -1846,11 +1846,11 @@ func TestBackupWithHooks(t *testing.T) { }, }, }). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ), }, wantExecutePodCommandCalls: []*expectedCall{ @@ -1902,10 +1902,10 @@ func TestBackupWithHooks(t *testing.T) { }, }, }). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), + builder.ForPod("ns-1", "pod-1").Result(), ), }, wantExecutePodCommandCalls: []*expectedCall{ @@ -1950,11 +1950,11 @@ func TestBackupWithHooks(t *testing.T) { }, }, }). - Backup(), + Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ), }, wantExecutePodCommandCalls: []*expectedCall{ @@ -2052,34 +2052,39 @@ func TestBackupWithRestic(t *testing.T) { }{ { name: "a pod annotated for restic backup should result in pod volume backups being returned", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1", test.WithAnnotations("backup.velero.io/backup-volumes", "foo")), + builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithAnnotations("backup.velero.io/backup-volumes", "foo")).Result(), ), }, want: []*velerov1.PodVolumeBackup{ - NewNamedPodVolumeBackupBuilder("velero", "pvb-1").PodVolumeBackup(), + builder.ForPodVolumeBackup("velero", "pvb-1").Result(), }, }, { name: "when PVC pod volumes are backed up using restic, their claimed PVs are not also snapshotted", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1", - test.WithAnnotations("backup.velero.io/backup-volumes", "vol-1,vol-2"), - test.WithVolume(test.NewVolume("vol-1", test.WithPVCSource("pvc-1"))), - test.WithVolume(test.NewVolume("vol-2", test.WithPVCSource("pvc-2"))), - ), + builder.ForPod("ns-1", "pod-1"). + Volumes( + builder.ForVolume("vol-1").PersistentVolumeClaimSource("pvc-1").Result(), + builder.ForVolume("vol-2").PersistentVolumeClaimSource("pvc-2").Result(), + ). + ObjectMeta( + builder.WithAnnotations("backup.velero.io/backup-volumes", "vol-1,vol-2"), + ). + Result(), ), test.PVCs( - test.NewPVC("ns-1", "pvc-1", test.WithPVName("pv-1")), - test.NewPVC("ns-1", "pvc-2", test.WithPVName("pv-2")), + builder.ForPersistentVolumeClaim("ns-1", "pvc-1").VolumeName("pv-1").Result(), + builder.ForPersistentVolumeClaim("ns-1", "pvc-2").VolumeName("pv-2").Result(), ), test.PVs( - test.NewPV("pv-1", test.WithClaimRef("ns-1", "pvc-1")), - test.NewPV("pv-2", test.WithClaimRef("ns-1", "pvc-2")), + + builder.ForPersistentVolume("pv-1").ClaimRef("ns-1", "pvc-1").Result(), + builder.ForPersistentVolume("pv-2").ClaimRef("ns-1", "pvc-2").Result(), ), }, vsl: newSnapshotLocation("velero", "default", "default"), @@ -2089,8 +2094,8 @@ func TestBackupWithRestic(t *testing.T) { WithVolume("pv-2", "vol-2", "", "type-1", 100, false), }, want: []*velerov1.PodVolumeBackup{ - NewNamedPodVolumeBackupBuilder("velero", "pvb-1").PodVolumeBackup(), - NewNamedPodVolumeBackupBuilder("velero", "pvb-2").PodVolumeBackup(), + builder.ForPodVolumeBackup("velero", "pvb-1").Result(), + builder.ForPodVolumeBackup("velero", "pvb-2").Result(), }, }, } @@ -2204,8 +2209,8 @@ func newSnapshotLocation(ns, name, provider string) *velerov1.VolumeSnapshotLoca } } -func defaultBackup() *Builder { - return NewNamedBackupBuilder(velerov1.DefaultNamespace, "backup-1") +func defaultBackup() *builder.BackupBuilder { + return builder.ForBackup(velerov1.DefaultNamespace, "backup-1") } func toUnstructuredOrFail(t *testing.T, obj interface{}) map[string]interface{} { diff --git a/pkg/backup/pod_volume_backup_builder.go b/pkg/backup/pod_volume_backup_builder.go deleted file mode 100644 index 9ab2b47e2..000000000 --- a/pkg/backup/pod_volume_backup_builder.go +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright 2019 the Velero 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 ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" -) - -// PodVolumeBackupBuilder is a helper for concisely constructing PodVolumeBackup API objects. -type PodVolumeBackupBuilder struct { - podVolumeBackup velerov1api.PodVolumeBackup -} - -// NewPodVolumeBackupBuilder returns a PodVolumeBackupBuilder for a PodVolumeBackup with no namespace/name. -func NewPodVolumeBackupBuilder() *PodVolumeBackupBuilder { - return NewNamedPodVolumeBackupBuilder("", "") -} - -// NewNamedPodVolumeBackupBuilder returns a PodVolumeBackupBuilder for a Backup with the specified namespace -// and name. -func NewNamedPodVolumeBackupBuilder(namespace, name string) *PodVolumeBackupBuilder { - return &PodVolumeBackupBuilder{ - podVolumeBackup: velerov1api.PodVolumeBackup{ - TypeMeta: metav1.TypeMeta{ - APIVersion: velerov1api.SchemeGroupVersion.String(), - Kind: "PodVolumeBackup", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - }, - } -} - -// PodVolumeBackup returns the built PodVolumeBackup API object. -func (p *PodVolumeBackupBuilder) PodVolumeBackup() *velerov1api.PodVolumeBackup { - return &p.podVolumeBackup -} - -// Namespace sets the PodVolumeBackup's namespace. -func (p *PodVolumeBackupBuilder) Namespace(namespace string) *PodVolumeBackupBuilder { - p.podVolumeBackup.Namespace = namespace - return p -} - -// Name sets the PodVolumeBackup's name. -func (p *PodVolumeBackupBuilder) Name(name string) *PodVolumeBackupBuilder { - p.podVolumeBackup.Name = name - return p -} - -// Labels sets the PodVolumeBackup's labels. -func (p *PodVolumeBackupBuilder) Labels(vals ...string) *PodVolumeBackupBuilder { - if p.podVolumeBackup.Labels == nil { - p.podVolumeBackup.Labels = map[string]string{} - } - - // if we don't have an even number of values, e.g. a key and a value - // for each pair, add an empty-string value at the end to serve as - // the default value for the last key. - if len(vals)%2 != 0 { - vals = append(vals, "") - } - - for i := 0; i < len(vals); i += 2 { - p.podVolumeBackup.Labels[vals[i]] = vals[i+1] - } - return p -} - -// Phase sets the PodVolumeBackup's phase. -func (p *PodVolumeBackupBuilder) Phase(phase velerov1api.PodVolumeBackupPhase) *PodVolumeBackupBuilder { - p.podVolumeBackup.Status.Phase = phase - return p -} diff --git a/pkg/builder/backup_builder.go b/pkg/builder/backup_builder.go new file mode 100644 index 000000000..ec80f7c2e --- /dev/null +++ b/pkg/builder/backup_builder.go @@ -0,0 +1,164 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" +) + +/* + +Example usage: + +var backup = builder.ForBackup("velero", "backup-1"). + ObjectMeta( + builder.WithLabels("foo", "bar"), + builder.WithClusterName("cluster-1"), + ). + SnapshotVolumes(true). + Result() + +*/ + +// BackupBuilder builds Backup objects. +type BackupBuilder struct { + object *velerov1api.Backup +} + +// ForBackup is the constructor for a BackupBuilder. +func ForBackup(ns, name string) *BackupBuilder { + return &BackupBuilder{ + object: &velerov1api.Backup{ + TypeMeta: metav1.TypeMeta{ + APIVersion: velerov1api.SchemeGroupVersion.String(), + Kind: "Backup", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built Backup. +func (b *BackupBuilder) Result() *velerov1api.Backup { + return b.object +} + +// ObjectMeta applies functional options to the Backup's ObjectMeta. +func (b *BackupBuilder) ObjectMeta(opts ...ObjectMetaOpt) *BackupBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// IncludedNamespaces sets the Backup's included namespaces. +func (b *BackupBuilder) IncludedNamespaces(namespaces ...string) *BackupBuilder { + b.object.Spec.IncludedNamespaces = namespaces + return b +} + +// ExcludedNamespaces sets the Backup's excluded namespaces. +func (b *BackupBuilder) ExcludedNamespaces(namespaces ...string) *BackupBuilder { + b.object.Spec.ExcludedNamespaces = namespaces + return b +} + +// IncludedResources sets the Backup's included resources. +func (b *BackupBuilder) IncludedResources(resources ...string) *BackupBuilder { + b.object.Spec.IncludedResources = resources + return b +} + +// ExcludedResources sets the Backup's excluded resources. +func (b *BackupBuilder) ExcludedResources(resources ...string) *BackupBuilder { + b.object.Spec.ExcludedResources = resources + return b +} + +// IncludeClusterResources sets the Backup's "include cluster resources" flag. +func (b *BackupBuilder) IncludeClusterResources(val bool) *BackupBuilder { + b.object.Spec.IncludeClusterResources = &val + return b +} + +// LabelSelector sets the Backup's label selector. +func (b *BackupBuilder) LabelSelector(selector *metav1.LabelSelector) *BackupBuilder { + b.object.Spec.LabelSelector = selector + return b +} + +// SnapshotVolumes sets the Backup's "snapshot volumes" flag. +func (b *BackupBuilder) SnapshotVolumes(val bool) *BackupBuilder { + b.object.Spec.SnapshotVolumes = &val + return b +} + +// Phase sets the Backup's phase. +func (b *BackupBuilder) Phase(phase velerov1api.BackupPhase) *BackupBuilder { + b.object.Status.Phase = phase + return b +} + +// StorageLocation sets the Backup's storage location. +func (b *BackupBuilder) StorageLocation(location string) *BackupBuilder { + b.object.Spec.StorageLocation = location + return b +} + +// VolumeSnapshotLocations sets the Backup's volume snapshot locations. +func (b *BackupBuilder) VolumeSnapshotLocations(locations ...string) *BackupBuilder { + b.object.Spec.VolumeSnapshotLocations = locations + return b +} + +// TTL sets the Backup's TTL. +func (b *BackupBuilder) TTL(ttl time.Duration) *BackupBuilder { + b.object.Spec.TTL.Duration = ttl + return b +} + +// Expiration sets the Backup's expiration. +func (b *BackupBuilder) Expiration(val time.Time) *BackupBuilder { + b.object.Status.Expiration.Time = val + return b +} + +// StartTimestamp sets the Backup's start timestamp. +func (b *BackupBuilder) StartTimestamp(val time.Time) *BackupBuilder { + b.object.Status.StartTimestamp.Time = val + return b +} + +// NoTypeMeta removes the type meta from the Backup. +func (b *BackupBuilder) NoTypeMeta() *BackupBuilder { + b.object.TypeMeta = metav1.TypeMeta{} + return b +} + +// Hooks sets the Backup's hooks. +func (b *BackupBuilder) Hooks(hooks velerov1api.BackupHooks) *BackupBuilder { + b.object.Spec.Hooks = hooks + return b +} diff --git a/pkg/builder/backup_storage_location_builder.go b/pkg/builder/backup_storage_location_builder.go new file mode 100644 index 000000000..3acccab03 --- /dev/null +++ b/pkg/builder/backup_storage_location_builder.go @@ -0,0 +1,79 @@ +/* +Copyright 2017, 2019 the Velero 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 builder + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" +) + +// BackupStorageLocationBuilder builds BackupStorageLocation objects. +type BackupStorageLocationBuilder struct { + object *velerov1api.BackupStorageLocation +} + +// ForBackupStorageLocation is the constructor for a BackupStorageLocationBuilder. +func ForBackupStorageLocation(ns, name string) *BackupStorageLocationBuilder { + return &BackupStorageLocationBuilder{ + object: &velerov1api.BackupStorageLocation{ + TypeMeta: metav1.TypeMeta{ + APIVersion: velerov1api.SchemeGroupVersion.String(), + Kind: "BackupStorageLocation", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built BackupStorageLocation. +func (b *BackupStorageLocationBuilder) Result() *velerov1api.BackupStorageLocation { + return b.object +} + +// ObjectMeta applies functional options to the BackupStorageLocation's ObjectMeta. +func (b *BackupStorageLocationBuilder) ObjectMeta(opts ...ObjectMetaOpt) *BackupStorageLocationBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// Provider sets the BackupStorageLocation's provider. +func (b *BackupStorageLocationBuilder) Provider(name string) *BackupStorageLocationBuilder { + b.object.Spec.Provider = name + return b +} + +// ObjectStorage sets the BackupStorageLocation's object storage. +func (b *BackupStorageLocationBuilder) ObjectStorage(bucketName string) *BackupStorageLocationBuilder { + if b.object.Spec.StorageType.ObjectStorage == nil { + b.object.Spec.StorageType.ObjectStorage = new(velerov1api.ObjectStorageLocation) + } + b.object.Spec.ObjectStorage.Bucket = bucketName + return b +} + +// AccessMode sets the BackupStorageLocation's access mode. +func (b *BackupStorageLocationBuilder) AccessMode(accessMode velerov1api.BackupStorageLocationAccessMode) *BackupStorageLocationBuilder { + b.object.Spec.AccessMode = accessMode + return b +} diff --git a/pkg/builder/config_map_builder.go b/pkg/builder/config_map_builder.go new file mode 100644 index 000000000..498811512 --- /dev/null +++ b/pkg/builder/config_map_builder.go @@ -0,0 +1,63 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ConfigMapBuilder builds ConfigMap objects. +type ConfigMapBuilder struct { + object *corev1api.ConfigMap +} + +// ForConfigMap is the constructor for a ConfigMapBuilder. +func ForConfigMap(ns, name string) *ConfigMapBuilder { + return &ConfigMapBuilder{ + object: &corev1api.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1api.SchemeGroupVersion.String(), + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built ConfigMap. +func (b *ConfigMapBuilder) Result() *corev1api.ConfigMap { + return b.object +} + +// ObjectMeta applies functional options to the ConfigMap's ObjectMeta. +func (b *ConfigMapBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ConfigMapBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// Data set's the ConfigMap's data. +func (b *ConfigMapBuilder) Data(vals ...string) *ConfigMapBuilder { + b.object.Data = setMapEntries(b.object.Data, vals...) + return b +} diff --git a/pkg/builder/deployment_builder.go b/pkg/builder/deployment_builder.go new file mode 100644 index 000000000..294d2f705 --- /dev/null +++ b/pkg/builder/deployment_builder.go @@ -0,0 +1,57 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + appsv1api "k8s.io/api/apps/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// DeploymentBuilder builds Deployment objects. +type DeploymentBuilder struct { + object *appsv1api.Deployment +} + +// ForDeployment is the constructor for a DeploymentBuilder. +func ForDeployment(ns, name string) *DeploymentBuilder { + return &DeploymentBuilder{ + object: &appsv1api.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: appsv1api.SchemeGroupVersion.String(), + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built Deployment. +func (b *DeploymentBuilder) Result() *appsv1api.Deployment { + return b.object +} + +// ObjectMeta applies functional options to the Deployment's ObjectMeta. +func (b *DeploymentBuilder) ObjectMeta(opts ...ObjectMetaOpt) *DeploymentBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} diff --git a/pkg/builder/namespace_builder.go b/pkg/builder/namespace_builder.go new file mode 100644 index 000000000..6f6de9a2f --- /dev/null +++ b/pkg/builder/namespace_builder.go @@ -0,0 +1,62 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// NamespaceBuilder builds Namespace objects. +type NamespaceBuilder struct { + object *corev1api.Namespace +} + +// ForNamespace is the constructor for a NamespaceBuilder. +func ForNamespace(name string) *NamespaceBuilder { + return &NamespaceBuilder{ + object: &corev1api.Namespace{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1api.SchemeGroupVersion.String(), + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, + } +} + +// Result returns the built Namespace. +func (b *NamespaceBuilder) Result() *corev1api.Namespace { + return b.object +} + +// ObjectMeta applies functional options to the Namespace's ObjectMeta. +func (b *NamespaceBuilder) ObjectMeta(opts ...ObjectMetaOpt) *NamespaceBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// Phase sets the namespace's phase +func (b *NamespaceBuilder) Phase(val corev1api.NamespacePhase) *NamespaceBuilder { + b.object.Status.Phase = val + return b +} diff --git a/pkg/builder/object_meta.go b/pkg/builder/object_meta.go new file mode 100644 index 000000000..1a2b56089 --- /dev/null +++ b/pkg/builder/object_meta.go @@ -0,0 +1,111 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" +) + +// ObjectMetaOpt is a functional option for ObjectMeta. +type ObjectMetaOpt func(metav1.Object) + +// WithName is a functional option that applies the specified +// name to an object. +func WithName(val string) func(obj metav1.Object) { + return func(obj metav1.Object) { + obj.SetName(val) + } +} + +// WithLabels is a functional option that applies the specified +// label keys/values to an object. +func WithLabels(vals ...string) func(obj metav1.Object) { + return func(obj metav1.Object) { + obj.SetLabels(setMapEntries(obj.GetLabels(), vals...)) + } +} + +// WithAnnotations is a functional option that applies the specified +// annotation keys/values to an object. +func WithAnnotations(vals ...string) func(obj metav1.Object) { + return func(obj metav1.Object) { + obj.SetAnnotations(setMapEntries(obj.GetAnnotations(), vals...)) + } +} + +func setMapEntries(m map[string]string, vals ...string) map[string]string { + if m == nil { + m = make(map[string]string) + } + + // if we don't have a value for every key, add an empty + // string at the end to serve as the value for the last + // key. + if len(vals)%2 != 0 { + vals = append(vals, "") + } + + for i := 0; i < len(vals); i += 2 { + key := vals[i] + val := vals[i+1] + + m[key] = val + } + + return m +} + +// WithClusterName is a functional option that applies the specified +// cluster name to an object. +func WithClusterName(val string) func(obj metav1.Object) { + return func(obj metav1.Object) { + obj.SetClusterName(val) + } +} + +// WithFinalizers is a functional option that applies the specified +// finalizers to an object. +func WithFinalizers(vals ...string) func(obj metav1.Object) { + return func(obj metav1.Object) { + obj.SetFinalizers(vals) + } +} + +// WithDeletionTimestamp is a functional option that applies the specified +// deletion timestamp to an object. +func WithDeletionTimestamp(val time.Time) func(obj metav1.Object) { + return func(obj metav1.Object) { + obj.SetDeletionTimestamp(&metav1.Time{Time: val}) + } +} + +// WithUID is a functional option that applies the specified UID to an object. +func WithUID(val string) func(obj metav1.Object) { + return func(obj metav1.Object) { + obj.SetUID(types.UID(val)) + } +} + +// WithGenerateName is a functional option that applies the specified generate name to an object. +func WithGenerateName(val string) func(obj metav1.Object) { + return func(obj metav1.Object) { + obj.SetGenerateName(val) + } +} diff --git a/pkg/builder/persistent_volume_builder.go b/pkg/builder/persistent_volume_builder.go new file mode 100644 index 000000000..648778bae --- /dev/null +++ b/pkg/builder/persistent_volume_builder.go @@ -0,0 +1,96 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// PersistentVolumeBuilder builds PersistentVolume objects. +type PersistentVolumeBuilder struct { + object *corev1api.PersistentVolume +} + +// ForPersistentVolume is the constructor for a PersistentVolumeBuilder. +func ForPersistentVolume(name string) *PersistentVolumeBuilder { + return &PersistentVolumeBuilder{ + object: &corev1api.PersistentVolume{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1api.SchemeGroupVersion.String(), + Kind: "PersistentVolume", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, + } +} + +// Result returns the built PersistentVolume. +func (b *PersistentVolumeBuilder) Result() *corev1api.PersistentVolume { + return b.object +} + +// ObjectMeta applies functional options to the PersistentVolume's ObjectMeta. +func (b *PersistentVolumeBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PersistentVolumeBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// ReclaimPolicy sets the PersistentVolume's reclaim policy. +func (b *PersistentVolumeBuilder) ReclaimPolicy(policy corev1api.PersistentVolumeReclaimPolicy) *PersistentVolumeBuilder { + b.object.Spec.PersistentVolumeReclaimPolicy = policy + return b +} + +// ClaimRef sets the PersistentVolume's claim ref. +func (b *PersistentVolumeBuilder) ClaimRef(ns, name string) *PersistentVolumeBuilder { + b.object.Spec.ClaimRef = &corev1api.ObjectReference{ + Namespace: ns, + Name: name, + } + return b +} + +// AWSEBSVolumeID sets the PersistentVolume's AWSElasticBlockStore volume ID. +func (b *PersistentVolumeBuilder) AWSEBSVolumeID(volumeID string) *PersistentVolumeBuilder { + if b.object.Spec.AWSElasticBlockStore == nil { + b.object.Spec.AWSElasticBlockStore = new(corev1api.AWSElasticBlockStoreVolumeSource) + } + b.object.Spec.AWSElasticBlockStore.VolumeID = volumeID + return b +} + +// CSI sets the PersistentVolume's CSI. +func (b *PersistentVolumeBuilder) CSI(driver, volumeHandle string) *PersistentVolumeBuilder { + if b.object.Spec.CSI == nil { + b.object.Spec.CSI = new(corev1api.CSIPersistentVolumeSource) + } + b.object.Spec.CSI.Driver = driver + b.object.Spec.CSI.VolumeHandle = volumeHandle + return b +} + +// StorageClass sets the PersistentVolume's storage class name. +func (b *PersistentVolumeBuilder) StorageClass(name string) *PersistentVolumeBuilder { + b.object.Spec.StorageClassName = name + return b +} diff --git a/pkg/builder/persistent_volume_claim_builder.go b/pkg/builder/persistent_volume_claim_builder.go new file mode 100644 index 000000000..a96cdcfa0 --- /dev/null +++ b/pkg/builder/persistent_volume_claim_builder.go @@ -0,0 +1,69 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// PersistentVolumeClaimBuilder builds PersistentVolumeClaim objects. +type PersistentVolumeClaimBuilder struct { + object *corev1api.PersistentVolumeClaim +} + +// ForPersistentVolumeClaim is the constructor for a PersistentVolumeClaimBuilder. +func ForPersistentVolumeClaim(ns, name string) *PersistentVolumeClaimBuilder { + return &PersistentVolumeClaimBuilder{ + object: &corev1api.PersistentVolumeClaim{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1api.SchemeGroupVersion.String(), + Kind: "PersistentVolumeClaim", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built PersistentVolumeClaim. +func (b *PersistentVolumeClaimBuilder) Result() *corev1api.PersistentVolumeClaim { + return b.object +} + +// ObjectMeta applies functional options to the PersistentVolumeClaim's ObjectMeta. +func (b *PersistentVolumeClaimBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PersistentVolumeClaimBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// VolumeName sets the PersistentVolumeClaim's volume name. +func (b *PersistentVolumeClaimBuilder) VolumeName(name string) *PersistentVolumeClaimBuilder { + b.object.Spec.VolumeName = name + return b +} + +// StorageClass sets the PersistentVolumeClaim's storage class name. +func (b *PersistentVolumeClaimBuilder) StorageClass(name string) *PersistentVolumeClaimBuilder { + b.object.Spec.StorageClassName = &name + return b +} diff --git a/pkg/builder/pod_builder.go b/pkg/builder/pod_builder.go new file mode 100644 index 000000000..1f12d3477 --- /dev/null +++ b/pkg/builder/pod_builder.go @@ -0,0 +1,71 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// PodBuilder builds Pod objects. +type PodBuilder struct { + object *corev1api.Pod +} + +// ForPod is the constructor for a PodBuilder. +func ForPod(ns, name string) *PodBuilder { + return &PodBuilder{ + object: &corev1api.Pod{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1api.SchemeGroupVersion.String(), + Kind: "Pod", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built Pod. +func (b *PodBuilder) Result() *corev1api.Pod { + return b.object +} + +// ObjectMeta applies functional options to the Pod's ObjectMeta. +func (b *PodBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PodBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// Volumes appends to the pod's volumes +func (b *PodBuilder) Volumes(volumes ...*corev1api.Volume) *PodBuilder { + for _, v := range volumes { + b.object.Spec.Volumes = append(b.object.Spec.Volumes, *v) + } + return b +} + +// NodeName sets the pod's node name +func (b *PodBuilder) NodeName(val string) *PodBuilder { + b.object.Spec.NodeName = val + return b +} diff --git a/pkg/builder/pod_volume_backup_builder.go b/pkg/builder/pod_volume_backup_builder.go new file mode 100644 index 000000000..39de13ef0 --- /dev/null +++ b/pkg/builder/pod_volume_backup_builder.go @@ -0,0 +1,64 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" +) + +// PodVolumeBackupBuilder builds PodVolumeBackup objects +type PodVolumeBackupBuilder struct { + object *velerov1api.PodVolumeBackup +} + +// ForPodVolumeBackup is the constructor for a PodVolumeBackupBuilder. +func ForPodVolumeBackup(ns, name string) *PodVolumeBackupBuilder { + return &PodVolumeBackupBuilder{ + object: &velerov1api.PodVolumeBackup{ + TypeMeta: metav1.TypeMeta{ + APIVersion: velerov1api.SchemeGroupVersion.String(), + Kind: "PodVolumeBackup", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built PodVolumeBackup. +func (b *PodVolumeBackupBuilder) Result() *velerov1api.PodVolumeBackup { + return b.object +} + +// ObjectMeta applies functional options to the PodVolumeBackup's ObjectMeta. +func (b *PodVolumeBackupBuilder) ObjectMeta(opts ...ObjectMetaOpt) *PodVolumeBackupBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// Phase sets the PodVolumeBackup's phase. +func (b *PodVolumeBackupBuilder) Phase(phase velerov1api.PodVolumeBackupPhase) *PodVolumeBackupBuilder { + b.object.Status.Phase = phase + return b +} diff --git a/pkg/builder/restore_builder.go b/pkg/builder/restore_builder.go new file mode 100644 index 000000000..e1a8fe0e8 --- /dev/null +++ b/pkg/builder/restore_builder.go @@ -0,0 +1,135 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" +) + +// RestoreBuilder builds Restore objects. +type RestoreBuilder struct { + object *velerov1api.Restore +} + +// ForRestore is the constructor for a RestoreBuilder. +func ForRestore(ns, name string) *RestoreBuilder { + return &RestoreBuilder{ + object: &velerov1api.Restore{ + TypeMeta: metav1.TypeMeta{ + APIVersion: velerov1api.SchemeGroupVersion.String(), + Kind: "Restore", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built Restore. +func (b *RestoreBuilder) Result() *velerov1api.Restore { + return b.object +} + +// ObjectMeta applies functional options to the Restore's ObjectMeta. +func (b *RestoreBuilder) ObjectMeta(opts ...ObjectMetaOpt) *RestoreBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// Backup sets the Restore's backup name. +func (b *RestoreBuilder) Backup(name string) *RestoreBuilder { + b.object.Spec.BackupName = name + return b +} + +// Schedule sets the Restore's schedule name. +func (b *RestoreBuilder) Schedule(name string) *RestoreBuilder { + b.object.Spec.ScheduleName = name + return b +} + +// IncludedNamespaces appends to the Restore's included namespaces. +func (b *RestoreBuilder) IncludedNamespaces(namespaces ...string) *RestoreBuilder { + b.object.Spec.IncludedNamespaces = append(b.object.Spec.IncludedNamespaces, namespaces...) + return b +} + +// ExcludedNamespaces appends to the Restore's excluded namespaces. +func (b *RestoreBuilder) ExcludedNamespaces(namespaces ...string) *RestoreBuilder { + b.object.Spec.ExcludedNamespaces = append(b.object.Spec.ExcludedNamespaces, namespaces...) + return b +} + +// IncludedResources appends to the Restore's included resources. +func (b *RestoreBuilder) IncludedResources(resources ...string) *RestoreBuilder { + b.object.Spec.IncludedResources = append(b.object.Spec.IncludedResources, resources...) + return b +} + +// ExcludedResources appends to the Restore's excluded resources. +func (b *RestoreBuilder) ExcludedResources(resources ...string) *RestoreBuilder { + b.object.Spec.ExcludedResources = append(b.object.Spec.ExcludedResources, resources...) + return b +} + +// IncludeClusterResources sets the Restore's "include cluster resources" flag. +func (b *RestoreBuilder) IncludeClusterResources(val bool) *RestoreBuilder { + b.object.Spec.IncludeClusterResources = &val + return b +} + +// LabelSelector sets the Restore's label selector. +func (b *RestoreBuilder) LabelSelector(selector *metav1.LabelSelector) *RestoreBuilder { + b.object.Spec.LabelSelector = selector + return b +} + +// NamespaceMappings sets the Restore's namespace mappings. +func (b *RestoreBuilder) NamespaceMappings(mapping ...string) *RestoreBuilder { + if b.object.Spec.NamespaceMapping == nil { + b.object.Spec.NamespaceMapping = make(map[string]string) + } + + if len(mapping)%2 != 0 { + panic("mapping must contain an even number of values") + } + + for i := 0; i < len(mapping); i += 2 { + b.object.Spec.NamespaceMapping[mapping[i]] = mapping[i+1] + } + + return b +} + +// Phase sets the Restore's phase. +func (b *RestoreBuilder) Phase(phase velerov1api.RestorePhase) *RestoreBuilder { + b.object.Status.Phase = phase + return b +} + +// RestorePVs sets the Restore's restore PVs. +func (b *RestoreBuilder) RestorePVs(val bool) *RestoreBuilder { + b.object.Spec.RestorePVs = &val + return b +} diff --git a/pkg/builder/schedule_builder.go b/pkg/builder/schedule_builder.go new file mode 100644 index 000000000..9396d9c46 --- /dev/null +++ b/pkg/builder/schedule_builder.go @@ -0,0 +1,91 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" +) + +// ScheduleBuilder builds Schedule objects. +type ScheduleBuilder struct { + object *velerov1api.Schedule +} + +// ForSchedule is the constructor for a ScheduleBuilder. +func ForSchedule(ns, name string) *ScheduleBuilder { + return &ScheduleBuilder{ + object: &velerov1api.Schedule{ + TypeMeta: metav1.TypeMeta{ + APIVersion: velerov1api.SchemeGroupVersion.String(), + Kind: "Schedule", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built Schedule. +func (b *ScheduleBuilder) Result() *velerov1api.Schedule { + return b.object +} + +// ObjectMeta applies functional options to the Schedule's ObjectMeta. +func (b *ScheduleBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ScheduleBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// Phase sets the Schedule's phase. +func (b *ScheduleBuilder) Phase(phase velerov1api.SchedulePhase) *ScheduleBuilder { + b.object.Status.Phase = phase + return b +} + +// ValidationError appends to the Schedule's validation errors. +func (b *ScheduleBuilder) ValidationError(err string) *ScheduleBuilder { + b.object.Status.ValidationErrors = append(b.object.Status.ValidationErrors, err) + return b +} + +// CronSchedule sets the Schedule's cron schedule. +func (b *ScheduleBuilder) CronSchedule(expression string) *ScheduleBuilder { + b.object.Spec.Schedule = expression + return b +} + +// LastBackupTime sets the Schedule's last backup time. +func (b *ScheduleBuilder) LastBackupTime(val string) *ScheduleBuilder { + t, _ := time.Parse("2006-01-02 15:04:05", val) + b.object.Status.LastBackup.Time = t + return b +} + +// Template sets the Schedule's template. +func (b *ScheduleBuilder) Template(spec velerov1api.BackupSpec) *ScheduleBuilder { + b.object.Spec.Template = spec + return b +} diff --git a/pkg/builder/secret_builder.go b/pkg/builder/secret_builder.go new file mode 100644 index 000000000..405ef33cb --- /dev/null +++ b/pkg/builder/secret_builder.go @@ -0,0 +1,57 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// SecretBuilder builds Secret objects. +type SecretBuilder struct { + object *corev1api.Secret +} + +// ForSecret is the constructor for a SecretBuilder. +func ForSecret(ns, name string) *SecretBuilder { + return &SecretBuilder{ + object: &corev1api.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1api.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built Secret. +func (b *SecretBuilder) Result() *corev1api.Secret { + return b.object +} + +// ObjectMeta applies functional options to the Secret's ObjectMeta. +func (b *SecretBuilder) ObjectMeta(opts ...ObjectMetaOpt) *SecretBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} diff --git a/pkg/builder/server_status_request_builder.go b/pkg/builder/server_status_request_builder.go new file mode 100644 index 000000000..7b5d67234 --- /dev/null +++ b/pkg/builder/server_status_request_builder.go @@ -0,0 +1,84 @@ +/* +Copyright 2018 the Velero 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 builder + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" +) + +// ServerStatusRequestBuilder builds ServerStatusRequest objects. +type ServerStatusRequestBuilder struct { + object *velerov1api.ServerStatusRequest +} + +// ForServerStatusRequest is the constructor for for a ServerStatusRequestBuilder. +func ForServerStatusRequest(ns, name string) *ServerStatusRequestBuilder { + return &ServerStatusRequestBuilder{ + object: &velerov1api.ServerStatusRequest{ + TypeMeta: metav1.TypeMeta{ + APIVersion: velerov1api.SchemeGroupVersion.String(), + Kind: "ServerStatusRequest", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built ServerStatusRequest. +func (b *ServerStatusRequestBuilder) Result() *velerov1api.ServerStatusRequest { + return b.object +} + +// ObjectMeta applies functional options to the ServerStatusRequest's ObjectMeta. +func (b *ServerStatusRequestBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ServerStatusRequestBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// Phase sets the ServerStatusRequest's phase. +func (b *ServerStatusRequestBuilder) Phase(phase velerov1api.ServerStatusRequestPhase) *ServerStatusRequestBuilder { + b.object.Status.Phase = phase + return b +} + +// ProcessedTimestamp sets the ServerStatusRequest's processed timestamp. +func (b *ServerStatusRequestBuilder) ProcessedTimestamp(time time.Time) *ServerStatusRequestBuilder { + b.object.Status.ProcessedTimestamp.Time = time + return b +} + +// ServerVersion sets the ServerStatusRequest's server version. +func (b *ServerStatusRequestBuilder) ServerVersion(version string) *ServerStatusRequestBuilder { + b.object.Status.ServerVersion = version + return b +} + +// Plugins sets the ServerStatusRequest's plugins. +func (b *ServerStatusRequestBuilder) Plugins(plugins []velerov1api.PluginInfo) *ServerStatusRequestBuilder { + b.object.Status.Plugins = plugins + return b +} diff --git a/pkg/builder/service_account_builder.go b/pkg/builder/service_account_builder.go new file mode 100644 index 000000000..7042c5604 --- /dev/null +++ b/pkg/builder/service_account_builder.go @@ -0,0 +1,57 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// ServiceAccountBuilder builds ServiceAccount objects. +type ServiceAccountBuilder struct { + object *corev1api.ServiceAccount +} + +// ForServiceAccount is the constructor for a ServiceAccountBuilder. +func ForServiceAccount(ns, name string) *ServiceAccountBuilder { + return &ServiceAccountBuilder{ + object: &corev1api.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1api.SchemeGroupVersion.String(), + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built ServiceAccount. +func (b *ServiceAccountBuilder) Result() *corev1api.ServiceAccount { + return b.object +} + +// ObjectMeta applies functional options to the ServiceAccount's ObjectMeta. +func (b *ServiceAccountBuilder) ObjectMeta(opts ...ObjectMetaOpt) *ServiceAccountBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} diff --git a/pkg/builder/storage_class_builder.go b/pkg/builder/storage_class_builder.go new file mode 100644 index 000000000..994543332 --- /dev/null +++ b/pkg/builder/storage_class_builder.go @@ -0,0 +1,56 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + storagev1api "k8s.io/api/storage/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// StorageClassBuilder builds StorageClass objects. +type StorageClassBuilder struct { + object *storagev1api.StorageClass +} + +// ForStorageClass is the constructor for a StorageClassBuilder. +func ForStorageClass(name string) *StorageClassBuilder { + return &StorageClassBuilder{ + object: &storagev1api.StorageClass{ + TypeMeta: metav1.TypeMeta{ + APIVersion: storagev1api.SchemeGroupVersion.String(), + Kind: "StorageClass", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, + } +} + +// Result returns the built StorageClass. +func (b *StorageClassBuilder) Result() *storagev1api.StorageClass { + return b.object +} + +// ObjectMeta applies functional options to the StorageClass's ObjectMeta. +func (b *StorageClassBuilder) ObjectMeta(opts ...ObjectMetaOpt) *StorageClassBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} diff --git a/pkg/builder/volume_builder.go b/pkg/builder/volume_builder.go new file mode 100644 index 000000000..cdc373aa4 --- /dev/null +++ b/pkg/builder/volume_builder.go @@ -0,0 +1,56 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + corev1api "k8s.io/api/core/v1" +) + +// VolumeBuilder builds Volume objects. +type VolumeBuilder struct { + object *corev1api.Volume +} + +// ForVolume is the constructor for a VolumeBuilder. +func ForVolume(name string) *VolumeBuilder { + return &VolumeBuilder{ + object: &corev1api.Volume{ + Name: name, + }, + } +} + +// Result returns the built Volume. +func (b *VolumeBuilder) Result() *corev1api.Volume { + return b.object +} + +// PersistentVolumeClaimSource sets the Volume's persistent volume claim source. +func (b *VolumeBuilder) PersistentVolumeClaimSource(claimName string) *VolumeBuilder { + b.object.PersistentVolumeClaim = &corev1api.PersistentVolumeClaimVolumeSource{ + ClaimName: claimName, + } + return b +} + +// CSISource sets the Volume's CSI source. +func (b *VolumeBuilder) CSISource(driver string) *VolumeBuilder { + b.object.CSI = &corev1api.CSIVolumeSource{ + Driver: driver, + } + return b +} diff --git a/pkg/builder/volume_snapshot_location_builder.go b/pkg/builder/volume_snapshot_location_builder.go new file mode 100644 index 000000000..4c028b2b6 --- /dev/null +++ b/pkg/builder/volume_snapshot_location_builder.go @@ -0,0 +1,64 @@ +/* +Copyright 2019 the Velero 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 builder + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" +) + +// VolumeSnapshotLocationBuilder builds VolumeSnapshotLocation objects. +type VolumeSnapshotLocationBuilder struct { + object *velerov1api.VolumeSnapshotLocation +} + +// ForVolumeSnapshotLocation is the constructor for a VolumeSnapshotLocationBuilder. +func ForVolumeSnapshotLocation(ns, name string) *VolumeSnapshotLocationBuilder { + return &VolumeSnapshotLocationBuilder{ + object: &velerov1api.VolumeSnapshotLocation{ + TypeMeta: metav1.TypeMeta{ + APIVersion: velerov1api.SchemeGroupVersion.String(), + Kind: "VolumeSnapshotLocation", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: ns, + Name: name, + }, + }, + } +} + +// Result returns the built VolumeSnapshotLocation. +func (b *VolumeSnapshotLocationBuilder) Result() *velerov1api.VolumeSnapshotLocation { + return b.object +} + +// ObjectMeta applies functional options to the VolumeSnapshotLocation's ObjectMeta. +func (b *VolumeSnapshotLocationBuilder) ObjectMeta(opts ...ObjectMetaOpt) *VolumeSnapshotLocationBuilder { + for _, opt := range opts { + opt(b.object) + } + + return b +} + +// Provider sets the VolumeSnapshotLocation's provider. +func (b *VolumeSnapshotLocationBuilder) Provider(name string) *VolumeSnapshotLocationBuilder { + b.object.Spec.Provider = name + return b +} diff --git a/pkg/cmd/cli/restic/server_test.go b/pkg/cmd/cli/restic/server_test.go index 5f230cc2e..a71fe2184 100644 --- a/pkg/cmd/cli/restic/server_test.go +++ b/pkg/cmd/cli/restic/server_test.go @@ -25,7 +25,7 @@ import ( v1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes/fake" - "github.com/heptio/velero/pkg/test" + "github.com/heptio/velero/pkg/builder" testutil "github.com/heptio/velero/pkg/util/test" ) @@ -39,8 +39,8 @@ func Test_validatePodVolumesHostPath(t *testing.T) { { name: "no error when pod volumes are present", pods: []*corev1.Pod{ - test.NewPod("foo", "bar", test.WithUID("foo")), - test.NewPod("zoo", "raz", test.WithUID("zoo")), + builder.ForPod("foo", "bar").ObjectMeta(builder.WithUID("foo")).Result(), + builder.ForPod("zoo", "raz").ObjectMeta(builder.WithUID("zoo")).Result(), }, dirs: []string{"foo", "zoo"}, wantErr: false, @@ -48,8 +48,8 @@ func Test_validatePodVolumesHostPath(t *testing.T) { { name: "no error when pod volumes are present and there are mirror pods", pods: []*corev1.Pod{ - test.NewPod("foo", "bar", test.WithUID("foo")), - test.NewPod("zoo", "raz", test.WithUID("zoo"), test.WithAnnotations(v1.MirrorPodAnnotationKey, "baz")), + builder.ForPod("foo", "bar").ObjectMeta(builder.WithUID("foo")).Result(), + builder.ForPod("zoo", "raz").ObjectMeta(builder.WithUID("zoo"), builder.WithAnnotations(v1.MirrorPodAnnotationKey, "baz")).Result(), }, dirs: []string{"foo", "baz"}, wantErr: false, @@ -57,8 +57,8 @@ func Test_validatePodVolumesHostPath(t *testing.T) { { name: "error when all pod volumes missing", pods: []*corev1.Pod{ - test.NewPod("foo", "bar", test.WithUID("foo")), - test.NewPod("zoo", "raz", test.WithUID("zoo")), + builder.ForPod("foo", "bar").ObjectMeta(builder.WithUID("foo")).Result(), + builder.ForPod("zoo", "raz").ObjectMeta(builder.WithUID("zoo")).Result(), }, dirs: []string{"unexpected-dir"}, wantErr: true, @@ -66,8 +66,8 @@ func Test_validatePodVolumesHostPath(t *testing.T) { { name: "error when some pod volumes missing", pods: []*corev1.Pod{ - test.NewPod("foo", "bar", test.WithUID("foo")), - test.NewPod("zoo", "raz", test.WithUID("zoo")), + builder.ForPod("foo", "bar").ObjectMeta(builder.WithUID("foo")).Result(), + builder.ForPod("zoo", "raz").ObjectMeta(builder.WithUID("zoo")).Result(), }, dirs: []string{"foo"}, wantErr: true, diff --git a/pkg/cmd/cli/serverstatus/server_status.go b/pkg/cmd/cli/serverstatus/server_status.go index 5dc41c900..f80670de3 100644 --- a/pkg/cmd/cli/serverstatus/server_status.go +++ b/pkg/cmd/cli/serverstatus/server_status.go @@ -24,8 +24,8 @@ import ( "k8s.io/apimachinery/pkg/watch" velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" + "github.com/heptio/velero/pkg/builder" velerov1client "github.com/heptio/velero/pkg/generated/clientset/versioned/typed/velero/v1" - "github.com/heptio/velero/pkg/serverstatusrequest" ) type ServerStatusGetter interface { @@ -38,7 +38,10 @@ type DefaultServerStatusGetter struct { } func (g *DefaultServerStatusGetter) GetServerStatus(client velerov1client.ServerStatusRequestsGetter) (*velerov1api.ServerStatusRequest, error) { - req := serverstatusrequest.NewBuilder().Namespace(g.Namespace).GenerateName("velero-cli-").ServerStatusRequest() + req := builder.ForServerStatusRequest(g.Namespace, ""). + ObjectMeta( + builder.WithGenerateName("velero-cli-"), + ).Result() created, err := client.ServerStatusRequests(g.Namespace).Create(req) if err != nil { diff --git a/pkg/cmd/cli/version/version_test.go b/pkg/cmd/cli/version/version_test.go index 9bc90313d..be316d484 100644 --- a/pkg/cmd/cli/version/version_test.go +++ b/pkg/cmd/cli/version/version_test.go @@ -26,10 +26,10 @@ import ( "github.com/stretchr/testify/mock" velerov1 "github.com/heptio/velero/pkg/apis/velero/v1" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/buildinfo" "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" v1 "github.com/heptio/velero/pkg/generated/clientset/versioned/typed/velero/v1" - "github.com/heptio/velero/pkg/serverstatusrequest" ) func TestPrintVersion(t *testing.T) { @@ -73,7 +73,7 @@ func TestPrintVersion(t *testing.T) { { name: "server status getter returns normally", clientOnly: false, - serverStatusRequest: serverstatusrequest.NewBuilder().ServerVersion("v1.0.1").ServerStatusRequest(), + serverStatusRequest: builder.ForServerStatusRequest("velero", "ssr-1").ServerVersion("v1.0.1").Result(), getterError: nil, want: clientVersion + "Server:\n\tVersion: v1.0.1\n", }, diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index ab65aac53..026084d58 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -35,6 +35,7 @@ import ( velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" pkgbackup "github.com/heptio/velero/pkg/backup" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" informers "github.com/heptio/velero/pkg/generated/informers/externalversions" "github.com/heptio/velero/pkg/metrics" @@ -44,7 +45,6 @@ import ( pluginmocks "github.com/heptio/velero/pkg/plugin/mocks" "github.com/heptio/velero/pkg/plugin/velero" "github.com/heptio/velero/pkg/util/logging" - velerotest "github.com/heptio/velero/pkg/util/test" ) type fakeBackupper struct { @@ -56,8 +56,8 @@ func (b *fakeBackupper) Backup(logger logrus.FieldLogger, backup *pkgbackup.Requ return args.Error(0) } -func defaultBackup() *pkgbackup.Builder { - return pkgbackup.NewNamedBackupBuilder(velerov1api.DefaultNamespace, "backup-1") +func defaultBackup() *builder.BackupBuilder { + return builder.ForBackup(velerov1api.DefaultNamespace, "backup-1") } func TestProcessBackupNonProcessedItems(t *testing.T) { @@ -77,22 +77,22 @@ func TestProcessBackupNonProcessedItems(t *testing.T) { { name: "FailedValidation backup is not processed", key: "velero/backup-1", - backup: defaultBackup().Phase(velerov1api.BackupPhaseFailedValidation).Backup(), + backup: defaultBackup().Phase(velerov1api.BackupPhaseFailedValidation).Result(), }, { name: "InProgress backup is not processed", key: "velero/backup-1", - backup: defaultBackup().Phase(velerov1api.BackupPhaseInProgress).Backup(), + backup: defaultBackup().Phase(velerov1api.BackupPhaseInProgress).Result(), }, { name: "Completed backup is not processed", key: "velero/backup-1", - backup: defaultBackup().Phase(velerov1api.BackupPhaseCompleted).Backup(), + backup: defaultBackup().Phase(velerov1api.BackupPhaseCompleted).Result(), }, { name: "Failed backup is not processed", key: "velero/backup-1", - backup: defaultBackup().Phase(velerov1api.BackupPhaseFailed).Backup(), + backup: defaultBackup().Phase(velerov1api.BackupPhaseFailed).Result(), }, } @@ -126,7 +126,7 @@ func TestProcessBackupNonProcessedItems(t *testing.T) { } func TestProcessBackupValidationFailures(t *testing.T) { - defaultBackupLocation := velerotest.NewTestBackupStorageLocation().WithName("loc-1").BackupStorageLocation + defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Result() tests := []struct { name string @@ -136,25 +136,25 @@ func TestProcessBackupValidationFailures(t *testing.T) { }{ { name: "invalid included/excluded resources fails validation", - backup: defaultBackup().IncludedResources("foo").ExcludedResources("foo").Backup(), + backup: defaultBackup().IncludedResources("foo").ExcludedResources("foo").Result(), backupLocation: defaultBackupLocation, expectedErrs: []string{"Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: foo"}, }, { name: "invalid included/excluded namespaces fails validation", - backup: defaultBackup().IncludedNamespaces("foo").ExcludedNamespaces("foo").Backup(), + backup: defaultBackup().IncludedNamespaces("foo").ExcludedNamespaces("foo").Result(), backupLocation: defaultBackupLocation, expectedErrs: []string{"Invalid included/excluded namespace lists: excludes list cannot contain an item in the includes list: foo"}, }, { name: "non-existent backup location fails validation", - backup: defaultBackup().StorageLocation("nonexistent").Backup(), + backup: defaultBackup().StorageLocation("nonexistent").Result(), expectedErrs: []string{"a BackupStorageLocation CRD with the name specified in the backup spec needs to be created before this backup can be executed. Error: backupstoragelocation.velero.io \"nonexistent\" not found"}, }, { name: "backup for read-only backup location fails validation", - backup: defaultBackup().StorageLocation("read-only").Backup(), - backupLocation: velerotest.NewTestBackupStorageLocation().WithName("read-only").WithAccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).BackupStorageLocation, + backup: defaultBackup().StorageLocation("read-only").Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result(), expectedErrs: []string{"backup can't be created because backup storage location read-only is currently in read-only mode"}, }, } @@ -214,15 +214,14 @@ func TestBackupLocationLabel(t *testing.T) { }{ { name: "valid backup location name should be used as a label", - backup: defaultBackup().Backup(), - backupLocation: velerotest.NewTestBackupStorageLocation().WithName("loc-1").BackupStorageLocation, + backup: defaultBackup().Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "loc-1").Result(), expectedBackupLocation: "loc-1", }, { - name: "invalid storage location name should be handled while creating label", - backup: defaultBackup().Backup(), - backupLocation: velerotest.NewTestBackupStorageLocation(). - WithName("defaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefault").BackupStorageLocation, + name: "invalid storage location name should be handled while creating label", + backup: defaultBackup().Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "defaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultdefault").Result(), expectedBackupLocation: "defaultdefaultdefaultdefaultdefaultdefaultdefaultdefaultd58343f", }, } @@ -273,13 +272,13 @@ func TestDefaultBackupTTL(t *testing.T) { }{ { name: "backup with no TTL specified", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), expectedTTL: defaultBackupTTL, expectedExpiration: metav1.NewTime(now.Add(defaultBackupTTL.Duration)), }, { name: "backup with TTL specified", - backup: defaultBackup().TTL(time.Hour).Backup(), + backup: defaultBackup().TTL(time.Hour).Result(), expectedTTL: metav1.Duration{Duration: 1 * time.Hour}, expectedExpiration: metav1.NewTime(now.Add(1 * time.Hour)), }, @@ -312,7 +311,7 @@ func TestDefaultBackupTTL(t *testing.T) { } func TestProcessBackupCompletions(t *testing.T) { - defaultBackupLocation := velerotest.NewTestBackupStorageLocation().WithName("loc-1").WithObjectStorage("store-1").BackupStorageLocation + defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").ObjectStorage("store-1").Result() now, err := time.Parse(time.RFC1123Z, time.RFC1123Z) require.NoError(t, err) @@ -329,7 +328,7 @@ func TestProcessBackupCompletions(t *testing.T) { // Completed { name: "backup with no backup location gets the default", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), backupLocation: defaultBackupLocation, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ @@ -357,8 +356,8 @@ func TestProcessBackupCompletions(t *testing.T) { }, { name: "backup with a specific backup location keeps it", - backup: defaultBackup().StorageLocation("alt-loc").Backup(), - backupLocation: velerotest.NewTestBackupStorageLocation().WithName("alt-loc").WithObjectStorage("store-1").BackupStorageLocation, + backup: defaultBackup().StorageLocation("alt-loc").Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "alt-loc").ObjectStorage("store-1").Result(), expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -385,12 +384,11 @@ func TestProcessBackupCompletions(t *testing.T) { }, { name: "backup for a location with ReadWrite access mode gets processed", - backup: defaultBackup().StorageLocation("read-write").Backup(), - backupLocation: velerotest.NewTestBackupStorageLocation(). - WithName("read-write"). - WithObjectStorage("store-1"). - WithAccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite). - BackupStorageLocation, + backup: defaultBackup().StorageLocation("read-write").Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "read-write"). + ObjectStorage("store-1"). + AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite). + Result(), expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ Kind: "Backup", @@ -417,7 +415,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, { name: "backup with a TTL has expiration set", - backup: defaultBackup().TTL(10 * time.Minute).Backup(), + backup: defaultBackup().TTL(10 * time.Minute).Result(), backupLocation: defaultBackupLocation, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ @@ -447,7 +445,7 @@ func TestProcessBackupCompletions(t *testing.T) { { name: "backup without an existing backup will succeed", backupExists: false, - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), backupLocation: defaultBackupLocation, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ @@ -478,7 +476,7 @@ func TestProcessBackupCompletions(t *testing.T) { { name: "backup with existing backup will fail", backupExists: true, - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), backupLocation: defaultBackupLocation, expectedResult: &velerov1api.Backup{ TypeMeta: metav1.TypeMeta{ @@ -506,7 +504,7 @@ func TestProcessBackupCompletions(t *testing.T) { }, { name: "error when checking if backup exists will cause backup to fail", - backup: defaultBackup().Backup(), + backup: defaultBackup().Result(), existenceCheckError: errors.New("Backup already exists in object storage"), backupLocation: defaultBackupLocation, expectedResult: &velerov1api.Backup{ @@ -613,7 +611,7 @@ func TestValidateAndGetSnapshotLocations(t *testing.T) { tests := []struct { name string backup *velerov1api.Backup - locations []*velerotest.TestVolumeSnapshotLocation + locations []*velerov1api.VolumeSnapshotLocation defaultLocations map[string]string expectedVolumeSnapshotLocationNames []string // adding these in the expected order will allow to test with better msgs in case of a test failure expectedErrors string @@ -621,76 +619,76 @@ func TestValidateAndGetSnapshotLocations(t *testing.T) { }{ { name: "location name does not correspond to any existing location", - backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("random-name").Backup(), - locations: []*velerotest.TestVolumeSnapshotLocation{ - velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-east-1"), - velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-west-1"), - velerotest.NewTestVolumeSnapshotLocation().WithProvider("fake-provider").WithName("some-name"), + backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("random-name").Result(), + locations: []*velerov1api.VolumeSnapshotLocation{ + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(), + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(), + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "some-name").Provider("fake-provider").Result(), }, expectedErrors: "a VolumeSnapshotLocation CRD for the location random-name with the name specified in the backup spec needs to be created before this snapshot can be executed. Error: volumesnapshotlocation.velero.io \"random-name\" not found", expectedSuccess: false, }, { name: "duplicate locationName per provider: should filter out dups", - backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-west-1", "aws-us-west-1").Backup(), - locations: []*velerotest.TestVolumeSnapshotLocation{ - velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-east-1"), - velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-west-1"), + backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-west-1", "aws-us-west-1").Result(), + locations: []*velerov1api.VolumeSnapshotLocation{ + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(), + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(), }, expectedVolumeSnapshotLocationNames: []string{"aws-us-west-1"}, expectedSuccess: true, }, { name: "multiple non-dupe location names per provider should error", - backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-east-1", "aws-us-west-1").Backup(), - locations: []*velerotest.TestVolumeSnapshotLocation{ - velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-east-1"), - velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-west-1"), - velerotest.NewTestVolumeSnapshotLocation().WithProvider("fake-provider").WithName("some-name"), + backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-east-1", "aws-us-west-1").Result(), + locations: []*velerov1api.VolumeSnapshotLocation{ + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(), + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(), + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "some-name").Provider("fake-provider").Result(), }, expectedErrors: "more than one VolumeSnapshotLocation name specified for provider aws: aws-us-west-1; unexpected name was aws-us-east-1", expectedSuccess: false, }, { name: "no location name for the provider exists, only one VSL for the provider: use it", - backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Backup(), - locations: []*velerotest.TestVolumeSnapshotLocation{ - velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-east-1"), + backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Result(), + locations: []*velerov1api.VolumeSnapshotLocation{ + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(), }, expectedVolumeSnapshotLocationNames: []string{"aws-us-east-1"}, expectedSuccess: true, }, { name: "no location name for the provider exists, no default, more than one VSL for the provider: error", - backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Backup(), - locations: []*velerotest.TestVolumeSnapshotLocation{ - velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-east-1"), - velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-west-1"), + backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Result(), + locations: []*velerov1api.VolumeSnapshotLocation{ + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(), + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(), }, expectedErrors: "provider aws has more than one possible volume snapshot location, and none were specified explicitly or as a default", }, { name: "no location name for the provider exists, more than one VSL for the provider: the provider's default should be added", - backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Backup(), + backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Result(), defaultLocations: map[string]string{"aws": "aws-us-east-1"}, - locations: []*velerotest.TestVolumeSnapshotLocation{ - velerotest.NewTestVolumeSnapshotLocation().WithName("aws-us-east-1").WithProvider("aws"), - velerotest.NewTestVolumeSnapshotLocation().WithName("aws-us-west-1").WithProvider("aws"), + locations: []*velerov1api.VolumeSnapshotLocation{ + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-east-1").Provider("aws").Result(), + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(), }, expectedVolumeSnapshotLocationNames: []string{"aws-us-east-1"}, expectedSuccess: true, }, { name: "no existing location name and no default location name given", - backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Backup(), + backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).Result(), expectedSuccess: true, }, { name: "multiple location names for a provider, default location name for another provider", - backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-west-1", "aws-us-west-1").Backup(), + backup: defaultBackup().Phase(velerov1api.BackupPhaseNew).VolumeSnapshotLocations("aws-us-west-1", "aws-us-west-1").Result(), defaultLocations: map[string]string{"fake-provider": "some-name"}, - locations: []*velerotest.TestVolumeSnapshotLocation{ - velerotest.NewTestVolumeSnapshotLocation().WithProvider("aws").WithName("aws-us-west-1"), - velerotest.NewTestVolumeSnapshotLocation().WithProvider("fake-provider").WithName("some-name"), + locations: []*velerov1api.VolumeSnapshotLocation{ + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "aws-us-west-1").Provider("aws").Result(), + builder.ForVolumeSnapshotLocation(velerov1api.DefaultNamespace, "some-name").Provider("fake-provider").Result(), }, expectedVolumeSnapshotLocationNames: []string{"aws-us-west-1", "some-name"}, expectedSuccess: true, @@ -713,7 +711,7 @@ func TestValidateAndGetSnapshotLocations(t *testing.T) { backup := test.backup.DeepCopy() backup.Spec.VolumeSnapshotLocations = test.backup.Spec.VolumeSnapshotLocations for _, location := range test.locations { - require.NoError(t, sharedInformers.Velero().V1().VolumeSnapshotLocations().Informer().GetStore().Add(location.VolumeSnapshotLocation)) + require.NoError(t, sharedInformers.Velero().V1().VolumeSnapshotLocations().Informer().GetStore().Add(location)) } providerLocations, errs := c.validateAndGetSnapshotLocations(backup) diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index b8336e885..b96733381 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -34,6 +34,7 @@ import ( v1 "github.com/heptio/velero/pkg/apis/velero/v1" pkgbackup "github.com/heptio/velero/pkg/backup" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" informers "github.com/heptio/velero/pkg/generated/informers/externalversions" "github.com/heptio/velero/pkg/metrics" @@ -266,8 +267,8 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("patching to InProgress fails", func(t *testing.T) { - backup := defaultBackup().Name("foo").StorageLocation("default").Backup() - location := velerotest.NewTestBackupStorageLocation().WithName("default").BackupStorageLocation + backup := builder.ForBackup(v1.DefaultNamespace, "foo").StorageLocation("default").Result() + location := builder.ForBackupStorageLocation("velero", "default").Result() td := setupBackupDeletionControllerTest(backup) @@ -298,8 +299,8 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("patching backup to Deleting fails", func(t *testing.T) { - backup := defaultBackup().Name("foo").StorageLocation("default").Backup() - location := velerotest.NewTestBackupStorageLocation().WithName("default").BackupStorageLocation + backup := builder.ForBackup(v1.DefaultNamespace, "foo").StorageLocation("default").Result() + location := builder.ForBackupStorageLocation("velero", "default").Result() td := setupBackupDeletionControllerTest(backup) @@ -364,7 +365,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("unable to find backup storage location", func(t *testing.T) { - backup := defaultBackup().Name("foo").StorageLocation("default").Backup() + backup := builder.ForBackup(v1.DefaultNamespace, "foo").StorageLocation("default").Result() td := setupBackupDeletionControllerTest(backup) @@ -390,8 +391,8 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("backup storage location is in read-only mode", func(t *testing.T) { - backup := defaultBackup().Name("foo").StorageLocation("default").Backup() - location := velerotest.NewTestBackupStorageLocation().WithName("default").WithAccessMode(v1.BackupStorageLocationAccessModeReadOnly).BackupStorageLocation + backup := builder.ForBackup(v1.DefaultNamespace, "foo").StorageLocation("default").Result() + location := builder.ForBackupStorageLocation("velero", "default").AccessMode(v1.BackupStorageLocationAccessModeReadOnly).Result() td := setupBackupDeletionControllerTest(backup) @@ -419,13 +420,13 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("full delete, no errors", func(t *testing.T) { - backup := defaultBackup().Name("foo").Backup() + backup := builder.ForBackup(v1.DefaultNamespace, "foo").Result() backup.UID = "uid" backup.Spec.StorageLocation = "primary" - restore1 := velerotest.NewTestRestore("velero", "restore-1", v1.RestorePhaseCompleted).WithBackup("foo").Restore - restore2 := velerotest.NewTestRestore("velero", "restore-2", v1.RestorePhaseCompleted).WithBackup("foo").Restore - restore3 := velerotest.NewTestRestore("velero", "restore-3", v1.RestorePhaseCompleted).WithBackup("some-other-backup").Restore + restore1 := builder.ForRestore("velero", "restore-1").Phase(v1.RestorePhaseCompleted).Backup("foo").Result() + restore2 := builder.ForRestore("velero", "restore-2").Phase(v1.RestorePhaseCompleted).Backup("foo").Result() + restore3 := builder.ForRestore("velero", "restore-3").Phase(v1.RestorePhaseCompleted).Backup("some-other-backup").Result() td := setupBackupDeletionControllerTest(backup, restore1, restore2, restore3) @@ -565,16 +566,26 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) { }) t.Run("full delete, no errors, with backup name greater than 63 chars", func(t *testing.T) { - backup := defaultBackup().Name("the-really-long-backup-name-that-is-much-more-than-63-characters").Backup() + backup := defaultBackup(). + ObjectMeta( + builder.WithName("the-really-long-backup-name-that-is-much-more-than-63-characters"), + ). + Result() backup.UID = "uid" backup.Spec.StorageLocation = "primary" - restore1 := velerotest.NewTestRestore("velero", "restore-1", v1.RestorePhaseCompleted). - WithBackup("the-really-long-backup-name-that-is-much-more-than-63-characters").Restore - restore2 := velerotest.NewTestRestore("velero", "restore-2", v1.RestorePhaseCompleted). - WithBackup("the-really-long-backup-name-that-is-much-more-than-63-characters").Restore - restore3 := velerotest.NewTestRestore("velero", "restore-3", v1.RestorePhaseCompleted). - WithBackup("some-other-backup").Restore + restore1 := builder.ForRestore("velero", "restore-1"). + Phase(v1.RestorePhaseCompleted). + Backup("the-really-long-backup-name-that-is-much-more-than-63-characters"). + Result() + restore2 := builder.ForRestore("velero", "restore-2"). + Phase(v1.RestorePhaseCompleted). + Backup("the-really-long-backup-name-that-is-much-more-than-63-characters"). + Result() + restore3 := builder.ForRestore("velero", "restore-3"). + Phase(v1.RestorePhaseCompleted). + Backup("some-other-backup"). + Result() td := setupBackupDeletionControllerTest(backup, restore1, restore2, restore3) td.req = pkgbackup.NewDeleteBackupRequest(backup.Name, string(backup.UID)) diff --git a/pkg/controller/backup_sync_controller_test.go b/pkg/controller/backup_sync_controller_test.go index 3465f71d6..984bc4115 100644 --- a/pkg/controller/backup_sync_controller_test.go +++ b/pkg/controller/backup_sync_controller_test.go @@ -32,7 +32,7 @@ import ( core "k8s.io/client-go/testing" velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" - pkgbackup "github.com/heptio/velero/pkg/backup" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" informers "github.com/heptio/velero/pkg/generated/informers/externalversions" "github.com/heptio/velero/pkg/label" @@ -43,10 +43,6 @@ import ( velerotest "github.com/heptio/velero/pkg/util/test" ) -func defaultPodVolumeBackup() *pkgbackup.PodVolumeBackupBuilder { - return pkgbackup.NewNamedPodVolumeBackupBuilder(velerov1api.DefaultNamespace, "pvb-1") -} - func defaultLocationsList(namespace string) []*velerov1api.BackupStorageLocation { return []*velerov1api.BackupStorageLocation{ { @@ -138,15 +134,15 @@ func TestBackupSyncControllerRun(t *testing.T) { cloudBuckets: map[string][]*cloudBackupData{ "bucket-1": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(), + backup: builder.ForBackup("ns-1", "backup-1").Result(), }, &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(), + backup: builder.ForBackup("ns-1", "backup-2").Result(), }, }, "bucket-2": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-3").Backup(), + backup: builder.ForBackup("ns-1", "backup-3").Result(), }, }, }, @@ -158,18 +154,18 @@ func TestBackupSyncControllerRun(t *testing.T) { cloudBuckets: map[string][]*cloudBackupData{ "bucket-1": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(), + backup: builder.ForBackup("ns-1", "backup-1").Result(), }, &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(), + backup: builder.ForBackup("ns-1", "backup-2").Result(), }, }, "bucket-2": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-2").Name("backup-3").Backup(), + backup: builder.ForBackup("ns-2", "backup-3").Result(), }, &cloudBackupData{ - backup: defaultBackup().Namespace("velero").Name("backup-4").Backup(), + backup: builder.ForBackup("velero", "backup-4").Result(), }, }, }, @@ -181,26 +177,26 @@ func TestBackupSyncControllerRun(t *testing.T) { cloudBuckets: map[string][]*cloudBackupData{ "bucket-1": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(), + backup: builder.ForBackup("ns-1", "backup-1").Result(), }, &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(), + backup: builder.ForBackup("ns-1", "backup-2").Result(), }, }, "bucket-2": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-3").Backup(), + backup: builder.ForBackup("ns-1", "backup-3").Result(), }, &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-4").Backup(), + backup: builder.ForBackup("ns-1", "backup-4").Result(), }, }, }, existingBackups: []*velerov1api.Backup{ // add a label to each existing backup so we can differentiate it from the cloud // backup during verification - defaultBackup().Namespace("ns-1").Name("backup-1").Labels("i-exist", "true").StorageLocation("location-1").Backup(), - defaultBackup().Namespace("ns-1").Name("backup-3").Labels("i-exist", "true").StorageLocation("location-2").Backup(), + builder.ForBackup("ns-1", "backup-1").StorageLocation("location-1").ObjectMeta(builder.WithLabels("i-exist", "true")).Result(), + builder.ForBackup("ns-1", "backup-3").StorageLocation("location-2").ObjectMeta(builder.WithLabels("i-exist", "true")).Result(), }, }, { @@ -210,14 +206,14 @@ func TestBackupSyncControllerRun(t *testing.T) { cloudBuckets: map[string][]*cloudBackupData{ "bucket-1": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(), + backup: builder.ForBackup("ns-1", "backup-1").Result(), }, }, }, existingBackups: []*velerov1api.Backup{ // add a label to each existing backup so we can differentiate it from the cloud // backup during verification - defaultBackup().Namespace("ns-1").Name("backup-1").Labels("i-exist", "true").StorageLocation("location-1").Backup(), + builder.ForBackup("ns-1", "backup-1").ObjectMeta(builder.WithLabels("i-exist", "true")).StorageLocation("location-1").Result(), }, }, { @@ -227,15 +223,15 @@ func TestBackupSyncControllerRun(t *testing.T) { cloudBuckets: map[string][]*cloudBackupData{ "bucket-1": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-1").StorageLocation("foo").Labels(velerov1api.StorageLocationLabel, "foo").Backup(), + backup: builder.ForBackup("ns-1", "backup-1").StorageLocation("foo").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "foo")).Result(), }, &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(), + backup: builder.ForBackup("ns-1", "backup-2").Result(), }, }, "bucket-2": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-3").StorageLocation("bar").Labels(velerov1api.StorageLocationLabel, "bar").Backup(), + backup: builder.ForBackup("ns-1", "backup-3").StorageLocation("bar").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "bar")).Result(), }, }, }, @@ -248,15 +244,15 @@ func TestBackupSyncControllerRun(t *testing.T) { cloudBuckets: map[string][]*cloudBackupData{ "bucket-1": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-1").StorageLocation("foo").Labels(velerov1api.StorageLocationLabel, "foo").Backup(), + backup: builder.ForBackup("ns-1", "backup-1").StorageLocation("foo").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "foo")).Result(), }, &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(), + backup: builder.ForBackup("ns-1", "backup-2").Result(), }, }, "bucket-2": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-3").StorageLocation("bar").Labels(velerov1api.StorageLocationLabel, "bar").Backup(), + backup: builder.ForBackup("ns-1", "backup-3").StorageLocation("bar").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "bar")).Result(), }, }, }, @@ -268,28 +264,28 @@ func TestBackupSyncControllerRun(t *testing.T) { cloudBuckets: map[string][]*cloudBackupData{ "bucket-1": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(), + backup: builder.ForBackup("ns-1", "backup-1").Result(), podVolumeBackups: []*velerov1api.PodVolumeBackup{ - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-1").PodVolumeBackup(), + builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(), }, }, &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(), + backup: builder.ForBackup("ns-1", "backup-2").Result(), podVolumeBackups: []*velerov1api.PodVolumeBackup{ - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-2").PodVolumeBackup(), + builder.ForPodVolumeBackup("ns-1", "pvb-2").Result(), }, }, }, "bucket-2": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-3").Backup(), + backup: builder.ForBackup("ns-1", "backup-3").Result(), }, &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-4").Backup(), + backup: builder.ForBackup("ns-1", "backup-4").Result(), podVolumeBackups: []*velerov1api.PodVolumeBackup{ - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-1").PodVolumeBackup(), - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-2").PodVolumeBackup(), - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-3").PodVolumeBackup(), + builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(), + builder.ForPodVolumeBackup("ns-1", "pvb-2").Result(), + builder.ForPodVolumeBackup("ns-1", "pvb-3").Result(), }, }, }, @@ -302,35 +298,35 @@ func TestBackupSyncControllerRun(t *testing.T) { cloudBuckets: map[string][]*cloudBackupData{ "bucket-1": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-1").Backup(), + backup: builder.ForBackup("ns-1", "backup-1").Result(), podVolumeBackups: []*velerov1api.PodVolumeBackup{ - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-1").PodVolumeBackup(), + builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(), }, }, &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-2").Backup(), + backup: builder.ForBackup("ns-1", "backup-2").Result(), podVolumeBackups: []*velerov1api.PodVolumeBackup{ - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-3").PodVolumeBackup(), + builder.ForPodVolumeBackup("ns-1", "pvb-3").Result(), }, }, }, "bucket-2": { &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-3").Backup(), + backup: builder.ForBackup("ns-1", "backup-3").Result(), }, &cloudBackupData{ - backup: defaultBackup().Namespace("ns-1").Name("backup-4").Backup(), + backup: builder.ForBackup("ns-1", "backup-4").Result(), podVolumeBackups: []*velerov1api.PodVolumeBackup{ - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-1").PodVolumeBackup(), - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-5").PodVolumeBackup(), - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-6").PodVolumeBackup(), + builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(), + builder.ForPodVolumeBackup("ns-1", "pvb-5").Result(), + builder.ForPodVolumeBackup("ns-1", "pvb-6").Result(), }, }, }, }, existingPodVolumeBackups: []*velerov1api.PodVolumeBackup{ - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-1").PodVolumeBackup(), - defaultPodVolumeBackup().Namespace("ns-1").Name("pvb-2").PodVolumeBackup(), + builder.ForPodVolumeBackup("ns-1", "pvb-1").Result(), + builder.ForPodVolumeBackup("ns-1", "pvb-2").Result(), }, }, } @@ -477,6 +473,10 @@ func TestBackupSyncControllerRun(t *testing.T) { } func TestDeleteOrphanedBackups(t *testing.T) { + baseBuilder := func(name string) *builder.BackupBuilder { + return builder.ForBackup("ns-1", name).ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "default")) + } + tests := []struct { name string cloudBackups sets.String @@ -489,9 +489,9 @@ func TestDeleteOrphanedBackups(t *testing.T) { namespace: "ns-1", cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), k8sBackups: []*velerov1api.Backup{ - defaultBackup().Namespace("ns-1").Name("backupA").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backupB").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backupC").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), + baseBuilder("backupA").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backupB").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backupC").Phase(velerov1api.BackupPhaseCompleted).Result(), }, expectedDeletes: sets.NewString("backupA", "backupB", "backupC"), }, @@ -500,9 +500,9 @@ func TestDeleteOrphanedBackups(t *testing.T) { namespace: "ns-1", cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), k8sBackups: []*velerov1api.Backup{ - defaultBackup().Namespace("ns-1").Name("backup-1").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-2").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-C").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), + baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-C").Phase(velerov1api.BackupPhaseCompleted).Result(), }, expectedDeletes: sets.NewString("backup-C"), }, @@ -511,9 +511,9 @@ func TestDeleteOrphanedBackups(t *testing.T) { namespace: "ns-1", cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), k8sBackups: []*velerov1api.Backup{ - defaultBackup().Namespace("ns-1").Name("backup-1").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-2").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-3").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), + baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-3").Phase(velerov1api.BackupPhaseCompleted).Result(), }, expectedDeletes: sets.NewString(), }, @@ -522,12 +522,12 @@ func TestDeleteOrphanedBackups(t *testing.T) { namespace: "ns-1", cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), k8sBackups: []*velerov1api.Backup{ - defaultBackup().Namespace("ns-1").Name("backupA").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("Deleting").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseDeleting).Backup(), - defaultBackup().Namespace("ns-1").Name("Failed").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseFailed).Backup(), - defaultBackup().Namespace("ns-1").Name("FailedValidation").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseFailedValidation).Backup(), - defaultBackup().Namespace("ns-1").Name("InProgress").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseInProgress).Backup(), - defaultBackup().Namespace("ns-1").Name("New").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseNew).Backup(), + baseBuilder("backupA").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("Deleting").Phase(velerov1api.BackupPhaseDeleting).Result(), + baseBuilder("Failed").Phase(velerov1api.BackupPhaseFailed).Result(), + baseBuilder("FailedValidation").Phase(velerov1api.BackupPhaseFailedValidation).Result(), + baseBuilder("InProgress").Phase(velerov1api.BackupPhaseInProgress).Result(), + baseBuilder("New").Phase(velerov1api.BackupPhaseNew).Result(), }, expectedDeletes: sets.NewString("backupA"), }, @@ -536,9 +536,9 @@ func TestDeleteOrphanedBackups(t *testing.T) { namespace: "ns-1", cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), k8sBackups: []*velerov1api.Backup{ - defaultBackup().Namespace("ns-1").Name("backup-1").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseFailed).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-2").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseFailedValidation).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-3").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseInProgress).Backup(), + baseBuilder("backup-1").Phase(velerov1api.BackupPhaseFailed).Result(), + baseBuilder("backup-2").Phase(velerov1api.BackupPhaseFailedValidation).Result(), + baseBuilder("backup-3").Phase(velerov1api.BackupPhaseInProgress).Result(), }, expectedDeletes: sets.NewString(), }, @@ -547,13 +547,13 @@ func TestDeleteOrphanedBackups(t *testing.T) { namespace: "ns-1", cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), k8sBackups: []*velerov1api.Backup{ - defaultBackup().Namespace("ns-1").Name("backup-1").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-2").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-C").Labels(velerov1api.StorageLocationLabel, "default").Phase(velerov1api.BackupPhaseCompleted).Backup(), + baseBuilder("backup-1").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-2").Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-C").Phase(velerov1api.BackupPhaseCompleted).Result(), - defaultBackup().Namespace("ns-1").Name("backup-4").Labels(velerov1api.StorageLocationLabel, "alternate").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-5").Labels(velerov1api.StorageLocationLabel, "alternate").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-6").Labels(velerov1api.StorageLocationLabel, "alternate").Phase(velerov1api.BackupPhaseCompleted).Backup(), + baseBuilder("backup-4").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-5").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(), + baseBuilder("backup-6").ObjectMeta(builder.WithLabels(velerov1api.StorageLocationLabel, "alternate")).Phase(velerov1api.BackupPhaseCompleted).Result(), }, expectedDeletes: sets.NewString("backup-C"), }, @@ -628,12 +628,24 @@ func TestStorageLabelsInDeleteOrphanedBackups(t *testing.T) { namespace: "ns-1", cloudBackups: sets.NewString("backup-1", "backup-2", "backup-3"), k8sBackups: []*velerov1api.Backup{ - defaultBackup().Namespace("ns-1").Name("backup-1"). - Labels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-2"). - Labels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779").Phase(velerov1api.BackupPhaseCompleted).Backup(), - defaultBackup().Namespace("ns-1").Name("backup-C"). - Labels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779").Phase(velerov1api.BackupPhaseCompleted).Backup(), + builder.ForBackup("ns-1", "backup-1"). + ObjectMeta( + builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"), + ). + Phase(velerov1api.BackupPhaseCompleted). + Result(), + builder.ForBackup("ns-1", "backup-2"). + ObjectMeta( + builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"), + ). + Phase(velerov1api.BackupPhaseCompleted). + Result(), + builder.ForBackup("ns-1", "backup-C"). + ObjectMeta( + builder.WithLabels(velerov1api.StorageLocationLabel, "the-really-long-location-name-that-is-much-more-than-63-c69e779"), + ). + Phase(velerov1api.BackupPhaseCompleted). + Result(), }, expectedDeletes: sets.NewString("backup-C"), }, diff --git a/pkg/controller/download_request_controller_test.go b/pkg/controller/download_request_controller_test.go index cb04ee6ab..d1822cc6d 100644 --- a/pkg/controller/download_request_controller_test.go +++ b/pkg/controller/download_request_controller_test.go @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/util/clock" v1 "github.com/heptio/velero/pkg/apis/velero/v1" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" informers "github.com/heptio/velero/pkg/generated/informers/externalversions" "github.com/heptio/velero/pkg/persistence" @@ -119,6 +120,10 @@ func newBackupLocation(name, provider, bucket string) *v1.BackupStorageLocation } func TestProcessDownloadRequest(t *testing.T) { + defaultBackup := func() *v1.Backup { + return builder.ForBackup(v1.DefaultNamespace, "a-backup").StorageLocation("a-location").Result() + } + tests := []struct { name string key string @@ -145,94 +150,94 @@ func TestProcessDownloadRequest(t *testing.T) { { name: "backup contents request for nonexistent backup returns an error", downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"), - backup: defaultBackup().Name("non-matching-backup").StorageLocation("a-location").Backup(), + backup: builder.ForBackup(v1.DefaultNamespace, "non-matching-backup").StorageLocation("a-location").Result(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectedErr: "backup.velero.io \"a-backup\" not found", }, { name: "restore log request for nonexistent restore returns an error", downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), - restore: velerotest.NewTestRestore(v1.DefaultNamespace, "non-matching-restore", v1.RestorePhaseCompleted).WithBackup("a-backup").Restore, - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + restore: builder.ForRestore(v1.DefaultNamespace, "non-matching-restore").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), + backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectedErr: "error getting Restore: restore.velero.io \"a-backup-20170912150214\" not found", }, { name: "backup contents request for backup with nonexistent location returns an error", downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"), - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + backup: defaultBackup(), backupLocation: newBackupLocation("non-matching-location", "a-provider", "a-bucket"), expectedErr: "backupstoragelocation.velero.io \"a-location\" not found", }, { name: "backup contents request with phase '' gets a url", downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"), - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "backup contents request with phase 'New' gets a url", downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindBackupContents, "a-backup"), - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "backup log request with phase '' gets a url", downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupLog, "a-backup"), - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "backup log request with phase 'New' gets a url", downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindBackupLog, "a-backup"), - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "restore log request with phase '' gets a url", downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), - restore: velerotest.NewTestRestore(v1.DefaultNamespace, "a-backup-20170912150214", v1.RestorePhaseCompleted).WithBackup("a-backup").Restore, - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), + backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "restore log request with phase 'New' gets a url", downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"), - restore: velerotest.NewTestRestore(v1.DefaultNamespace, "a-backup-20170912150214", v1.RestorePhaseCompleted).WithBackup("a-backup").Restore, - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), + backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "restore results request with phase '' gets a url", downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreResults, "a-backup-20170912150214"), - restore: velerotest.NewTestRestore(v1.DefaultNamespace, "a-backup-20170912150214", v1.RestorePhaseCompleted).WithBackup("a-backup").Restore, - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), + backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "restore results request with phase 'New' gets a url", downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindRestoreResults, "a-backup-20170912150214"), - restore: velerotest.NewTestRestore(v1.DefaultNamespace, "a-backup-20170912150214", v1.RestorePhaseCompleted).WithBackup("a-backup").Restore, - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(), + backup: defaultBackup(), backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"), expectGetsURL: true, }, { name: "request with phase 'Processed' is not deleted if not expired", downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, v1.DownloadTargetKindBackupLog, "a-backup-20170912150214"), - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + backup: defaultBackup(), }, { name: "request with phase 'Processed' is deleted if expired", downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, v1.DownloadTargetKindBackupLog, "a-backup-20170912150214"), - backup: defaultBackup().Name("a-backup").StorageLocation("a-location").Backup(), + backup: defaultBackup(), expired: true, }, } diff --git a/pkg/controller/gc_controller_test.go b/pkg/controller/gc_controller_test.go index 7b61cfa64..8c0aedd46 100644 --- a/pkg/controller/gc_controller_test.go +++ b/pkg/controller/gc_controller_test.go @@ -33,6 +33,7 @@ import ( core "k8s.io/client-go/testing" api "github.com/heptio/velero/pkg/apis/velero/v1" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" informers "github.com/heptio/velero/pkg/generated/informers/externalversions" "github.com/heptio/velero/pkg/util/kube" @@ -66,7 +67,7 @@ func TestGCControllerEnqueueAllBackups(t *testing.T) { var expected []string for i := 0; i < 3; i++ { - backup := defaultBackup().Name(fmt.Sprintf("backup-%d", i)).Backup() + backup := builder.ForBackup(api.DefaultNamespace, fmt.Sprintf("backup-%d", i)).Result() sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(backup) expected = append(expected, kube.NamespaceAndName(backup)) } @@ -97,7 +98,7 @@ Loop: } func TestGCControllerHasUpdateFunc(t *testing.T) { - backup := defaultBackup().Backup() + backup := defaultBackup().Result() expected := kube.NamespaceAndName(backup) client := fake.NewSimpleClientset(backup) @@ -151,7 +152,7 @@ func TestGCControllerHasUpdateFunc(t *testing.T) { func TestGCControllerProcessQueueItem(t *testing.T) { fakeClock := clock.NewFakeClock(time.Now()) - defaultBackupLocation := velerotest.NewTestBackupStorageLocation().WithName("default").BackupStorageLocation + defaultBackupLocation := builder.ForBackupStorageLocation("velero", "default").Result() tests := []struct { name string @@ -167,31 +168,31 @@ func TestGCControllerProcessQueueItem(t *testing.T) { }, { name: "unexpired backup is not deleted", - backup: defaultBackup().Expiration(fakeClock.Now().Add(time.Minute)).StorageLocation("default").Backup(), + 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").Backup(), - backupLocation: velerotest.NewTestBackupStorageLocation().WithName("read-only").WithAccessMode(api.BackupStorageLocationAccessModeReadOnly).BackupStorageLocation, + backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-only").Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(api.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").Backup(), - backupLocation: velerotest.NewTestBackupStorageLocation().WithName("read-write").WithAccessMode(api.BackupStorageLocationAccessModeReadWrite).BackupStorageLocation, + backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-write").Result(), + backupLocation: builder.ForBackupStorageLocation("velero", "read-write").AccessMode(api.BackupStorageLocationAccessModeReadWrite).Result(), expectDeletion: true, }, { name: "expired backup with no pending deletion requests is deleted", - backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Backup(), + 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", - backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Backup(), + backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(), backupLocation: defaultBackupLocation, deleteBackupRequests: []*api.DeleteBackupRequest{ { @@ -212,7 +213,7 @@ func TestGCControllerProcessQueueItem(t *testing.T) { }, { name: "expired backup with only processed deletion requests is deleted", - backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Backup(), + backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(), backupLocation: defaultBackupLocation, deleteBackupRequests: []*api.DeleteBackupRequest{ { @@ -233,7 +234,7 @@ func TestGCControllerProcessQueueItem(t *testing.T) { }, { name: "create DeleteBackupRequest error returns an error", - backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Backup(), + backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(), backupLocation: defaultBackupLocation, expectDeletion: true, createDeleteBackupRequestError: true, diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index 743a8204c..85fa9d1d4 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -36,7 +36,7 @@ import ( "k8s.io/client-go/tools/cache" api "github.com/heptio/velero/pkg/apis/velero/v1" - velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" informers "github.com/heptio/velero/pkg/generated/informers/externalversions" listers "github.com/heptio/velero/pkg/generated/listers/velero/v1" @@ -66,17 +66,17 @@ func TestFetchBackupInfo(t *testing.T) { { name: "lister has backup", backupName: "backup-1", - informerLocations: []*api.BackupStorageLocation{velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation}, - informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Backup()}, - expectedRes: defaultBackup().StorageLocation("default").Backup(), + informerLocations: []*api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").ObjectStorage("bucket").Result()}, + informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()}, + expectedRes: defaultBackup().StorageLocation("default").Result(), }, { name: "lister does not have a backup, but backupSvc does", backupName: "backup-1", - backupStoreBackup: defaultBackup().StorageLocation("default").Backup(), - informerLocations: []*api.BackupStorageLocation{velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation}, - informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Backup()}, - expectedRes: defaultBackup().StorageLocation("default").Backup(), + backupStoreBackup: defaultBackup().StorageLocation("default").Result(), + informerLocations: []*api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").ObjectStorage("bucket").Result()}, + informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()}, + expectedRes: defaultBackup().StorageLocation("default").Result(), }, { name: "no backup", @@ -173,17 +173,17 @@ func TestProcessQueueItemSkips(t *testing.T) { { name: "restore with phase InProgress does not get processed", restoreKey: "foo/bar", - restore: velerotest.NewTestRestore("foo", "bar", api.RestorePhaseInProgress).Restore, + restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseInProgress).Result(), }, { name: "restore with phase Completed does not get processed", restoreKey: "foo/bar", - restore: velerotest.NewTestRestore("foo", "bar", api.RestorePhaseCompleted).Restore, + restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseCompleted).Result(), }, { name: "restore with phase FailedValidation does not get processed", restoreKey: "foo/bar", - restore: velerotest.NewTestRestore("foo", "bar", api.RestorePhaseFailedValidation).Restore, + restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseFailedValidation).Result(), }, } @@ -227,6 +227,8 @@ func TestProcessQueueItemSkips(t *testing.T) { } func TestProcessQueueItem(t *testing.T) { + defaultStorageLocation := builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").ObjectStorage("bucket").Result() + tests := []struct { name string restoreKey string @@ -246,48 +248,48 @@ func TestProcessQueueItem(t *testing.T) { }{ { name: "restore with both namespace in both includedNamespaces and excludedNamespaces fails validation", - location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "another-1", "*", api.RestorePhaseNew).WithExcludedNamespace("another-1").Restore, - backup: defaultBackup().StorageLocation("default").Backup(), + location: defaultStorageLocation, + restore: NewRestore("foo", "bar", "backup-1", "another-1", "*", api.RestorePhaseNew).ExcludedNamespaces("another-1").Result(), + backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Invalid included/excluded namespace lists: excludes list cannot contain an item in the includes list: another-1"}, }, { name: "restore with resource in both includedResources and excludedResources fails validation", - location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "*", "a-resource", api.RestorePhaseNew).WithExcludedResource("a-resource").Restore, - backup: defaultBackup().StorageLocation("default").Backup(), + location: defaultStorageLocation, + restore: NewRestore("foo", "bar", "backup-1", "*", "a-resource", api.RestorePhaseNew).ExcludedResources("a-resource").Result(), + backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: a-resource"}, }, { name: "new restore with empty backup and schedule names fails validation", - restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).Restore, + restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Either a backup or schedule must be specified as a source for the restore, but not both"}, }, { name: "new restore with backup and schedule names provided fails validation", - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).WithSchedule("sched-1").Restore, + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Schedule("sched-1").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Either a backup or schedule must be specified as a source for the restore, but not both"}, }, { name: "valid restore with schedule name gets executed", - location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation, - restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).WithSchedule("sched-1").Restore, - backup: defaultBackup().StorageLocation("default").Labels(velerov1api.ScheduleNameLabel, "sched-1").Phase(api.BackupPhaseCompleted).Backup(), + location: defaultStorageLocation, + restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).Schedule("sched-1").Result(), + backup: defaultBackup().StorageLocation("default").ObjectMeta(builder.WithLabels(api.ScheduleNameLabel, "sched-1")).Phase(api.BackupPhaseCompleted).Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseInProgress), - expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).WithSchedule("sched-1").Restore, + expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Schedule("sched-1").Result(), }, { name: "restore with non-existent backup name fails", - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "*", api.RestorePhaseNew).Restore, + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "*", api.RestorePhaseNew).Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{"Error retrieving backup: backup.velero.io \"backup-1\" not found"}, @@ -295,30 +297,30 @@ func TestProcessQueueItem(t *testing.T) { }, { name: "restorer throwing an error causes the restore to fail", - location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Restore, - backup: defaultBackup().StorageLocation("default").Backup(), + location: defaultStorageLocation, + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(), + backup: defaultBackup().StorageLocation("default").Result(), restorerError: errors.New("blarg"), expectedErr: false, expectedPhase: string(api.RestorePhaseInProgress), expectedFinalPhase: string(api.RestorePhasePartiallyFailed), expectedRestoreErrors: 1, - expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Restore, + expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Result(), }, { name: "valid restore gets executed", - location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Restore, - backup: defaultBackup().StorageLocation("default").Backup(), + location: defaultStorageLocation, + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(), + backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseInProgress), - expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Restore, + expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Result(), }, { name: "restoration of nodes is not supported", - location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "nodes", api.RestorePhaseNew).Restore, - backup: defaultBackup().StorageLocation("default").Backup(), + location: defaultStorageLocation, + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "nodes", api.RestorePhaseNew).Result(), + backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{ @@ -328,9 +330,9 @@ func TestProcessQueueItem(t *testing.T) { }, { name: "restoration of events is not supported", - location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events", api.RestorePhaseNew).Restore, - backup: defaultBackup().StorageLocation("default").Backup(), + location: defaultStorageLocation, + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events", api.RestorePhaseNew).Result(), + backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{ @@ -340,9 +342,9 @@ func TestProcessQueueItem(t *testing.T) { }, { name: "restoration of events.events.k8s.io is not supported", - location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events.events.k8s.io", api.RestorePhaseNew).Restore, - backup: defaultBackup().StorageLocation("default").Backup(), + location: defaultStorageLocation, + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events.events.k8s.io", api.RestorePhaseNew).Result(), + backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{ @@ -352,9 +354,9 @@ func TestProcessQueueItem(t *testing.T) { }, { name: "restoration of backups.velero.io is not supported", - location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "backups.velero.io", api.RestorePhaseNew).Restore, - backup: defaultBackup().StorageLocation("default").Backup(), + location: defaultStorageLocation, + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "backups.velero.io", api.RestorePhaseNew).Result(), + backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{ @@ -364,9 +366,9 @@ func TestProcessQueueItem(t *testing.T) { }, { name: "restoration of restores.velero.io is not supported", - location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation, - restore: NewRestore("foo", "bar", "backup-1", "ns-1", "restores.velero.io", api.RestorePhaseNew).Restore, - backup: defaultBackup().StorageLocation("default").Backup(), + location: defaultStorageLocation, + restore: NewRestore("foo", "bar", "backup-1", "ns-1", "restores.velero.io", api.RestorePhaseNew).Result(), + backup: defaultBackup().StorageLocation("default").Result(), expectedErr: false, expectedPhase: string(api.RestorePhaseFailedValidation), expectedValidationErrors: []string{ @@ -376,12 +378,12 @@ func TestProcessQueueItem(t *testing.T) { }, { name: "backup download error results in failed restore", - location: velerotest.NewTestBackupStorageLocation().WithName("default").WithProvider("myCloud").WithObjectStorage("bucket").BackupStorageLocation, - restore: NewRestore(api.DefaultNamespace, "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Restore, + location: defaultStorageLocation, + restore: NewRestore(api.DefaultNamespace, "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(), expectedPhase: string(api.RestorePhaseInProgress), expectedFinalPhase: string(api.RestorePhaseFailed), backupStoreGetBackupContentsErr: errors.New("Couldn't download backup"), - backup: defaultBackup().StorageLocation("default").Backup(), + backup: defaultBackup().StorageLocation("default").Result(), }, } @@ -656,9 +658,9 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { // no backups created from the schedule: fail validation require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add( defaultBackup(). - Labels(velerov1api.ScheduleNameLabel, "non-matching-schedule"). - Phase(velerov1api.BackupPhaseCompleted). - Backup(), + ObjectMeta(builder.WithLabels(api.ScheduleNameLabel, "non-matching-schedule")). + Phase(api.BackupPhaseCompleted). + Result(), )) errs := c.validateAndComplete(restore, pluginManager) @@ -668,10 +670,12 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { // no completed backups created from the schedule: fail validation require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add( defaultBackup(). - Name("backup-2"). - Labels(velerov1api.ScheduleNameLabel, "schedule-1"). - Phase(velerov1api.BackupPhaseInProgress). - Backup(), + ObjectMeta( + builder.WithName("backup-2"), + builder.WithLabels(api.ScheduleNameLabel, "schedule-1"), + ). + Phase(api.BackupPhaseInProgress). + Result(), )) errs = c.validateAndComplete(restore, pluginManager) @@ -683,19 +687,23 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) { require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add( defaultBackup(). - Name("foo"). - Labels(velerov1api.ScheduleNameLabel, "schedule-1"). - Phase(velerov1api.BackupPhaseCompleted). + ObjectMeta( + builder.WithName("foo"), + builder.WithLabels(api.ScheduleNameLabel, "schedule-1"), + ). + Phase(api.BackupPhaseCompleted). StartTimestamp(now). - Backup(), + Result(), )) require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add( defaultBackup(). - Name("foo"). - Labels(velerov1api.ScheduleNameLabel, "schedule-1"). - Phase(velerov1api.BackupPhaseCompleted). + ObjectMeta( + builder.WithName("foo"), + builder.WithLabels(api.ScheduleNameLabel, "schedule-1"), + ). + Phase(api.BackupPhaseCompleted). StartTimestamp(now.Add(time.Second)). - Backup(), + Result(), )) errs = c.validateAndComplete(restore, pluginManager) @@ -792,20 +800,18 @@ func TestMostRecentCompletedBackup(t *testing.T) { assert.Equal(t, expected, mostRecentCompletedBackup(backups)) } -func NewRestore(ns, name, backup, includeNS, includeResource string, phase api.RestorePhase) *velerotest.TestRestore { - restore := velerotest.NewTestRestore(ns, name, phase).WithBackup(backup) +func NewRestore(ns, name, backup, includeNS, includeResource string, phase api.RestorePhase) *builder.RestoreBuilder { + restore := builder.ForRestore(ns, name).Phase(phase).Backup(backup) if includeNS != "" { - restore = restore.WithIncludedNamespace(includeNS) + restore = restore.IncludedNamespaces(includeNS) } if includeResource != "" { - restore = restore.WithIncludedResource(includeResource) + restore = restore.IncludedResources(includeResource) } - for _, n := range nonRestorableResources { - restore = restore.WithExcludedResource(n) - } + restore.ExcludedResources(nonRestorableResources...) return restore } diff --git a/pkg/controller/schedule_controller_test.go b/pkg/controller/schedule_controller_test.go index 39ce80d0b..9fc33de88 100644 --- a/pkg/controller/schedule_controller_test.go +++ b/pkg/controller/schedule_controller_test.go @@ -31,8 +31,8 @@ import ( core "k8s.io/client-go/testing" "k8s.io/client-go/tools/cache" - api "github.com/heptio/velero/pkg/apis/velero/v1" velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" informers "github.com/heptio/velero/pkg/generated/informers/externalversions" "github.com/heptio/velero/pkg/metrics" @@ -40,15 +40,19 @@ import ( ) func TestProcessSchedule(t *testing.T) { + newScheduleBuilder := func(phase velerov1api.SchedulePhase) *builder.ScheduleBuilder { + return builder.ForSchedule("ns", "name").Phase(phase) + } + tests := []struct { name string scheduleKey string - schedule *api.Schedule + schedule *velerov1api.Schedule fakeClockTime string expectedErr bool expectedPhase string expectedValidationErrors []string - expectedBackupCreate *api.Backup + expectedBackupCreate *velerov1api.Backup expectedLastBackup string }{ { @@ -63,54 +67,53 @@ func TestProcessSchedule(t *testing.T) { }, { name: "schedule with phase FailedValidation does not get processed", - schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseFailedValidation).Schedule, + schedule: newScheduleBuilder(velerov1api.SchedulePhaseFailedValidation).Result(), expectedErr: false, }, { name: "schedule with phase New gets validated and failed if invalid", - schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseNew).Schedule, + schedule: newScheduleBuilder(velerov1api.SchedulePhaseNew).Result(), expectedErr: false, - expectedPhase: string(api.SchedulePhaseFailedValidation), + expectedPhase: string(velerov1api.SchedulePhaseFailedValidation), expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"}, }, { name: "schedule with phase gets validated and failed if invalid", - schedule: velerotest.NewTestSchedule("ns", "name").Schedule, + schedule: newScheduleBuilder(velerov1api.SchedulePhase("")).Result(), expectedErr: false, - expectedPhase: string(api.SchedulePhaseFailedValidation), + expectedPhase: string(velerov1api.SchedulePhaseFailedValidation), expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"}, }, { name: "schedule with phase Enabled gets re-validated and failed if invalid", - schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseEnabled).Schedule, + schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).Result(), expectedErr: false, - expectedPhase: string(api.SchedulePhaseFailedValidation), + expectedPhase: string(velerov1api.SchedulePhaseFailedValidation), expectedValidationErrors: []string{"Schedule must be a non-empty valid Cron expression"}, }, { name: "schedule with phase New gets validated and triggers a backup", - schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseNew).WithCronSchedule("@every 5m").Schedule, + schedule: newScheduleBuilder(velerov1api.SchedulePhaseNew).CronSchedule("@every 5m").Result(), fakeClockTime: "2017-01-01 12:00:00", expectedErr: false, - expectedPhase: string(api.SchedulePhaseEnabled), - expectedBackupCreate: defaultBackup().Namespace("ns").Name("name-20170101120000").Labels(velerov1api.ScheduleNameLabel, "name").NoTypeMeta().Backup(), + expectedPhase: string(velerov1api.SchedulePhaseEnabled), + expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).NoTypeMeta().Result(), expectedLastBackup: "2017-01-01 12:00:00", }, { name: "schedule with phase Enabled gets re-validated and triggers a backup if valid", - schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseEnabled).WithCronSchedule("@every 5m").Schedule, + schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).CronSchedule("@every 5m").Result(), fakeClockTime: "2017-01-01 12:00:00", expectedErr: false, - expectedBackupCreate: defaultBackup().Namespace("ns").Name("name-20170101120000").Labels(velerov1api.ScheduleNameLabel, "name").NoTypeMeta().Backup(), + expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).NoTypeMeta().Result(), expectedLastBackup: "2017-01-01 12:00:00", }, { - name: "schedule that's already run gets LastBackup updated", - schedule: velerotest.NewTestSchedule("ns", "name").WithPhase(api.SchedulePhaseEnabled). - WithCronSchedule("@every 5m").WithLastBackupTime("2000-01-01 00:00:00").Schedule, + name: "schedule that's already run gets LastBackup updated", + schedule: newScheduleBuilder(velerov1api.SchedulePhaseEnabled).CronSchedule("@every 5m").LastBackupTime("2000-01-01 00:00:00").Result(), fakeClockTime: "2017-01-01 12:00:00", expectedErr: false, - expectedBackupCreate: defaultBackup().Namespace("ns").Name("name-20170101120000").Labels(velerov1api.ScheduleNameLabel, "name").NoTypeMeta().Backup(), + expectedBackupCreate: builder.ForBackup("ns", "name-20170101120000").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "name")).NoTypeMeta().Result(), expectedLastBackup: "2017-01-01 12:00:00", }, } @@ -161,7 +164,7 @@ func TestProcessSchedule(t *testing.T) { // these are the fields that may be updated by the controller phase, found, err := unstructured.NestedString(patchMap, "status", "phase") if err == nil && found { - res.Status.Phase = api.SchedulePhase(phase) + res.Status.Phase = velerov1api.SchedulePhase(phase) } lastBackupStr, found, err := unstructured.NestedString(patchMap, "status", "lastBackup") @@ -192,9 +195,9 @@ func TestProcessSchedule(t *testing.T) { index := 0 type PatchStatus struct { - ValidationErrors []string `json:"validationErrors"` - Phase api.SchedulePhase `json:"phase"` - LastBackup time.Time `json:"lastBackup"` + ValidationErrors []string `json:"validationErrors"` + Phase velerov1api.SchedulePhase `json:"phase"` + LastBackup time.Time `json:"lastBackup"` } type Patch struct { @@ -214,7 +217,7 @@ func TestProcessSchedule(t *testing.T) { expected := Patch{ Status: PatchStatus{ ValidationErrors: test.expectedValidationErrors, - Phase: api.SchedulePhase(test.expectedPhase), + Phase: velerov1api.SchedulePhase(test.expectedPhase), }, } @@ -227,7 +230,7 @@ func TestProcessSchedule(t *testing.T) { require.True(t, len(actions) > index, "len(actions) is too small") action := core.NewCreateAction( - api.SchemeGroupVersion.WithResource("backups"), + velerov1api.SchemeGroupVersion.WithResource("backups"), created.Namespace, created) @@ -257,43 +260,47 @@ func parseTime(timeString string) time.Time { } func TestGetNextRunTime(t *testing.T) { + defaultSchedule := func() *velerov1api.Schedule { + return builder.ForSchedule("velero", "schedule-1").CronSchedule("@every 5m").Result() + } + tests := []struct { name string - schedule *api.Schedule + schedule *velerov1api.Schedule lastRanOffset string expectedDue bool expectedNextRunTimeOffset string }{ { name: "first run", - schedule: &api.Schedule{Spec: api.ScheduleSpec{Schedule: "@every 5m"}}, + schedule: defaultSchedule(), expectedDue: true, expectedNextRunTimeOffset: "5m", }, { name: "just ran", - schedule: &api.Schedule{Spec: api.ScheduleSpec{Schedule: "@every 5m"}}, + schedule: defaultSchedule(), lastRanOffset: "0s", expectedDue: false, expectedNextRunTimeOffset: "5m", }, { name: "almost but not quite time to run", - schedule: &api.Schedule{Spec: api.ScheduleSpec{Schedule: "@every 5m"}}, + schedule: defaultSchedule(), lastRanOffset: "4m59s", expectedDue: false, expectedNextRunTimeOffset: "5m", }, { name: "time to run again", - schedule: &api.Schedule{Spec: api.ScheduleSpec{Schedule: "@every 5m"}}, + schedule: defaultSchedule(), lastRanOffset: "5m", expectedDue: true, expectedNextRunTimeOffset: "5m", }, { name: "several runs missed", - schedule: &api.Schedule{Spec: api.ScheduleSpec{Schedule: "@every 5m"}}, + schedule: defaultSchedule(), lastRanOffset: "5h", expectedDue: true, expectedNextRunTimeOffset: "5m", @@ -339,14 +346,7 @@ func TestParseCronSchedule(t *testing.T) { // Start with a Schedule with: // - schedule: once a day at 9am // - last backup: 2017-08-10 12:27:00 (just happened) - s := &api.Schedule{ - Spec: api.ScheduleSpec{ - Schedule: "0 9 * * *", - }, - Status: api.ScheduleStatus{ - LastBackup: metav1.NewTime(now), - }, - } + s := builder.ForSchedule("velero", "schedule-1").CronSchedule("0 9 * * *").LastBackupTime(now.Format("2006-01-02 15:04:05")).Result() logger := velerotest.NewLogger() @@ -383,121 +383,51 @@ func TestParseCronSchedule(t *testing.T) { func TestGetBackup(t *testing.T) { tests := []struct { name string - schedule *api.Schedule + schedule *velerov1api.Schedule testClockTime string - expectedBackup *api.Backup + expectedBackup *velerov1api.Backup }{ { - name: "ensure name is formatted correctly (AM time)", - schedule: &api.Schedule{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "foo", - Name: "bar", - }, - Spec: api.ScheduleSpec{ - Template: api.BackupSpec{}, - }, - }, - testClockTime: "2017-07-25 09:15:00", - expectedBackup: &api.Backup{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "foo", - Name: "bar-20170725091500", - Labels: map[string]string{ - velerov1api.ScheduleNameLabel: "bar", - }, - }, - Spec: api.BackupSpec{}, - }, + name: "ensure name is formatted correctly (AM time)", + schedule: builder.ForSchedule("foo", "bar").Result(), + testClockTime: "2017-07-25 09:15:00", + expectedBackup: builder.ForBackup("foo", "bar-20170725091500").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar")).Result(), }, { - name: "ensure name is formatted correctly (PM time)", - schedule: &api.Schedule{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "foo", - Name: "bar", - }, - Spec: api.ScheduleSpec{ - Template: api.BackupSpec{}, - }, - }, - testClockTime: "2017-07-25 14:15:00", - expectedBackup: &api.Backup{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "foo", - Name: "bar-20170725141500", - Labels: map[string]string{ - velerov1api.ScheduleNameLabel: "bar", - }, - }, - Spec: api.BackupSpec{}, - }, + name: "ensure name is formatted correctly (PM time)", + schedule: builder.ForSchedule("foo", "bar").Result(), + testClockTime: "2017-07-25 14:15:00", + expectedBackup: builder.ForBackup("foo", "bar-20170725141500").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar")).Result(), }, { name: "ensure schedule backup template is copied", - schedule: &api.Schedule{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "foo", - Name: "bar", - }, - Spec: api.ScheduleSpec{ - Template: api.BackupSpec{ - IncludedNamespaces: []string{"ns-1", "ns-2"}, - ExcludedNamespaces: []string{"ns-3"}, - IncludedResources: []string{"foo", "bar"}, - ExcludedResources: []string{"baz"}, - LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"label": "value"}}, - TTL: metav1.Duration{Duration: time.Duration(300)}, - }, - }, - }, + schedule: builder.ForSchedule("foo", "bar"). + Template(builder.ForBackup("", ""). + IncludedNamespaces("ns-1", "ns-2"). + ExcludedNamespaces("ns-3"). + IncludedResources("foo", "bar"). + ExcludedResources("baz"). + LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"label": "value"}}). + TTL(time.Duration(300)). + Result(). + Spec). + Result(), testClockTime: "2017-07-25 09:15:00", - expectedBackup: &api.Backup{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "foo", - Name: "bar-20170725091500", - Labels: map[string]string{ - velerov1api.ScheduleNameLabel: "bar", - }, - }, - Spec: api.BackupSpec{ - IncludedNamespaces: []string{"ns-1", "ns-2"}, - ExcludedNamespaces: []string{"ns-3"}, - IncludedResources: []string{"foo", "bar"}, - ExcludedResources: []string{"baz"}, - LabelSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"label": "value"}}, - TTL: metav1.Duration{Duration: time.Duration(300)}, - }, - }, + expectedBackup: builder.ForBackup("foo", "bar-20170725091500"). + ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "bar")). + IncludedNamespaces("ns-1", "ns-2"). + ExcludedNamespaces("ns-3"). + IncludedResources("foo", "bar"). + ExcludedResources("baz"). + LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"label": "value"}}). + TTL(time.Duration(300)). + Result(), }, { - name: "ensure schedule labels is copied", - schedule: &api.Schedule{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "foo", - Name: "bar", - Labels: map[string]string{ - "foo": "bar", - "bar": "baz", - }, - }, - Spec: api.ScheduleSpec{ - Template: api.BackupSpec{}, - }, - }, - testClockTime: "2017-07-25 14:15:00", - expectedBackup: &api.Backup{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "foo", - Name: "bar-20170725141500", - Labels: map[string]string{ - velerov1api.ScheduleNameLabel: "bar", - "bar": "baz", - "foo": "bar", - }, - }, - Spec: api.BackupSpec{}, - }, + name: "ensure schedule labels is 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(), }, } diff --git a/pkg/restore/builder.go b/pkg/restore/builder.go deleted file mode 100644 index 474e04484..000000000 --- a/pkg/restore/builder.go +++ /dev/null @@ -1,114 +0,0 @@ -/* -Copyright 2019 the Velero 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 restore - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" -) - -// Builder is a helper for concisely constructing Restore API objects. -type Builder struct { - restore velerov1api.Restore -} - -// NewBuilder returns a Builder for a Restore with no namespace/name. -func NewBuilder() *Builder { - return NewNamedBuilder("", "") -} - -// NewNamedBuilder returns a Builder for a Restore with the specified namespace -// and name. -func NewNamedBuilder(namespace, name string) *Builder { - return &Builder{ - restore: velerov1api.Restore{ - TypeMeta: metav1.TypeMeta{ - APIVersion: velerov1api.SchemeGroupVersion.String(), - Kind: "Restore", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - }, - } -} - -// Restore returns the built Restore API object. -func (b *Builder) Restore() *velerov1api.Restore { - return &b.restore -} - -// Backup sets the Restore's backup name. -func (b *Builder) Backup(name string) *Builder { - b.restore.Spec.BackupName = name - return b -} - -// IncludedNamespaces sets the Restore's included namespaces. -func (b *Builder) IncludedNamespaces(namespaces ...string) *Builder { - b.restore.Spec.IncludedNamespaces = namespaces - return b -} - -// ExcludedNamespaces sets the Restore's excluded namespaces. -func (b *Builder) ExcludedNamespaces(namespaces ...string) *Builder { - b.restore.Spec.ExcludedNamespaces = namespaces - return b -} - -// IncludedResources sets the Restore's included resources. -func (b *Builder) IncludedResources(resources ...string) *Builder { - b.restore.Spec.IncludedResources = resources - return b -} - -// ExcludedResources sets the Restore's excluded resources. -func (b *Builder) ExcludedResources(resources ...string) *Builder { - b.restore.Spec.ExcludedResources = resources - return b -} - -// IncludeClusterResources sets the Restore's "include cluster resources" flag. -func (b *Builder) IncludeClusterResources(val bool) *Builder { - b.restore.Spec.IncludeClusterResources = &val - return b -} - -// LabelSelector sets the Restore's label selector. -func (b *Builder) LabelSelector(selector *metav1.LabelSelector) *Builder { - b.restore.Spec.LabelSelector = selector - return b -} - -// NamespaceMappings sets the Restore's namespace mappings. -func (b *Builder) NamespaceMappings(mapping ...string) *Builder { - if b.restore.Spec.NamespaceMapping == nil { - b.restore.Spec.NamespaceMapping = make(map[string]string) - } - - if len(mapping)%2 != 0 { - panic("mapping must contain an even number of values") - } - - for i := 0; i < len(mapping); i += 2 { - b.restore.Spec.NamespaceMapping[mapping[i]] = mapping[i+1] - } - - return b -} diff --git a/pkg/restore/change_storageclass_action_test.go b/pkg/restore/change_storageclass_action_test.go index 9075dccd7..4a363e88e 100644 --- a/pkg/restore/change_storageclass_action_test.go +++ b/pkg/restore/change_storageclass_action_test.go @@ -29,8 +29,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/fake" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/plugin/velero" - "github.com/heptio/velero/pkg/test" ) // TestChangeStorageClassActionExecute runs the ChangeStorageClassAction's Execute @@ -48,93 +48,93 @@ func TestChangeStorageClassActionExecute(t *testing.T) { }{ { name: "a valid mapping for a persistent volume is applied correctly", - pvOrPVC: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")), - configMap: test.NewConfigMap("velero", "change-storage-class", - test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"), - test.WithConfigMapData("storageclass-1", "storageclass-2"), - ), - storageClass: test.NewStorageClass("storageclass-2"), - want: test.NewPV("pv-1", test.WithStorageClassName("storageclass-2")), + pvOrPVC: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), + configMap: builder.ForConfigMap("velero", "change-storage-classs"). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + Data("storageclass-1", "storageclass-2"). + Result(), + storageClass: builder.ForStorageClass("storageclass-2").Result(), + want: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-2").Result(), }, { name: "a valid mapping for a persistent volume claim is applied correctly", - pvOrPVC: test.NewPVC("velero", "pvc-1", test.WithStorageClassName("storageclass-1")), - configMap: test.NewConfigMap("velero", "change-storage-class", - test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"), - test.WithConfigMapData("storageclass-1", "storageclass-2"), - ), - storageClass: test.NewStorageClass("storageclass-2"), - want: test.NewPVC("velero", "pvc-1", test.WithStorageClassName("storageclass-2")), + pvOrPVC: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(), + configMap: builder.ForConfigMap("velero", "change-storage-classs"). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + Data("storageclass-1", "storageclass-2"). + Result(), + storageClass: builder.ForStorageClass("storageclass-2").Result(), + want: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-2").Result(), }, { name: "when no config map exists for the plugin, the item is returned as-is", - pvOrPVC: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")), - configMap: test.NewConfigMap("velero", "change-storage-class", - test.WithLabels("velero.io/plugin-config", "true", "velero.io/some-other-plugin", "RestoreItemAction"), - test.WithConfigMapData("storageclass-1", "storageclass-2"), - ), - want: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")), + pvOrPVC: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), + configMap: builder.ForConfigMap("velero", "change-storage-classs"). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/some-other-plugin", "RestoreItemAction")). + Data("storageclass-1", "storageclass-2"). + Result(), + want: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), }, { name: "when no storage class mappings exist in the plugin config map, the item is returned as-is", - pvOrPVC: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")), - configMap: test.NewConfigMap("velero", "change-storage-class", - test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"), - ), - want: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")), + pvOrPVC: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), + configMap: builder.ForConfigMap("velero", "change-storage-classs"). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + Result(), + want: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), }, { name: "when persistent volume has no storage class, the item is returned as-is", - pvOrPVC: test.NewPV("pv-1"), - configMap: test.NewConfigMap("velero", "change-storage-class", - test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"), - test.WithConfigMapData("storageclass-1", "storageclass-2"), - ), - want: test.NewPV("pv-1"), + pvOrPVC: builder.ForPersistentVolume("pv-1").Result(), + configMap: builder.ForConfigMap("velero", "change-storage-classs"). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + Data("storageclass-1", "storageclass-2"). + Result(), + want: builder.ForPersistentVolume("pv-1").Result(), }, { name: "when persistent volume claim has no storage class, the item is returned as-is", - pvOrPVC: test.NewPVC("velero", "pvc-1"), - configMap: test.NewConfigMap("velero", "change-storage-class", - test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"), - test.WithConfigMapData("storageclass-1", "storageclass-2"), - ), - want: test.NewPVC("velero", "pvc-1"), + pvOrPVC: builder.ForPersistentVolumeClaim("velero", "pvc-1").Result(), + configMap: builder.ForConfigMap("velero", "change-storage-classs"). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + Data("storageclass-1", "storageclass-2"). + Result(), + want: builder.ForPersistentVolumeClaim("velero", "pvc-1").Result(), }, { name: "when persistent volume's storage class has no mapping in the config map, the item is returned as-is", - pvOrPVC: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")), - configMap: test.NewConfigMap("velero", "change-storage-class", - test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"), - test.WithConfigMapData("storageclass-3", "storageclass-4"), - ), - want: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")), + pvOrPVC: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), + configMap: builder.ForConfigMap("velero", "change-storage-classs"). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + Data("storageclass-3", "storageclass-4"). + Result(), + want: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), }, { name: "when persistent volume claim's storage class has no mapping in the config map, the item is returned as-is", - pvOrPVC: test.NewPVC("velero", "pvc-1", test.WithStorageClassName("storageclass-1")), - configMap: test.NewConfigMap("velero", "change-storage-class", - test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"), - test.WithConfigMapData("storageclass-3", "storageclass-4"), - ), - want: test.NewPVC("velero", "pvc-1", test.WithStorageClassName("storageclass-1")), + pvOrPVC: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(), + configMap: builder.ForConfigMap("velero", "change-storage-classs"). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + Data("storageclass-3", "storageclass-4"). + Result(), + want: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(), }, { name: "when persistent volume's storage class is mapped to a nonexistent storage class, an error is returned", - pvOrPVC: test.NewPV("pv-1", test.WithStorageClassName("storageclass-1")), - configMap: test.NewConfigMap("velero", "change-storage-class", - test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"), - test.WithConfigMapData("storageclass-1", "nonexistent-storage-class"), - ), + pvOrPVC: builder.ForPersistentVolume("pv-1").StorageClass("storageclass-1").Result(), + configMap: builder.ForConfigMap("velero", "change-storage-classs"). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + Data("storageclass-1", "nonexistent-storage-class"). + Result(), wantErr: errors.New("error getting storage class nonexistent-storage-class from API: storageclasses.storage.k8s.io \"nonexistent-storage-class\" not found"), }, { name: "when persistent volume claim's storage class is mapped to a nonexistent storage class, an error is returned", - pvOrPVC: test.NewPVC("velero", "pvc-1", test.WithStorageClassName("storageclass-1")), - configMap: test.NewConfigMap("velero", "change-storage-class", - test.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction"), - test.WithConfigMapData("storageclass-1", "nonexistent-storage-class"), - ), + pvOrPVC: builder.ForPersistentVolumeClaim("velero", "pvc-1").StorageClass("storageclass-1").Result(), + configMap: builder.ForConfigMap("velero", "change-storage-classs"). + ObjectMeta(builder.WithLabels("velero.io/plugin-config", "true", "velero.io/change-storage-class", "RestoreItemAction")). + Data("storageclass-1", "nonexistent-storage-class"). + Result(), wantErr: errors.New("error getting storage class nonexistent-storage-class from API: storageclasses.storage.k8s.io \"nonexistent-storage-class\" not found"), }, } diff --git a/pkg/restore/pv_restorer_test.go b/pkg/restore/pv_restorer_test.go index 6fb6cc006..d3b92658c 100644 --- a/pkg/restore/pv_restorer_test.go +++ b/pkg/restore/pv_restorer_test.go @@ -26,7 +26,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" api "github.com/heptio/velero/pkg/apis/velero/v1" - "github.com/heptio/velero/pkg/backup" + "github.com/heptio/velero/pkg/builder" cloudprovidermocks "github.com/heptio/velero/pkg/cloudprovider/mocks" "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" informers "github.com/heptio/velero/pkg/generated/informers/externalversions" @@ -35,8 +35,8 @@ import ( "github.com/heptio/velero/pkg/volume" ) -func defaultBackup() *backup.Builder { - return backup.NewNamedBackupBuilder(api.DefaultNamespace, "backup-1") +func defaultBackup() *builder.BackupBuilder { + return builder.ForBackup(api.DefaultNamespace, "backup-1") } func TestExecutePVAction_NoSnapshotRestores(t *testing.T) { @@ -53,46 +53,46 @@ func TestExecutePVAction_NoSnapshotRestores(t *testing.T) { { name: "no name should error", obj: NewTestUnstructured().WithMetadata().Unstructured, - restore: velerotest.NewDefaultTestRestore().Restore, + restore: builder.ForRestore(api.DefaultNamespace, "").Result(), expectedErr: true, }, { name: "no spec should error", obj: NewTestUnstructured().WithName("pv-1").Unstructured, - restore: velerotest.NewDefaultTestRestore().Restore, + restore: builder.ForRestore(api.DefaultNamespace, "").Result(), expectedErr: true, }, { name: "ensure spec.claimRef is deleted", obj: NewTestUnstructured().WithName("pv-1").WithAnnotations("a", "b").WithSpec("claimRef", "someOtherField").Unstructured, - restore: velerotest.NewDefaultTestRestore().WithRestorePVs(false).Restore, - backup: defaultBackup().Phase(api.BackupPhaseInProgress).Backup(), + restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(false).Result(), + backup: defaultBackup().Phase(api.BackupPhaseInProgress).Result(), expectedRes: NewTestUnstructured().WithAnnotations("a", "b").WithName("pv-1").WithSpec("someOtherField").Unstructured, }, { name: "ensure spec.storageClassName is retained", obj: NewTestUnstructured().WithName("pv-1").WithAnnotations("a", "b").WithSpec("storageClassName", "someOtherField").Unstructured, - restore: velerotest.NewDefaultTestRestore().WithRestorePVs(false).Restore, - backup: defaultBackup().Phase(api.BackupPhaseInProgress).Backup(), + restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(false).Result(), + backup: defaultBackup().Phase(api.BackupPhaseInProgress).Result(), expectedRes: NewTestUnstructured().WithAnnotations("a", "b").WithName("pv-1").WithSpec("storageClassName", "someOtherField").Unstructured, }, { name: "if backup.spec.snapshotVolumes is false, ignore restore.spec.restorePVs and return early", obj: NewTestUnstructured().WithName("pv-1").WithAnnotations("a", "b").WithSpec("claimRef", "storageClassName", "someOtherField").Unstructured, - restore: velerotest.NewDefaultTestRestore().WithRestorePVs(true).Restore, - backup: defaultBackup().Phase(api.BackupPhaseInProgress).SnapshotVolumes(false).Backup(), + restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(true).Result(), + backup: defaultBackup().Phase(api.BackupPhaseInProgress).SnapshotVolumes(false).Result(), expectedRes: NewTestUnstructured().WithName("pv-1").WithAnnotations("a", "b").WithSpec("storageClassName", "someOtherField").Unstructured, }, { name: "restore.spec.restorePVs=false, return early", obj: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured, - restore: velerotest.NewDefaultTestRestore().WithRestorePVs(false).Restore, - backup: defaultBackup().Phase(api.BackupPhaseInProgress).Backup(), + restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(false).Result(), + backup: defaultBackup().Phase(api.BackupPhaseInProgress).Result(), volumeSnapshots: []*volume.Snapshot{ newSnapshot("pv-1", "loc-1", "gp", "az-1", "snap-1", 1000), }, locations: []*api.VolumeSnapshotLocation{ - velerotest.NewTestVolumeSnapshotLocation().WithName("loc-1").VolumeSnapshotLocation, + builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-1").Result(), }, expectedErr: false, expectedRes: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured, @@ -100,11 +100,11 @@ func TestExecutePVAction_NoSnapshotRestores(t *testing.T) { { name: "volumeSnapshots is empty: return early", obj: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured, - restore: velerotest.NewDefaultTestRestore().WithRestorePVs(true).Restore, - backup: defaultBackup().Backup(), + restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(true).Result(), + backup: defaultBackup().Result(), locations: []*api.VolumeSnapshotLocation{ - velerotest.NewTestVolumeSnapshotLocation().WithName("loc-1").VolumeSnapshotLocation, - velerotest.NewTestVolumeSnapshotLocation().WithName("loc-2").VolumeSnapshotLocation, + builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-1").Result(), + builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-2").Result(), }, volumeSnapshots: []*volume.Snapshot{}, expectedRes: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured, @@ -112,11 +112,11 @@ func TestExecutePVAction_NoSnapshotRestores(t *testing.T) { { name: "volumeSnapshots doesn't have a snapshot for PV: return early", obj: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured, - restore: velerotest.NewDefaultTestRestore().WithRestorePVs(true).Restore, - backup: defaultBackup().Backup(), + restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(true).Result(), + backup: defaultBackup().Result(), locations: []*api.VolumeSnapshotLocation{ - velerotest.NewTestVolumeSnapshotLocation().WithName("loc-1").VolumeSnapshotLocation, - velerotest.NewTestVolumeSnapshotLocation().WithName("loc-2").VolumeSnapshotLocation, + builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-1").Result(), + builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-2").Result(), }, volumeSnapshots: []*volume.Snapshot{ newSnapshot("non-matching-pv-1", "loc-1", "type-1", "az-1", "snap-1", 1), @@ -178,11 +178,11 @@ func TestExecutePVAction_SnapshotRestores(t *testing.T) { { name: "backup with a matching volume.Snapshot for PV executes restore", obj: NewTestUnstructured().WithName("pv-1").WithSpec().Unstructured, - restore: velerotest.NewDefaultTestRestore().WithRestorePVs(true).Restore, - backup: defaultBackup().Backup(), + restore: builder.ForRestore(api.DefaultNamespace, "").RestorePVs(true).Result(), + backup: defaultBackup().Result(), locations: []*api.VolumeSnapshotLocation{ - velerotest.NewTestVolumeSnapshotLocation().WithName("loc-1").WithProvider("provider-1").VolumeSnapshotLocation, - velerotest.NewTestVolumeSnapshotLocation().WithName("loc-2").WithProvider("provider-2").VolumeSnapshotLocation, + builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-1").Provider("provider-1").Result(), + builder.ForVolumeSnapshotLocation(api.DefaultNamespace, "loc-2").Provider("provider-2").Result(), }, volumeSnapshots: []*volume.Snapshot{ newSnapshot("pv-1", "loc-1", "type-1", "az-1", "snap-1", 1), diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index 2d51a9a3b..c70cc7ec5 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -44,7 +44,7 @@ import ( kubetesting "k8s.io/client-go/testing" velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" - "github.com/heptio/velero/pkg/backup" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/client" "github.com/heptio/velero/pkg/discovery" velerov1informers "github.com/heptio/velero/pkg/generated/informers/externalversions" @@ -75,16 +75,16 @@ func TestRestoreResourceFiltering(t *testing.T) { }{ { name: "no filters restores everything", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -98,16 +98,16 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "included resources filter only restores resources of those types", - restore: defaultRestore().IncludedResources("pods").Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludedResources("pods").Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -120,16 +120,16 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "excluded resources filter only restores resources not of those types", - restore: defaultRestore().ExcludedResources("pvs").Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().ExcludedResources("pvs").Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -142,20 +142,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "included namespaces filter only restores resources in those namespaces", - restore: defaultRestore().IncludedNamespaces("ns-1").Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludedNamespaces("ns-1").Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -170,20 +170,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "excluded namespaces filter only restores resources not in those namespaces", - restore: defaultRestore().ExcludedNamespaces("ns-2").Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().ExcludedNamespaces("ns-2").Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -198,20 +198,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "IncludeClusterResources=false only restores namespaced resources", - restore: defaultRestore().IncludeClusterResources(false).Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludeClusterResources(false).Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -226,20 +226,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "label selector only restores matching resources", - restore: defaultRestore().LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}}).Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}}).Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1", test.WithLabels("a", "b")), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2", test.WithLabels("a", "b")), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").ObjectMeta(builder.WithLabels("a", "b")).Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1", test.WithLabels("a", "b")), - test.NewPV("pv-2", test.WithLabels("a", "c")), + builder.ForPersistentVolume("pv-1").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPersistentVolume("pv-2").ObjectMeta(builder.WithLabels("a", "c")).Result(), ). done(), apiResources: []*test.APIResource{ @@ -255,20 +255,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "should include cluster-scoped resources if restoring subset of namespaces and IncludeClusterResources=true", - restore: defaultRestore().IncludedNamespaces("ns-1").IncludeClusterResources(true).Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludedNamespaces("ns-1").IncludeClusterResources(true).Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -284,20 +284,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "should not include cluster-scoped resources if restoring subset of namespaces and IncludeClusterResources=false", - restore: defaultRestore().IncludedNamespaces("ns-1").IncludeClusterResources(false).Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludedNamespaces("ns-1").IncludeClusterResources(false).Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -312,20 +312,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "should include cluster-scoped resources if restoring all namespaces and IncludeClusterResources=true", - restore: defaultRestore().IncludeClusterResources(true).Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludeClusterResources(true).Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -341,20 +341,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "should not include cluster-scoped resources if restoring all namespaces and IncludeClusterResources=false", - restore: defaultRestore().IncludeClusterResources(false).Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludeClusterResources(false).Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -369,20 +369,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "when a wildcard and a specific resource are included, the wildcard takes precedence", - restore: defaultRestore().IncludedResources("*", "pods").Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludedResources("*", "pods").Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -398,20 +398,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "wildcard excludes are ignored", - restore: defaultRestore().ExcludedResources("*").Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().ExcludedResources("*").Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -427,20 +427,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "unresolvable included resources are ignored", - restore: defaultRestore().IncludedResources("pods", "unresolvable").Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludedResources("pods", "unresolvable").Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -454,20 +454,20 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "unresolvable excluded resources are ignored", - restore: defaultRestore().ExcludedResources("deployments", "unresolvable").Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().ExcludedResources("deployments", "unresolvable").Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -482,17 +482,17 @@ func TestRestoreResourceFiltering(t *testing.T) { }, { name: "mirror pods are not restored", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), - tarball: newTarWriter(t).addItems("pods", test.NewPod("ns-1", "pod-1", test.WithAnnotations(corev1api.MirrorPodAnnotationKey, "foo"))).done(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), + tarball: newTarWriter(t).addItems("pods", builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithAnnotations(corev1api.MirrorPodAnnotationKey, "foo")).Result()).done(), apiResources: []*test.APIResource{test.Pods()}, want: map[*test.APIResource][]string{test.Pods(): {}}, }, { name: "service accounts are restored", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), - tarball: newTarWriter(t).addItems("serviceaccounts", test.NewServiceAccount("ns-1", "sa-1")).done(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), + tarball: newTarWriter(t).addItems("serviceaccounts", builder.ForServiceAccount("ns-1", "sa-1").Result()).done(), apiResources: []*test.APIResource{test.ServiceAccounts()}, want: map[*test.APIResource][]string{test.ServiceAccounts(): {"ns-1/sa-1"}}, }, @@ -539,16 +539,16 @@ func TestRestoreNamespaceMapping(t *testing.T) { }{ { name: "namespace mappings are applied", - restore: defaultRestore().NamespaceMappings("ns-1", "mapped-ns-1", "ns-2", "mapped-ns-2").Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().NamespaceMappings("ns-1", "mapped-ns-1", "ns-2", "mapped-ns-2").Result(), + backup: defaultBackup().Result(), apiResources: []*test.APIResource{ test.Pods(), }, tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), - test.NewPod("ns-3", "pod-3"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), + builder.ForPod("ns-3", "pod-3").Result(), ). done(), want: map[*test.APIResource][]string{ @@ -598,28 +598,28 @@ func TestRestoreResourcePriorities(t *testing.T) { }{ { name: "resources are restored according to the specified resource priorities", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1"), - test.NewPod("ns-2", "pod-2"), + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").Result(), ). addItems("persistentvolumes", - test.NewPV("pv-1"), - test.NewPV("pv-2"), + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), ). addItems("deployments.apps", - test.NewDeployment("ns-1", "deploy-1"), - test.NewDeployment("ns-2", "deploy-2"), + builder.ForDeployment("ns-1", "deploy-1").Result(), + builder.ForDeployment("ns-2", "deploy-2").Result(), ). addItems("serviceaccounts", - test.NewServiceAccount("ns-1", "sa-1"), - test.NewServiceAccount("ns-2", "sa-2"), + builder.ForServiceAccount("ns-1", "sa-1").Result(), + builder.ForServiceAccount("ns-2", "sa-2").Result(), ). addItems("persistentvolumeclaims", - test.NewPVC("ns-1", "pvc-1"), - test.NewPVC("ns-2", "pvc-2"), + builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result(), + builder.ForPersistentVolumeClaim("ns-2", "pvc-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -676,8 +676,8 @@ func TestInvalidTarballContents(t *testing.T) { }{ { name: "empty tarball returns an error", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). done(), wantErrs: Result{ @@ -686,12 +686,12 @@ func TestInvalidTarballContents(t *testing.T) { }, { name: "invalid JSON is reported as an error and restore continues", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). add("resources/pods/namespaces/ns-1/pod-1.json", []byte("invalid JSON")). addItems("pods", - test.NewPod("ns-1", "pod-2"), + builder.ForPod("ns-1", "pod-2").Result(), ). done(), apiResources: []*test.APIResource{ @@ -748,15 +748,18 @@ func TestRestoreItems(t *testing.T) { }{ { name: "metadata other than namespace/name/labels/annotations gets removed", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", - test.NewPod("ns-1", "pod-1", - test.WithLabels("key-1", "val-1"), - test.WithAnnotations("key-1", "val-1"), - test.WithClusterName("cluster-1"), - test.WithFinalizers("finalizer-1")), + builder.ForPod("ns-1", "pod-1"). + ObjectMeta( + builder.WithLabels("key-1", "val-1"), + builder.WithAnnotations("key-1", "val-1"), + builder.WithClusterName("cluster-1"), + builder.WithFinalizers("finalizer-1"), + ). + Result(), ). done(), apiResources: []*test.APIResource{ @@ -764,17 +767,19 @@ func TestRestoreItems(t *testing.T) { }, want: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1", - test.WithLabels("key-1", "val-1", "velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"), - test.WithAnnotations("key-1", "val-1"), - ), + builder.ForPod("ns-1", "pod-1"). + ObjectMeta( + builder.WithLabels("key-1", "val-1", "velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"), + builder.WithAnnotations("key-1", "val-1"), + ). + Result(), ), }, }, { name: "status gets removed", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("pods", &corev1api.Pod{ @@ -797,80 +802,90 @@ func TestRestoreItems(t *testing.T) { }, want: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1", test.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1")), + builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1")).Result(), ), }, }, { name: "object gets labeled with full backup and restore names when they're both shorter than 63 characters", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result()). done(), apiResources: []*test.APIResource{ test.Pods(), }, want: []*test.APIResource{ - test.Pods(test.NewPod("ns-1", "pod-1", test.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"))), + test.Pods(builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1")).Result()), }, }, { name: "object gets labeled with full backup and restore names when they're both equal to 63 characters", - restore: NewNamedBuilder(velerov1api.DefaultNamespace, "the-really-long-kube-service-name-that-is-exactly-63-characters"). + restore: builder.ForRestore(velerov1api.DefaultNamespace, "the-really-long-kube-service-name-that-is-exactly-63-characters"). Backup("the-really-long-kube-service-name-that-is-exactly-63-characters"). - Restore(), - backup: backup.NewNamedBackupBuilder(velerov1api.DefaultNamespace, "the-really-long-kube-service-name-that-is-exactly-63-characters").Backup(), + Result(), + backup: builder.ForBackup(velerov1api.DefaultNamespace, "the-really-long-kube-service-name-that-is-exactly-63-characters").Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result()). done(), apiResources: []*test.APIResource{ test.Pods(), }, want: []*test.APIResource{ - test.Pods(test.NewPod("ns-1", "pod-1", test.WithLabels( - "velero.io/backup-name", "the-really-long-kube-service-name-that-is-exactly-63-characters", - "velero.io/restore-name", "the-really-long-kube-service-name-that-is-exactly-63-characters", - ))), + test.Pods( + builder.ForPod("ns-1", "pod-1"). + ObjectMeta( + builder.WithLabels( + "velero.io/backup-name", "the-really-long-kube-service-name-that-is-exactly-63-characters", + "velero.io/restore-name", "the-really-long-kube-service-name-that-is-exactly-63-characters", + ), + ).Result(), + ), }, }, { name: "object gets labeled with shortened backup and restore names when they're both longer than 63 characters", - restore: NewNamedBuilder(velerov1api.DefaultNamespace, "the-really-long-kube-service-name-that-is-much-greater-than-63-characters"). + restore: builder.ForRestore(velerov1api.DefaultNamespace, "the-really-long-kube-service-name-that-is-much-greater-than-63-characters"). Backup("the-really-long-kube-service-name-that-is-much-greater-than-63-characters"). - Restore(), - backup: backup.NewNamedBackupBuilder(velerov1api.DefaultNamespace, "the-really-long-kube-service-name-that-is-much-greater-than-63-characters").Backup(), + Result(), + backup: builder.ForBackup(velerov1api.DefaultNamespace, "the-really-long-kube-service-name-that-is-much-greater-than-63-characters").Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result()). done(), apiResources: []*test.APIResource{ test.Pods(), }, want: []*test.APIResource{ - test.Pods(test.NewPod("ns-1", "pod-1", test.WithLabels( - "velero.io/backup-name", "the-really-long-kube-service-name-that-is-much-greater-th8a11b3", - "velero.io/restore-name", "the-really-long-kube-service-name-that-is-much-greater-th8a11b3", - ))), + test.Pods(builder.ForPod("ns-1", "pod-1"). + ObjectMeta( + builder.WithLabels( + "velero.io/backup-name", "the-really-long-kube-service-name-that-is-much-greater-th8a11b3", + "velero.io/restore-name", "the-really-long-kube-service-name-that-is-much-greater-th8a11b3", + ), + ). + Result(), + ), }, }, { name: "no error when service account already exists in cluster and is identical to the backed up one", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("serviceaccounts", test.NewServiceAccount("ns-1", "sa-1")). + addItems("serviceaccounts", builder.ForServiceAccount("ns-1", "sa-1").Result()). done(), apiResources: []*test.APIResource{ - test.ServiceAccounts(test.NewServiceAccount("ns-1", "sa-1")), + test.ServiceAccounts(builder.ForServiceAccount("ns-1", "sa-1").Result()), }, want: []*test.APIResource{ - test.ServiceAccounts(test.NewServiceAccount("ns-1", "sa-1")), + test.ServiceAccounts(builder.ForServiceAccount("ns-1", "sa-1").Result()), }, }, { name: "service account secrets and image pull secrets are restored when service account already exists in cluster", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("serviceaccounts", &corev1api.ServiceAccount{ TypeMeta: metav1.TypeMeta{ @@ -886,7 +901,7 @@ func TestRestoreItems(t *testing.T) { }). done(), apiResources: []*test.APIResource{ - test.ServiceAccounts(test.NewServiceAccount("ns-1", "sa-1")), + test.ServiceAccounts(builder.ForServiceAccount("ns-1", "sa-1").Result()), }, want: []*test.APIResource{ test.ServiceAccounts(&corev1api.ServiceAccount{ @@ -994,11 +1009,11 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) { }{ { name: "single action with no selector runs for all items", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1"), test.NewPod("ns-2", "pod-2")). - addItems("persistentvolumes", test.NewPV("pv-1"), test.NewPV("pv-2")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()). + addItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result(), builder.ForPersistentVolume("pv-2").Result()). done(), apiResources: []*test.APIResource{test.Pods(), test.PVs()}, actions: map[*recordResourcesAction][]string{ @@ -1007,11 +1022,11 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) { }, { name: "single action with a resource selector for namespaced resources runs only for matching resources", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1"), test.NewPod("ns-2", "pod-2")). - addItems("persistentvolumes", test.NewPV("pv-1"), test.NewPV("pv-2")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()). + addItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result(), builder.ForPersistentVolume("pv-2").Result()). done(), apiResources: []*test.APIResource{test.Pods(), test.PVs()}, actions: map[*recordResourcesAction][]string{ @@ -1020,11 +1035,11 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) { }, { name: "single action with a resource selector for cluster-scoped resources runs only for matching resources", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1"), test.NewPod("ns-2", "pod-2")). - addItems("persistentvolumes", test.NewPV("pv-1"), test.NewPV("pv-2")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()). + addItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result(), builder.ForPersistentVolume("pv-2").Result()). done(), apiResources: []*test.APIResource{test.Pods(), test.PVs()}, actions: map[*recordResourcesAction][]string{ @@ -1033,12 +1048,12 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) { }, { name: "single action with a namespace selector runs only for resources in that namespace", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1"), test.NewPod("ns-2", "pod-2")). - addItems("persistentvolumeclaims", test.NewPVC("ns-1", "pvc-1"), test.NewPVC("ns-2", "pvc-2")). - addItems("persistentvolumes", test.NewPV("pv-1"), test.NewPV("pv-2")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()). + addItems("persistentvolumeclaims", builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result(), builder.ForPersistentVolumeClaim("ns-2", "pvc-2").Result()). + addItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result(), builder.ForPersistentVolume("pv-2").Result()). done(), apiResources: []*test.APIResource{test.Pods(), test.PVCs(), test.PVs()}, actions: map[*recordResourcesAction][]string{ @@ -1047,12 +1062,12 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) { }, { name: "single action with a resource and namespace selector runs only for matching resources in that namespace", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1"), test.NewPod("ns-2", "pod-2")). - addItems("persistentvolumeclaims", test.NewPVC("ns-1", "pvc-1"), test.NewPVC("ns-2", "pvc-2")). - addItems("persistentvolumes", test.NewPV("pv-1"), test.NewPV("pv-2")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()). + addItems("persistentvolumeclaims", builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result(), builder.ForPersistentVolumeClaim("ns-2", "pvc-2").Result()). + addItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result(), builder.ForPersistentVolume("pv-2").Result()). done(), apiResources: []*test.APIResource{test.Pods(), test.PVCs(), test.PVs()}, actions: map[*recordResourcesAction][]string{ @@ -1061,12 +1076,12 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) { }, { name: "multiple actions, each with a different resource selector using short name, run for matching resources", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1"), test.NewPod("ns-2", "pod-2")). - addItems("persistentvolumeclaims", test.NewPVC("ns-1", "pvc-1"), test.NewPVC("ns-2", "pvc-2")). - addItems("persistentvolumes", test.NewPV("pv-1"), test.NewPV("pv-2")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()). + addItems("persistentvolumeclaims", builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result(), builder.ForPersistentVolumeClaim("ns-2", "pvc-2").Result()). + addItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result(), builder.ForPersistentVolume("pv-2").Result()). done(), apiResources: []*test.APIResource{test.Pods(), test.PVCs(), test.PVs()}, actions: map[*recordResourcesAction][]string{ @@ -1076,11 +1091,11 @@ func TestRestoreActionsRunForCorrectItems(t *testing.T) { }, { name: "actions with selectors that don't match anything don't run for any resources", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1")). - addItems("persistentvolumeclaims", test.NewPVC("ns-2", "pvc-2")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result()). + addItems("persistentvolumeclaims", builder.ForPersistentVolumeClaim("ns-2", "pvc-2").Result()). done(), apiResources: []*test.APIResource{test.Pods(), test.PVCs(), test.PVs()}, actions: map[*recordResourcesAction][]string{ @@ -1181,9 +1196,9 @@ func TestRestoreActionModifications(t *testing.T) { }{ { name: "action that adds a label to item gets restored", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), - tarball: newTarWriter(t).addItems("pods", test.NewPod("ns-1", "pod-1")).done(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), + tarball: newTarWriter(t).addItems("pods", builder.ForPod("ns-1", "pod-1").Result()).done(), apiResources: []*test.APIResource{test.Pods()}, actions: []velero.RestoreItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { @@ -1192,14 +1207,15 @@ func TestRestoreActionModifications(t *testing.T) { }, want: []*test.APIResource{ test.Pods( - test.NewPod("ns-1", "pod-1", test.WithLabels("updated", "true"))), + builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("updated", "true")).Result(), + ), }, }, { name: "action that removes a label to item gets restored", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), - tarball: newTarWriter(t).addItems("pods", test.NewPod("ns-1", "pod-1", test.WithLabels("should-be-removed", "true"))).done(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), + tarball: newTarWriter(t).addItems("pods", builder.ForPod("ns-1", "pod-1").ObjectMeta(builder.WithLabels("should-be-removed", "true")).Result()).done(), apiResources: []*test.APIResource{test.Pods()}, actions: []velero.RestoreItemAction{ modifyingActionGetter(func(item *unstructured.Unstructured) { @@ -1207,8 +1223,7 @@ func TestRestoreActionModifications(t *testing.T) { }), }, want: []*test.APIResource{ - test.Pods( - test.NewPod("ns-1", "pod-1")), + test.Pods(builder.ForPod("ns-1", "pod-1").Result()), }, }, // TODO action that modifies namespace/name - what's the expected behavior? @@ -1270,9 +1285,9 @@ func TestRestoreActionAdditionalItems(t *testing.T) { }{ { name: "additional items that are already being restored are not restored twice", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), - tarball: newTarWriter(t).addItems("pods", test.NewPod("ns-1", "pod-1"), test.NewPod("ns-2", "pod-2")).done(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), + tarball: newTarWriter(t).addItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()).done(), apiResources: []*test.APIResource{test.Pods()}, actions: []velero.RestoreItemAction{ &pluggableAction{ @@ -1293,9 +1308,9 @@ func TestRestoreActionAdditionalItems(t *testing.T) { }, { name: "when using a restore namespace filter, additional items that are in a non-included namespace are not restored", - restore: defaultRestore().IncludedNamespaces("ns-1").Restore(), - backup: defaultBackup().Backup(), - tarball: newTarWriter(t).addItems("pods", test.NewPod("ns-1", "pod-1"), test.NewPod("ns-2", "pod-2")).done(), + restore: defaultRestore().IncludedNamespaces("ns-1").Result(), + backup: defaultBackup().Result(), + tarball: newTarWriter(t).addItems("pods", builder.ForPod("ns-1", "pod-1").Result(), builder.ForPod("ns-2", "pod-2").Result()).done(), apiResources: []*test.APIResource{test.Pods()}, actions: []velero.RestoreItemAction{ &pluggableAction{ @@ -1315,11 +1330,11 @@ func TestRestoreActionAdditionalItems(t *testing.T) { }, { name: "when using a restore namespace filter, additional items that are cluster-scoped are restored", - restore: defaultRestore().IncludedNamespaces("ns-1").Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludedNamespaces("ns-1").Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1")). - addItems("persistentvolumes", test.NewPV("pv-1")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result()). + addItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result()). done(), apiResources: []*test.APIResource{test.Pods(), test.PVs()}, actions: []velero.RestoreItemAction{ @@ -1341,11 +1356,11 @@ func TestRestoreActionAdditionalItems(t *testing.T) { }, { name: "when using a restore resource filter, additional items that are non-included resources are not restored", - restore: defaultRestore().IncludedResources("pods").Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludedResources("pods").Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1")). - addItems("persistentvolumes", test.NewPV("pv-1")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result()). + addItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result()). done(), apiResources: []*test.APIResource{test.Pods(), test.PVs()}, actions: []velero.RestoreItemAction{ @@ -1367,11 +1382,11 @@ func TestRestoreActionAdditionalItems(t *testing.T) { }, { name: "when IncludeClusterResources=false, additional items that are cluster-scoped are not restored", - restore: defaultRestore().IncludeClusterResources(false).Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().IncludeClusterResources(false).Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). - addItems("pods", test.NewPod("ns-1", "pod-1")). - addItems("persistentvolumes", test.NewPV("pv-1")). + addItems("pods", builder.ForPod("ns-1", "pod-1").Result()). + addItems("persistentvolumes", builder.ForPersistentVolume("pv-1").Result()). done(), apiResources: []*test.APIResource{test.Pods(), test.PVs()}, actions: []velero.RestoreItemAction{ @@ -1458,37 +1473,23 @@ func TestShouldRestore(t *testing.T) { name: "when PV is found and has associated PVC and namespace that aren't deleting, result is false", pvName: "pv-1", apiResources: []*test.APIResource{ - test.PVs(&corev1api.PersistentVolume{ - TypeMeta: test.NewPV("").TypeMeta, - ObjectMeta: test.NewPV("pv-1").ObjectMeta, - Spec: corev1api.PersistentVolumeSpec{ - ClaimRef: &corev1api.ObjectReference{ - Namespace: "ns-1", - Name: "pvc-1", - }, - }, - }), - test.PVCs(test.NewPVC("ns-1", "pvc-1")), + test.PVs( + builder.ForPersistentVolume("pv-1").ClaimRef("ns-1", "pvc-1").Result(), + ), + test.PVCs(builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result()), }, - namespaces: []*corev1api.Namespace{test.NewNamespace("ns-1")}, + namespaces: []*corev1api.Namespace{builder.ForNamespace("ns-1").Result()}, want: false, }, { name: "when PV is found and has associated PVC that is deleting, result is false + timeout error", pvName: "pv-1", apiResources: []*test.APIResource{ - test.PVs(&corev1api.PersistentVolume{ - TypeMeta: test.NewPV("").TypeMeta, - ObjectMeta: test.NewPV("pv-1").ObjectMeta, - Spec: corev1api.PersistentVolumeSpec{ - ClaimRef: &corev1api.ObjectReference{ - Namespace: "ns-1", - Name: "pvc-1", - }, - }, - }), + test.PVs( + builder.ForPersistentVolume("pv-1").ClaimRef("ns-1", "pvc-1").Result(), + ), test.PVCs( - test.NewPVC("ns-1", "pvc-1", test.WithDeletionTimestamp(time.Now())), + builder.ForPersistentVolumeClaim("ns-1", "pvc-1").ObjectMeta(builder.WithDeletionTimestamp(time.Now())).Result(), ), }, want: false, @@ -1498,26 +1499,13 @@ func TestShouldRestore(t *testing.T) { name: "when PV is found, has associated PVC that's not deleting, has associated NS that is terminating, result is false + timeout error", pvName: "pv-1", apiResources: []*test.APIResource{ - test.PVs(&corev1api.PersistentVolume{ - TypeMeta: test.NewPV("").TypeMeta, - ObjectMeta: test.NewPV("pv-1").ObjectMeta, - Spec: corev1api.PersistentVolumeSpec{ - ClaimRef: &corev1api.ObjectReference{ - Namespace: "ns-1", - Name: "pvc-1", - }, - }, - }), - test.PVCs(test.NewPVC("ns-1", "pvc-1")), + test.PVs( + builder.ForPersistentVolume("pv-1").ClaimRef("ns-1", "pvc-1").Result(), + ), + test.PVCs(builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result()), }, namespaces: []*corev1api.Namespace{ - { - TypeMeta: test.NewNamespace("").TypeMeta, - ObjectMeta: test.NewNamespace("ns-1").ObjectMeta, - Status: corev1api.NamespaceStatus{ - Phase: corev1api.NamespaceTerminating, - }, - }, + builder.ForNamespace("ns-1").Phase(corev1api.NamespaceTerminating).Result(), }, want: false, wantErr: errors.New("timed out waiting for the condition"), @@ -1526,20 +1514,13 @@ func TestShouldRestore(t *testing.T) { name: "when PV is found, has associated PVC that's not deleting, has associated NS that has deletion timestamp, result is false + timeout error", pvName: "pv-1", apiResources: []*test.APIResource{ - test.PVs(&corev1api.PersistentVolume{ - TypeMeta: test.NewPV("").TypeMeta, - ObjectMeta: test.NewPV("pv-1").ObjectMeta, - Spec: corev1api.PersistentVolumeSpec{ - ClaimRef: &corev1api.ObjectReference{ - Namespace: "ns-1", - Name: "pvc-1", - }, - }, - }), - test.PVCs(test.NewPVC("ns-1", "pvc-1")), + test.PVs( + builder.ForPersistentVolume("pv-1").ClaimRef("ns-1", "pvc-1").Result(), + ), + test.PVCs(builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result()), }, namespaces: []*corev1api.Namespace{ - test.NewNamespace("ns-1", test.WithDeletionTimestamp(time.Now())), + builder.ForNamespace("ns-1").ObjectMeta(builder.WithDeletionTimestamp(time.Now())).Result(), }, want: false, wantErr: errors.New("timed out waiting for the condition"), @@ -1548,16 +1529,9 @@ func TestShouldRestore(t *testing.T) { name: "when PV is found, associated PVC is not found, result is false + timeout error", pvName: "pv-1", apiResources: []*test.APIResource{ - test.PVs(&corev1api.PersistentVolume{ - TypeMeta: test.NewPV("").TypeMeta, - ObjectMeta: test.NewPV("pv-1").ObjectMeta, - Spec: corev1api.PersistentVolumeSpec{ - ClaimRef: &corev1api.ObjectReference{ - Namespace: "ns-1", - Name: "pvc-1", - }, - }, - }), + test.PVs( + builder.ForPersistentVolume("pv-1").ClaimRef("ns-1", "pvc-1").Result(), + ), }, want: false, wantErr: errors.New("timed out waiting for the condition"), @@ -1566,17 +1540,10 @@ func TestShouldRestore(t *testing.T) { name: "when PV is found, has associated PVC, associated namespace not found, result is false + timeout error", pvName: "pv-1", apiResources: []*test.APIResource{ - test.PVs(&corev1api.PersistentVolume{ - TypeMeta: test.NewPV("").TypeMeta, - ObjectMeta: test.NewPV("pv-1").ObjectMeta, - Spec: corev1api.PersistentVolumeSpec{ - ClaimRef: &corev1api.ObjectReference{ - Namespace: "ns-1", - Name: "pvc-1", - }, - }, - }), - test.PVCs(test.NewPVC("ns-1", "pvc-1")), + test.PVs( + builder.ForPersistentVolume("pv-1").ClaimRef("ns-1", "pvc-1").Result(), + ), + test.PVCs(builder.ForPersistentVolumeClaim("ns-1", "pvc-1").Result()), }, want: false, wantErr: errors.New("timed out waiting for the condition"), @@ -1744,17 +1711,19 @@ func TestRestorePersistentVolumes(t *testing.T) { }{ { name: "when a PV with a reclaim policy of delete has no snapshot and does not exist in-cluster, it does not get restored, and its PVC gets reset for dynamic provisioning", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("persistentvolumes", - test.NewPV("pv-1", test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimDelete), test.WithClaimRef("ns-1", "pvc-1")), + builder.ForPersistentVolume("pv-1").ReclaimPolicy(corev1api.PersistentVolumeReclaimDelete).ClaimRef("ns-1", "pvc-1").Result(), ). addItems("persistentvolumeclaims", - test.NewPVC("ns-1", "pvc-1", - test.WithPVName("pv-1"), - test.WithAnnotations("pv.kubernetes.io/bind-completed", "true", "pv.kubernetes.io/bound-by-controller", "true", "foo", "bar"), - ), + builder.ForPersistentVolumeClaim("ns-1", "pvc-1"). + VolumeName("pv-1"). + ObjectMeta( + builder.WithAnnotations("pv.kubernetes.io/bind-completed", "true", "pv.kubernetes.io/bound-by-controller", "true", "foo", "bar"), + ). + Result(), ). done(), apiResources: []*test.APIResource{ @@ -1764,20 +1733,22 @@ func TestRestorePersistentVolumes(t *testing.T) { want: []*test.APIResource{ test.PVs(), test.PVCs( - test.NewPVC("ns-1", "pvc-1", - test.WithAnnotations("foo", "bar"), - test.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"), - ), + builder.ForPersistentVolumeClaim("ns-1", "pvc-1"). + ObjectMeta( + builder.WithAnnotations("foo", "bar"), + builder.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"), + ). + Result(), ), }, }, { name: "when a PV with a reclaim policy of retain has no snapshot and does not exist in-cluster, it gets restored, without its claim ref", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("persistentvolumes", - test.NewPV("pv-1", test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimRetain), test.WithClaimRef("ns-1", "pvc-1")), + builder.ForPersistentVolume("pv-1").ReclaimPolicy(corev1api.PersistentVolumeReclaimRetain).ClaimRef("ns-1", "pvc-1").Result(), ). done(), apiResources: []*test.APIResource{ @@ -1786,20 +1757,23 @@ func TestRestorePersistentVolumes(t *testing.T) { }, want: []*test.APIResource{ test.PVs( - test.NewPV("pv-1", - test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimRetain), - test.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"), - ), + builder.ForPersistentVolume("pv-1"). + ReclaimPolicy(corev1api.PersistentVolumeReclaimRetain). + ObjectMeta( + builder.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"), + ). + Result(), ), }, }, { name: "when a PV with a reclaim policy of delete has a snapshot and does not exist in-cluster, the snapshot and PV are restored", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("persistentvolumes", - test.NewPV("pv-1", test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimDelete), test.WithAWSEBSVolumeID("old-volume"))). + builder.ForPersistentVolume("pv-1").ReclaimPolicy(corev1api.PersistentVolumeReclaimDelete).AWSEBSVolumeID("old-volume").Result(), + ). done(), apiResources: []*test.APIResource{ test.PVs(), @@ -1836,21 +1810,27 @@ func TestRestorePersistentVolumes(t *testing.T) { }, want: []*test.APIResource{ test.PVs( - test.NewPV("pv-1", - test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimDelete), - test.WithAWSEBSVolumeID("new-volume"), - test.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"), - ), + builder.ForPersistentVolume("pv-1"). + ReclaimPolicy(corev1api.PersistentVolumeReclaimDelete). + AWSEBSVolumeID("new-volume"). + ObjectMeta( + builder.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"), + ). + Result(), ), }, }, { name: "when a PV with a reclaim policy of retain has a snapshot and does not exist in-cluster, the snapshot and PV are restored", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("persistentvolumes", - test.NewPV("pv-1", test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimRetain), test.WithAWSEBSVolumeID("old-volume"))). + builder.ForPersistentVolume("pv-1"). + ReclaimPolicy(corev1api.PersistentVolumeReclaimRetain). + AWSEBSVolumeID("old-volume"). + Result(), + ). done(), apiResources: []*test.APIResource{ test.PVs(), @@ -1887,25 +1867,34 @@ func TestRestorePersistentVolumes(t *testing.T) { }, want: []*test.APIResource{ test.PVs( - test.NewPV("pv-1", - test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimRetain), - test.WithAWSEBSVolumeID("new-volume"), - test.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"), - ), + builder.ForPersistentVolume("pv-1"). + ReclaimPolicy(corev1api.PersistentVolumeReclaimRetain). + AWSEBSVolumeID("new-volume"). + ObjectMeta( + builder.WithLabels("velero.io/backup-name", "backup-1", "velero.io/restore-name", "restore-1"), + ). + Result(), ), }, }, { name: "when a PV with a reclaim policy of delete has a snapshot and exists in-cluster, neither the snapshot nor the PV are restored", - restore: defaultRestore().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("persistentvolumes", - test.NewPV("pv-1", test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimDelete), test.WithAWSEBSVolumeID("old-volume"))). + builder.ForPersistentVolume("pv-1"). + ReclaimPolicy(corev1api.PersistentVolumeReclaimDelete). + AWSEBSVolumeID("old-volume"). + Result(), + ). done(), apiResources: []*test.APIResource{ test.PVs( - test.NewPV("pv-1", test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimDelete), test.WithAWSEBSVolumeID("old-volume")), + builder.ForPersistentVolume("pv-1"). + ReclaimPolicy(corev1api.PersistentVolumeReclaimDelete). + AWSEBSVolumeID("old-volume"). + Result(), ), test.PVCs(), }, @@ -1941,21 +1930,31 @@ func TestRestorePersistentVolumes(t *testing.T) { }, want: []*test.APIResource{ test.PVs( - test.NewPV("pv-1", test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimDelete), test.WithAWSEBSVolumeID("old-volume")), + builder.ForPersistentVolume("pv-1"). + ReclaimPolicy(corev1api.PersistentVolumeReclaimDelete). + AWSEBSVolumeID("old-volume"). + Result(), ), }, }, { name: "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().Restore(), - backup: defaultBackup().Backup(), + restore: defaultRestore().Result(), + backup: defaultBackup().Result(), tarball: newTarWriter(t). addItems("persistentvolumes", - test.NewPV("pv-1", test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimRetain), test.WithAWSEBSVolumeID("old-volume"))). + builder.ForPersistentVolume("pv-1"). + ReclaimPolicy(corev1api.PersistentVolumeReclaimRetain). + AWSEBSVolumeID("old-volume"). + Result(), + ). done(), apiResources: []*test.APIResource{ test.PVs( - test.NewPV("pv-1", test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimRetain), test.WithAWSEBSVolumeID("old-volume")), + builder.ForPersistentVolume("pv-1"). + ReclaimPolicy(corev1api.PersistentVolumeReclaimRetain). + AWSEBSVolumeID("old-volume"). + Result(), ), test.PVCs(), }, @@ -1990,7 +1989,12 @@ func TestRestorePersistentVolumes(t *testing.T) { "provider-1": &volumeSnapshotter{}, }, want: []*test.APIResource{ - test.PVs(test.NewPV("pv-1", test.WithReclaimPolicy(corev1api.PersistentVolumeReclaimRetain), test.WithAWSEBSVolumeID("old-volume"))), + test.PVs( + builder.ForPersistentVolume("pv-1"). + ReclaimPolicy(corev1api.PersistentVolumeReclaimRetain). + AWSEBSVolumeID("old-volume"). + Result(), + ), }, }, } @@ -2298,8 +2302,8 @@ func (cr *createRecorder) reactor() func(kubetesting.Action) (bool, runtime.Obje } } -func defaultRestore() *Builder { - return NewNamedBuilder(velerov1api.DefaultNamespace, "restore-1").Backup("backup-1") +func defaultRestore() *builder.RestoreBuilder { + return builder.ForRestore(velerov1api.DefaultNamespace, "restore-1").Backup("backup-1") } // assertAPIContents asserts that the dynamic client on the provided harness contains diff --git a/pkg/serverstatusrequest/builder.go b/pkg/serverstatusrequest/builder.go deleted file mode 100644 index b7d4bebad..000000000 --- a/pkg/serverstatusrequest/builder.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright 2018 the Velero 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 serverstatusrequest - -import ( - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" -) - -type Builder struct { - serverStatusRequest velerov1api.ServerStatusRequest -} - -// NewBuilder returns a Builder for a ServerStatusRequest. -func NewBuilder() *Builder { - return &Builder{ - serverStatusRequest: velerov1api.ServerStatusRequest{ - TypeMeta: metav1.TypeMeta{ - APIVersion: velerov1api.SchemeGroupVersion.String(), - Kind: "ServerStatusRequest", - }, - }, - } -} - -// ServerStatusRequest returns the built ServerStatusRequest API object. -func (b *Builder) ServerStatusRequest() *velerov1api.ServerStatusRequest { - return &b.serverStatusRequest -} - -func (b *Builder) Namespace(namespace string) *Builder { - b.serverStatusRequest.Namespace = namespace - return b -} - -func (b *Builder) Name(name string) *Builder { - b.serverStatusRequest.Name = name - return b -} - -func (b *Builder) GenerateName(name string) *Builder { - b.serverStatusRequest.GenerateName = name - return b -} - -func (b *Builder) Phase(phase velerov1api.ServerStatusRequestPhase) *Builder { - b.serverStatusRequest.Status.Phase = phase - return b -} - -func (b *Builder) ProcessedTimestamp(time time.Time) *Builder { - b.serverStatusRequest.Status.ProcessedTimestamp.Time = time - return b -} - -func (b *Builder) ServerVersion(version string) *Builder { - b.serverStatusRequest.Status.ServerVersion = version - return b -} - -func (b *Builder) Plugins(plugins []velerov1api.PluginInfo) *Builder { - b.serverStatusRequest.Status.Plugins = plugins - return b -} diff --git a/pkg/serverstatusrequest/process_test.go b/pkg/serverstatusrequest/process_test.go index fc6966655..b4de2f464 100644 --- a/pkg/serverstatusrequest/process_test.go +++ b/pkg/serverstatusrequest/process_test.go @@ -29,13 +29,14 @@ import ( "k8s.io/apimachinery/pkg/util/clock" velerov1api "github.com/heptio/velero/pkg/apis/velero/v1" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/buildinfo" "github.com/heptio/velero/pkg/generated/clientset/versioned/fake" "github.com/heptio/velero/pkg/plugin/framework" ) -func statusRequestBuilder() *Builder { - return NewBuilder().Namespace(velerov1api.DefaultNamespace).Name("sr-1") +func statusRequestBuilder() *builder.ServerStatusRequestBuilder { + return builder.ForServerStatusRequest(velerov1api.DefaultNamespace, "sr-1") } func TestProcess(t *testing.T) { @@ -56,7 +57,7 @@ func TestProcess(t *testing.T) { }{ { name: "server status request with empty phase gets processed", - req: statusRequestBuilder().ServerStatusRequest(), + req: statusRequestBuilder().Result(), reqPluginLister: &fakePluginLister{ plugins: []framework.PluginIdentifier{ { @@ -75,13 +76,13 @@ func TestProcess(t *testing.T) { Kind: "VolumeSnapshotter", }, }). - ServerStatusRequest(), + Result(), }, { name: "server status request with phase=New gets processed", req: statusRequestBuilder(). Phase(velerov1api.ServerStatusRequestPhaseNew). - ServerStatusRequest(), + Result(), reqPluginLister: &fakePluginLister{ plugins: []framework.PluginIdentifier{ { @@ -108,14 +109,14 @@ func TestProcess(t *testing.T) { Kind: "VolumeSnapshotter", }, }). - ServerStatusRequest(), + Result(), }, { name: "server status request with phase=Processed gets deleted if expired", req: statusRequestBuilder(). Phase(velerov1api.ServerStatusRequestPhaseProcessed). ProcessedTimestamp(now.Add(-61 * time.Second)). - ServerStatusRequest(), + Result(), reqPluginLister: &fakePluginLister{ plugins: []framework.PluginIdentifier{ { @@ -131,20 +132,20 @@ func TestProcess(t *testing.T) { req: statusRequestBuilder(). Phase(velerov1api.ServerStatusRequestPhaseProcessed). ProcessedTimestamp(now.Add(-59 * time.Second)). - ServerStatusRequest(), + Result(), expected: statusRequestBuilder(). Phase(velerov1api.ServerStatusRequestPhaseProcessed). ProcessedTimestamp(now.Add(-59 * time.Second)). - ServerStatusRequest(), + Result(), }, { name: "server status request with invalid phase returns an error", req: statusRequestBuilder(). Phase(velerov1api.ServerStatusRequestPhase("an-invalid-phase")). - ServerStatusRequest(), + Result(), expected: statusRequestBuilder(). Phase(velerov1api.ServerStatusRequestPhase("an-invalid-phase")). - ServerStatusRequest(), + Result(), expectedErrMsg: "unexpected ServerStatusRequest phase \"an-invalid-phase\"", }, } diff --git a/pkg/test/resources.go b/pkg/test/resources.go index f1376f4b6..153dc83b0 100644 --- a/pkg/test/resources.go +++ b/pkg/test/resources.go @@ -17,14 +17,8 @@ limitations under the License. package test import ( - "time" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - storagev1 "k8s.io/api/storage/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" ) // APIResource stores information about a specific Kubernetes API @@ -135,386 +129,3 @@ func ServiceAccounts(items ...metav1.Object) *APIResource { Items: items, } } - -type ObjectOpts func(metav1.Object) - -func NewPod(ns, name string, opts ...ObjectOpts) *corev1.Pod { - obj := &corev1.Pod{ - TypeMeta: metav1.TypeMeta{ - Kind: "Pod", - APIVersion: "v1", - }, - ObjectMeta: objectMeta(ns, name), - } - - for _, opt := range opts { - opt(obj) - } - - return obj -} - -func NewPVC(ns, name string, opts ...ObjectOpts) *corev1.PersistentVolumeClaim { - obj := &corev1.PersistentVolumeClaim{ - TypeMeta: metav1.TypeMeta{ - Kind: "PersistentVolumeClaim", - APIVersion: "v1", - }, - ObjectMeta: objectMeta(ns, name), - } - - for _, opt := range opts { - opt(obj) - } - - return obj -} - -func NewPV(name string, opts ...ObjectOpts) *corev1.PersistentVolume { - obj := &corev1.PersistentVolume{ - TypeMeta: metav1.TypeMeta{ - Kind: "PersistentVolume", - APIVersion: "v1", - }, - ObjectMeta: objectMeta("", name), - } - - for _, opt := range opts { - opt(obj) - } - - return obj -} - -func NewSecret(ns, name string, opts ...ObjectOpts) *corev1.Secret { - obj := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - APIVersion: "v1", - }, - ObjectMeta: objectMeta(ns, name), - } - - for _, opt := range opts { - opt(obj) - } - - return obj -} - -func NewDeployment(ns, name string, opts ...ObjectOpts) *appsv1.Deployment { - obj := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - Kind: "Deployment", - APIVersion: "apps/v1", - }, - ObjectMeta: objectMeta(ns, name), - } - - for _, opt := range opts { - opt(obj) - } - - return obj -} - -func NewServiceAccount(ns, name string, opts ...ObjectOpts) *corev1.ServiceAccount { - obj := &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - Kind: "ServiceAccount", - APIVersion: "v1", - }, - ObjectMeta: objectMeta(ns, name), - } - - for _, opt := range opts { - opt(obj) - } - - return obj -} - -func NewNamespace(name string, opts ...ObjectOpts) *corev1.Namespace { - obj := &corev1.Namespace{ - TypeMeta: metav1.TypeMeta{ - Kind: "Namespace", - APIVersion: "v1", - }, - ObjectMeta: objectMeta("", name), - } - - for _, opt := range opts { - opt(obj) - } - - return obj -} - -func NewConfigMap(ns, name string, opts ...ObjectOpts) *corev1.ConfigMap { - obj := &corev1.ConfigMap{ - TypeMeta: metav1.TypeMeta{ - Kind: "ConfigMap", - APIVersion: "v1", - }, - ObjectMeta: objectMeta(ns, name), - } - - for _, opt := range opts { - opt(obj) - } - - return obj -} - -func NewStorageClass(name string, opts ...ObjectOpts) *storagev1.StorageClass { - obj := &storagev1.StorageClass{ - TypeMeta: metav1.TypeMeta{ - Kind: "StorageClass", - APIVersion: "storage/v1", - }, - ObjectMeta: objectMeta("", name), - } - - for _, opt := range opts { - opt(obj) - } - - return obj -} - -// VolumeOpts exists because corev1.Volume does not implement metav1.Object -type VolumeOpts func(*corev1.Volume) - -func NewVolume(name string, opts ...VolumeOpts) *corev1.Volume { - obj := &corev1.Volume{Name: name} - - for _, opt := range opts { - opt(obj) - } - - return obj -} - -func objectMeta(ns, name string) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Namespace: ns, - Name: name, - } -} - -// WithLabels is a functional option that applies the specified -// label keys/values to an object. -func WithLabels(labels ...string) func(obj metav1.Object) { - return func(obj metav1.Object) { - objLabels := obj.GetLabels() - if objLabels == nil { - objLabels = make(map[string]string) - } - - if len(labels)%2 != 0 { - labels = append(labels, "") - } - - for i := 0; i < len(labels); i += 2 { - objLabels[labels[i]] = labels[i+1] - } - - obj.SetLabels(objLabels) - } -} - -// WithAnnotations is a functional option that applies the specified -// annotation keys/values to an object. -func WithAnnotations(vals ...string) func(obj metav1.Object) { - return func(obj metav1.Object) { - objAnnotations := obj.GetAnnotations() - if objAnnotations == nil { - objAnnotations = make(map[string]string) - } - - if len(vals)%2 != 0 { - vals = append(vals, "") - } - - for i := 0; i < len(vals); i += 2 { - objAnnotations[vals[i]] = vals[i+1] - } - - obj.SetAnnotations(objAnnotations) - } -} - -// WithClusterName is a functional option that applies the specified -// cluster name to an object. -func WithClusterName(val string) func(obj metav1.Object) { - return func(obj metav1.Object) { - obj.SetClusterName(val) - } -} - -// WithFinalizers is a functional option that applies the specified -// finalizers to an object. -func WithFinalizers(vals ...string) func(obj metav1.Object) { - return func(obj metav1.Object) { - obj.SetFinalizers(vals) - } -} - -// WithDeletionTimestamp is a functional option that applies the specified -// deletion timestamp to an object. -func WithDeletionTimestamp(val time.Time) func(obj metav1.Object) { - return func(obj metav1.Object) { - obj.SetDeletionTimestamp(&metav1.Time{Time: val}) - } -} - -// WithUID is a functional option that applies the specified UID to an object. -func WithUID(val string) func(obj metav1.Object) { - return func(obj metav1.Object) { - obj.SetUID(types.UID(val)) - } -} - -// WithReclaimPolicy is a functional option for persistent volumes that sets -// the specified reclaim policy. It panics if the object is not a persistent -// volume. -func WithReclaimPolicy(policy corev1.PersistentVolumeReclaimPolicy) func(obj metav1.Object) { - return func(obj metav1.Object) { - pv, ok := obj.(*corev1.PersistentVolume) - if !ok { - panic("WithReclaimPolicy is only valid for persistent volumes") - } - - pv.Spec.PersistentVolumeReclaimPolicy = policy - } -} - -// WithClaimRef is a functional option for persistent volumes that sets the specified -// claim ref. It panics if the object is not a persistent volume. -func WithClaimRef(ns, name string) func(obj metav1.Object) { - return func(obj metav1.Object) { - pv, ok := obj.(*corev1.PersistentVolume) - if !ok { - panic("WithClaimRef is only valid for persistent volumes") - } - - pv.Spec.ClaimRef = &corev1.ObjectReference{ - Namespace: ns, - Name: name, - } - } -} - -// WithAWSEBSVolumeID is a functional option for persistent volumes that sets the specified -// AWS EBS volume ID. It panics if the object is not a persistent volume. -func WithAWSEBSVolumeID(volumeID string) func(obj metav1.Object) { - return func(obj metav1.Object) { - pv, ok := obj.(*corev1.PersistentVolume) - if !ok { - panic("WithClaimRef is only valid for persistent volumes") - } - - if pv.Spec.AWSElasticBlockStore == nil { - pv.Spec.AWSElasticBlockStore = new(corev1.AWSElasticBlockStoreVolumeSource) - } - - pv.Spec.AWSElasticBlockStore.VolumeID = volumeID - } -} - -// WithCSI is a functional option for persistent volumes that sets the specified CSI driver name -// and volume handle. It panics if the object is not a persistent volume. -func WithCSI(driver, volumeHandle string) func(object metav1.Object) { - return func(obj metav1.Object) { - pv, ok := obj.(*corev1.PersistentVolume) - if !ok { - panic("WithCSI is only valid for persistent volumes") - } - if pv.Spec.CSI == nil { - pv.Spec.CSI = new(corev1.CSIPersistentVolumeSource) - } - pv.Spec.CSI.Driver = driver - pv.Spec.CSI.VolumeHandle = volumeHandle - } -} - -// WithPVName is a functional option for persistent volume claims that sets the specified -// persistent volume name. It panics if the object is not a persistent volume claim. -func WithPVName(name string) func(obj metav1.Object) { - return func(obj metav1.Object) { - pvc, ok := obj.(*corev1.PersistentVolumeClaim) - if !ok { - panic("WithPVName is only valid for persistent volume claims") - } - - pvc.Spec.VolumeName = name - } -} - -// WithStorageClassName is a functional option for persistent volumes or -// persistent volume claims that sets the specified storage class name. -// It panics if the object is not a persistent volume or persistent volume -// claim. -func WithStorageClassName(name string) func(obj metav1.Object) { - return func(obj metav1.Object) { - switch obj.(type) { - case *corev1.PersistentVolume: - obj.(*corev1.PersistentVolume).Spec.StorageClassName = name - case *corev1.PersistentVolumeClaim: - obj.(*corev1.PersistentVolumeClaim).Spec.StorageClassName = &name - default: - panic("WithStorageClassName is only valid for persistent volumes and persistent volume claims") - } - } -} - -// WithVolume is a functional option for pods that sets the specified -// volume on the pod's Spec. It panics if the object is not a pod. -func WithVolume(volume *corev1.Volume) func(obj metav1.Object) { - return func(obj metav1.Object) { - pod, ok := obj.(*corev1.Pod) - if !ok { - panic("WithVolume is only valid for pods") - } - pod.Spec.Volumes = append(pod.Spec.Volumes, *volume) - } -} - -// WithPVCSource is a functional option for volumes that creates a -// PersistentVolumeClaimVolumeSource with the specified name. -func WithPVCSource(claimName string) func(vol *corev1.Volume) { - return func(vol *corev1.Volume) { - vol.VolumeSource.PersistentVolumeClaim = &corev1.PersistentVolumeClaimVolumeSource{ClaimName: claimName} - } -} - -// WithCSISource is a functional option for volumes that creates a -// CSIVolumeSource with the specified driver name. -func WithCSISource(driverName string) func(vol *corev1.Volume) { - return func(vol *corev1.Volume) { - vol.VolumeSource.CSI = &corev1.CSIVolumeSource{Driver: driverName} - } -} - -// WithConfigMapData is a functional option for config maps that puts the specified -// values in the Data field. It panics if the object is not a config map. -func WithConfigMapData(vals ...string) func(obj metav1.Object) { - return func(obj metav1.Object) { - cm, ok := obj.(*corev1.ConfigMap) - if !ok { - panic("WithConfigMapData is only valid for config maps") - } - - if cm.Data == nil { - cm.Data = make(map[string]string) - } - - if len(vals)%2 != 0 { - vals = append(vals, "") - } - - for i := 0; i < len(vals); i += 2 { - cm.Data[vals[i]] = vals[i+1] - } - } -} diff --git a/pkg/util/kube/utils_test.go b/pkg/util/kube/utils_test.go index db102d344..6eab3e588 100644 --- a/pkg/util/kube/utils_test.go +++ b/pkg/util/kube/utils_test.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" kubeinformers "k8s.io/client-go/informers" + "github.com/heptio/velero/pkg/builder" "github.com/heptio/velero/pkg/test" velerotest "github.com/heptio/velero/pkg/util/test" ) @@ -152,30 +153,26 @@ func TestGetVolumeDirectorySuccess(t *testing.T) { }{ { name: "Non-CSI volume with a PVC/PV returns the volume's name", - pod: test.NewPod("ns-1", "my-pod", - test.WithVolume(test.NewVolume("my-vol", test.WithPVCSource("my-pvc")))), - pvc: test.NewPVC("ns-1", "my-pvc", test.WithPVName("a-pv")), - pv: test.NewPV("a-pv"), + pod: builder.ForPod("ns-1", "my-pod").Volumes(builder.ForVolume("my-vol").PersistentVolumeClaimSource("my-pvc").Result()).Result(), + pvc: builder.ForPersistentVolumeClaim("ns-1", "my-pvc").VolumeName("a-pv").Result(), + pv: builder.ForPersistentVolume("a-pv").Result(), want: "a-pv", }, { name: "CSI volume with a PVC/PV appends '/mount' to the volume name", - pod: test.NewPod("ns-1", "my-pod", - test.WithVolume(test.NewVolume("my-vol", test.WithPVCSource("my-pvc")))), - pvc: test.NewPVC("ns-1", "my-pvc", test.WithPVName("a-pv")), - pv: test.NewPV("a-pv", test.WithCSI("csi.test.com", "provider-volume-id")), + pod: builder.ForPod("ns-1", "my-pod").Volumes(builder.ForVolume("my-vol").PersistentVolumeClaimSource("my-pvc").Result()).Result(), + pvc: builder.ForPersistentVolumeClaim("ns-1", "my-pvc").VolumeName("a-pv").Result(), + pv: builder.ForPersistentVolume("a-pv").CSI("csi.test.com", "provider-volume-id").Result(), want: "a-pv/mount", }, { name: "CSI volume mounted without a PVC appends '/mount' to the volume name", - pod: test.NewPod("ns-1", "my-pod", - test.WithVolume(test.NewVolume("my-vol", test.WithCSISource("csi.test.com")))), + pod: builder.ForPod("ns-1", "my-pod").Volumes(builder.ForVolume("my-vol").CSISource("csi.test.com").Result()).Result(), want: "my-vol/mount", }, { name: "Non-CSI volume without a PVC returns the volume name", - pod: test.NewPod("ns-1", "my-pod", - test.WithVolume(test.NewVolume("my-vol"))), + pod: builder.ForPod("ns-1", "my-pod").Volumes(builder.ForVolume("my-vol").Result()).Result(), want: "my-vol", }, } diff --git a/pkg/util/test/test_backup_storage_location.go b/pkg/util/test/test_backup_storage_location.go deleted file mode 100644 index d26ccce12..000000000 --- a/pkg/util/test/test_backup_storage_location.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2017, 2019 the Velero 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 test - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - v1 "github.com/heptio/velero/pkg/apis/velero/v1" -) - -type TestBackupStorageLocation struct { - *v1.BackupStorageLocation -} - -func NewTestBackupStorageLocation() *TestBackupStorageLocation { - return &TestBackupStorageLocation{ - BackupStorageLocation: &v1.BackupStorageLocation{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: v1.DefaultNamespace, - }, - }, - } -} - -func (b *TestBackupStorageLocation) WithNamespace(namespace string) *TestBackupStorageLocation { - b.Namespace = namespace - return b -} - -func (b *TestBackupStorageLocation) WithName(name string) *TestBackupStorageLocation { - b.Name = name - return b -} - -func (b *TestBackupStorageLocation) WithLabel(key, value string) *TestBackupStorageLocation { - if b.Labels == nil { - b.Labels = make(map[string]string) - } - b.Labels[key] = value - return b -} - -func (b *TestBackupStorageLocation) WithProvider(name string) *TestBackupStorageLocation { - b.Spec.Provider = name - return b -} - -func (b *TestBackupStorageLocation) WithObjectStorage(bucketName string) *TestBackupStorageLocation { - if b.Spec.StorageType.ObjectStorage == nil { - b.Spec.StorageType.ObjectStorage = &v1.ObjectStorageLocation{} - } - b.Spec.ObjectStorage.Bucket = bucketName - return b -} - -func (b *TestBackupStorageLocation) WithAccessMode(accessMode v1.BackupStorageLocationAccessMode) *TestBackupStorageLocation { - b.Spec.AccessMode = accessMode - return b -} diff --git a/pkg/util/test/test_restore.go b/pkg/util/test/test_restore.go deleted file mode 100644 index 5598c7ff7..000000000 --- a/pkg/util/test/test_restore.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright 2017 the Velero 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 test - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - api "github.com/heptio/velero/pkg/apis/velero/v1" -) - -type TestRestore struct { - *api.Restore -} - -func NewTestRestore(ns, name string, phase api.RestorePhase) *TestRestore { - return &TestRestore{ - Restore: &api.Restore{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns, - Name: name, - }, - Spec: api.RestoreSpec{}, - Status: api.RestoreStatus{ - Phase: phase, - }, - }, - } -} - -func NewDefaultTestRestore() *TestRestore { - return NewTestRestore(api.DefaultNamespace, "", api.RestorePhase("")) -} - -func (r *TestRestore) WithIncludedNamespace(name string) *TestRestore { - r.Spec.IncludedNamespaces = append(r.Spec.IncludedNamespaces, name) - return r -} - -func (r *TestRestore) WithExcludedNamespace(name string) *TestRestore { - r.Spec.ExcludedNamespaces = append(r.Spec.ExcludedNamespaces, name) - return r -} - -func (r *TestRestore) WithValidationError(err string) *TestRestore { - r.Status.ValidationErrors = append(r.Status.ValidationErrors, err) - return r -} - -func (r *TestRestore) WithBackup(name string) *TestRestore { - r.Spec.BackupName = name - return r -} - -func (r *TestRestore) WithSchedule(name string) *TestRestore { - r.Spec.ScheduleName = name - return r -} - -func (r *TestRestore) WithErrors(i int) *TestRestore { - r.Status.Errors = i - return r -} - -func (r *TestRestore) WithRestorePVs(value bool) *TestRestore { - r.Spec.RestorePVs = &value - return r -} - -func (r *TestRestore) WithMappedNamespace(from string, to string) *TestRestore { - if r.Spec.NamespaceMapping == nil { - r.Spec.NamespaceMapping = make(map[string]string) - } - r.Spec.NamespaceMapping[from] = to - return r -} - -func (r *TestRestore) WithIncludedResource(resource string) *TestRestore { - r.Spec.IncludedResources = append(r.Spec.IncludedResources, resource) - return r -} - -func (r *TestRestore) WithExcludedResource(resource string) *TestRestore { - r.Spec.ExcludedResources = append(r.Spec.ExcludedResources, resource) - return r -} diff --git a/pkg/util/test/test_schedule.go b/pkg/util/test/test_schedule.go deleted file mode 100644 index 84ff61bad..000000000 --- a/pkg/util/test/test_schedule.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2017 the Velero 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 test - -import ( - "time" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - api "github.com/heptio/velero/pkg/apis/velero/v1" -) - -type TestSchedule struct { - *api.Schedule -} - -func NewTestSchedule(namespace, name string) *TestSchedule { - return &TestSchedule{ - Schedule: &api.Schedule{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - }, - }, - } -} - -func (s *TestSchedule) WithPhase(phase api.SchedulePhase) *TestSchedule { - s.Status.Phase = phase - return s -} - -func (s *TestSchedule) WithValidationError(msg string) *TestSchedule { - s.Status.ValidationErrors = append(s.Status.ValidationErrors, msg) - return s -} - -func (s *TestSchedule) WithCronSchedule(cronExpression string) *TestSchedule { - s.Spec.Schedule = cronExpression - return s -} - -func (s *TestSchedule) WithLastBackupTime(timeString string) *TestSchedule { - t, _ := time.Parse("2006-01-02 15:04:05", timeString) - s.Status.LastBackup = metav1.Time{Time: t} - return s -} diff --git a/pkg/util/test/test_volume_snapshot_location.go b/pkg/util/test/test_volume_snapshot_location.go deleted file mode 100644 index d46649a8f..000000000 --- a/pkg/util/test/test_volume_snapshot_location.go +++ /dev/null @@ -1,77 +0,0 @@ -/* -Copyright 2018 the Velero 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 test - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - v1 "github.com/heptio/velero/pkg/apis/velero/v1" -) - -type TestVolumeSnapshotLocation struct { - *v1.VolumeSnapshotLocation -} - -func NewTestVolumeSnapshotLocation() *TestVolumeSnapshotLocation { - return &TestVolumeSnapshotLocation{ - VolumeSnapshotLocation: &v1.VolumeSnapshotLocation{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: v1.DefaultNamespace, - }, - Spec: v1.VolumeSnapshotLocationSpec{ - Provider: "aws", - Config: map[string]string{"region": "us-west-1"}, - }, - }, - } -} - -func (location *TestVolumeSnapshotLocation) WithName(name string) *TestVolumeSnapshotLocation { - location.Name = name - return location -} - -func (location *TestVolumeSnapshotLocation) WithProvider(name string) *TestVolumeSnapshotLocation { - location.Spec.Provider = name - return location -} - -func (location *TestVolumeSnapshotLocation) WithProviderConfig(info []LocationInfo) []*TestVolumeSnapshotLocation { - var locations []*TestVolumeSnapshotLocation - - for _, v := range info { - location := &TestVolumeSnapshotLocation{ - VolumeSnapshotLocation: &v1.VolumeSnapshotLocation{ - ObjectMeta: metav1.ObjectMeta{ - Name: v.Name, - Namespace: v1.DefaultNamespace, - }, - Spec: v1.VolumeSnapshotLocationSpec{ - Provider: v.Provider, - Config: v.Config, - }, - }, - } - locations = append(locations, location) - } - return locations -} - -type LocationInfo struct { - Name, Provider string - Config map[string]string -}