Updating namespaces to be DNS labels instead of DNS names.

pull/6/head
Vishnu Kannan 2015-05-12 15:13:03 -07:00
parent 969023f3ec
commit d10af89493
7 changed files with 51 additions and 11 deletions

View File

@ -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. A *Namespace* must exist prior to associating content with it.

View File

@ -99,7 +99,7 @@ type ObjectMeta struct {
// equivalent to the "default" namespace, but "default" is the canonical representation. // 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 // Not all objects are required to be scoped to a namespace - the value of this field for
// those objects will be empty. // 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 is a URL representing this object.
SelfLink string `json:"selfLink,omitempty" description:"URL for the object; populated by the system, read-only"` SelfLink string `json:"selfLink,omitempty" description:"URL for the object; populated by the system, read-only"`

View File

@ -574,7 +574,7 @@ type TypeMeta struct {
SelfLink string `json:"selfLink,omitempty" description:"URL for the object; populated by the system, read-only"` 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"` 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"` 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 // 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 // field is set by the server when a graceful deletion is requested by the user, and is not

View File

@ -572,7 +572,7 @@ type TypeMeta struct {
SelfLink string `json:"selfLink,omitempty" description:"URL for the object; populated by the system, read-only"` 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"` 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"` 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 // 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 // field is set by the server when a graceful deletion is requested by the user, and is not

View File

@ -99,7 +99,7 @@ type ObjectMeta struct {
// equivalent to the "default" namespace, but "default" is the canonical representation. // 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 // Not all objects are required to be scoped to a namespace - the value of this field for
// those objects will be empty. // 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 is a URL representing this object.
SelfLink string `json:"selfLink,omitempty" description:"URL for the object; populated by the system, read-only"` SelfLink string `json:"selfLink,omitempty" description:"URL for the object; populated by the system, read-only"`

View File

@ -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 // Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed. // trailing dashes are allowed.
func ValidateNamespaceName(name string, prefix bool) (bool, string) { 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. // 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 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. // nameIsDNS952Label is a ValidateNameFunc for names that must be a DNS 952 label.
func nameIsDNS952Label(name string, prefix bool) (bool, string) { func nameIsDNS952Label(name string, prefix bool) (bool, string) {
if prefix { if prefix {
@ -210,8 +221,8 @@ func ValidateObjectMeta(meta *api.ObjectMeta, requiresNamespace bool, nameFn Val
if requiresNamespace { if requiresNamespace {
if len(meta.Namespace) == 0 { if len(meta.Namespace) == 0 {
allErrs = append(allErrs, errs.NewFieldRequired("namespace")) allErrs = append(allErrs, errs.NewFieldRequired("namespace"))
} else if !util.IsDNS1123Subdomain(meta.Namespace) { } else if ok, _ := ValidateNamespaceName(meta.Namespace, false); !ok {
allErrs = append(allErrs, errs.NewFieldInvalid("namespace", meta.Namespace, dnsSubdomainErrorMsg)) allErrs = append(allErrs, errs.NewFieldInvalid("namespace", meta.Namespace, dns1123LabelErrorMsg))
} }
} else { } else {
if len(meta.Namespace) != 0 { if len(meta.Namespace) != 0 {

View File

@ -17,6 +17,7 @@ limitations under the License.
package validation package validation
import ( import (
"math/rand"
"strings" "strings"
"testing" "testing"
"time" "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) { func TestValidateObjectMetaUpdateIgnoresCreationTimestamp(t *testing.T) {
if errs := ValidateObjectMetaUpdate( if errs := ValidateObjectMetaUpdate(
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: util.NewTime(time.Unix(10, 0))}, &api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: util.NewTime(time.Unix(10, 0))},
@ -2517,7 +2546,7 @@ func TestValidateLimitRange(t *testing.T) {
}, },
"invalid Namespace": { "invalid Namespace": {
api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: spec}, api.LimitRange{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: spec},
dnsSubdomainErrorMsg, dns1123LabelErrorMsg,
}, },
} }
for k, v := range errorCases { for k, v := range errorCases {
@ -2584,7 +2613,7 @@ func TestValidateResourceQuota(t *testing.T) {
}, },
"invalid Namespace": { "invalid Namespace": {
api.ResourceQuota{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: spec}, api.ResourceQuota{ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "^Invalid"}, Spec: spec},
dnsSubdomainErrorMsg, dns1123LabelErrorMsg,
}, },
} }
for k, v := range errorCases { for k, v := range errorCases {
@ -2992,7 +3021,7 @@ func TestValidateEndpoints(t *testing.T) {
"invalid namespace": { "invalid namespace": {
endpoints: api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "no@#invalid.;chars\"allowed"}}, endpoints: api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "mysvc", Namespace: "no@#invalid.;chars\"allowed"}},
errorType: "FieldValueInvalid", errorType: "FieldValueInvalid",
errorDetail: dnsSubdomainErrorMsg, errorDetail: dns1123LabelErrorMsg,
}, },
"invalid name": { "invalid name": {
endpoints: api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "-_Invliad^&Characters", Namespace: "namespace"}}, endpoints: api.Endpoints{ObjectMeta: api.ObjectMeta{Name: "-_Invliad^&Characters", Namespace: "namespace"}},