From d10af894937f8a7d15642dffb5aefc4c2e8538b2 Mon Sep 17 00:00:00 2001 From: Vishnu Kannan Date: Tue, 12 May 2015 15:13:03 -0700 Subject: [PATCH] Updating namespaces to be DNS labels instead of DNS names. --- docs/design/namespaces.md | 2 +- pkg/api/v1/types.go | 2 +- pkg/api/v1beta1/types.go | 2 +- pkg/api/v1beta2/types.go | 2 +- pkg/api/v1beta3/types.go | 2 +- pkg/api/validation/validation.go | 17 ++++++++++--- pkg/api/validation/validation_test.go | 35 ++++++++++++++++++++++++--- 7 files changed, 51 insertions(+), 11 deletions(-) diff --git a/docs/design/namespaces.md b/docs/design/namespaces.md index ade07e112a..c4a1a90d31 100644 --- a/docs/design/namespaces.md +++ b/docs/design/namespaces.md @@ -51,7 +51,7 @@ type Namespace struct { } ``` -A *Namespace* name is a DNS compatible subdomain. +A *Namespace* name is a DNS compatible label. A *Namespace* must exist prior to associating content with it. diff --git a/pkg/api/v1/types.go b/pkg/api/v1/types.go index 21980ab935..75507b54df 100644 --- a/pkg/api/v1/types.go +++ b/pkg/api/v1/types.go @@ -99,7 +99,7 @@ type ObjectMeta struct { // equivalent to the "default" namespace, but "default" is the canonical representation. // Not all objects are required to be scoped to a namespace - the value of this field for // those objects will be empty. - Namespace string `json:"namespace,omitempty" description:"namespace of the object; cannot be updated"` + Namespace string `json:"namespace,omitempty" description:"namespace of the object; must be a DNS_LABEL; cannot be updated"` // SelfLink is a URL representing this object. SelfLink string `json:"selfLink,omitempty" description:"URL for the object; populated by the system, read-only"` diff --git a/pkg/api/v1beta1/types.go b/pkg/api/v1beta1/types.go index e833f7c933..7f0e36577c 100644 --- a/pkg/api/v1beta1/types.go +++ b/pkg/api/v1beta1/types.go @@ -574,7 +574,7 @@ type TypeMeta struct { SelfLink string `json:"selfLink,omitempty" description:"URL for the object; populated by the system, read-only"` ResourceVersion uint64 `json:"resourceVersion,omitempty" description:"string that identifies the internal version of this object that can be used by clients to determine when objects have changed; populated by the system, read-only; value must be treated as opaque by clients and passed unmodified back to the server: http://docs.k8s.io/api-conventions.md#concurrency-control-and-consistency"` APIVersion string `json:"apiVersion,omitempty" description:"version of the schema the object should have"` - Namespace string `json:"namespace,omitempty" description:"namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default; cannot be updated"` + Namespace string `json:"namespace,omitempty" description:"namespace to which the object belongs; must be a DNS_LABEL; 'default' by default; cannot be updated"` // DeletionTimestamp is the time after which this resource will be deleted. This // field is set by the server when a graceful deletion is requested by the user, and is not diff --git a/pkg/api/v1beta2/types.go b/pkg/api/v1beta2/types.go index 9fb2bf319d..9194ef6cb2 100644 --- a/pkg/api/v1beta2/types.go +++ b/pkg/api/v1beta2/types.go @@ -572,7 +572,7 @@ type TypeMeta struct { SelfLink string `json:"selfLink,omitempty" description:"URL for the object; populated by the system, read-only"` ResourceVersion uint64 `json:"resourceVersion,omitempty" description:"string that identifies the internal version of this object that can be used by clients to determine when objects have changed; populated by the system, read-only; value must be treated as opaque by clients and passed unmodified back to the server: http://docs.k8s.io/api-conventions.md#concurrency-control-and-consistency"` APIVersion string `json:"apiVersion,omitempty" description:"version of the schema the object should have"` - Namespace string `json:"namespace,omitempty" description:"namespace to which the object belongs; must be a DNS_SUBDOMAIN; 'default' by default; cannot be updated"` + Namespace string `json:"namespace,omitempty" description:"namespace to which the object belongs; must be a DNS_LABEL; 'default' by default; cannot be updated"` // DeletionTimestamp is the time after which this resource will be deleted. This // field is set by the server when a graceful deletion is requested by the user, and is not diff --git a/pkg/api/v1beta3/types.go b/pkg/api/v1beta3/types.go index 1f388f85c2..3627e16279 100644 --- a/pkg/api/v1beta3/types.go +++ b/pkg/api/v1beta3/types.go @@ -99,7 +99,7 @@ type ObjectMeta struct { // equivalent to the "default" namespace, but "default" is the canonical representation. // Not all objects are required to be scoped to a namespace - the value of this field for // those objects will be empty. - Namespace string `json:"namespace,omitempty" description:"namespace of the object; cannot be updated"` + Namespace string `json:"namespace,omitempty" description:"namespace of the object; must be a DNS_LABEL; cannot be updated"` // SelfLink is a URL representing this object. SelfLink string `json:"selfLink,omitempty" description:"URL for the object; populated by the system, read-only"` diff --git a/pkg/api/validation/validation.go b/pkg/api/validation/validation.go index bab284dcfd..ba33a02eae 100644 --- a/pkg/api/validation/validation.go +++ b/pkg/api/validation/validation.go @@ -126,7 +126,7 @@ func ValidateNodeName(name string, prefix bool) (bool, string) { // Prefix indicates this name will be used as part of generation, in which case // trailing dashes are allowed. func ValidateNamespaceName(name string, prefix bool) (bool, string) { - return nameIsDNSSubdomain(name, prefix) + return nameIsDNSLabel(name, prefix) } // ValidateLimitRangeName can be used to check whether the given limit range name is valid. @@ -176,6 +176,17 @@ func nameIsDNSSubdomain(name string, prefix bool) (bool, string) { return false, dnsSubdomainErrorMsg } +// nameIsDNSLabel is a ValidateNameFunc for names that must be a DNS 1123 label. +func nameIsDNSLabel(name string, prefix bool) (bool, string) { + if prefix { + name = maskTrailingDash(name) + } + if util.IsDNS1123Label(name) { + return true, "" + } + return false, dns1123LabelErrorMsg +} + // nameIsDNS952Label is a ValidateNameFunc for names that must be a DNS 952 label. func nameIsDNS952Label(name string, prefix bool) (bool, string) { if prefix { @@ -210,8 +221,8 @@ func ValidateObjectMeta(meta *api.ObjectMeta, requiresNamespace bool, nameFn Val if requiresNamespace { if len(meta.Namespace) == 0 { allErrs = append(allErrs, errs.NewFieldRequired("namespace")) - } else if !util.IsDNS1123Subdomain(meta.Namespace) { - allErrs = append(allErrs, errs.NewFieldInvalid("namespace", meta.Namespace, dnsSubdomainErrorMsg)) + } else if ok, _ := ValidateNamespaceName(meta.Namespace, false); !ok { + allErrs = append(allErrs, errs.NewFieldInvalid("namespace", meta.Namespace, dns1123LabelErrorMsg)) } } else { if len(meta.Namespace) != 0 { diff --git a/pkg/api/validation/validation_test.go b/pkg/api/validation/validation_test.go index ef1ea40ae3..64077e3159 100644 --- a/pkg/api/validation/validation_test.go +++ b/pkg/api/validation/validation_test.go @@ -17,6 +17,7 @@ limitations under the License. package validation import ( + "math/rand" "strings" "testing" "time" @@ -54,6 +55,34 @@ func TestValidateObjectMetaCustomName(t *testing.T) { } } +// Ensure namespace names follow dns label format +func TestValidateObjectMetaNamespaces(t *testing.T) { + errs := ValidateObjectMeta(&api.ObjectMeta{Name: "test", Namespace: "foo.bar"}, false, func(s string, prefix bool) (bool, string) { + return true, "" + }) + if len(errs) != 1 { + t.Fatalf("unexpected errors: %v", errs) + } + if !strings.Contains(errs[0].Error(), "invalid value 'foo.bar'") { + t.Errorf("unexpected error message: %v", errs) + } + maxLength := 63 + letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + b := make([]rune, maxLength+1) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + errs = ValidateObjectMeta(&api.ObjectMeta{Name: "test", Namespace: string(b)}, false, func(s string, prefix bool) (bool, string) { + return true, "" + }) + if len(errs) != 1 { + t.Fatalf("unexpected errors: %v", errs) + } + if !strings.Contains(errs[0].Error(), "invalid value") { + t.Errorf("unexpected error message: %v", errs) + } +} + func TestValidateObjectMetaUpdateIgnoresCreationTimestamp(t *testing.T) { if errs := ValidateObjectMetaUpdate( &api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: util.NewTime(time.Unix(10, 0))}, @@ -2517,7 +2546,7 @@ func TestValidateLimitRange(t *testing.T) { }, "invalid Namespace": { api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: spec}, - dnsSubdomainErrorMsg, + dns1123LabelErrorMsg, }, } for k, v := range errorCases { @@ -2584,7 +2613,7 @@ func TestValidateResourceQuota(t *testing.T) { }, "invalid Namespace": { api.ResourceQuota{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: spec}, - dnsSubdomainErrorMsg, + dns1123LabelErrorMsg, }, } for k, v := range errorCases { @@ -2992,7 +3021,7 @@ func TestValidateEndpoints(t *testing.T) { "invalid namespace": { endpoints: api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "no@#invalid.;chars\"allowed"}}, errorType: "FieldValueInvalid", - errorDetail: dnsSubdomainErrorMsg, + errorDetail: dns1123LabelErrorMsg, }, "invalid name": { endpoints: api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "-_Invliad^&Characters", Namespace: "namespace"}},