fix(config): allow @ . : / in context names

Real kubeconfig context names commonly contain characters the
validator rejected:
  - 'admin@home', 'user@cluster.example.com' (kubectl rename, EKS
    aws-iam-authenticator)
  - 'cluster.example.com', 'gke_proj_zone_cluster.prod' (FQDN, GKE)
  - 'arn:aws:eks:us-east-1:123:cluster/foo' (EKS ARN)

kubeconfig itself imposes no character restrictions, so requiring
[a-zA-Z0-9_-] only was kportal-specific over-validation that blocked
legitimate users. Widen the allowed set to add '@', '.', ':', '/'.
Names must still start and end with a letter or digit so YAML
specials and leading whitespace remain rejected.

Tests cover the new positive cases and tighten negative coverage
(starts-with-@, ends-with-/, ends-with-dot).
This commit is contained in:
2026-05-06 12:11:37 +01:00
parent 0ccc855123
commit c413b808f1
2 changed files with 21 additions and 10 deletions
+10 -4
View File
@@ -26,9 +26,15 @@ var (
// A series of DNS labels separated by dots (no consecutive dots allowed)
dns1123SubdomainRegexp = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`)
// contextNameRegexp matches valid context names
// Allows alphanumeric characters, hyphens, and underscores (to support various kubeconfig naming conventions)
contextNameRegexp = regexp.MustCompile(`^[a-zA-Z0-9]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$`)
// contextNameRegexp matches valid kubeconfig context names.
// kubeconfig itself imposes no character restriction; we accept the union
// of common naming conventions seen in the wild:
// - hyphens / underscores: minikube, docker-desktop, gke_proj_zone_cluster
// - "@": user@cluster (kubectl rename, EKS aws-iam-authenticator)
// - ".": cluster.example.com, GKE dotted names
// - ":" and "/": EKS ARNs (arn:aws:eks:us-east-1:123:cluster/foo)
// Must start and end with an alphanumeric character.
contextNameRegexp = regexp.MustCompile(`^[a-zA-Z0-9]([a-zA-Z0-9._:/@_-]*[a-zA-Z0-9])?$`)
// validResourceTypes contains the allowed Kubernetes resource types
validResourceTypes = []string{"pod", "service"}
@@ -571,7 +577,7 @@ func validateContextName(name, field string) *ValidationError {
if !contextNameRegexp.MatchString(name) {
return &ValidationError{
Field: field,
Message: fmt.Sprintf("Context name '%s' is not valid (must consist of alphanumeric characters, hyphens, or underscores, and start/end with alphanumeric)", name),
Message: fmt.Sprintf("Context name '%s' is not valid (allowed: letters, digits, hyphens, underscores, dots, '@', ':', '/'; must start and end with a letter or digit)", name),
}
}
+11 -6
View File
@@ -1524,7 +1524,7 @@ func TestValidator_ValidateContextAndNamespaceNames(t *testing.T) {
},
},
expectErrors: true,
errorContains: []string{"not valid", "alphanumeric"},
errorContains: []string{"not valid", "letter or digit"},
},
{
name: "context name too long",
@@ -1560,7 +1560,7 @@ func TestValidator_ValidateContextAndNamespaceNames(t *testing.T) {
},
},
expectErrors: true,
errorContains: []string{"not valid", "start/end with alphanumeric"},
errorContains: []string{"not valid", "letter or digit"},
},
{
name: "invalid context name ends with underscore",
@@ -1578,7 +1578,7 @@ func TestValidator_ValidateContextAndNamespaceNames(t *testing.T) {
},
},
expectErrors: true,
errorContains: []string{"not valid", "start/end with alphanumeric"},
errorContains: []string{"not valid", "letter or digit"},
},
{
name: "invalid namespace name with spaces",
@@ -1893,6 +1893,11 @@ func TestValidateContextName(t *testing.T) {
{name: "valid single char", contextName: "a", errorMsg: "", expectError: false},
{name: "valid single digit", contextName: "1", errorMsg: "", expectError: false},
{name: "valid starts with digit", contextName: "123-cluster", errorMsg: "", expectError: false},
{name: "valid user@cluster", contextName: "admin@home", errorMsg: "", expectError: false},
{name: "valid user@fqdn", contextName: "user@cluster.example.com", errorMsg: "", expectError: false},
{name: "valid dotted FQDN", contextName: "cluster.example.com", errorMsg: "", expectError: false},
{name: "valid GKE dotted", contextName: "gke_proj_zone_cluster.prod", errorMsg: "", expectError: false},
{name: "valid EKS ARN", contextName: "arn:aws:eks:us-east-1:123:cluster/foo", errorMsg: "", expectError: false},
// Invalid cases
{name: "invalid empty", contextName: "", errorMsg: "not valid", expectError: true},
@@ -1900,10 +1905,10 @@ func TestValidateContextName(t *testing.T) {
{name: "invalid ends with hyphen", contextName: "cluster-", errorMsg: "not valid", expectError: true},
{name: "invalid starts with underscore", contextName: "_cluster", errorMsg: "not valid", expectError: true},
{name: "invalid ends with underscore", contextName: "cluster_", errorMsg: "not valid", expectError: true},
{name: "invalid starts with @", contextName: "@cluster", errorMsg: "not valid", expectError: true},
{name: "invalid ends with /", contextName: "cluster/", errorMsg: "not valid", expectError: true},
{name: "invalid ends with .", contextName: "cluster.", errorMsg: "not valid", expectError: true},
{name: "invalid with spaces", contextName: "my cluster", errorMsg: "not valid", expectError: true},
{name: "invalid with dots", contextName: "my.cluster", errorMsg: "not valid", expectError: true},
{name: "invalid with special chars", contextName: "cluster@123", errorMsg: "not valid", expectError: true},
{name: "invalid with slash", contextName: "cluster/name", errorMsg: "not valid", expectError: true},
{name: "invalid too long", contextName: strings.Repeat("a", 254), errorMsg: "exceeds maximum length", expectError: true},
}