snip links from genericapiserver to api/validation

pull/6/head
deads2k 2017-01-04 12:09:24 -05:00
parent 5ee52e8bce
commit 80143ee0b4
12 changed files with 882 additions and 666 deletions

View File

@ -23,7 +23,7 @@ go_library(
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/api/validation/genericvalidation:go_default_library",
"//pkg/api/validation/path:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/genericapiserver/api/request:go_default_library",

View File

@ -19,7 +19,7 @@ package rest
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/api/validation/genericvalidation"
path "k8s.io/kubernetes/pkg/api/validation/path"
genericapirequest "k8s.io/kubernetes/pkg/genericapiserver/api/request"
"k8s.io/kubernetes/pkg/runtime"
@ -83,7 +83,7 @@ func BeforeCreate(strategy RESTCreateStrategy, ctx genericapirequest.Context, ob
// Custom validation (including name validation) passed
// Now run common validation on object meta
// Do this *after* custom validation so that specific error messages are shown whenever possible
if errs := validation.ValidateObjectMeta(objectMeta, strategy.NamespaceScoped(), path.ValidatePathSegmentName, field.NewPath("metadata")); len(errs) > 0 {
if errs := genericvalidation.ValidateObjectMeta(objectMeta, strategy.NamespaceScoped(), path.ValidatePathSegmentName, field.NewPath("metadata")); len(errs) > 0 {
return errors.NewInvalid(kind.GroupKind(), objectMeta.Name, errs)
}

View File

@ -22,7 +22,7 @@ import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/api/validation/genericvalidation"
genericapirequest "k8s.io/kubernetes/pkg/genericapiserver/api/request"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/validation/field"
@ -67,7 +67,7 @@ func validateCommonFields(obj, old runtime.Object) (field.ErrorList, error) {
if err != nil {
return nil, fmt.Errorf("failed to get old object metadata: %v", err)
}
allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(objectMeta, oldObjectMeta, field.NewPath("metadata"))...)
allErrs = append(allErrs, genericvalidation.ValidateObjectMetaUpdate(objectMeta, oldObjectMeta, field.NewPath("metadata"))...)
return allErrs, nil
}

View File

@ -24,7 +24,7 @@ go_library(
"//pkg/api/resource:go_default_library",
"//pkg/api/service:go_default_library",
"//pkg/api/util:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/api/validation/genericvalidation:go_default_library",
"//pkg/apimachinery/registered:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/apis/meta/v1/unstructured:go_default_library",

View File

@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"validation.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/apis/meta/v1/validation:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/util/sets:go_default_library",
"//pkg/util/validation:go_default_library",
"//pkg/util/validation/field:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["validation_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/util/validation/field:go_default_library",
],
)

View File

@ -0,0 +1,18 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// this package is split out to snip links between the API server and the kube resource validation functions
package genericvalidation

View File

@ -0,0 +1,313 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package genericvalidation
import (
"fmt"
"strings"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
v1validation "k8s.io/kubernetes/pkg/apis/meta/v1/validation"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/sets"
"k8s.io/kubernetes/pkg/util/validation"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// TODO: delete this global variable when we enable the validation of common
// fields by default.
var RepairMalformedUpdates bool = true
const IsNegativeErrorMsg string = `must be greater than or equal to 0`
const FieldImmutableErrorMsg string = `field is immutable`
const totalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
// BannedOwners is a black list of object that are not allowed to be owners.
var BannedOwners = map[schema.GroupVersionKind]struct{}{
v1.SchemeGroupVersion.WithKind("Event"): {},
}
// ValidateNameFunc validates that the provided name is valid for a given resource type.
// Not all resources have the same validation rules for names. Prefix is true
// if the name will have a value appended to it. If the name is not valid,
// this returns a list of descriptions of individual characteristics of the
// value that were not valid. Otherwise this returns an empty list or nil.
type ValidateNameFunc func(name string, prefix bool) []string
// NameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain.
func NameIsDNSSubdomain(name string, prefix bool) []string {
if prefix {
name = maskTrailingDash(name)
}
return validation.IsDNS1123Subdomain(name)
}
// NameIsDNSLabel is a ValidateNameFunc for names that must be a DNS 1123 label.
func NameIsDNSLabel(name string, prefix bool) []string {
if prefix {
name = maskTrailingDash(name)
}
return validation.IsDNS1123Label(name)
}
// NameIsDNS1035Label is a ValidateNameFunc for names that must be a DNS 952 label.
func NameIsDNS1035Label(name string, prefix bool) []string {
if prefix {
name = maskTrailingDash(name)
}
return validation.IsDNS1035Label(name)
}
// ValidateNamespaceName can be used to check whether the given namespace name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
var ValidateNamespaceName = NameIsDNSLabel
// ValidateClusterName can be used to check whether the given cluster name is valid.
var ValidateClusterName = NameIsDNS1035Label
// ValidateServiceAccountName can be used to check whether the given service account name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
var ValidateServiceAccountName = NameIsDNSSubdomain
// maskTrailingDash replaces the final character of a string with a subdomain safe
// value if is a dash.
func maskTrailingDash(name string) string {
if strings.HasSuffix(name, "-") {
return name[:len(name)-2] + "a"
}
return name
}
// Validates that given value is not negative.
func ValidateNonnegativeField(value int64, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if value < 0 {
allErrs = append(allErrs, field.Invalid(fldPath, value, IsNegativeErrorMsg))
}
return allErrs
}
// ValidateAnnotations validates that a set of annotations are correctly defined.
func ValidateAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
var totalSize int64
for k, v := range annotations {
for _, msg := range validation.IsQualifiedName(strings.ToLower(k)) {
allErrs = append(allErrs, field.Invalid(fldPath, k, msg))
}
totalSize += (int64)(len(k)) + (int64)(len(v))
}
if totalSize > (int64)(totalAnnotationSizeLimitB) {
allErrs = append(allErrs, field.TooLong(fldPath, "", totalAnnotationSizeLimitB))
}
return allErrs
}
func validateOwnerReference(ownerReference metav1.OwnerReference, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
gvk := schema.FromAPIVersionAndKind(ownerReference.APIVersion, ownerReference.Kind)
// gvk.Group is empty for the legacy group.
if len(gvk.Version) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("apiVersion"), ownerReference.APIVersion, "version must not be empty"))
}
if len(gvk.Kind) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), ownerReference.Kind, "kind must not be empty"))
}
if len(ownerReference.Name) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), ownerReference.Name, "name must not be empty"))
}
if len(ownerReference.UID) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), ownerReference.UID, "uid must not be empty"))
}
if _, ok := BannedOwners[gvk]; ok {
allErrs = append(allErrs, field.Invalid(fldPath, ownerReference, fmt.Sprintf("%s is disallowed from being an owner", gvk)))
}
return allErrs
}
func ValidateOwnerReferences(ownerReferences []metav1.OwnerReference, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
controllerName := ""
for _, ref := range ownerReferences {
allErrs = append(allErrs, validateOwnerReference(ref, fldPath)...)
if ref.Controller != nil && *ref.Controller {
if controllerName != "" {
allErrs = append(allErrs, field.Invalid(fldPath, ownerReferences,
fmt.Sprintf("Only one reference can have Controller set to true. Found \"true\" in references for %v and %v", controllerName, ref.Name)))
} else {
controllerName = ref.Name
}
}
}
return allErrs
}
// Validate finalizer names
func ValidateFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, msg := range validation.IsQualifiedName(stringValue) {
allErrs = append(allErrs, field.Invalid(fldPath, stringValue, msg))
}
if len(allErrs) != 0 {
return allErrs
}
if len(strings.Split(stringValue, "/")) == 1 {
if !api.IsStandardFinalizerName(stringValue) {
return append(allErrs, field.Invalid(fldPath, stringValue, "name is neither a standard finalizer name nor is it fully qualified"))
}
}
return field.ErrorList{}
}
func ValidateNoNewFinalizers(newFinalizers []string, oldFinalizers []string, fldPath *field.Path) field.ErrorList {
const newFinalizersErrorMsg string = `no new finalizers can be added if the object is being deleted`
allErrs := field.ErrorList{}
extra := sets.NewString(newFinalizers...).Difference(sets.NewString(oldFinalizers...))
if len(extra) != 0 {
allErrs = append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("no new finalizers can be added if the object is being deleted, found new finalizers %#v", extra.List())))
}
return allErrs
}
func ValidateImmutableField(newVal, oldVal interface{}, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if !api.Semantic.DeepEqual(oldVal, newVal) {
allErrs = append(allErrs, field.Invalid(fldPath, newVal, FieldImmutableErrorMsg))
}
return allErrs
}
// ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
// been performed.
// It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before.
func ValidateObjectMeta(meta *api.ObjectMeta, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(meta.GenerateName) != 0 {
for _, msg := range nameFn(meta.GenerateName, true) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("generateName"), meta.GenerateName, msg))
}
}
// If the generated name validates, but the calculated value does not, it's a problem with generation, and we
// report it here. This may confuse users, but indicates a programming bug and still must be validated.
// If there are multiple fields out of which one is required then add an or as a separator
if len(meta.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name or generateName is required"))
} else {
for _, msg := range nameFn(meta.Name, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), meta.Name, msg))
}
}
if requiresNamespace {
if len(meta.Namespace) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), ""))
} else {
for _, msg := range ValidateNamespaceName(meta.Namespace, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), meta.Namespace, msg))
}
}
} else {
if len(meta.Namespace) != 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("namespace"), "not allowed on this type"))
}
}
if len(meta.ClusterName) != 0 {
for _, msg := range ValidateClusterName(meta.ClusterName, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("clusterName"), meta.ClusterName, msg))
}
}
allErrs = append(allErrs, ValidateNonnegativeField(meta.Generation, fldPath.Child("generation"))...)
allErrs = append(allErrs, v1validation.ValidateLabels(meta.Labels, fldPath.Child("labels"))...)
allErrs = append(allErrs, ValidateAnnotations(meta.Annotations, fldPath.Child("annotations"))...)
allErrs = append(allErrs, ValidateOwnerReferences(meta.OwnerReferences, fldPath.Child("ownerReferences"))...)
for _, finalizer := range meta.Finalizers {
allErrs = append(allErrs, ValidateFinalizerName(finalizer, fldPath.Child("finalizers"))...)
}
return allErrs
}
// ValidateObjectMetaUpdate validates an object's metadata when updated
func ValidateObjectMetaUpdate(newMeta, oldMeta *api.ObjectMeta, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if !RepairMalformedUpdates && newMeta.UID != oldMeta.UID {
allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), newMeta.UID, "field is immutable"))
}
// in the event it is left empty, set it, to allow clients more flexibility
// TODO: remove the following code that repairs the update request when we retire the clients that modify the immutable fields.
// Please do not copy this pattern elsewhere; validation functions should not be modifying the objects they are passed!
if RepairMalformedUpdates {
if len(newMeta.UID) == 0 {
newMeta.UID = oldMeta.UID
}
// ignore changes to timestamp
if oldMeta.CreationTimestamp.IsZero() {
oldMeta.CreationTimestamp = newMeta.CreationTimestamp
} else {
newMeta.CreationTimestamp = oldMeta.CreationTimestamp
}
// an object can never remove a deletion timestamp or clear/change grace period seconds
if !oldMeta.DeletionTimestamp.IsZero() {
newMeta.DeletionTimestamp = oldMeta.DeletionTimestamp
}
if oldMeta.DeletionGracePeriodSeconds != nil && newMeta.DeletionGracePeriodSeconds == nil {
newMeta.DeletionGracePeriodSeconds = oldMeta.DeletionGracePeriodSeconds
}
}
// TODO: needs to check if newMeta==nil && oldMeta !=nil after the repair logic is removed.
if newMeta.DeletionGracePeriodSeconds != nil && (oldMeta.DeletionGracePeriodSeconds == nil || *newMeta.DeletionGracePeriodSeconds != *oldMeta.DeletionGracePeriodSeconds) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("deletionGracePeriodSeconds"), newMeta.DeletionGracePeriodSeconds, "field is immutable; may only be changed via deletion"))
}
if newMeta.DeletionTimestamp != nil && (oldMeta.DeletionTimestamp == nil || !newMeta.DeletionTimestamp.Equal(*oldMeta.DeletionTimestamp)) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("deletionTimestamp"), newMeta.DeletionTimestamp, "field is immutable; may only be changed via deletion"))
}
// Finalizers cannot be added if the object is already being deleted.
if oldMeta.DeletionTimestamp != nil {
allErrs = append(allErrs, ValidateNoNewFinalizers(newMeta.Finalizers, oldMeta.Finalizers, fldPath.Child("finalizers"))...)
}
// Reject updates that don't specify a resource version
if len(newMeta.ResourceVersion) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceVersion"), newMeta.ResourceVersion, "must be specified for an update"))
}
// Generation shouldn't be decremented
if newMeta.Generation < oldMeta.Generation {
allErrs = append(allErrs, field.Invalid(fldPath.Child("generation"), newMeta.Generation, "must not be decremented"))
}
allErrs = append(allErrs, ValidateImmutableField(newMeta.Name, oldMeta.Name, fldPath.Child("name"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.Namespace, oldMeta.Namespace, fldPath.Child("namespace"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.UID, oldMeta.UID, fldPath.Child("uid"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.CreationTimestamp, oldMeta.CreationTimestamp, fldPath.Child("creationTimestamp"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.ClusterName, oldMeta.ClusterName, fldPath.Child("clusterName"))...)
allErrs = append(allErrs, v1validation.ValidateLabels(newMeta.Labels, fldPath.Child("labels"))...)
allErrs = append(allErrs, ValidateAnnotations(newMeta.Annotations, fldPath.Child("annotations"))...)
allErrs = append(allErrs, ValidateOwnerReferences(newMeta.OwnerReferences, fldPath.Child("ownerReferences"))...)
return allErrs
}

View File

@ -0,0 +1,479 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package genericvalidation
import (
"math/rand"
"reflect"
"strings"
"testing"
"time"
"k8s.io/kubernetes/pkg/api"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/util/validation/field"
)
const (
maxLengthErrMsg = "must be no more than"
namePartErrMsg = "name part must consist of"
nameErrMsg = "a qualified name must consist of"
)
// Ensure custom name functions are allowed
func TestValidateObjectMetaCustomName(t *testing.T) {
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", GenerateName: "foo"},
false,
func(s string, prefix bool) []string {
if s == "test" {
return nil
}
return []string{"name-gen"}
},
field.NewPath("field"))
if len(errs) != 1 {
t.Fatalf("unexpected errors: %v", errs)
}
if !strings.Contains(errs[0].Error(), "name-gen") {
t.Errorf("unexpected error message: %v", errs)
}
}
// Ensure namespace names follow dns label format
func TestValidateObjectMetaNamespaces(t *testing.T) {
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", Namespace: "foo.bar"},
true,
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
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)},
true,
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
if len(errs) != 2 {
t.Fatalf("unexpected errors: %v", errs)
}
if !strings.Contains(errs[0].Error(), "Invalid value") || !strings.Contains(errs[1].Error(), "Invalid value") {
t.Errorf("unexpected error message: %v", errs)
}
}
func TestValidateObjectMetaOwnerReferences(t *testing.T) {
trueVar := true
falseVar := false
testCases := []struct {
description string
ownerReferences []metav1.OwnerReference
expectError bool
expectedErrorMessage string
}{
{
description: "simple success - third party extension.",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "1",
},
},
expectError: false,
expectedErrorMessage: "",
},
{
description: "simple failures - event shouldn't be set as an owner",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "Event",
Name: "name",
UID: "1",
},
},
expectError: true,
expectedErrorMessage: "is disallowed from being an owner",
},
{
description: "simple controller ref success - one reference with Controller set",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "1",
Controller: &falseVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "2",
Controller: &trueVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "3",
Controller: &falseVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "4",
},
},
expectError: false,
expectedErrorMessage: "",
},
{
description: "simple controller ref failure - two references with Controller set",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "1",
Controller: &falseVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "2",
Controller: &trueVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "3",
Controller: &trueVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "4",
},
},
expectError: true,
expectedErrorMessage: "Only one reference can have Controller set to true",
},
}
for _, tc := range testCases {
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", Namespace: "test", OwnerReferences: tc.ownerReferences},
true,
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
if len(errs) != 0 && !tc.expectError {
t.Errorf("unexpected error: %v in test case %v", errs, tc.description)
}
if len(errs) == 0 && tc.expectError {
t.Errorf("expect error in test case %v", tc.description)
}
if len(errs) != 0 && !strings.Contains(errs[0].Error(), tc.expectedErrorMessage) {
t.Errorf("unexpected error message: %v in test case %v", errs, tc.description)
}
}
}
func TestValidateObjectMetaUpdateIgnoresCreationTimestamp(t *testing.T) {
if errs := ValidateObjectMetaUpdate(
&api.ObjectMeta{Name: "test", ResourceVersion: "1"},
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))},
field.NewPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
if errs := ValidateObjectMetaUpdate(
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))},
&api.ObjectMeta{Name: "test", ResourceVersion: "1"},
field.NewPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
if errs := ValidateObjectMetaUpdate(
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))},
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(11, 0))},
field.NewPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
}
func TestValidateFinalizersUpdate(t *testing.T) {
testcases := map[string]struct {
Old api.ObjectMeta
New api.ObjectMeta
ExpectedErr string
}{
"invalid adding finalizers": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a", "y/b"}},
ExpectedErr: "y/b",
},
"invalid changing finalizers": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/b"}},
ExpectedErr: "x/b",
},
"valid removing finalizers": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a", "y/b"}},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}},
ExpectedErr: "",
},
"valid adding finalizers for objects not being deleted": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{"x/a"}},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{"x/a", "y/b"}},
ExpectedErr: "",
},
}
for name, tc := range testcases {
errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field"))
if len(errs) == 0 {
if len(tc.ExpectedErr) != 0 {
t.Errorf("case: %q, expected error to contain %q", name, tc.ExpectedErr)
}
} else if e, a := tc.ExpectedErr, errs.ToAggregate().Error(); !strings.Contains(a, e) {
t.Errorf("case: %q, expected error to contain %q, got error %q", name, e, a)
}
}
}
func TestValidateObjectMetaUpdatePreventsDeletionFieldMutation(t *testing.T) {
now := metav1.NewTime(time.Unix(1000, 0).UTC())
later := metav1.NewTime(time.Unix(2000, 0).UTC())
gracePeriodShort := int64(30)
gracePeriodLong := int64(40)
testcases := map[string]struct {
Old api.ObjectMeta
New api.ObjectMeta
ExpectedNew api.ObjectMeta
ExpectedErrs []string
}{
"valid without deletion fields": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedErrs: []string{},
},
"valid with deletion fields": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedErrs: []string{},
},
"invalid set deletionTimestamp": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedErrs: []string{"field.deletionTimestamp: Invalid value: \"1970-01-01T00:16:40Z\": field is immutable; may only be changed via deletion"},
},
"invalid clear deletionTimestamp": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedErrs: []string{}, // no errors, validation copies the old value
},
"invalid change deletionTimestamp": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &later},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedErrs: []string{}, // no errors, validation copies the old value
},
"invalid set deletionGracePeriodSeconds": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedErrs: []string{"field.deletionGracePeriodSeconds: Invalid value: 30: field is immutable; may only be changed via deletion"},
},
"invalid clear deletionGracePeriodSeconds": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedErrs: []string{}, // no errors, validation copies the old value
},
"invalid change deletionGracePeriodSeconds": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodLong},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodLong},
ExpectedErrs: []string{"field.deletionGracePeriodSeconds: Invalid value: 40: field is immutable; may only be changed via deletion"},
},
}
for k, tc := range testcases {
errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field"))
if len(errs) != len(tc.ExpectedErrs) {
t.Logf("%s: Expected: %#v", k, tc.ExpectedErrs)
t.Logf("%s: Got: %#v", k, errs)
t.Errorf("%s: expected %d errors, got %d", k, len(tc.ExpectedErrs), len(errs))
continue
}
for i := range errs {
if errs[i].Error() != tc.ExpectedErrs[i] {
t.Errorf("%s: error #%d: expected %q, got %q", k, i, tc.ExpectedErrs[i], errs[i].Error())
}
}
if !reflect.DeepEqual(tc.New, tc.ExpectedNew) {
t.Errorf("%s: Expected after validation:\n%#v\ngot\n%#v", k, tc.ExpectedNew, tc.New)
}
}
}
func TestObjectMetaGenerationUpdate(t *testing.T) {
testcases := map[string]struct {
Old api.ObjectMeta
New api.ObjectMeta
ExpectedErrs []string
}{
"invalid generation change - decremented": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 4},
ExpectedErrs: []string{"field.generation: Invalid value: 4: must not be decremented"},
},
"valid generation change - incremented by one": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 1},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 2},
ExpectedErrs: []string{},
},
"valid generation field - not updated": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5},
ExpectedErrs: []string{},
},
}
for k, tc := range testcases {
errList := []string{}
errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field"))
if len(errs) != len(tc.ExpectedErrs) {
t.Logf("%s: Expected: %#v", k, tc.ExpectedErrs)
for _, err := range errs {
errList = append(errList, err.Error())
}
t.Logf("%s: Got: %#v", k, errList)
t.Errorf("%s: expected %d errors, got %d", k, len(tc.ExpectedErrs), len(errs))
continue
}
for i := range errList {
if errList[i] != tc.ExpectedErrs[i] {
t.Errorf("%s: error #%d: expected %q, got %q", k, i, tc.ExpectedErrs[i], errList[i])
}
}
}
}
// Ensure trailing slash is allowed in generate name
func TestValidateObjectMetaTrimsTrailingSlash(t *testing.T) {
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", GenerateName: "foo-"},
false,
NameIsDNSSubdomain,
field.NewPath("field"))
if len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
}
func TestValidateAnnotations(t *testing.T) {
successCases := []map[string]string{
{"simple": "bar"},
{"now-with-dashes": "bar"},
{"1-starts-with-num": "bar"},
{"1234": "bar"},
{"simple/simple": "bar"},
{"now-with-dashes/simple": "bar"},
{"now-with-dashes/now-with-dashes": "bar"},
{"now.with.dots/simple": "bar"},
{"now-with.dashes-and.dots/simple": "bar"},
{"1-num.2-num/3-num": "bar"},
{"1234/5678": "bar"},
{"1.2.3.4/5678": "bar"},
{"UpperCase123": "bar"},
{"a": strings.Repeat("b", totalAnnotationSizeLimitB-1)},
{
"a": strings.Repeat("b", totalAnnotationSizeLimitB/2-1),
"c": strings.Repeat("d", totalAnnotationSizeLimitB/2-1),
},
}
for i := range successCases {
errs := ValidateAnnotations(successCases[i], field.NewPath("field"))
if len(errs) != 0 {
t.Errorf("case[%d] expected success, got %#v", i, errs)
}
}
nameErrorCases := []struct {
annotations map[string]string
expect string
}{
{map[string]string{"nospecialchars^=@": "bar"}, namePartErrMsg},
{map[string]string{"cantendwithadash-": "bar"}, namePartErrMsg},
{map[string]string{"only/one/slash": "bar"}, nameErrMsg},
{map[string]string{strings.Repeat("a", 254): "bar"}, maxLengthErrMsg},
}
for i := range nameErrorCases {
errs := ValidateAnnotations(nameErrorCases[i].annotations, field.NewPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d]: expected failure", i)
} else {
if !strings.Contains(errs[0].Detail, nameErrorCases[i].expect) {
t.Errorf("case[%d]: error details do not include %q: %q", i, nameErrorCases[i].expect, errs[0].Detail)
}
}
}
totalSizeErrorCases := []map[string]string{
{"a": strings.Repeat("b", totalAnnotationSizeLimitB)},
{
"a": strings.Repeat("b", totalAnnotationSizeLimitB/2),
"c": strings.Repeat("d", totalAnnotationSizeLimitB/2),
},
}
for i := range totalSizeErrorCases {
errs := ValidateAnnotations(totalSizeErrorCases[i], field.NewPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d] expected failure", i)
}
}
}

View File

@ -27,17 +27,17 @@ import (
"strings"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
utilpod "k8s.io/kubernetes/pkg/api/pod"
"k8s.io/kubernetes/pkg/api/resource"
apiservice "k8s.io/kubernetes/pkg/api/service"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/api/validation/genericvalidation"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
unversionedvalidation "k8s.io/kubernetes/pkg/apis/meta/v1/validation"
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
"k8s.io/kubernetes/pkg/capabilities"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/security/apparmor"
utilconfig "k8s.io/kubernetes/pkg/util/config"
"k8s.io/kubernetes/pkg/util/intstr"
@ -48,22 +48,18 @@ import (
// TODO: delete this global variable when we enable the validation of common
// fields by default.
var RepairMalformedUpdates bool = true
var RepairMalformedUpdates bool = genericvalidation.RepairMalformedUpdates
const isNegativeErrorMsg string = `must be greater than or equal to 0`
const isNegativeErrorMsg string = genericvalidation.IsNegativeErrorMsg
const isInvalidQuotaResource string = `must be a standard resource for quota`
const fieldImmutableErrorMsg string = `field is immutable`
const fieldImmutableErrorMsg string = genericvalidation.FieldImmutableErrorMsg
const isNotIntegerErrorMsg string = `must be an integer`
var pdPartitionErrorMsg string = validation.InclusiveRangeError(1, 255)
var volumeModeErrorMsg string = "must be a number between 0 and 0777 (octal), both inclusive"
const totalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
// BannedOwners is a black list of object that are not allowed to be owners.
var BannedOwners = map[schema.GroupVersionKind]struct{}{
v1.SchemeGroupVersion.WithKind("Event"): {},
}
var BannedOwners = genericvalidation.BannedOwners
// ValidateHasLabel requires that api.ObjectMeta has a Label with key and expectedValue
func ValidateHasLabel(meta api.ObjectMeta, fldPath *field.Path, key, expectedValue string) field.ErrorList {
@ -83,18 +79,7 @@ func ValidateHasLabel(meta api.ObjectMeta, fldPath *field.Path, key, expectedVal
// ValidateAnnotations validates that a set of annotations are correctly defined.
func ValidateAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
var totalSize int64
for k, v := range annotations {
for _, msg := range validation.IsQualifiedName(strings.ToLower(k)) {
allErrs = append(allErrs, field.Invalid(fldPath, k, msg))
}
totalSize += (int64)(len(k)) + (int64)(len(v))
}
if totalSize > (int64)(totalAnnotationSizeLimitB) {
allErrs = append(allErrs, field.TooLong(fldPath, "", totalAnnotationSizeLimitB))
}
return allErrs
return genericvalidation.ValidateAnnotations(annotations, fldPath)
}
func ValidateDNS1123Label(value string, fldPath *field.Path) field.ErrorList {
@ -185,43 +170,8 @@ func ValidateEndpointsSpecificAnnotations(annotations map[string]string, fldPath
return allErrs
}
func validateOwnerReference(ownerReference metav1.OwnerReference, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
gvk := schema.FromAPIVersionAndKind(ownerReference.APIVersion, ownerReference.Kind)
// gvk.Group is empty for the legacy group.
if len(gvk.Version) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("apiVersion"), ownerReference.APIVersion, "version must not be empty"))
}
if len(gvk.Kind) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), ownerReference.Kind, "kind must not be empty"))
}
if len(ownerReference.Name) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), ownerReference.Name, "name must not be empty"))
}
if len(ownerReference.UID) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), ownerReference.UID, "uid must not be empty"))
}
if _, ok := BannedOwners[gvk]; ok {
allErrs = append(allErrs, field.Invalid(fldPath, ownerReference, fmt.Sprintf("%s is disallowed from being an owner", gvk)))
}
return allErrs
}
func ValidateOwnerReferences(ownerReferences []metav1.OwnerReference, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
controllerName := ""
for _, ref := range ownerReferences {
allErrs = append(allErrs, validateOwnerReference(ref, fldPath)...)
if ref.Controller != nil && *ref.Controller {
if controllerName != "" {
allErrs = append(allErrs, field.Invalid(fldPath, ownerReferences,
fmt.Sprintf("Only one reference can have Controller set to true. Found \"true\" in references for %v and %v", controllerName, ref.Name)))
} else {
controllerName = ref.Name
}
}
}
return allErrs
return genericvalidation.ValidateOwnerReferences(ownerReferences, fldPath)
}
// ValidateNameFunc validates that the provided name is valid for a given resource type.
@ -229,7 +179,7 @@ func ValidateOwnerReferences(ownerReferences []metav1.OwnerReference, fldPath *f
// if the name will have a value appended to it. If the name is not valid,
// this returns a list of descriptions of individual characteristics of the
// value that were not valid. Otherwise this returns an empty list or nil.
type ValidateNameFunc func(name string, prefix bool) []string
type ValidateNameFunc genericvalidation.ValidateNameFunc
// maskTrailingDash replaces the final character of a string with a subdomain safe
// value if is a dash.
@ -264,7 +214,7 @@ var ValidateNodeName = NameIsDNSSubdomain
// ValidateNamespaceName can be used to check whether the given namespace name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
var ValidateNamespaceName = NameIsDNSLabel
var ValidateNamespaceName = genericvalidation.ValidateNamespaceName
// ValidateLimitRangeName can be used to check whether the given limit range name is valid.
// Prefix indicates this name will be used as part of generation, in which case
@ -285,7 +235,7 @@ var ValidateSecretName = NameIsDNSSubdomain
// ValidateServiceAccountName can be used to check whether the given service account name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
var ValidateServiceAccountName = NameIsDNSSubdomain
var ValidateServiceAccountName = genericvalidation.ValidateServiceAccountName
// ValidateEndpointsName can be used to check whether the given endpoints name is valid.
// Prefix indicates this name will be used as part of generation, in which case
@ -293,39 +243,27 @@ var ValidateServiceAccountName = NameIsDNSSubdomain
var ValidateEndpointsName = NameIsDNSSubdomain
// ValidateClusterName can be used to check whether the given cluster name is valid.
var ValidateClusterName = NameIsDNS1035Label
var ValidateClusterName = genericvalidation.ValidateClusterName
// TODO update all references to these functions to point to the genericvalidation ones
// NameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain.
func NameIsDNSSubdomain(name string, prefix bool) []string {
if prefix {
name = maskTrailingDash(name)
}
return validation.IsDNS1123Subdomain(name)
return genericvalidation.NameIsDNSSubdomain(name, prefix)
}
// NameIsDNSLabel is a ValidateNameFunc for names that must be a DNS 1123 label.
func NameIsDNSLabel(name string, prefix bool) []string {
if prefix {
name = maskTrailingDash(name)
}
return validation.IsDNS1123Label(name)
return genericvalidation.NameIsDNSLabel(name, prefix)
}
// NameIsDNS1035Label is a ValidateNameFunc for names that must be a DNS 952 label.
func NameIsDNS1035Label(name string, prefix bool) []string {
if prefix {
name = maskTrailingDash(name)
}
return validation.IsDNS1035Label(name)
return genericvalidation.NameIsDNS1035Label(name, prefix)
}
// Validates that given value is not negative.
func ValidateNonnegativeField(value int64, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if value < 0 {
allErrs = append(allErrs, field.Invalid(fldPath, value, isNegativeErrorMsg))
}
return allErrs
return genericvalidation.ValidateNonnegativeField(value, fldPath)
}
// Validates that a Quantity is not negative
@ -338,11 +276,7 @@ func ValidateNonnegativeQuantity(value resource.Quantity, fldPath *field.Path) f
}
func ValidateImmutableField(newVal, oldVal interface{}, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if !api.Semantic.DeepEqual(oldVal, newVal) {
allErrs = append(allErrs, field.Invalid(fldPath, newVal, fieldImmutableErrorMsg))
}
return allErrs
return genericvalidation.ValidateImmutableField(newVal, oldVal, fldPath)
}
func ValidateImmutableAnnotation(newVal string, oldVal string, annotation string, fldPath *field.Path) field.ErrorList {
@ -359,124 +293,16 @@ func ValidateImmutableAnnotation(newVal string, oldVal string, annotation string
// It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before.
// TODO: Remove calls to this method scattered in validations of specific resources, e.g., ValidatePodUpdate.
func ValidateObjectMeta(meta *api.ObjectMeta, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(meta.GenerateName) != 0 {
for _, msg := range nameFn(meta.GenerateName, true) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("generateName"), meta.GenerateName, msg))
}
}
// If the generated name validates, but the calculated value does not, it's a problem with generation, and we
// report it here. This may confuse users, but indicates a programming bug and still must be validated.
// If there are multiple fields out of which one is required then add an or as a separator
if len(meta.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name or generateName is required"))
} else {
for _, msg := range nameFn(meta.Name, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), meta.Name, msg))
}
}
if requiresNamespace {
if len(meta.Namespace) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), ""))
} else {
for _, msg := range ValidateNamespaceName(meta.Namespace, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), meta.Namespace, msg))
}
}
} else {
if len(meta.Namespace) != 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("namespace"), "not allowed on this type"))
}
}
if len(meta.ClusterName) != 0 {
for _, msg := range ValidateClusterName(meta.ClusterName, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("clusterName"), meta.ClusterName, msg))
}
}
allErrs = append(allErrs, ValidateNonnegativeField(meta.Generation, fldPath.Child("generation"))...)
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(meta.Labels, fldPath.Child("labels"))...)
allErrs = append(allErrs, ValidateAnnotations(meta.Annotations, fldPath.Child("annotations"))...)
allErrs = append(allErrs, ValidateOwnerReferences(meta.OwnerReferences, fldPath.Child("ownerReferences"))...)
for _, finalizer := range meta.Finalizers {
allErrs = append(allErrs, validateFinalizerName(finalizer, fldPath.Child("finalizers"))...)
}
return allErrs
return genericvalidation.ValidateObjectMeta(meta, requiresNamespace, genericvalidation.ValidateNameFunc(nameFn), fldPath)
}
// ValidateObjectMetaUpdate validates an object's metadata when updated
func ValidateObjectMetaUpdate(newMeta, oldMeta *api.ObjectMeta, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if !RepairMalformedUpdates && newMeta.UID != oldMeta.UID {
allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), newMeta.UID, "field is immutable"))
}
// in the event it is left empty, set it, to allow clients more flexibility
// TODO: remove the following code that repairs the update request when we retire the clients that modify the immutable fields.
// Please do not copy this pattern elsewhere; validation functions should not be modifying the objects they are passed!
if RepairMalformedUpdates {
if len(newMeta.UID) == 0 {
newMeta.UID = oldMeta.UID
}
// ignore changes to timestamp
if oldMeta.CreationTimestamp.IsZero() {
oldMeta.CreationTimestamp = newMeta.CreationTimestamp
} else {
newMeta.CreationTimestamp = oldMeta.CreationTimestamp
}
// an object can never remove a deletion timestamp or clear/change grace period seconds
if !oldMeta.DeletionTimestamp.IsZero() {
newMeta.DeletionTimestamp = oldMeta.DeletionTimestamp
}
if oldMeta.DeletionGracePeriodSeconds != nil && newMeta.DeletionGracePeriodSeconds == nil {
newMeta.DeletionGracePeriodSeconds = oldMeta.DeletionGracePeriodSeconds
}
}
// TODO: needs to check if newMeta==nil && oldMeta !=nil after the repair logic is removed.
if newMeta.DeletionGracePeriodSeconds != nil && (oldMeta.DeletionGracePeriodSeconds == nil || *newMeta.DeletionGracePeriodSeconds != *oldMeta.DeletionGracePeriodSeconds) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("deletionGracePeriodSeconds"), newMeta.DeletionGracePeriodSeconds, "field is immutable; may only be changed via deletion"))
}
if newMeta.DeletionTimestamp != nil && (oldMeta.DeletionTimestamp == nil || !newMeta.DeletionTimestamp.Equal(*oldMeta.DeletionTimestamp)) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("deletionTimestamp"), newMeta.DeletionTimestamp, "field is immutable; may only be changed via deletion"))
}
// Finalizers cannot be added if the object is already being deleted.
if oldMeta.DeletionTimestamp != nil {
allErrs = append(allErrs, ValidateNoNewFinalizers(newMeta.Finalizers, oldMeta.Finalizers, fldPath.Child("finalizers"))...)
}
// Reject updates that don't specify a resource version
if len(newMeta.ResourceVersion) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceVersion"), newMeta.ResourceVersion, "must be specified for an update"))
}
// Generation shouldn't be decremented
if newMeta.Generation < oldMeta.Generation {
allErrs = append(allErrs, field.Invalid(fldPath.Child("generation"), newMeta.Generation, "must not be decremented"))
}
allErrs = append(allErrs, ValidateImmutableField(newMeta.Name, oldMeta.Name, fldPath.Child("name"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.Namespace, oldMeta.Namespace, fldPath.Child("namespace"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.UID, oldMeta.UID, fldPath.Child("uid"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.CreationTimestamp, oldMeta.CreationTimestamp, fldPath.Child("creationTimestamp"))...)
allErrs = append(allErrs, ValidateImmutableField(newMeta.ClusterName, oldMeta.ClusterName, fldPath.Child("clusterName"))...)
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(newMeta.Labels, fldPath.Child("labels"))...)
allErrs = append(allErrs, ValidateAnnotations(newMeta.Annotations, fldPath.Child("annotations"))...)
allErrs = append(allErrs, ValidateOwnerReferences(newMeta.OwnerReferences, fldPath.Child("ownerReferences"))...)
return allErrs
return genericvalidation.ValidateObjectMetaUpdate(newMeta, oldMeta, fldPath)
}
func ValidateNoNewFinalizers(newFinalizers []string, oldFinalizers []string, fldPath *field.Path) field.ErrorList {
const newFinalizersErrorMsg string = `no new finalizers can be added if the object is being deleted`
allErrs := field.ErrorList{}
extra := sets.NewString(newFinalizers...).Difference(sets.NewString(oldFinalizers...))
if len(extra) != 0 {
allErrs = append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("no new finalizers can be added if the object is being deleted, found new finalizers %#v", extra.List())))
}
return allErrs
return genericvalidation.ValidateNoNewFinalizers(newFinalizers, oldFinalizers, fldPath)
}
func validateVolumes(volumes []api.Volume, fldPath *field.Path) (sets.String, field.ErrorList) {
@ -3482,21 +3308,7 @@ func ValidateNamespace(namespace *api.Namespace) field.ErrorList {
// Validate finalizer names
func validateFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, msg := range validation.IsQualifiedName(stringValue) {
allErrs = append(allErrs, field.Invalid(fldPath, stringValue, msg))
}
if len(allErrs) != 0 {
return allErrs
}
if len(strings.Split(stringValue, "/")) == 1 {
if !api.IsStandardFinalizerName(stringValue) {
return append(allErrs, field.Invalid(fldPath, stringValue, "name is neither a standard finalizer name nor is it fully qualified"))
}
}
return field.ErrorList{}
return genericvalidation.ValidateFinalizerName(stringValue, fldPath)
}
// ValidateNamespaceUpdate tests to make sure a namespace update can be applied.

View File

@ -17,11 +17,9 @@ limitations under the License.
package validation
import (
"math/rand"
"reflect"
"strings"
"testing"
"time"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/resource"
@ -55,450 +53,6 @@ func expectPrefix(t *testing.T, prefix string, errs field.ErrorList) {
}
}
// Ensure custom name functions are allowed
func TestValidateObjectMetaCustomName(t *testing.T) {
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", GenerateName: "foo"},
false,
func(s string, prefix bool) []string {
if s == "test" {
return nil
}
return []string{"name-gen"}
},
field.NewPath("field"))
if len(errs) != 1 {
t.Fatalf("unexpected errors: %v", errs)
}
if !strings.Contains(errs[0].Error(), "name-gen") {
t.Errorf("unexpected error message: %v", errs)
}
}
// Ensure namespace names follow dns label format
func TestValidateObjectMetaNamespaces(t *testing.T) {
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", Namespace: "foo.bar"},
true,
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
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)},
true,
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
if len(errs) != 2 {
t.Fatalf("unexpected errors: %v", errs)
}
if !strings.Contains(errs[0].Error(), "Invalid value") || !strings.Contains(errs[1].Error(), "Invalid value") {
t.Errorf("unexpected error message: %v", errs)
}
}
func TestValidateObjectMetaOwnerReferences(t *testing.T) {
trueVar := true
falseVar := false
testCases := []struct {
description string
ownerReferences []metav1.OwnerReference
expectError bool
expectedErrorMessage string
}{
{
description: "simple success - third party extension.",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "1",
},
},
expectError: false,
expectedErrorMessage: "",
},
{
description: "simple failures - event shouldn't be set as an owner",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "v1",
Kind: "Event",
Name: "name",
UID: "1",
},
},
expectError: true,
expectedErrorMessage: "is disallowed from being an owner",
},
{
description: "simple controller ref success - one reference with Controller set",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "1",
Controller: &falseVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "2",
Controller: &trueVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "3",
Controller: &falseVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "4",
},
},
expectError: false,
expectedErrorMessage: "",
},
{
description: "simple controller ref failure - two references with Controller set",
ownerReferences: []metav1.OwnerReference{
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "1",
Controller: &falseVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "2",
Controller: &trueVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "3",
Controller: &trueVar,
},
{
APIVersion: "thirdpartyVersion",
Kind: "thirdpartyKind",
Name: "name",
UID: "4",
},
},
expectError: true,
expectedErrorMessage: "Only one reference can have Controller set to true",
},
}
for _, tc := range testCases {
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", Namespace: "test", OwnerReferences: tc.ownerReferences},
true,
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
if len(errs) != 0 && !tc.expectError {
t.Errorf("unexpected error: %v in test case %v", errs, tc.description)
}
if len(errs) == 0 && tc.expectError {
t.Errorf("expect error in test case %v", tc.description)
}
if len(errs) != 0 && !strings.Contains(errs[0].Error(), tc.expectedErrorMessage) {
t.Errorf("unexpected error message: %v in test case %v", errs, tc.description)
}
}
}
func TestValidateObjectMetaUpdateIgnoresCreationTimestamp(t *testing.T) {
if errs := ValidateObjectMetaUpdate(
&api.ObjectMeta{Name: "test", ResourceVersion: "1"},
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))},
field.NewPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
if errs := ValidateObjectMetaUpdate(
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))},
&api.ObjectMeta{Name: "test", ResourceVersion: "1"},
field.NewPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
if errs := ValidateObjectMetaUpdate(
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(10, 0))},
&api.ObjectMeta{Name: "test", ResourceVersion: "1", CreationTimestamp: metav1.NewTime(time.Unix(11, 0))},
field.NewPath("field"),
); len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
}
func TestValidateFinalizersUpdate(t *testing.T) {
testcases := map[string]struct {
Old api.ObjectMeta
New api.ObjectMeta
ExpectedErr string
}{
"invalid adding finalizers": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a", "y/b"}},
ExpectedErr: "y/b",
},
"invalid changing finalizers": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/b"}},
ExpectedErr: "x/b",
},
"valid removing finalizers": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a", "y/b"}},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &metav1.Time{}, Finalizers: []string{"x/a"}},
ExpectedErr: "",
},
"valid adding finalizers for objects not being deleted": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{"x/a"}},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", Finalizers: []string{"x/a", "y/b"}},
ExpectedErr: "",
},
}
for name, tc := range testcases {
errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field"))
if len(errs) == 0 {
if len(tc.ExpectedErr) != 0 {
t.Errorf("case: %q, expected error to contain %q", name, tc.ExpectedErr)
}
} else if e, a := tc.ExpectedErr, errs.ToAggregate().Error(); !strings.Contains(a, e) {
t.Errorf("case: %q, expected error to contain %q, got error %q", name, e, a)
}
}
}
func TestValidateObjectMetaUpdatePreventsDeletionFieldMutation(t *testing.T) {
now := metav1.NewTime(time.Unix(1000, 0).UTC())
later := metav1.NewTime(time.Unix(2000, 0).UTC())
gracePeriodShort := int64(30)
gracePeriodLong := int64(40)
testcases := map[string]struct {
Old api.ObjectMeta
New api.ObjectMeta
ExpectedNew api.ObjectMeta
ExpectedErrs []string
}{
"valid without deletion fields": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedErrs: []string{},
},
"valid with deletion fields": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now, DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedErrs: []string{},
},
"invalid set deletionTimestamp": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedErrs: []string{"field.deletionTimestamp: Invalid value: \"1970-01-01T00:16:40Z\": field is immutable; may only be changed via deletion"},
},
"invalid clear deletionTimestamp": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedErrs: []string{}, // no errors, validation copies the old value
},
"invalid change deletionTimestamp": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &later},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionTimestamp: &now},
ExpectedErrs: []string{}, // no errors, validation copies the old value
},
"invalid set deletionGracePeriodSeconds": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedErrs: []string{"field.deletionGracePeriodSeconds: Invalid value: 30: field is immutable; may only be changed via deletion"},
},
"invalid clear deletionGracePeriodSeconds": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1"},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
ExpectedErrs: []string{}, // no errors, validation copies the old value
},
"invalid change deletionGracePeriodSeconds": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodShort},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodLong},
ExpectedNew: api.ObjectMeta{Name: "test", ResourceVersion: "1", DeletionGracePeriodSeconds: &gracePeriodLong},
ExpectedErrs: []string{"field.deletionGracePeriodSeconds: Invalid value: 40: field is immutable; may only be changed via deletion"},
},
}
for k, tc := range testcases {
errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field"))
if len(errs) != len(tc.ExpectedErrs) {
t.Logf("%s: Expected: %#v", k, tc.ExpectedErrs)
t.Logf("%s: Got: %#v", k, errs)
t.Errorf("%s: expected %d errors, got %d", k, len(tc.ExpectedErrs), len(errs))
continue
}
for i := range errs {
if errs[i].Error() != tc.ExpectedErrs[i] {
t.Errorf("%s: error #%d: expected %q, got %q", k, i, tc.ExpectedErrs[i], errs[i].Error())
}
}
if !reflect.DeepEqual(tc.New, tc.ExpectedNew) {
t.Errorf("%s: Expected after validation:\n%#v\ngot\n%#v", k, tc.ExpectedNew, tc.New)
}
}
}
func TestObjectMetaGenerationUpdate(t *testing.T) {
testcases := map[string]struct {
Old api.ObjectMeta
New api.ObjectMeta
ExpectedErrs []string
}{
"invalid generation change - decremented": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 4},
ExpectedErrs: []string{"field.generation: Invalid value: 4: must not be decremented"},
},
"valid generation change - incremented by one": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 1},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 2},
ExpectedErrs: []string{},
},
"valid generation field - not updated": {
Old: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5},
New: api.ObjectMeta{Name: "test", ResourceVersion: "1", Generation: 5},
ExpectedErrs: []string{},
},
}
for k, tc := range testcases {
errList := []string{}
errs := ValidateObjectMetaUpdate(&tc.New, &tc.Old, field.NewPath("field"))
if len(errs) != len(tc.ExpectedErrs) {
t.Logf("%s: Expected: %#v", k, tc.ExpectedErrs)
for _, err := range errs {
errList = append(errList, err.Error())
}
t.Logf("%s: Got: %#v", k, errList)
t.Errorf("%s: expected %d errors, got %d", k, len(tc.ExpectedErrs), len(errs))
continue
}
for i := range errList {
if errList[i] != tc.ExpectedErrs[i] {
t.Errorf("%s: error #%d: expected %q, got %q", k, i, tc.ExpectedErrs[i], errList[i])
}
}
}
}
// Ensure trailing slash is allowed in generate name
func TestValidateObjectMetaTrimsTrailingSlash(t *testing.T) {
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", GenerateName: "foo-"},
false,
NameIsDNSSubdomain,
field.NewPath("field"))
if len(errs) != 0 {
t.Fatalf("unexpected errors: %v", errs)
}
}
func TestValidateAnnotations(t *testing.T) {
successCases := []map[string]string{
{"simple": "bar"},
{"now-with-dashes": "bar"},
{"1-starts-with-num": "bar"},
{"1234": "bar"},
{"simple/simple": "bar"},
{"now-with-dashes/simple": "bar"},
{"now-with-dashes/now-with-dashes": "bar"},
{"now.with.dots/simple": "bar"},
{"now-with.dashes-and.dots/simple": "bar"},
{"1-num.2-num/3-num": "bar"},
{"1234/5678": "bar"},
{"1.2.3.4/5678": "bar"},
{"UpperCase123": "bar"},
{"a": strings.Repeat("b", totalAnnotationSizeLimitB-1)},
{
"a": strings.Repeat("b", totalAnnotationSizeLimitB/2-1),
"c": strings.Repeat("d", totalAnnotationSizeLimitB/2-1),
},
}
for i := range successCases {
errs := ValidateAnnotations(successCases[i], field.NewPath("field"))
if len(errs) != 0 {
t.Errorf("case[%d] expected success, got %#v", i, errs)
}
}
nameErrorCases := []struct {
annotations map[string]string
expect string
}{
{map[string]string{"nospecialchars^=@": "bar"}, namePartErrMsg},
{map[string]string{"cantendwithadash-": "bar"}, namePartErrMsg},
{map[string]string{"only/one/slash": "bar"}, nameErrMsg},
{map[string]string{strings.Repeat("a", 254): "bar"}, maxLengthErrMsg},
}
for i := range nameErrorCases {
errs := ValidateAnnotations(nameErrorCases[i].annotations, field.NewPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d]: expected failure", i)
} else {
if !strings.Contains(errs[0].Detail, nameErrorCases[i].expect) {
t.Errorf("case[%d]: error details do not include %q: %q", i, nameErrorCases[i].expect, errs[0].Detail)
}
}
}
totalSizeErrorCases := []map[string]string{
{"a": strings.Repeat("b", totalAnnotationSizeLimitB)},
{
"a": strings.Repeat("b", totalAnnotationSizeLimitB/2),
"c": strings.Repeat("d", totalAnnotationSizeLimitB/2),
},
}
for i := range totalSizeErrorCases {
errs := ValidateAnnotations(totalSizeErrorCases[i], field.NewPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d] expected failure", i)
}
}
}
func testVolume(name string, namespace string, spec api.PersistentVolumeSpec) *api.PersistentVolume {
objMeta := api.ObjectMeta{Name: name}
if namespace != "" {

View File

@ -18,7 +18,7 @@ go_library(
deps = [
"//pkg/api:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/api/validation/genericvalidation:go_default_library",
"//pkg/auth/authenticator:go_default_library",
"//vendor:github.com/dgrijalva/jwt-go",
"//vendor:github.com/golang/glog",

View File

@ -23,7 +23,7 @@ import (
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/api/validation/genericvalidation"
)
const (
@ -53,10 +53,10 @@ func SplitUsername(username string) (string, string, error) {
return "", "", invalidUsernameErr
}
namespace, name := parts[0], parts[1]
if len(validation.ValidateNamespaceName(namespace, false)) != 0 {
if len(genericvalidation.ValidateNamespaceName(namespace, false)) != 0 {
return "", "", invalidUsernameErr
}
if len(validation.ValidateServiceAccountName(name, false)) != 0 {
if len(genericvalidation.ValidateServiceAccountName(name, false)) != 0 {
return "", "", invalidUsernameErr
}
return namespace, name, nil