Merge pull request #9743 from Joeavaikath/cherrypick-9684

Fix wildcard expansion when includes is empty and excludes has wildca…
This commit is contained in:
Xun Jiang/Bruce Jiang
2026-04-24 13:44:06 +08:00
committed by GitHub
5 changed files with 66 additions and 19 deletions

View File

@@ -0,0 +1 @@
Fix wildcard expansion when includes is empty and excludes has wildcards

View File

@@ -570,6 +570,13 @@ func (b *backupReconciler) prepareBackupRequest(ctx context.Context, backup *vel
}
}
// Empty IncludedNamespaces means "include all namespaces". Normalize
// to ["*"] so that downstream wildcard expansion does not collapse
// an empty-includes + wildcard-excludes combination into "back up nothing".
if len(request.Spec.IncludedNamespaces) == 0 {
request.Spec.IncludedNamespaces = []string{"*"}
}
// validate the included/excluded namespaces
for _, err := range collections.ValidateNamespaceIncludesExcludes(request.Spec.IncludedNamespaces, request.Spec.ExcludedNamespaces) {
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid included/excluded namespace lists: %v", err))

View File

@@ -320,6 +320,34 @@ func TestBackupLocationLabel(t *testing.T) {
}
}
func TestPrepareBackupRequest_EmptyIncludedNamespacesNormalizedToWildcard(t *testing.T) {
formatFlag := logging.FormatText
logger := logging.DefaultLogger(logrus.DebugLevel, formatFlag)
apiServer := velerotest.NewAPIServer(t)
discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger)
require.NoError(t, err)
backupLocation := builder.ForBackupStorageLocation("velero", "loc-1").Result()
fakeClient := velerotest.NewFakeControllerRuntimeClient(t, backupLocation)
c := &backupReconciler{
discoveryHelper: discoveryHelper,
kbClient: fakeClient,
defaultBackupLocation: backupLocation.Name,
clock: &clock.RealClock{},
formatFlag: formatFlag,
}
backup := defaultBackup().Result()
backup.Spec.IncludedNamespaces = nil
res := c.prepareBackupRequest(ctx, backup, logger)
defer res.WorkerPool.Stop()
assert.Equal(t, []string{"*"}, res.Spec.IncludedNamespaces)
}
func Test_prepareBackupRequest_BackupStorageLocation(t *testing.T) {
var (
defaultBackupTTL = metav1.Duration{Duration: 24 * 30 * time.Hour}
@@ -709,11 +737,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.True(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -749,11 +777,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: "alt-loc",
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -793,11 +821,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: "read-write",
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.True(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -834,11 +862,11 @@ func TestProcessBackupCompletions(t *testing.T) {
Spec: velerov1api.BackupSpec{
TTL: metav1.Duration{Duration: 10 * time.Minute},
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -875,11 +903,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.True(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -917,11 +945,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -959,11 +987,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.True(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -1001,11 +1029,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.True(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -1043,11 +1071,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -1086,11 +1114,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.True(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFailed,
@@ -1129,11 +1157,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.True(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFailed,
@@ -1172,11 +1200,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.True(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -1216,11 +1244,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -1260,11 +1288,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -1304,11 +1332,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.True(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -1349,11 +1377,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.False(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -1393,11 +1421,11 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.True(),
ExcludedClusterScopedResources: autoExcludeClusterScopedResources,
ExcludedNamespaceScopedResources: autoExcludeNamespaceScopedResources,
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -1441,13 +1469,13 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.True(),
IncludedClusterScopedResources: []string{"storageclasses"},
ExcludedClusterScopedResources: append([]string{"clusterroles"}, autoExcludeClusterScopedResources...),
IncludedNamespaceScopedResources: []string{"pods"},
ExcludedNamespaceScopedResources: append([]string{"secrets"}, autoExcludeNamespaceScopedResources...),
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,
@@ -1491,13 +1519,13 @@ func TestProcessBackupCompletions(t *testing.T) {
},
Spec: velerov1api.BackupSpec{
StorageLocation: defaultBackupLocation.Name,
IncludedNamespaces: []string{"*"},
DefaultVolumesToFsBackup: boolptr.False(),
SnapshotMoveData: boolptr.True(),
IncludedClusterScopedResources: []string{"storageclasses"},
ExcludedClusterScopedResources: append([]string{"clusterroles"}, autoExcludeClusterScopedResources...),
IncludedNamespaceScopedResources: []string{"pods"},
ExcludedNamespaceScopedResources: append([]string{"secrets"}, autoExcludeNamespaceScopedResources...),
IncludedNamespaces: []string{"*"},
},
Status: velerov1api.BackupStatus{
Phase: velerov1api.BackupPhaseFinalizing,

View File

@@ -19,6 +19,11 @@ func ShouldExpandWildcards(includes []string, excludes []string, fromBackup bool
return false
}
// Empty includes is equivalent to * (match all) - don't expand
if len(includes) == 0 {
return false
}
wildcardFound := false
for _, include := range includes {
if containsWildcardPattern(include) {

View File

@@ -90,6 +90,12 @@ func TestShouldExpandWildcards(t *testing.T) {
fromBackup: true,
expected: false,
},
{
name: "empty includes with wildcard excludes - should not expand",
includes: []string{},
excludes: []string{"ns*"},
expected: false,
},
{
name: "complex wildcard patterns",
includes: []string{"*-prod"},