|
| 1 | +# Design proposal: Namespace Selection by Label Selector for Velero Backups |
| 2 | + |
| 3 | +## Abstract |
| 4 | + |
| 5 | +This proposal modifies Velero's backup behavior to treat namespaces selected by `LabelSelector` or `OrLabelSelectors` as if they were explicitly listed in `includedNamespaces`, causing all resources within those namespaces to be included in the backup. |
| 6 | +Currently, when a namespace matches a label selector, only the namespace object itself and individually labeled resources are backed up, which is not the expected behavior for most users who want complete namespace backups. |
| 7 | + |
| 8 | +## Background |
| 9 | + |
| 10 | +Users frequently need to backup entire namespaces dynamically based on labels rather than maintaining static lists in `includedNamespaces`. |
| 11 | +The current `LabelSelector` behavior only includes resources that individually match the selector, making it difficult to achieve complete namespace backups through labeling. |
| 12 | +This creates operational overhead where users must either manually maintain namespace lists or label every resource within target namespaces. |
| 13 | + |
| 14 | +Issue #7492 requests this functionality with strong community support (+5 reactions) and multiple real-world use cases from users managing multi-cluster environments where namespace lists vary dynamically. |
| 15 | + |
| 16 | +## Goals |
| 17 | + |
| 18 | +- Enable complete namespace backup through namespace label selection |
| 19 | +- Provide clear precedence rules when combining inclusion/exclusion methods |
| 20 | +- Support full Kubernetes label selector syntax including complex operators |
| 21 | + |
| 22 | +## Non Goals |
| 23 | + |
| 24 | +- Changing behavior of resource-level label selectors within namespaces (resources still respect individual selectors within selected namespaces) |
| 25 | +- Adding new API fields (we will modify existing `LabelSelector` behavior) |
| 26 | +- Supporting per-resource granular control within label-selected namespaces |
| 27 | + |
| 28 | +## High-Level Design |
| 29 | + |
| 30 | +When a namespace matches `spec.labelSelector` or any `spec.orLabelSelectors`, Velero will treat that namespace as if it were explicitly listed in `spec.includedNamespaces`. |
| 31 | +All resources within matching namespaces will be included in the backup, regardless of their individual labels. |
| 32 | +The final namespace selection follows the precedence: `(includedNamespaces ∪ labelSelector_matches ∪ orLabelSelector_matches) - excludedNamespaces`. |
| 33 | + |
| 34 | +## Detailed Design |
| 35 | + |
| 36 | +### Namespace Selection Logic |
| 37 | + |
| 38 | +The namespace selection algorithm follows this formula: |
| 39 | + |
| 40 | +```text |
| 41 | +Final Namespaces = (ExplicitIncludes ∪ LabelSelectorMatches ∪ OrLabelSelectorMatches) - ExplicitExcludes |
| 42 | +``` |
| 43 | + |
| 44 | +Where: |
| 45 | + |
| 46 | +- `ExplicitIncludes`: Namespaces in `spec.includedNamespaces` |
| 47 | +- `LabelSelectorMatches`: Namespaces matching `spec.labelSelector` |
| 48 | +- `OrLabelSelectorMatches`: Namespaces matching any selector in `spec.orLabelSelectors[]` |
| 49 | +- `ExplicitExcludes`: Namespaces in `spec.excludedNamespaces` (highest precedence) |
| 50 | + |
| 51 | +### Implementation Changes |
| 52 | + |
| 53 | +#### Core Logic Modification |
| 54 | + |
| 55 | +The primary change occurs in `pkg/backup/item_collector.go` in the `nsTracker.init()` method (lines 102-166). |
| 56 | +Currently, this method tracks namespaces for filtering but doesn't change the fundamental inclusion behavior. |
| 57 | + |
| 58 | +**Current behavior:** |
| 59 | +```go |
| 60 | +// Namespace matches selector -> track for resource filtering only |
| 61 | +if nt.singleLabelSelector != nil && nt.singleLabelSelector.Matches(labels.Set(namespace.GetLabels())) { |
| 62 | + nt.track(namespace.GetName()) |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +**New behavior:** |
| 67 | +```go |
| 68 | +// Namespace matches selector -> track AND include all resources in namespace |
| 69 | +if nt.singleLabelSelector != nil && nt.singleLabelSelector.Matches(labels.Set(namespace.GetLabels())) { |
| 70 | + nt.track(namespace.GetName()) |
| 71 | + // This namespace now behaves like it's in includedNamespaces |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +#### Label Selector Support |
| 76 | + |
| 77 | +Full Kubernetes label selector support including: |
| 78 | + |
| 79 | +**Equality-based requirements:** |
| 80 | +- `key=value` (equality) |
| 81 | +- `key!=value` (inequality) |
| 82 | + |
| 83 | +**Set-based requirements:** |
| 84 | +- `key in (value1,value2)` (set inclusion) |
| 85 | +- `key notin (value1,value2)` (set exclusion) |
| 86 | +- `key` (key exists) |
| 87 | +- `!key` (key does not exist) |
| 88 | + |
| 89 | +**MatchExpressions support:** |
| 90 | +```yaml |
| 91 | +labelSelector: |
| 92 | + matchLabels: |
| 93 | + environment: production |
| 94 | + matchExpressions: |
| 95 | + - key: tier |
| 96 | + operator: In |
| 97 | + values: ["web", "api"] |
| 98 | + - key: deprecated |
| 99 | + operator: DoesNotExist |
| 100 | +``` |
| 101 | +
|
| 102 | +### API Schema (No Changes Required) |
| 103 | +
|
| 104 | +The existing `BackupSpec` already supports the required fields: |
| 105 | +```go |
| 106 | +type BackupSpec struct { |
| 107 | + // ... existing fields ... |
| 108 | + LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"` |
| 109 | + OrLabelSelectors []*metav1.LabelSelector `json:"orLabelSelectors,omitempty"` |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +### Example Usage Scenarios |
| 114 | + |
| 115 | +#### Scenario 1: Basic Environment-Based Backup |
| 116 | +```yaml |
| 117 | +apiVersion: velero.io/v1 |
| 118 | +kind: Backup |
| 119 | +metadata: |
| 120 | + name: production-backup |
| 121 | +spec: |
| 122 | + labelSelector: |
| 123 | + matchLabels: |
| 124 | + environment: production |
| 125 | +``` |
| 126 | +Result: All namespaces with `environment=production` are completely backed up. |
| 127 | + |
| 128 | +#### Scenario 2: Complex Selection with Exclusions |
| 129 | +```yaml |
| 130 | +spec: |
| 131 | + includedNamespaces: ["critical-ns"] |
| 132 | + labelSelector: |
| 133 | + matchExpressions: |
| 134 | + - key: backup-tier |
| 135 | + operator: In |
| 136 | + values: ["daily", "weekly"] |
| 137 | + - key: deprecated |
| 138 | + operator: DoesNotExist |
| 139 | + excludedNamespaces: ["temp-ns"] |
| 140 | +``` |
| 141 | +Result: `critical-ns` + (namespaces with `backup-tier` in daily/weekly AND no `deprecated` label) - `temp-ns`. |
| 142 | + |
| 143 | +#### Scenario 3: OrLabelSelectors Usage |
| 144 | +```yaml |
| 145 | +spec: |
| 146 | + orLabelSelectors: |
| 147 | + - matchLabels: |
| 148 | + backup: "true" |
| 149 | + - matchLabels: |
| 150 | + critical: "true" |
| 151 | + environment: "production" |
| 152 | +``` |
| 153 | +Result: Namespaces with `backup=true` OR (`critical=true` AND `environment=production`). |
| 154 | + |
| 155 | +### Logging and Observability |
| 156 | + |
| 157 | +Enhanced logging will provide clear visibility into namespace selection: |
| 158 | + |
| 159 | +``` |
| 160 | +INFO Backup namespace selection: |
| 161 | + Explicitly included: [ns1, ns2] |
| 162 | + Selected by labelSelector 'environment=production': [ns3, ns4] |
| 163 | + Selected by orLabelSelectors: [ns5, ns6] |
| 164 | + Explicitly excluded: [ns2, temp-ns] |
| 165 | + Final included namespaces: [ns1, ns3, ns4, ns5, ns6] |
| 166 | +``` |
| 167 | + |
| 168 | +Backup status will include selected namespace information: |
| 169 | +```yaml |
| 170 | +status: |
| 171 | + phase: Completed |
| 172 | + namespaces: |
| 173 | + included: ["ns1", "ns3", "ns4", "ns5", "ns6"] |
| 174 | + explicitlyIncluded: ["ns1"] |
| 175 | + selectedByLabels: ["ns3", "ns4", "ns5", "ns6"] |
| 176 | + excluded: ["ns2", "temp-ns"] |
| 177 | +``` |
| 178 | + |
| 179 | +### Validation and Warnings |
| 180 | + |
| 181 | +Warning messages for potentially confusing configurations: |
| 182 | +- "Namespace 'ns2' is both explicitly included and excluded - exclusion takes precedence" |
| 183 | +- "Using both labelSelector and orLabelSelectors - both will be evaluated" |
| 184 | +- "Complex label selectors may select unexpected namespaces - verify selection before running" |
| 185 | + |
| 186 | +### Breaking Change Considerations |
| 187 | + |
| 188 | +This is a **breaking change** from current behavior: |
| 189 | +- Existing backups using `labelSelector` will include more resources |
| 190 | +- Backup size and duration may increase |
| 191 | +- Current behavior has limited practical value, reducing migration impact |
| 192 | + |
| 193 | +Migration considerations: |
| 194 | +- Document change prominently in release notes |
| 195 | +- Provide before/after examples |
| 196 | +- Consider deprecation warning in prior version |
| 197 | + |
| 198 | +## Alternatives Considered |
| 199 | + |
| 200 | +### Alternative 1: New NamespaceSelector Field |
| 201 | +```go |
| 202 | +type BackupSpec struct { |
| 203 | + NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"` |
| 204 | +} |
| 205 | +``` |
| 206 | +**Pros:** No breaking changes, clear separation of concerns |
| 207 | +**Cons:** Adds API complexity, community feedback against new fields |
| 208 | + |
| 209 | +### Alternative 2: Boolean Flag to Change Behavior |
| 210 | +```go |
| 211 | +type BackupSpec struct { |
| 212 | + IncludeResourcesInLabeledNamespaces *bool `json:"includeResourcesInLabeledNamespaces,omitempty"` |
| 213 | +} |
| 214 | +``` |
| 215 | +**Pros:** Backward compatible, explicit opt-in |
| 216 | +**Cons:** Complex configuration, confusing API surface |
| 217 | + |
| 218 | +### Alternative 3: Separate Namespace and Resource Selectors |
| 219 | +Keep existing `labelSelector` for resources, add `namespaceSelector` for namespaces. |
| 220 | +**Cons:** Most complex option, unclear interaction patterns |
| 221 | + |
| 222 | +**Decision:** Modify existing behavior (chosen approach) based on community feedback and limited utility of current behavior. |
| 223 | + |
| 224 | +## Security Considerations |
| 225 | + |
| 226 | +### RBAC Implications |
| 227 | +Users with permission to modify backup specs can now potentially backup additional namespaces through label manipulation. |
| 228 | +Existing RBAC controls on backup resources remain the primary security boundary. |
| 229 | + |
| 230 | +### Label-based Access Control |
| 231 | +Organizations using label-based policies should ensure backup service accounts have appropriate namespace and resource access. |
| 232 | +No additional privileges are required beyond current backup operations. |
| 233 | + |
| 234 | +### Sensitive Data Exposure |
| 235 | +The expanded scope of backups may include additional sensitive data. |
| 236 | +Users should audit namespace labels and exclusion rules to prevent unintended data exposure. |
| 237 | + |
| 238 | +## Compatibility |
| 239 | + |
| 240 | +### Backward Compatibility |
| 241 | +**Breaking Change:** Existing backups using `labelSelector` will behave differently. |
| 242 | +Impact assessment: |
| 243 | +- Current `labelSelector` usage is limited due to reduced utility |
| 244 | +- Most users already avoid this pattern in favor of explicit namespace listing |
| 245 | +- Change aligns behavior with user expectations |
| 246 | + |
| 247 | +### API Compatibility |
| 248 | +No API schema changes required. |
| 249 | +Existing backup configurations remain syntactically valid but may produce different results. |
| 250 | + |
| 251 | +### Velero Version Compatibility |
| 252 | +- Backups created with new behavior remain compatible with older Velero versions for restore |
| 253 | +- Backup metadata format unchanged |
| 254 | +- No changes to backup storage format |
| 255 | + |
| 256 | +### Kubernetes Version Compatibility |
| 257 | +No changes to Kubernetes version requirements. |
| 258 | +Label selector functionality leverages existing Kubernetes APIs. |
| 259 | + |
| 260 | +## Implementation |
| 261 | + |
| 262 | +### Development Phases |
| 263 | + |
| 264 | +**Phase 1: Core Logic Implementation** |
| 265 | +- Modify `nsTracker` behavior in `item_collector.go` |
| 266 | +- Update namespace selection algorithm |
| 267 | +- Add comprehensive unit tests |
| 268 | +- Timeline: 2 weeks |
| 269 | + |
| 270 | +**Phase 2: Full Kubernetes Label Selector Support** |
| 271 | +- Implement support for all operator types (`In`, `NotIn`, `Exists`, `DoesNotExist`) |
| 272 | +- Add validation for complex selectors |
| 273 | +- Timeline: 1 week |
| 274 | + |
| 275 | +**Phase 3: Enhanced Logging and Observability** |
| 276 | +- Add detailed namespace selection logging |
| 277 | +- Update backup status reporting |
| 278 | +- Add validation warnings |
| 279 | +- Timeline: 1 week |
| 280 | + |
| 281 | +**Phase 4: Documentation and Testing** |
| 282 | +- Update user documentation with examples |
| 283 | +- Add integration and E2E tests |
| 284 | +- Create migration guide |
| 285 | +- Timeline: 2 weeks |
| 286 | + |
| 287 | +### Testing Strategy |
| 288 | + |
| 289 | +**Unit Tests:** |
| 290 | +- Namespace selection algorithm with all operator combinations |
| 291 | +- Precedence rules with various include/exclude patterns |
| 292 | +- Edge cases (empty selectors, wildcard usage, non-existent namespaces) |
| 293 | + |
| 294 | +**Integration Tests:** |
| 295 | +- Backup creation with namespace selectors |
| 296 | +- Verification of complete namespace resource inclusion |
| 297 | +- Backup status and logging validation |
| 298 | + |
| 299 | +**E2E Tests:** |
| 300 | +- Multi-namespace backup/restore scenarios |
| 301 | +- Cross-cluster restore operations |
| 302 | +- Performance testing with large namespace counts |
| 303 | + |
| 304 | +### Resources |
| 305 | + |
| 306 | +- **Primary Developer:** Tiger Kaovilai (@kaovilai) |
| 307 | +- **Reviewers:** Daniel Jiang, blackpiglet, shubham-pampattiwar |
| 308 | +- **Target Release:** Velero v1.18 |
| 309 | +- **Estimated Effort:** 6 weeks total development + testing |
| 310 | + |
| 311 | +## Open Issues |
| 312 | + |
| 313 | +### Issue 1: Performance Impact with Large Cluster |
| 314 | +Large clusters with many namespaces may experience performance degradation during label selector evaluation. |
| 315 | +**Potential Solution:** Implement caching for namespace label queries, optimize selector evaluation. |
| 316 | + |
| 317 | +### Issue 2: Label Change Detection |
| 318 | +Currently, Velero doesn't re-evaluate namespace selection if labels change between backup creation and execution. |
| 319 | +**Potential Solution:** Document current behavior, consider future enhancement for dynamic re-evaluation. |
| 320 | + |
| 321 | +### Issue 3: Complex Selector User Education |
| 322 | +Users may create overly complex selectors that are difficult to understand or maintain. |
| 323 | +**Potential Solution:** Provide comprehensive documentation, examples, and possibly a selector validation tool. |
| 324 | + |
| 325 | +### Issue 4: Interaction with Other Velero Features |
| 326 | +Need to verify behavior interaction with: |
| 327 | +- Resource policies |
| 328 | +- Item actions and plugins |
| 329 | +- Hooks and pre/post backup operations |
| 330 | +**Status:** Requires additional analysis and testing during implementation. |
0 commit comments