mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-07-25 17:43:00 +00:00
Merge pull request #10052 from adam-jian-zhang/prioritize-exact-ns-match-for-1.18
Run the E2E test on kind / get-go-version (push) Successful in 1m10s
Run the E2E test on kind / setup-test-matrix (push) Successful in 3s
Main CI / get-go-version (push) Successful in 11s
Run the E2E test on kind / build (push) Failing after 24s
Run the E2E test on kind / run-e2e-test (push) Has been skipped
Main CI / Build (push) Failing after 28s
Run the E2E test on kind / get-go-version (push) Successful in 1m10s
Run the E2E test on kind / setup-test-matrix (push) Successful in 3s
Main CI / get-go-version (push) Successful in 11s
Run the E2E test on kind / build (push) Failing after 24s
Run the E2E test on kind / run-e2e-test (push) Has been skipped
Main CI / Build (push) Failing after 28s
[Cherry-pick]Prioritize exact namespace match in restore (#10033)
This commit is contained in:
@@ -0,0 +1 @@
|
||||
Fix issue #10032, prioritize exact namespace match in restore
|
||||
@@ -307,6 +307,9 @@ The `getNamespaceFilter()` method on `restoreContext` takes the original namespa
|
||||
**Plugin Additional Items (Restore-Side):**
|
||||
Like the backup side — which is permissive at Stage 2 to allow CSI plugin-injected resources through — the restore side is permissive for AdditionalItems in `restoreItem()`. If a restore plugin requests an additional item, it is allowed to bypass the fine-grained `namespacedFilterPolicies` and `clusterScopedFilterPolicy` kind, name, and label selector checks. This allows plugins to successfully restore dependencies (like a PV needed by a PVC, or a specific Secret) without the user having to explicitly authorize every single dependent resource type in their configuration. Note that these additional items must still pass global resource/namespace exclusions.
|
||||
|
||||
**Exact Namespace Match Priority:**
|
||||
If a namespace matches both an exact name pattern and a glob pattern across different `namespacedFilterPolicies` entries, the exact match always takes precedence, regardless of list order. This aligns with the backup pipeline behavior and ensures specific overrides are always honored.
|
||||
|
||||
**Multiple Glob Patterns Matching Same Namespace (Incorrect Order):**
|
||||
```yaml
|
||||
namespacedFilterPolicies:
|
||||
@@ -665,7 +668,7 @@ data:
|
||||
|
||||
### Restore with Glob Namespace Patterns
|
||||
|
||||
Apply the same filter to all namespaces matching a pattern. **Critical: Order patterns from most specific to least specific:**
|
||||
Apply the same filter to all namespaces matching a pattern. **Note on Precedence:** Exact namespace matches always take precedence regardless of where they are listed. However, if multiple glob patterns could match a namespace, they are evaluated in the order they appear. Always list specific globs before broad globs.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
@@ -677,19 +680,21 @@ data:
|
||||
policy: |
|
||||
version: v1
|
||||
namespacedFilterPolicies:
|
||||
# More specific patterns first
|
||||
# Globs must be ordered specific-to-broad
|
||||
- namespaces:
|
||||
- "team-frontend-prod" # Most specific (exact match)
|
||||
resourceFilters:
|
||||
- kinds: [Deployment, Service, ConfigMap, Secret, PersistentVolumeClaim]
|
||||
- namespaces:
|
||||
- "team-frontend-*" # Less specific (pattern match)
|
||||
- "team-frontend-*" # specific pattern match
|
||||
resourceFilters:
|
||||
- kinds: [Deployment, Service, ConfigMap]
|
||||
- namespaces:
|
||||
- "team-*" # Least specific (broad pattern)
|
||||
- "team-*" # broad pattern
|
||||
resourceFilters:
|
||||
- kinds: [Deployment, Service]
|
||||
|
||||
# Exact matches always win, even if placed at the bottom
|
||||
- namespaces:
|
||||
- "team-frontend-prod" # exact match
|
||||
resourceFilters:
|
||||
- kinds: [Deployment, Service, ConfigMap, Secret, PersistentVolumeClaim]
|
||||
```
|
||||
|
||||
**Pattern Matching Results:**
|
||||
|
||||
+10
-6
@@ -478,7 +478,15 @@ func (ctx *restoreContext) getNamespaceFilter(namespace string) *resolvedNamespa
|
||||
return filter
|
||||
}
|
||||
|
||||
// 2. Walk patterns in definition order (first-match semantics)
|
||||
// 2. Check for exact match first (O(1) map lookup)
|
||||
// This ensures exact namespace matches take precedence over globs,
|
||||
// regardless of where they are listed in the configuration.
|
||||
if filter, ok := ctx.namespacedFilterMap[namespace]; ok {
|
||||
ctx.namespaceFilterCache[namespace] = filter
|
||||
return filter
|
||||
}
|
||||
|
||||
// 3. Walk patterns in definition order using pre-compiled globs
|
||||
// Note: namespaceFilterCache is mutated below without synchronization. This is safe
|
||||
// today because resource collection runs sequentially. If the restore loop is
|
||||
// parallelized in the future, these map writes will need a lock to prevent data races.
|
||||
@@ -489,14 +497,10 @@ func (ctx *restoreContext) getNamespaceFilter(namespace string) *resolvedNamespa
|
||||
ctx.namespaceFilterCache[namespace] = filter
|
||||
return filter
|
||||
}
|
||||
} else if p.pattern == namespace {
|
||||
filter := ctx.namespacedFilterMap[p.pattern]
|
||||
ctx.namespaceFilterCache[namespace] = filter
|
||||
return filter
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Cache the miss so we don't re-evaluate failed matches
|
||||
// 4. Cache the miss so we don't re-evaluate failed matches
|
||||
ctx.namespaceFilterCache[namespace] = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ namespacedFilterPolicies:
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespaced filter policy with glob namespace match and first-match semantics",
|
||||
name: "namespaced filter policy with exact match priority over glob (glob listed first)",
|
||||
restore: defaultRestore().Result(),
|
||||
backup: defaultBackup().Result(),
|
||||
policyYAML: `version: v1
|
||||
@@ -94,7 +94,43 @@ namespacedFilterPolicies:
|
||||
test.Pods(),
|
||||
},
|
||||
want: map[*test.APIResource][]string{
|
||||
test.Pods(): {"ns-1/pod-1", "ns-2/pod-1"},
|
||||
test.Pods(): {"ns-1/pod-2", "ns-2/pod-1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "namespaced filter policy with exact match priority over glob (exact listed first)",
|
||||
restore: defaultRestore().Result(),
|
||||
backup: defaultBackup().Result(),
|
||||
policyYAML: `version: v1
|
||||
namespacedFilterPolicies:
|
||||
- namespaces:
|
||||
- ns-1
|
||||
resourceFilters:
|
||||
- kinds:
|
||||
- pods
|
||||
names:
|
||||
- pod-2
|
||||
- namespaces:
|
||||
- ns-*
|
||||
resourceFilters:
|
||||
- kinds:
|
||||
- pods
|
||||
names:
|
||||
- pod-1
|
||||
`,
|
||||
tarball: test.NewTarWriter(t).
|
||||
AddItems("pods",
|
||||
builder.ForPod("ns-1", "pod-1").Result(),
|
||||
builder.ForPod("ns-1", "pod-2").Result(),
|
||||
builder.ForPod("ns-2", "pod-1").Result(),
|
||||
builder.ForPod("ns-2", "pod-2").Result(),
|
||||
).
|
||||
Done(),
|
||||
apiResources: []*test.APIResource{
|
||||
test.Pods(),
|
||||
},
|
||||
want: map[*test.APIResource][]string{
|
||||
test.Pods(): {"ns-1/pod-2", "ns-2/pod-1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user