Skip to content

Commit a363a76

Browse files
committed
Auto-exclude Velero namespace from backups instead of hard error
Replace the hard validation error with auto-exclude + warning log when the Velero namespace would be included in a backup. This avoids a breaking change where plain `velero backup create` would fail, and follows the existing precedent for namespaces with the velero.io/exclude-from-backup=true label. Detects the Velero namespace using the Kubernetes service account namespace file (/var/run/secrets/kubernetes.io/serviceaccount/namespace), falling back to request.Backup.Namespace. Signed-off-by: Achin Mishra <achinmishra@meta.com>
1 parent e6bdff6 commit a363a76

3 files changed

Lines changed: 122 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Auto-exclude the Velero namespace from backups with a warning instead of failing validation

pkg/controller/backup_controller.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,20 @@ func (b *backupReconciler) prepareBackupRequest(ctx context.Context, backup *vel
575575
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid included/excluded namespace lists: %v", err))
576576
}
577577

578+
// Auto-exclude the Velero namespace from the backup.
579+
// Velero does not support backing up its own namespace.
580+
veleroNs := request.Backup.Namespace
581+
if nsData, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace"); err == nil {
582+
veleroNs = string(nsData)
583+
}
584+
nsFilter := collections.NewIncludesExcludes().
585+
Includes(request.Spec.IncludedNamespaces...).
586+
Excludes(request.Spec.ExcludedNamespaces...)
587+
if nsFilter.ShouldInclude(veleroNs) {
588+
logger.Warnf("Velero namespace %q is being excluded from this backup because Velero does not support backing up its own namespace.", veleroNs)
589+
request.Spec.ExcludedNamespaces = append(request.Spec.ExcludedNamespaces, veleroNs)
590+
}
591+
578592
// validate that only one exists orLabelSelector or just labelSelector (singular)
579593
if request.Spec.OrLabelSelectors != nil && request.Spec.LabelSelector != nil {
580594
request.Status.ValidationErrors = append(request.Status.ValidationErrors, "encountered labelSelector as well as orLabelSelectors in backup spec, only one can be specified")

pkg/controller/backup_controller_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,94 @@ func TestProcessBackupValidationFailures(t *testing.T) {
270270
}
271271
}
272272

273+
func TestProcessBackupVeleroNamespaceValidation(t *testing.T) {
274+
defaultBackupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Phase(velerov1api.BackupStorageLocationPhaseAvailable).Result()
275+
276+
tests := []struct {
277+
name string
278+
backup *velerov1api.Backup
279+
expectedExcludedNamespaces []string
280+
expectValidationError bool
281+
}{
282+
{
283+
name: "velero namespace explicitly in IncludedNamespaces is auto-excluded",
284+
backup: builder.ForBackup(velerov1api.DefaultNamespace, "backup-1").Phase(velerov1api.BackupPhaseReadyToStart).IncludedNamespaces("velero").Result(),
285+
expectedExcludedNamespaces: []string{"velero"},
286+
},
287+
{
288+
name: "all namespaces (empty includes) auto-excludes velero namespace",
289+
backup: builder.ForBackup(velerov1api.DefaultNamespace, "backup-1").Phase(velerov1api.BackupPhaseReadyToStart).Result(),
290+
expectedExcludedNamespaces: []string{"velero"},
291+
},
292+
{
293+
name: "velero namespace already excluded stays excluded",
294+
backup: builder.ForBackup(velerov1api.DefaultNamespace, "backup-1").Phase(velerov1api.BackupPhaseReadyToStart).ExcludedNamespaces("velero").Result(),
295+
expectedExcludedNamespaces: []string{"velero"},
296+
},
297+
{
298+
name: "glob pattern matching velero namespace auto-excludes it",
299+
backup: builder.ForBackup(velerov1api.DefaultNamespace, "backup-1").Phase(velerov1api.BackupPhaseReadyToStart).IncludedNamespaces("vel*").Result(),
300+
expectedExcludedNamespaces: []string{"velero"},
301+
},
302+
{
303+
name: "glob pattern in excludes matching velero namespace keeps it excluded",
304+
backup: builder.ForBackup(velerov1api.DefaultNamespace, "backup-1").Phase(velerov1api.BackupPhaseReadyToStart).ExcludedNamespaces("vel*").Result(),
305+
expectedExcludedNamespaces: []string{"vel*"},
306+
},
307+
{
308+
name: "glob pattern not matching velero namespace does not auto-exclude",
309+
backup: builder.ForBackup(velerov1api.DefaultNamespace, "backup-1").Phase(velerov1api.BackupPhaseReadyToStart).IncludedNamespaces("prod-*").Result(),
310+
expectedExcludedNamespaces: nil,
311+
},
312+
{
313+
name: "single char wildcard matching velero namespace auto-excludes it",
314+
backup: builder.ForBackup(velerov1api.DefaultNamespace, "backup-1").Phase(velerov1api.BackupPhaseReadyToStart).IncludedNamespaces("veler?").Result(),
315+
expectedExcludedNamespaces: []string{"velero"},
316+
},
317+
{
318+
name: "character class wildcard matching velero namespace auto-excludes it",
319+
backup: builder.ForBackup(velerov1api.DefaultNamespace, "backup-1").Phase(velerov1api.BackupPhaseReadyToStart).IncludedNamespaces("[vV]elero").Result(),
320+
expectedExcludedNamespaces: []string{"velero"},
321+
expectValidationError: true,
322+
},
323+
}
324+
325+
for _, test := range tests {
326+
t.Run(test.name, func(t *testing.T) {
327+
formatFlag := logging.FormatText
328+
logger := logging.DefaultLogger(logrus.DebugLevel, formatFlag)
329+
330+
apiServer := velerotest.NewAPIServer(t)
331+
discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger)
332+
require.NoError(t, err)
333+
334+
fakeClient := velerotest.NewFakeControllerRuntimeClient(t, defaultBackupLocation)
335+
336+
c := &backupReconciler{
337+
logger: logger,
338+
discoveryHelper: discoveryHelper,
339+
kbClient: fakeClient,
340+
defaultBackupLocation: defaultBackupLocation.Name,
341+
clock: &clock.RealClock{},
342+
formatFlag: formatFlag,
343+
metrics: metrics.NewServerMetrics(),
344+
backupTracker: NewBackupTracker(),
345+
}
346+
347+
require.NotNil(t, test.backup)
348+
349+
res := c.prepareBackupRequest(ctx, test.backup, logger)
350+
defer res.WorkerPool.Stop()
351+
require.NotNil(t, res)
352+
353+
if !test.expectValidationError {
354+
assert.Empty(t, res.Status.ValidationErrors)
355+
}
356+
assert.Equal(t, test.expectedExcludedNamespaces, res.Spec.ExcludedNamespaces)
357+
})
358+
}
359+
}
360+
273361
func TestBackupLocationLabel(t *testing.T) {
274362
tests := []struct {
275363
name string
@@ -711,6 +799,7 @@ func TestProcessBackupCompletions(t *testing.T) {
711799
StorageLocation: defaultBackupLocation.Name,
712800
DefaultVolumesToFsBackup: boolptr.True(),
713801
SnapshotMoveData: boolptr.False(),
802+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
714803
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
715804
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
716805
},
@@ -750,6 +839,7 @@ func TestProcessBackupCompletions(t *testing.T) {
750839
StorageLocation: "alt-loc",
751840
DefaultVolumesToFsBackup: boolptr.False(),
752841
SnapshotMoveData: boolptr.False(),
842+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
753843
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
754844
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
755845
},
@@ -793,6 +883,7 @@ func TestProcessBackupCompletions(t *testing.T) {
793883
StorageLocation: "read-write",
794884
DefaultVolumesToFsBackup: boolptr.True(),
795885
SnapshotMoveData: boolptr.False(),
886+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
796887
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
797888
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
798889
},
@@ -833,6 +924,7 @@ func TestProcessBackupCompletions(t *testing.T) {
833924
StorageLocation: defaultBackupLocation.Name,
834925
DefaultVolumesToFsBackup: boolptr.False(),
835926
SnapshotMoveData: boolptr.False(),
927+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
836928
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
837929
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
838930
},
@@ -873,6 +965,7 @@ func TestProcessBackupCompletions(t *testing.T) {
873965
StorageLocation: defaultBackupLocation.Name,
874966
DefaultVolumesToFsBackup: boolptr.True(),
875967
SnapshotMoveData: boolptr.False(),
968+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
876969
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
877970
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
878971
},
@@ -914,6 +1007,7 @@ func TestProcessBackupCompletions(t *testing.T) {
9141007
StorageLocation: defaultBackupLocation.Name,
9151008
DefaultVolumesToFsBackup: boolptr.False(),
9161009
SnapshotMoveData: boolptr.False(),
1010+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
9171011
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
9181012
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
9191013
},
@@ -955,6 +1049,7 @@ func TestProcessBackupCompletions(t *testing.T) {
9551049
StorageLocation: defaultBackupLocation.Name,
9561050
DefaultVolumesToFsBackup: boolptr.True(),
9571051
SnapshotMoveData: boolptr.False(),
1052+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
9581053
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
9591054
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
9601055
},
@@ -996,6 +1091,7 @@ func TestProcessBackupCompletions(t *testing.T) {
9961091
StorageLocation: defaultBackupLocation.Name,
9971092
DefaultVolumesToFsBackup: boolptr.True(),
9981093
SnapshotMoveData: boolptr.False(),
1094+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
9991095
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
10001096
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
10011097
},
@@ -1037,6 +1133,7 @@ func TestProcessBackupCompletions(t *testing.T) {
10371133
StorageLocation: defaultBackupLocation.Name,
10381134
DefaultVolumesToFsBackup: boolptr.False(),
10391135
SnapshotMoveData: boolptr.False(),
1136+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
10401137
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
10411138
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
10421139
},
@@ -1079,6 +1176,7 @@ func TestProcessBackupCompletions(t *testing.T) {
10791176
StorageLocation: defaultBackupLocation.Name,
10801177
DefaultVolumesToFsBackup: boolptr.True(),
10811178
SnapshotMoveData: boolptr.False(),
1179+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
10821180
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
10831181
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
10841182
},
@@ -1121,6 +1219,7 @@ func TestProcessBackupCompletions(t *testing.T) {
11211219
StorageLocation: defaultBackupLocation.Name,
11221220
DefaultVolumesToFsBackup: boolptr.True(),
11231221
SnapshotMoveData: boolptr.False(),
1222+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
11241223
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
11251224
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
11261225
},
@@ -1163,6 +1262,7 @@ func TestProcessBackupCompletions(t *testing.T) {
11631262
StorageLocation: defaultBackupLocation.Name,
11641263
DefaultVolumesToFsBackup: boolptr.False(),
11651264
SnapshotMoveData: boolptr.True(),
1265+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
11661266
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
11671267
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
11681268
},
@@ -1206,6 +1306,7 @@ func TestProcessBackupCompletions(t *testing.T) {
12061306
StorageLocation: defaultBackupLocation.Name,
12071307
DefaultVolumesToFsBackup: boolptr.False(),
12081308
SnapshotMoveData: boolptr.False(),
1309+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
12091310
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
12101311
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
12111312
},
@@ -1249,6 +1350,7 @@ func TestProcessBackupCompletions(t *testing.T) {
12491350
StorageLocation: defaultBackupLocation.Name,
12501351
DefaultVolumesToFsBackup: boolptr.False(),
12511352
SnapshotMoveData: boolptr.False(),
1353+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
12521354
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
12531355
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
12541356
},
@@ -1292,6 +1394,7 @@ func TestProcessBackupCompletions(t *testing.T) {
12921394
StorageLocation: defaultBackupLocation.Name,
12931395
DefaultVolumesToFsBackup: boolptr.False(),
12941396
SnapshotMoveData: boolptr.True(),
1397+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
12951398
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
12961399
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
12971400
},
@@ -1336,6 +1439,7 @@ func TestProcessBackupCompletions(t *testing.T) {
13361439
StorageLocation: defaultBackupLocation.Name,
13371440
DefaultVolumesToFsBackup: boolptr.False(),
13381441
SnapshotMoveData: boolptr.False(),
1442+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
13391443
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
13401444
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
13411445
},
@@ -1379,6 +1483,7 @@ func TestProcessBackupCompletions(t *testing.T) {
13791483
StorageLocation: defaultBackupLocation.Name,
13801484
DefaultVolumesToFsBackup: boolptr.False(),
13811485
SnapshotMoveData: boolptr.True(),
1486+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
13821487
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
13831488
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
13841489
},
@@ -1427,6 +1532,7 @@ func TestProcessBackupCompletions(t *testing.T) {
14271532
DefaultVolumesToFsBackup: boolptr.False(),
14281533
SnapshotMoveData: boolptr.True(),
14291534
IncludedClusterScopedResources: []string{"storageclasses"},
1535+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
14301536
ExcludedClusterScopedResources: append([]string{"clusterroles"}, autoExcludeClusterScopedResources...),
14311537
IncludedNamespaceScopedResources: []string{"pods"},
14321538
ExcludedNamespaceScopedResources: append([]string{"secrets"}, autoExcludeNamespaceScopedResources...),
@@ -1476,6 +1582,7 @@ func TestProcessBackupCompletions(t *testing.T) {
14761582
DefaultVolumesToFsBackup: boolptr.False(),
14771583
SnapshotMoveData: boolptr.True(),
14781584
IncludedClusterScopedResources: []string{"storageclasses"},
1585+
ExcludedNamespaces: []string{velerov1api.DefaultNamespace},
14791586
ExcludedClusterScopedResources: append([]string{"clusterroles"}, autoExcludeClusterScopedResources...),
14801587
IncludedNamespaceScopedResources: []string{"pods"},
14811588
ExcludedNamespaceScopedResources: append([]string{"secrets"}, autoExcludeNamespaceScopedResources...),

0 commit comments

Comments
 (0)