diff --git a/pkg/util/collections/includes_excludes.go b/pkg/util/collections/includes_excludes.go index 898d1a260..231689be0 100644 --- a/pkg/util/collections/includes_excludes.go +++ b/pkg/util/collections/includes_excludes.go @@ -164,21 +164,13 @@ func ValidateNamespaceIncludesExcludes(includesList, excludesList []string) []er excludes := sets.NewString(excludesList...) for _, itm := range includes.List() { - // Although asterisks is not a valid Kubernetes namespace name, it is - // allowed here. - if itm != "*" { - if nsErrs := validateNamespaceName(itm); nsErrs != nil { - errs = append(errs, nsErrs...) - } + if nsErrs := validateNamespaceName(itm); nsErrs != nil { + errs = append(errs, nsErrs...) } } - for _, itm := range excludes.List() { - // Asterisks in excludes list have been checked previously. - if itm != "*" { - if nsErrs := validateNamespaceName(itm); nsErrs != nil { - errs = append(errs, nsErrs...) - } + if nsErrs := validateNamespaceName(itm); nsErrs != nil { + errs = append(errs, nsErrs...) } } @@ -188,7 +180,18 @@ func ValidateNamespaceIncludesExcludes(includesList, excludesList []string) []er func validateNamespaceName(ns string) []error { var errs []error - if errMsgs := validation.ValidateNamespaceName(ns, false); errMsgs != nil { + // Velero interprets empty string as "no namespace", so allow it even though + // it is not a valid Kubernetes name. + if ns == "" { + return nil + } + + // Kubernetes does not allow asterisks in namespaces but Velero uses them as + // wildcards. Replace asterisks with an arbitrary letter to pass Kubernetes + // validation. + tmpNamespace := strings.ReplaceAll(ns, "*", "x") + + if errMsgs := validation.ValidateNamespaceName(tmpNamespace, false); errMsgs != nil { for _, msg := range errMsgs { errs = append(errs, errors.Errorf("invalid namespace %q: %s", ns, msg)) } diff --git a/pkg/util/collections/includes_excludes_test.go b/pkg/util/collections/includes_excludes_test.go index a9841c25a..672a309d2 100644 --- a/pkg/util/collections/includes_excludes_test.go +++ b/pkg/util/collections/includes_excludes_test.go @@ -207,11 +207,6 @@ func TestValidateNamespaceIncludesExcludes(t *testing.T) { includes: []string{}, wantErr: false, }, - { - name: "empty string is invalid", - includes: []string{""}, - wantErr: true, - }, { name: "asterisk by itself is valid", includes: []string{"*"}, @@ -232,7 +227,7 @@ func TestValidateNamespaceIncludesExcludes(t *testing.T) { { name: "special characters in name is invalid", includes: []string{"foo?", "foo.bar", "bar_321"}, - excludes: []string{"$foo", "foo*bar", "bar=321"}, + excludes: []string{"$foo", "foo>bar", "bar=321"}, wantErr: true, }, { @@ -240,11 +235,33 @@ func TestValidateNamespaceIncludesExcludes(t *testing.T) { includes: []string{}, wantErr: false, }, + { + name: "empty string includes is valid (includes nothing)", + includes: []string{""}, + wantErr: false, + }, + { + name: "empty string excludes is valid (excludes nothing)", + excludes: []string{""}, + wantErr: false, + }, { name: "include everything using asterisk is valid", includes: []string{"*"}, wantErr: false, }, + { + name: "excludes can contain wildcard", + includes: []string{"foo", "bar"}, + excludes: []string{"nginx-ingress-*", "*-bar", "*-ingress-*"}, + wantErr: false, + }, + { + name: "includes can contain wildcard", + includes: []string{"*-foo", "kube-*", "*kube*"}, + excludes: []string{"bar"}, + wantErr: false, + }, { name: "include everything not allowed with other includes", includes: []string{"*", "foo"},