diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 0304e71ced..8e6e3c5a42 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -239,7 +239,6 @@ func (kb *kubernetesBackupper) BackupWithResolvers( if err := kb.writeBackupVersion(tw); err != nil { return errors.WithStack(err) } - backupRequest.NamespaceIncludesExcludes = getNamespaceIncludesExcludes(backupRequest.Backup) log.Infof("Including namespaces: %s", backupRequest.NamespaceIncludesExcludes.IncludesString()) log.Infof("Excluding namespaces: %s", backupRequest.NamespaceIncludesExcludes.ExcludesString()) diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 77969cfe96..87f9f60e6c 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -177,96 +177,96 @@ func TestBackupOldResourceFiltering(t *testing.T) { want []string actions []biav2.BackupItemAction }{ - // { - // name: "no filters backs up everything", - // backup: defaultBackup().Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/pods/namespaces/zoo/raz.json", - // "resources/deployments.apps/namespaces/foo/bar.json", - // "resources/deployments.apps/namespaces/zoo/raz.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", - // }, - // }, - // { - // name: "included resources filter only backs up resources of those types", - // backup: defaultBackup(). - // IncludedResources("pods"). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/pods/namespaces/zoo/raz.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // }, - // }, - // { - // name: "excluded resources filter only backs up resources not of those types", - // backup: defaultBackup(). - // ExcludedResources("deployments"). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/pods/namespaces/zoo/raz.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // }, - // }, - // { - // name: "included namespaces filter only backs up resources in those namespaces", - // backup: defaultBackup(). - // IncludedNamespaces("foo"). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/deployments.apps/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", - // }, - // }, + { + name: "no filters backs up everything", + backup: defaultBackup().Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/pods/namespaces/zoo/raz.json", + "resources/deployments.apps/namespaces/foo/bar.json", + "resources/deployments.apps/namespaces/zoo/raz.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", + "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", + }, + }, + { + name: "included resources filter only backs up resources of those types", + backup: defaultBackup(). + IncludedResources("pods"). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/pods/namespaces/zoo/raz.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", + }, + }, + { + name: "excluded resources filter only backs up resources not of those types", + backup: defaultBackup(). + ExcludedResources("deployments"). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/pods/namespaces/zoo/raz.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", + }, + }, + { + name: "included namespaces filter only backs up resources in those namespaces", + backup: defaultBackup(). + IncludedNamespaces("foo"). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/deployments.apps/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", + }, + }, { name: "included namespaces by label filter only backs up resources in those namespaces", backup: defaultBackup(). @@ -285,608 +285,610 @@ func TestBackupOldResourceFiltering(t *testing.T) { builder.ForDeployment("zoo", "raz").Result(), ), }, + want: []string{ + "resources/deployments.apps/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/namespaces/foo/bar.json", + "resources/namespaces/cluster/foo.json", + "resources/namespaces/v1-preferredversion/cluster/foo.json", + }, + }, + { + name: "excluded namespaces filter only backs up resources not in those namespaces", + backup: defaultBackup(). + ExcludedNamespaces("zoo"). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/deployments.apps/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", + }, + }, + { + name: "IncludeClusterResources=false only backs up namespaced resources", + backup: defaultBackup(). + IncludeClusterResources(false). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + test.PVs( + builder.ForPersistentVolume("bar").Result(), + builder.ForPersistentVolume("baz").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/pods/namespaces/zoo/raz.json", + "resources/deployments.apps/namespaces/foo/bar.json", + "resources/deployments.apps/namespaces/zoo/raz.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", + "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", + }, + }, + { + name: "label selector only backs up matching resources", + backup: defaultBackup(). + LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}}). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels("a", "b")).Result(), + ), + test.PVs( + builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels("a", "c")).Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/deployments.apps/namespaces/zoo/raz.json", + "resources/persistentvolumes/cluster/bar.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", + "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", + }, + }, + { + name: "OrLabelSelector only backs up matching resources", + backup: defaultBackup(). + OrLabelSelector([]*metav1.LabelSelector{{MatchLabels: map[string]string{"a1": "b1"}}, {MatchLabels: map[string]string{"a2": "b2"}}, + {MatchLabels: map[string]string{"a3": "b3"}}, {MatchLabels: map[string]string{"a4": "b4"}}}). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels("a1", "b1")).Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels("a2", "b2")).Result(), + ), + test.PVs( + builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a4", "b4")).Result(), + builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels("a5", "b5")).Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/deployments.apps/namespaces/zoo/raz.json", + "resources/persistentvolumes/cluster/bar.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", + "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", + }, + }, + { + name: "resources with velero.io/exclude-from-backup=true label are not included", + backup: defaultBackup(). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "true")).Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "true")).Result(), + ), + test.PVs( + builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "true")).Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/zoo/raz.json", + "resources/deployments.apps/namespaces/foo/bar.json", + "resources/persistentvolumes/cluster/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", + "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", + "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", + }, + }, + { + 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"}}). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "true", "a", "b")).Result(), + builder.ForPod("zoo", "raz").ObjectMeta(builder.WithLabels("a", "b")).Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "true", "a", "b")).Result(), + ), + test.PVs( + builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels("a", "b", velerov1.ExcludeFromBackupLabel, "true")).Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/zoo/raz.json", + "resources/persistentvolumes/cluster/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", + "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", + }, + }, + { + name: "resources with velero.io/exclude-from-backup label specified but not 'true' are included", + backup: defaultBackup(). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "false")).Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "1")).Result(), + ), + test.PVs( + builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), + builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "")).Result(), + ), + }, want: []string{ "resources/pods/namespaces/foo/bar.json", + "resources/pods/namespaces/zoo/raz.json", "resources/deployments.apps/namespaces/foo/bar.json", + "resources/deployments.apps/namespaces/zoo/raz.json", + "resources/persistentvolumes/cluster/bar.json", + "resources/persistentvolumes/cluster/baz.json", "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", + "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", + "resources/persistentvolumes/v1-preferredversion/cluster/baz.json", + }, + }, + { + name: "should include cluster-scoped resources if backing up subset of namespaces and IncludeClusterResources=true", + backup: defaultBackup(). + IncludedNamespaces("ns-1", "ns-2"). + IncludeClusterResources(true). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), + ), + test.PVs( + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/ns-1/pod-1.json", + "resources/pods/namespaces/ns-2/pod-1.json", + "resources/persistentvolumes/cluster/pv-1.json", + "resources/persistentvolumes/cluster/pv-2.json", + "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", + "resources/persistentvolumes/v1-preferredversion/cluster/pv-1.json", + "resources/persistentvolumes/v1-preferredversion/cluster/pv-2.json", + }, + }, + { + name: "should not include cluster-scoped resource if backing up subset of namespaces and IncludeClusterResources=false", + backup: defaultBackup(). + IncludedNamespaces("ns-1", "ns-2"). + IncludeClusterResources(false). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), + ), + test.PVs( + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/ns-1/pod-1.json", + "resources/pods/namespaces/ns-2/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", + }, + }, + { + name: "should not include cluster-scoped resource if backing up subset of namespaces and IncludeClusterResources=nil", + backup: defaultBackup(). + IncludedNamespaces("ns-1", "ns-2"). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), + ), + test.PVs( + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/ns-1/pod-1.json", + "resources/pods/namespaces/ns-2/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", + }, + }, + { + name: "should include cluster-scoped resources if backing up all namespaces and IncludeClusterResources=true", + backup: defaultBackup(). + IncludeClusterResources(true). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), + ), + test.PVs( + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/ns-1/pod-1.json", + "resources/pods/namespaces/ns-2/pod-1.json", + "resources/pods/namespaces/ns-3/pod-1.json", + "resources/persistentvolumes/cluster/pv-1.json", + "resources/persistentvolumes/cluster/pv-2.json", + "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-3/pod-1.json", + "resources/persistentvolumes/v1-preferredversion/cluster/pv-1.json", + "resources/persistentvolumes/v1-preferredversion/cluster/pv-2.json", + }, + }, + { + name: "should not include cluster-scoped resources if backing up all namespaces and IncludeClusterResources=false", + backup: defaultBackup(). + IncludeClusterResources(false). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), + ), + test.PVs( + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/ns-1/pod-1.json", + "resources/pods/namespaces/ns-2/pod-1.json", + "resources/pods/namespaces/ns-3/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-3/pod-1.json", + }, + }, + { + name: "should include cluster-scoped resources if backing up all namespaces and IncludeClusterResources=nil", + backup: defaultBackup(). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-1").Result(), + builder.ForPod("ns-3", "pod-1").Result(), + ), + test.PVs( + builder.ForPersistentVolume("pv-1").Result(), + builder.ForPersistentVolume("pv-2").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/ns-1/pod-1.json", + "resources/pods/namespaces/ns-2/pod-1.json", + "resources/pods/namespaces/ns-3/pod-1.json", + "resources/persistentvolumes/cluster/pv-1.json", + "resources/persistentvolumes/cluster/pv-2.json", + "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-3/pod-1.json", + "resources/persistentvolumes/v1-preferredversion/cluster/pv-1.json", + "resources/persistentvolumes/v1-preferredversion/cluster/pv-2.json", + }, + }, + { + name: "when a wildcard and a specific resource are included, the wildcard takes precedence", + backup: defaultBackup(). + IncludedResources("*", "pods"). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/pods/namespaces/zoo/raz.json", + "resources/deployments.apps/namespaces/foo/bar.json", + "resources/deployments.apps/namespaces/zoo/raz.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", + "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", + }, + }, + { + name: "wildcard excludes are ignored", + backup: defaultBackup(). + ExcludedResources("*"). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/pods/namespaces/zoo/raz.json", + "resources/deployments.apps/namespaces/foo/bar.json", + "resources/deployments.apps/namespaces/zoo/raz.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", + "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", + }, + }, + { + name: "unresolvable included resources are ignored", + backup: defaultBackup(). + IncludedResources("pods", "unresolvable"). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/pods/namespaces/zoo/raz.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", + }, + }, + { + name: "when all included resources are unresolvable, nothing is included", + backup: defaultBackup(). + IncludedResources("unresolvable-1", "unresolvable-2"). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + }, + want: []string{}, + }, + { + name: "unresolvable excluded resources are ignored", + backup: defaultBackup(). + ExcludedResources("deployments", "unresolvable"). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/pods/namespaces/zoo/raz.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", + }, + }, + { + name: "when all excluded resources are unresolvable, nothing is excluded", + backup: defaultBackup(). + IncludedResources("*"). + ExcludedResources("unresolvable-1", "unresolvable-2"). + Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/foo/bar.json", + "resources/pods/namespaces/zoo/raz.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", + "resources/deployments.apps/namespaces/foo/bar.json", + "resources/deployments.apps/namespaces/zoo/raz.json", + "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", + }, + }, + { + name: "terminating resources are not backed up", + backup: defaultBackup().Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("ns-1", "pod-1").Result(), + builder.ForPod("ns-2", "pod-2").ObjectMeta(builder.WithDeletionTimestamp(time.Now())).Result(), + ), + }, + want: []string{ + "resources/pods/namespaces/ns-1/pod-1.json", + "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", + }, + }, + { + name: "new filters' default value should not impact the old filters' function", + backup: defaultBackup().IncludedNamespaces("foo").IncludeClusterResources(true).Result(), + apiResources: []*test.APIResource{ + test.Pods( + builder.ForPod("foo", "bar").Volumes(builder.ForVolume("foo").PersistentVolumeClaimSource("test-1").Result()).Result(), + builder.ForPod("zoo", "raz").Result(), + ), + test.Deployments( + builder.ForDeployment("foo", "bar").Result(), + builder.ForDeployment("zoo", "raz").Result(), + ), + test.PVCs( + builder.ForPersistentVolumeClaim("foo", "test-1").VolumeName("test1").Result(), + ), + test.PVs( + builder.ForPersistentVolume("test1").Result(), + builder.ForPersistentVolume("test2").Result(), + ), + }, + want: []string{ + "resources/deployments.apps/namespaces/foo/bar.json", + "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", + "resources/persistentvolumeclaims/namespaces/foo/test-1.json", + "resources/persistentvolumeclaims/v1-preferredversion/namespaces/foo/test-1.json", + "resources/persistentvolumes/cluster/test1.json", + "resources/persistentvolumes/cluster/test2.json", + "resources/persistentvolumes/v1-preferredversion/cluster/test1.json", + "resources/persistentvolumes/v1-preferredversion/cluster/test2.json", + "resources/pods/namespaces/foo/bar.json", + "resources/pods/v1-preferredversion/namespaces/foo/bar.json", + }, + actions: []biav2.BackupItemAction{ + &pluggableAction{ + selector: velero.ResourceSelector{IncludedResources: []string{"persistentvolumeclaims"}}, + executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, string, []velero.ResourceIdentifier, error) { + additionalItems := []velero.ResourceIdentifier{ + {GroupResource: kuberesource.PersistentVolumes, Name: "test1"}, + } + + return item, additionalItems, "", nil, nil + }, + }, + }, + }, + { + name: "Resource's CRD should be included", + backup: defaultBackup().IncludedNamespaces("foo").Result(), + apiResources: []*test.APIResource{ + test.CRDs( + builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(), + builder.ForCustomResourceDefinitionV1Beta1("volumesnapshotlocations.velero.io").Result(), + builder.ForCustomResourceDefinitionV1Beta1("test.velero.io").Result(), + ), + test.VSLs( + builder.ForVolumeSnapshotLocation("foo", "bar").Result(), + ), + test.Backups( + builder.ForBackup("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/customresourcedefinitions.apiextensions.k8s.io/cluster/volumesnapshotlocations.velero.io.json", + "resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/volumesnapshotlocations.velero.io.json", + "resources/volumesnapshotlocations.velero.io/namespaces/foo/bar.json", + "resources/volumesnapshotlocations.velero.io/v1-preferredversion/namespaces/foo/bar.json", + }, + }, + { + name: "Resource's CRD is not included, when CRD is excluded.", + backup: defaultBackup().IncludedNamespaces("foo").ExcludedResources("customresourcedefinitions.apiextensions.k8s.io").Result(), + apiResources: []*test.APIResource{ + test.CRDs( + builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(), + builder.ForCustomResourceDefinitionV1Beta1("volumesnapshotlocations.velero.io").Result(), + builder.ForCustomResourceDefinitionV1Beta1("test.velero.io").Result(), + ), + test.VSLs( + builder.ForVolumeSnapshotLocation("foo", "bar").Result(), + ), + test.Backups( + builder.ForBackup("zoo", "raz").Result(), + ), + }, + want: []string{ + "resources/volumesnapshotlocations.velero.io/namespaces/foo/bar.json", + "resources/volumesnapshotlocations.velero.io/v1-preferredversion/namespaces/foo/bar.json", }, }, - // { - // name: "excluded namespaces filter only backs up resources not in those namespaces", - // backup: defaultBackup(). - // ExcludedNamespaces("zoo"). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/deployments.apps/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", - // }, - // }, - // { - // name: "IncludeClusterResources=false only backs up namespaced resources", - // backup: defaultBackup(). - // IncludeClusterResources(false). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("bar").Result(), - // builder.ForPersistentVolume("baz").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/pods/namespaces/zoo/raz.json", - // "resources/deployments.apps/namespaces/foo/bar.json", - // "resources/deployments.apps/namespaces/zoo/raz.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", - // }, - // }, - // { - // name: "label selector only backs up matching resources", - // backup: defaultBackup(). - // LabelSelector(&metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}}). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels("a", "b")).Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), - // builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels("a", "c")).Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/deployments.apps/namespaces/zoo/raz.json", - // "resources/persistentvolumes/cluster/bar.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", - // }, - // }, - // { - // name: "OrLabelSelector only backs up matching resources", - // backup: defaultBackup(). - // OrLabelSelector([]*metav1.LabelSelector{{MatchLabels: map[string]string{"a1": "b1"}}, {MatchLabels: map[string]string{"a2": "b2"}}, - // {MatchLabels: map[string]string{"a3": "b3"}}, {MatchLabels: map[string]string{"a4": "b4"}}}). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels("a1", "b1")).Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels("a2", "b2")).Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a4", "b4")).Result(), - // builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels("a5", "b5")).Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/deployments.apps/namespaces/zoo/raz.json", - // "resources/persistentvolumes/cluster/bar.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", - // }, - // }, - // { - // name: "resources with velero.io/exclude-from-backup=true label are not included", - // backup: defaultBackup(). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "true")).Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "true")).Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), - // builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "true")).Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/zoo/raz.json", - // "resources/deployments.apps/namespaces/foo/bar.json", - // "resources/persistentvolumes/cluster/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", - // }, - // }, - // { - // 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"}}). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "true", "a", "b")).Result(), - // builder.ForPod("zoo", "raz").ObjectMeta(builder.WithLabels("a", "b")).Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "true", "a", "b")).Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), - // builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels("a", "b", velerov1.ExcludeFromBackupLabel, "true")).Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/zoo/raz.json", - // "resources/persistentvolumes/cluster/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", - // }, - // }, - // { - // name: "resources with velero.io/exclude-from-backup label specified but not 'true' are included", - // backup: defaultBackup(). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "false")).Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "1")).Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("bar").ObjectMeta(builder.WithLabels("a", "b")).Result(), - // builder.ForPersistentVolume("baz").ObjectMeta(builder.WithLabels(velerov1.ExcludeFromBackupLabel, "")).Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/pods/namespaces/zoo/raz.json", - // "resources/deployments.apps/namespaces/foo/bar.json", - // "resources/deployments.apps/namespaces/zoo/raz.json", - // "resources/persistentvolumes/cluster/bar.json", - // "resources/persistentvolumes/cluster/baz.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/bar.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/baz.json", - // }, - // }, - // { - // name: "should include cluster-scoped resources if backing up subset of namespaces and IncludeClusterResources=true", - // backup: defaultBackup(). - // IncludedNamespaces("ns-1", "ns-2"). - // IncludeClusterResources(true). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("ns-1", "pod-1").Result(), - // builder.ForPod("ns-2", "pod-1").Result(), - // builder.ForPod("ns-3", "pod-1").Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("pv-1").Result(), - // builder.ForPersistentVolume("pv-2").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/ns-1/pod-1.json", - // "resources/pods/namespaces/ns-2/pod-1.json", - // "resources/persistentvolumes/cluster/pv-1.json", - // "resources/persistentvolumes/cluster/pv-2.json", - // "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/pv-1.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/pv-2.json", - // }, - // }, - // { - // name: "should not include cluster-scoped resource if backing up subset of namespaces and IncludeClusterResources=false", - // backup: defaultBackup(). - // IncludedNamespaces("ns-1", "ns-2"). - // IncludeClusterResources(false). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("ns-1", "pod-1").Result(), - // builder.ForPod("ns-2", "pod-1").Result(), - // builder.ForPod("ns-3", "pod-1").Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("pv-1").Result(), - // builder.ForPersistentVolume("pv-2").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/ns-1/pod-1.json", - // "resources/pods/namespaces/ns-2/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", - // }, - // }, - // { - // name: "should not include cluster-scoped resource if backing up subset of namespaces and IncludeClusterResources=nil", - // backup: defaultBackup(). - // IncludedNamespaces("ns-1", "ns-2"). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("ns-1", "pod-1").Result(), - // builder.ForPod("ns-2", "pod-1").Result(), - // builder.ForPod("ns-3", "pod-1").Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("pv-1").Result(), - // builder.ForPersistentVolume("pv-2").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/ns-1/pod-1.json", - // "resources/pods/namespaces/ns-2/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", - // }, - // }, - // { - // name: "should include cluster-scoped resources if backing up all namespaces and IncludeClusterResources=true", - // backup: defaultBackup(). - // IncludeClusterResources(true). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("ns-1", "pod-1").Result(), - // builder.ForPod("ns-2", "pod-1").Result(), - // builder.ForPod("ns-3", "pod-1").Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("pv-1").Result(), - // builder.ForPersistentVolume("pv-2").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/ns-1/pod-1.json", - // "resources/pods/namespaces/ns-2/pod-1.json", - // "resources/pods/namespaces/ns-3/pod-1.json", - // "resources/persistentvolumes/cluster/pv-1.json", - // "resources/persistentvolumes/cluster/pv-2.json", - // "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-3/pod-1.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/pv-1.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/pv-2.json", - // }, - // }, - // { - // name: "should not include cluster-scoped resources if backing up all namespaces and IncludeClusterResources=false", - // backup: defaultBackup(). - // IncludeClusterResources(false). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("ns-1", "pod-1").Result(), - // builder.ForPod("ns-2", "pod-1").Result(), - // builder.ForPod("ns-3", "pod-1").Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("pv-1").Result(), - // builder.ForPersistentVolume("pv-2").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/ns-1/pod-1.json", - // "resources/pods/namespaces/ns-2/pod-1.json", - // "resources/pods/namespaces/ns-3/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-3/pod-1.json", - // }, - // }, - // { - // name: "should include cluster-scoped resources if backing up all namespaces and IncludeClusterResources=nil", - // backup: defaultBackup(). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("ns-1", "pod-1").Result(), - // builder.ForPod("ns-2", "pod-1").Result(), - // builder.ForPod("ns-3", "pod-1").Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("pv-1").Result(), - // builder.ForPersistentVolume("pv-2").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/ns-1/pod-1.json", - // "resources/pods/namespaces/ns-2/pod-1.json", - // "resources/pods/namespaces/ns-3/pod-1.json", - // "resources/persistentvolumes/cluster/pv-1.json", - // "resources/persistentvolumes/cluster/pv-2.json", - // "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-3/pod-1.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/pv-1.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/pv-2.json", - // }, - // }, - // { - // name: "when a wildcard and a specific resource are included, the wildcard takes precedence", - // backup: defaultBackup(). - // IncludedResources("*", "pods"). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/pods/namespaces/zoo/raz.json", - // "resources/deployments.apps/namespaces/foo/bar.json", - // "resources/deployments.apps/namespaces/zoo/raz.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", - // }, - // }, - // { - // name: "wildcard excludes are ignored", - // backup: defaultBackup(). - // ExcludedResources("*"). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/pods/namespaces/zoo/raz.json", - // "resources/deployments.apps/namespaces/foo/bar.json", - // "resources/deployments.apps/namespaces/zoo/raz.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", - // }, - // }, - // { - // name: "unresolvable included resources are ignored", - // backup: defaultBackup(). - // IncludedResources("pods", "unresolvable"). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/pods/namespaces/zoo/raz.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // }, - // }, - // { - // name: "when all included resources are unresolvable, nothing is included", - // backup: defaultBackup(). - // IncludedResources("unresolvable-1", "unresolvable-2"). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // }, - // want: []string{}, - // }, - // { - // name: "unresolvable excluded resources are ignored", - // backup: defaultBackup(). - // ExcludedResources("deployments", "unresolvable"). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/pods/namespaces/zoo/raz.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // }, - // }, - // { - // name: "when all excluded resources are unresolvable, nothing is excluded", - // backup: defaultBackup(). - // IncludedResources("*"). - // ExcludedResources("unresolvable-1", "unresolvable-2"). - // Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/foo/bar.json", - // "resources/pods/namespaces/zoo/raz.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/zoo/raz.json", - // "resources/deployments.apps/namespaces/foo/bar.json", - // "resources/deployments.apps/namespaces/zoo/raz.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json", - // }, - // }, - // { - // name: "terminating resources are not backed up", - // backup: defaultBackup().Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("ns-1", "pod-1").Result(), - // builder.ForPod("ns-2", "pod-2").ObjectMeta(builder.WithDeletionTimestamp(time.Now())).Result(), - // ), - // }, - // want: []string{ - // "resources/pods/namespaces/ns-1/pod-1.json", - // "resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json", - // }, - // }, - // { - // name: "new filters' default value should not impact the old filters' function", - // backup: defaultBackup().IncludedNamespaces("foo").IncludeClusterResources(true).Result(), - // apiResources: []*test.APIResource{ - // test.Pods( - // builder.ForPod("foo", "bar").Volumes(builder.ForVolume("foo").PersistentVolumeClaimSource("test-1").Result()).Result(), - // builder.ForPod("zoo", "raz").Result(), - // ), - // test.Deployments( - // builder.ForDeployment("foo", "bar").Result(), - // builder.ForDeployment("zoo", "raz").Result(), - // ), - // test.PVCs( - // builder.ForPersistentVolumeClaim("foo", "test-1").VolumeName("test1").Result(), - // ), - // test.PVs( - // builder.ForPersistentVolume("test1").Result(), - // builder.ForPersistentVolume("test2").Result(), - // ), - // }, - // want: []string{ - // "resources/deployments.apps/namespaces/foo/bar.json", - // "resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json", - // "resources/persistentvolumeclaims/namespaces/foo/test-1.json", - // "resources/persistentvolumeclaims/v1-preferredversion/namespaces/foo/test-1.json", - // "resources/persistentvolumes/cluster/test1.json", - // "resources/persistentvolumes/cluster/test2.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/test1.json", - // "resources/persistentvolumes/v1-preferredversion/cluster/test2.json", - // "resources/pods/namespaces/foo/bar.json", - // "resources/pods/v1-preferredversion/namespaces/foo/bar.json", - // }, - // actions: []biav2.BackupItemAction{ - // &pluggableAction{ - // selector: velero.ResourceSelector{IncludedResources: []string{"persistentvolumeclaims"}}, - // executeFunc: func(item runtime.Unstructured, backup *velerov1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, string, []velero.ResourceIdentifier, error) { - // additionalItems := []velero.ResourceIdentifier{ - // {GroupResource: kuberesource.PersistentVolumes, Name: "test1"}, - // } - - // return item, additionalItems, "", nil, nil - // }, - // }, - // }, - // }, - // { - // name: "Resource's CRD should be included", - // backup: defaultBackup().IncludedNamespaces("foo").Result(), - // apiResources: []*test.APIResource{ - // test.CRDs( - // builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(), - // builder.ForCustomResourceDefinitionV1Beta1("volumesnapshotlocations.velero.io").Result(), - // builder.ForCustomResourceDefinitionV1Beta1("test.velero.io").Result(), - // ), - // test.VSLs( - // builder.ForVolumeSnapshotLocation("foo", "bar").Result(), - // ), - // test.Backups( - // builder.ForBackup("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/customresourcedefinitions.apiextensions.k8s.io/cluster/volumesnapshotlocations.velero.io.json", - // "resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/volumesnapshotlocations.velero.io.json", - // "resources/volumesnapshotlocations.velero.io/namespaces/foo/bar.json", - // "resources/volumesnapshotlocations.velero.io/v1-preferredversion/namespaces/foo/bar.json", - // }, - // }, - // { - // name: "Resource's CRD is not included, when CRD is excluded.", - // backup: defaultBackup().IncludedNamespaces("foo").ExcludedResources("customresourcedefinitions.apiextensions.k8s.io").Result(), - // apiResources: []*test.APIResource{ - // test.CRDs( - // builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(), - // builder.ForCustomResourceDefinitionV1Beta1("volumesnapshotlocations.velero.io").Result(), - // builder.ForCustomResourceDefinitionV1Beta1("test.velero.io").Result(), - // ), - // test.VSLs( - // builder.ForVolumeSnapshotLocation("foo", "bar").Result(), - // ), - // test.Backups( - // builder.ForBackup("zoo", "raz").Result(), - // ), - // }, - // want: []string{ - // "resources/volumesnapshotlocations.velero.io/namespaces/foo/bar.json", - // "resources/volumesnapshotlocations.velero.io/v1-preferredversion/namespaces/foo/bar.json", - // }, - // }, } for _, tc := range tests { diff --git a/pkg/backup/item_collector.go b/pkg/backup/item_collector.go index a1fce5ee74..b4c464f6a5 100644 --- a/pkg/backup/item_collector.go +++ b/pkg/backup/item_collector.go @@ -46,6 +46,8 @@ import ( type itemCollector struct { log logrus.FieldLogger backupRequest *Request + // Namespaces that are included by the backup's labelSelector will backup all resources in that namespace even if resources are not labeled. + namespacesIncludedByLabelSelector map[string]struct{} discoveryHelper discovery.Helper dynamicFactory client.DynamicFactory cohabitatingResources map[string]*cohabitatingResource @@ -436,13 +438,29 @@ func (r *itemCollector) getResourceItems( // Namespace are filtered by namespace include/exclude filters, // backup LabelSelectors and OrLabelSelectors are checked too. if gr == kuberesource.Namespaces { - return r.collectNamespaces( + namespaces, err := r.collectNamespaces( resource, gv, gr, preferredGVR, log, ) + // The namespaces collected contains namespaces selected by label selector. + // Per https://github.com/vmware-tanzu/velero/issues/7492#issuecomment-1986146411 we want these to act as IncludedNamespaces so add to stringset. + for i := range namespaces { + if namespaces[i] != nil { + if r.backupRequest.NamespaceIncludesExcludes.ShouldInclude(namespaces[i].name) { + // this namespace is included by the backup's label selector, not the namespace include/exclude filter + // this is a special case where we want to include this namespace in the backup and all resources in it regardless of the resource's label selector + if r.namespacesIncludedByLabelSelector == nil { + r.namespacesIncludedByLabelSelector = make(map[string]struct{}) + } + r.namespacesIncludedByLabelSelector[namespaces[i].name] = struct{}{} + } + r.backupRequest.NamespaceIncludesExcludes.Includes(namespaces[i].name) + } + } + return namespaces, err } clusterScoped := !resource.Namespaced @@ -509,9 +527,13 @@ func (r *itemCollector) listResourceByLabelsPerNamespace( logger.WithError(err).Error("Error getting dynamic client") return nil, err } - + ignoreItemLabels := false + if _, namespaceSelectedByLabelSelector := r.namespacesIncludedByLabelSelector[namespace]; namespaceSelectedByLabelSelector { + logger.Infof("Listing all items in namespace %s because ns was selected by the backup's label selector", namespace) + ignoreItemLabels = true + } var orLabelSelectors []string - if r.backupRequest.Spec.OrLabelSelectors != nil { + if r.backupRequest.Spec.OrLabelSelectors != nil && !ignoreItemLabels { for _, s := range r.backupRequest.Spec.OrLabelSelectors { orLabelSelectors = append(orLabelSelectors, metav1.FormatLabelSelector(s)) } @@ -537,7 +559,7 @@ func (r *itemCollector) listResourceByLabelsPerNamespace( } var labelSelector string - if selector := r.backupRequest.Spec.LabelSelector; selector != nil { + if selector := r.backupRequest.Spec.LabelSelector; selector != nil && !ignoreItemLabels { labelSelector = metav1.FormatLabelSelector(selector) } @@ -594,7 +616,8 @@ func sortCoreGroup(group *metav1.APIResourceList) { // pod is backed up, we can perform a pre hook, then process pvcs and pvs (including taking a // snapshot), then perform a post hook on the pod. const ( - pod = iota + namespaces = iota + pod pvc pv other @@ -604,6 +627,8 @@ const ( // pods, pvcs, pvs, everything else. func coreGroupResourcePriority(resource string) int { switch strings.ToLower(resource) { + case "namespaces": + return namespaces case "pods": return pod case "persistentvolumeclaims":