Skip to content

Commit

Permalink
Add new resource filters can separate cluster and namespace scope res…
Browse files Browse the repository at this point in the history
…ources.

Signed-off-by: Xun Jiang <[email protected]>
  • Loading branch information
Xun Jiang committed Feb 8, 2023
1 parent 5db9437 commit ac4455a
Show file tree
Hide file tree
Showing 20 changed files with 927 additions and 79 deletions.
1 change: 1 addition & 0 deletions changelogs/unreleased/5838-blackpiglet
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add new resource filters can separate cluster and namespace scope resources.
33 changes: 33 additions & 0 deletions config/crd/v1/bases/velero.io_backups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ spec:
Use DefaultVolumesToFsBackup instead."
nullable: true
type: boolean
excludedClusterScopeResources:
description: ExcludedClusterScopeResources is a slice of cluster scope
resource type names to exclude from the backup. If set to "*", all
cluster scope resource types are excluded.
items:
type: string
nullable: true
type: array
excludedNamespacedResources:
description: ExcludedNamespacedResources is a slice of namespace scope
resource type names to exclude from the backup. If set to "*", all
namespace scope resource types are excluded.
items:
type: string
nullable: true
type: array
excludedNamespaces:
description: ExcludedNamespaces contains a list of namespaces that
are not included in the backup.
Expand Down Expand Up @@ -259,6 +275,23 @@ spec:
resources should be included for consideration in the backup.
nullable: true
type: boolean
includedClusterScopeResources:
description: IncludedClusterScopeResources is a slice of cluster scope
resource type names to include in the backup. If set to "*", all
cluster scope resource types are included. The default value is
empty, which means only related cluster scope resources are included.
items:
type: string
nullable: true
type: array
includedNamespacedResources:
description: IncludedNamespacedResources is a slice of namespace scope
resource type names to include in the backup. The default value
is "*".
items:
type: string
nullable: true
type: array
includedNamespaces:
description: IncludedNamespaces is a slice of namespace names to include
objects from. If empty, all namespaces are included.
Expand Down
34 changes: 34 additions & 0 deletions config/crd/v1/bases/velero.io_schedules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ spec:
entirely in future. Use DefaultVolumesToFsBackup instead."
nullable: true
type: boolean
excludedClusterScopeResources:
description: ExcludedClusterScopeResources is a slice of cluster
scope resource type names to exclude from the backup. If set
to "*", all cluster scope resource types are excluded.
items:
type: string
nullable: true
type: array
excludedNamespacedResources:
description: ExcludedNamespacedResources is a slice of namespace
scope resource type names to exclude from the backup. If set
to "*", all namespace scope resource types are excluded.
items:
type: string
nullable: true
type: array
excludedNamespaces:
description: ExcludedNamespaces contains a list of namespaces
that are not included in the backup.
Expand Down Expand Up @@ -294,6 +310,24 @@ spec:
resources should be included for consideration in the backup.
nullable: true
type: boolean
includedClusterScopeResources:
description: IncludedClusterScopeResources is a slice of cluster
scope resource type names to include in the backup. If set to
"*", all cluster scope resource types are included. The default
value is empty, which means only related cluster scope resources
are included.
items:
type: string
nullable: true
type: array
includedNamespacedResources:
description: IncludedNamespacedResources is a slice of namespace
scope resource type names to include in the backup. The default
value is "*".
items:
type: string
nullable: true
type: array
includedNamespaces:
description: IncludedNamespaces is a slice of namespace names
to include objects from. If empty, all namespaces are included.
Expand Down
4 changes: 2 additions & 2 deletions config/crd/v1/crds/crds.go

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions pkg/apis/velero/v1/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,36 @@ type BackupSpec struct {
// +nullable
ExcludedResources []string `json:"excludedResources,omitempty"`

// IncludedClusterScopeResources is a slice of cluster scope
// resource type names to include in the backup.
// If set to "*", all cluster scope resource types are included.
// The default value is empty, which means only related cluster
// scope resources are included.
// +optional
// +nullable
IncludedClusterScopeResources []string `json:"includedClusterScopeResources,omitempty"`

// ExcludedClusterScopeResources is a slice of cluster scope
// resource type names to exclude from the backup.
// If set to "*", all cluster scope resource types are excluded.
// +optional
// +nullable
ExcludedClusterScopeResources []string `json:"excludedClusterScopeResources,omitempty"`

// IncludedNamespacedResources is a slice of namespace scope
// resource type names to include in the backup.
// The default value is "*".
// +optional
// +nullable
IncludedNamespacedResources []string `json:"includedNamespacedResources,omitempty"`

// ExcludedNamespacedResources is a slice of namespace scope
// resource type names to exclude from the backup.
// If set to "*", all namespace scope resource types are excluded.
// +optional
// +nullable
ExcludedNamespacedResources []string `json:"excludedNamespacedResources,omitempty"`

// LabelSelector is a metav1.LabelSelector to filter with
// when adding individual objects to the backup. If empty
// or nil, all objects are included. Optional.
Expand Down
20 changes: 20 additions & 0 deletions pkg/apis/velero/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 14 additions & 3 deletions pkg/backup/backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,15 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger,
backupRequest.ResourceIncludesExcludes = collections.GetResourceIncludesExcludes(kb.discoveryHelper, backupRequest.Spec.IncludedResources, backupRequest.Spec.ExcludedResources)
log.Infof("Including resources: %s", backupRequest.ResourceIncludesExcludes.IncludesString())
log.Infof("Excluding resources: %s", backupRequest.ResourceIncludesExcludes.ExcludesString())

backupRequest.NamespaceResourceIncludesExcludes = collections.GetScopedResourceIncludesExcludes(kb.discoveryHelper, backupRequest.Spec.IncludedNamespacedResources, backupRequest.Spec.ExcludedNamespacedResources, true)
log.Infof("Including namespaced resources: %s", backupRequest.NamespaceResourceIncludesExcludes.IncludesString())
log.Infof("Excluding namespaced resources: %s", backupRequest.NamespaceResourceIncludesExcludes.ExcludesString())

backupRequest.ClusterResourceIncludesExcludes = collections.GetScopedResourceIncludesExcludes(kb.discoveryHelper, backupRequest.Spec.IncludedClusterScopeResources, backupRequest.Spec.ExcludedClusterScopeResources, false)
log.Infof("Including cluster-scoped resources: %s", backupRequest.ClusterResourceIncludesExcludes.ClusterIncludesString())
log.Infof("Excluding cluster-scoped resources: %s", backupRequest.ClusterResourceIncludesExcludes.ExcludesString())

log.Infof("Backing up all volumes using pod volume backup: %t", boolptr.IsSetToTrue(backupRequest.Backup.Spec.DefaultVolumesToFsBackup))

var err error
Expand Down Expand Up @@ -392,9 +401,11 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger,
quit <- struct{}{}

// back up CRD for resource if found. We should only need to do this if we've backed up at least
// one item for the resource and IncludeClusterResources is nil. If IncludeClusterResources is false
// we don't want to back it up, and if it's true it will already be included.
if backupRequest.Spec.IncludeClusterResources == nil {
// one item for the resource when IncludeClusterResources is nil, or cluster-scoped resource filters
// are not specified. If IncludeClusterResources is false we don't want to back it up,
// and if it's true it will already be included.
if backupRequest.Spec.IncludeClusterResources == nil ||
(len(backupRequest.Spec.IncludedClusterScopeResources) == 0 && len(backupRequest.Spec.ExcludedClusterScopeResources) == 0) {
for gr := range backedUpGroupResources {
kb.backupCRD(log, gr, itemBackupper)
}
Expand Down
111 changes: 110 additions & 1 deletion pkg/backup/backup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ func TestBackupProgressIsUpdated(t *testing.T) {
// verifies that the set of items written to the backup tarball are
// correct. Validation is done by looking at the names of the files in
// the backup tarball; the contents of the files are not checked.
func TestBackupResourceFiltering(t *testing.T) {
func TestBackupOldResourceFiltering(t *testing.T) {
tests := []struct {
name string
backup *velerov1.Backup
Expand Down Expand Up @@ -2972,3 +2972,112 @@ func assertTarballOrdering(t *testing.T, backupFile io.Reader, orderedResources
lastSeen = current
}
}

func TestBackupNewResourceFiltering(t *testing.T) {
tests := []struct {
name string
backup *velerov1.Backup
apiResources []*test.APIResource
want []string
}{
{
name: "no namespaced resources + some cluster resources",
backup: defaultBackup().IncludedClusterScopeResources("persistentvolumes").ExcludedNamespacedResources("*").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("testing").Result(),
),
},
want: []string{
"resources/persistentvolumes/cluster/testing.json",
"resources/persistentvolumes/v1-preferredversion/cluster/testing.json",
},
},
{
name: "no namespaced resources + all cluster resources",
backup: defaultBackup().IncludedClusterScopeResources("*").ExcludedNamespacedResources("*").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("test1").Result(),
builder.ForPersistentVolume("test2").Result(),
),
test.CRDs(
builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(),
),
},
want: []string{
"resources/customresourcedefinitions.apiextensions.k8s.io/cluster/backups.velero.io.json",
"resources/persistentvolumes/cluster/test1.json",
"resources/persistentvolumes/cluster/test2.json",
"resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/backups.velero.io.json",
"resources/persistentvolumes/v1-preferredversion/cluster/test1.json",
"resources/persistentvolumes/v1-preferredversion/cluster/test2.json",
},
},
{
name: "some namespaced resources + no cluster resources",
backup: defaultBackup().ExcludedClusterScopeResources("*").IncludedNamespaces("foo", "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(),
),
test.PVs(
builder.ForPersistentVolume("test1").Result(),
builder.ForPersistentVolume("test2").Result(),
),
test.CRDs(
builder.ForCustomResourceDefinitionV1Beta1("backups.velero.io").Result(),
),
},
want: []string{
"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",
"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",
},
},
}

for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
var (
h = newHarness(t)
req = &Request{Backup: tc.backup}
backupFile = bytes.NewBuffer([]byte{})
)

for _, resource := range tc.apiResources {
h.addItems(t, resource)
}

h.backupper.Backup(h.log, req, backupFile, nil, nil)

assertTarballContents(t, backupFile, append(tc.want, "metadata/version")...)
})
}
}
57 changes: 48 additions & 9 deletions pkg/backup/item_backupper.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,17 +98,33 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr
log.Info("Excluding item because namespace is excluded")
return false, nil
}
// NOTE: we specifically allow namespaces to be backed up even if IncludeClusterResources is
// false.
if namespace == "" && groupResource != kuberesource.Namespaces && ib.backupRequest.Spec.IncludeClusterResources != nil && !*ib.backupRequest.Spec.IncludeClusterResources {
log.Info("Excluding item because resource is cluster-scoped and backup.spec.includeClusterResources is false")
return false, nil
}

if !ib.backupRequest.ResourceIncludesExcludes.ShouldInclude(groupResource.String()) {
log.Info("Excluding item because resource is excluded")
return false, nil
if useOldResourceFilters(ib.backupRequest.Spec) {
// NOTE: we specifically allow namespaces to be backed up even if IncludeClusterResources is false.
if namespace == "" && groupResource != kuberesource.Namespaces &&
ib.backupRequest.Spec.IncludeClusterResources != nil && !*ib.backupRequest.Spec.IncludeClusterResources {
log.Info("Excluding item because resource is cluster-scoped and is excluded by cluster filter.")
return false, nil
}

if !ib.backupRequest.ResourceIncludesExcludes.ShouldInclude(groupResource.String()) {
log.Info("Excluding item because resource is excluded")
return false, nil
}
} else {
// NOTE: we specifically allow namespaces to be backed up even if excluded by cluster-scoped resource filters.
if namespace == "" && groupResource != kuberesource.Namespaces &&
!ib.backupRequest.ClusterResourceIncludesExcludes.ClusterScopedShouldInclude(groupResource.String()) {
log.Info("Excluding item because resource is cluster-scoped and is excluded by cluster filter.")
return false, nil
}

if namespace != "" && !ib.backupRequest.NamespaceResourceIncludesExcludes.NamespacedShouldInclude(groupResource.String()) {
log.Info("Excluding namespaced item because resource is excluded.")
return false, nil
}
}

}

if metadata.GetDeletionTimestamp() != nil {
Expand Down Expand Up @@ -590,3 +606,26 @@ func zoneFromPVNodeAffinity(res *corev1api.PersistentVolume, topologyKeys ...str

return "", ""
}

// useOldResourceFilters checks whether to use old resource filters (IncludeClusterResources,
// IncludedResources and ExcludedResources), depending the backup's filters setting.
func useOldResourceFilters(backupSpec velerov1api.BackupSpec) bool {
// If all resource filters are none, it is treated as using old parameter filters.
if backupSpec.IncludeClusterResources == nil &&
len(backupSpec.IncludedResources) == 0 &&
len(backupSpec.ExcludedResources) == 0 &&
len(backupSpec.IncludedClusterScopeResources) == 0 &&
len(backupSpec.ExcludedClusterScopeResources) == 0 &&
len(backupSpec.IncludedNamespacedResources) == 0 &&
len(backupSpec.ExcludedNamespacedResources) == 0 {
return true
}

if backupSpec.IncludeClusterResources != nil ||
len(backupSpec.IncludedResources) > 0 ||
len(backupSpec.ExcludedResources) > 0 {
return true
}

return false
}
Loading

0 comments on commit ac4455a

Please sign in to comment.