Merge pull request #9821 from adam-jian-zhang/enhance-backup-filters-interface

extend backup resource policy
This commit is contained in:
Adam Zhang
2026-05-22 14:08:21 +08:00
committed by GitHub
3 changed files with 107 additions and 6 deletions

View File

@@ -0,0 +1 @@
Fix issue #9811, add interface to support ClusterScopedFilterPolicy and NamespacedFilterPolicy

View File

@@ -54,6 +54,31 @@ type Action struct {
Parameters map[string]any `yaml:"parameters,omitempty"`
}
// ResourceFilter defines a filter for specific resource kinds.
type ResourceFilter struct {
Kinds []string `yaml:"kinds"`
LabelSelector map[string]string `yaml:"labelSelector,omitempty"`
OrLabelSelectors []map[string]string `yaml:"orLabelSelectors,omitempty"`
Names []string `yaml:"names,omitempty"`
ExcludedNames []string `yaml:"excludedNames,omitempty"`
}
// IsCatchAll returns true if the filter is a catch-all entry (empty kinds or ["*"])
func (rf *ResourceFilter) IsCatchAll() bool {
return len(rf.Kinds) == 0 || (len(rf.Kinds) == 1 && rf.Kinds[0] == "*")
}
// ClusterScopedFilterPolicy defines backup filters scoped globally to cluster-scoped resources.
type ClusterScopedFilterPolicy struct {
ResourceFilters []ResourceFilter `yaml:"resourceFilters"`
}
// NamespacedFilterPolicy defines backup filters scoped to specific namespaces.
type NamespacedFilterPolicy struct {
Namespaces []string `yaml:"namespaces"`
ResourceFilters []ResourceFilter `yaml:"resourceFilters"`
}
// IncludeExcludePolicy defined policy to include or exclude resources based on the names
type IncludeExcludePolicy struct {
// The following fields have the same semantics as those from the spec of backup.
@@ -95,17 +120,21 @@ type VolumePolicy struct {
// ResourcePolicies currently defined slice of volume policies to handle backup
type ResourcePolicies struct {
Version string `yaml:"version"`
VolumePolicies []VolumePolicy `yaml:"volumePolicies"`
IncludeExcludePolicy *IncludeExcludePolicy `yaml:"includeExcludePolicy"`
Version string `yaml:"version"`
VolumePolicies []VolumePolicy `yaml:"volumePolicies"`
IncludeExcludePolicy *IncludeExcludePolicy `yaml:"includeExcludePolicy"`
ClusterScopedFilterPolicy *ClusterScopedFilterPolicy `yaml:"clusterScopedFilterPolicy,omitempty"`
NamespacedFilterPolicies []NamespacedFilterPolicy `yaml:"namespacedFilterPolicies,omitempty"`
// we may support other resource policies in the future, and they could be added separately
// OtherResourcePolicies []OtherResourcePolicy
}
type Policies struct {
version string
volumePolicies []volPolicy
includeExcludePolicy *IncludeExcludePolicy
version string
volumePolicies []volPolicy
includeExcludePolicy *IncludeExcludePolicy
clusterScopedFilterPolicy *ClusterScopedFilterPolicy
namespacedFilterPolicies []NamespacedFilterPolicy
// OtherPolicies
}
@@ -158,6 +187,8 @@ func (p *Policies) BuildPolicy(resPolicies *ResourcePolicies) error {
p.version = resPolicies.Version
p.includeExcludePolicy = resPolicies.IncludeExcludePolicy
p.clusterScopedFilterPolicy = resPolicies.ClusterScopedFilterPolicy
p.namespacedFilterPolicies = resPolicies.NamespacedFilterPolicies
return nil
}
@@ -235,6 +266,14 @@ func (p *Policies) GetIncludeExcludePolicy() *IncludeExcludePolicy {
return p.includeExcludePolicy
}
func (p *Policies) GetClusterScopedFilterPolicy() *ClusterScopedFilterPolicy {
return p.clusterScopedFilterPolicy
}
func (p *Policies) GetNamespacedFilterPolicies() []NamespacedFilterPolicy {
return p.namespacedFilterPolicies
}
func GetResourcePoliciesFromBackup(
backup velerov1api.Backup,
client crclient.Client,

View File

@@ -19,6 +19,9 @@ package backup
import (
"sync"
"github.com/gobwas/glob"
"k8s.io/apimachinery/pkg/labels"
"github.com/vmware-tanzu/velero/internal/hook"
"github.com/vmware-tanzu/velero/internal/resourcepolicies"
"github.com/vmware-tanzu/velero/internal/volume"
@@ -34,6 +37,21 @@ type itemKey struct {
name string
}
// ResolvedResourceFilter holds the materialized filter state for one kind-group
// within a namespace.
type ResolvedResourceFilter struct {
LabelSelector labels.Selector
OrLabelSelectors []labels.Selector
NameIE *collections.IncludesExcludes
}
// ResolvedNamespaceFilter holds the materialized filter state for a namespace.
// ResourceFilterMap is keyed by the resolved group-resource string.
type ResolvedNamespaceFilter struct {
ResourceFilterMap map[string]*ResolvedResourceFilter
CatchAllFilter *ResolvedResourceFilter
}
type SynchronizedVSList struct {
sync.Mutex
VolumeSnapshotList []*volume.Snapshot
@@ -70,6 +88,27 @@ type Request struct {
SkippedPVTracker *skipPVTracker
VolumesInformation volume.BackupVolumesInformation
WorkerPool *ItemBlockWorkerPool
// ClusterScopedFilterMap holds resolved global filters for cluster-scoped resources.
// Key is the resolved group-resource string.
ClusterScopedFilterMap map[string]*ResolvedResourceFilter
// NamespacedFilterMap holds resolved per-namespace filters.
// Key is either an exact namespace name or a glob pattern.
NamespacedFilterMap map[string]*ResolvedNamespaceFilter
// NamespacedFilterPatterns preserves the order of patterns for first-match semantics
// and caches pre-compiled globs to avoid repeated compilation in the hot path.
NamespacedFilterPatterns []NamespacedFilterPattern
}
// NamespacedFilterPattern pairs a namespace pattern string with its pre-compiled
// glob so that GetNamespaceFilter does not recompile on every call.
// Compiled is nil for exact-match (non-glob) patterns, which are looked up
// directly in NamespacedFilterMap.
type NamespacedFilterPattern struct {
Pattern string
Compiled glob.Glob
}
// BackupVolumesInformation contains the information needs by generating
@@ -107,3 +146,25 @@ func (r *Request) FillVolumesInformation() {
func (r *Request) StopWorkerPool() {
r.WorkerPool.Stop()
}
// GetNamespaceFilter returns the resolved filter for a namespace, or nil
// if the namespace should use global filters. Uses first-match semantics
// when multiple patterns could match the same namespace.
func (r *Request) GetNamespaceFilter(namespace string) *ResolvedNamespaceFilter {
if r.NamespacedFilterMap == nil {
return nil
}
// First check for exact match
if f, ok := r.NamespacedFilterMap[namespace]; ok {
return f
}
// Walk patterns in definition order using pre-compiled globs (no allocation per call)
for _, p := range r.NamespacedFilterPatterns {
if p.Compiled != nil && p.Compiled.Match(namespace) {
return r.NamespacedFilterMap[p.Pattern]
}
}
return nil
}