Make name validators return string slices

This commit is contained in:
Tim Hockin
2015-12-15 22:03:20 -08:00
parent 66d0d87829
commit 152c86ab06
17 changed files with 140 additions and 181 deletions

View File

@@ -366,7 +366,7 @@ func (t *Tester) testCreateValidatesNames(valid runtime.Object) {
ctx := t.TestContext()
_, err := t.storage.(rest.Creater).Create(ctx, objCopy)
if !errors.IsInvalid(err) {
t.Errorf("%s: Expected to get an invalid resource error, got %v", invalidName, err)
t.Errorf("%s: Expected to get an invalid resource error, got '%v'", invalidName, err)
}
}
@@ -378,7 +378,7 @@ func (t *Tester) testCreateValidatesNames(valid runtime.Object) {
ctx := t.TestContext()
_, err := t.storage.(rest.Creater).Create(ctx, objCopy)
if !errors.IsInvalid(err) {
t.Errorf("%s: Expected to get an invalid resource error, got %v", invalidSuffix, err)
t.Errorf("%s: Expected to get an invalid resource error, got '%v'", invalidSuffix, err)
}
}
}

View File

@@ -55,8 +55,8 @@ func ValidateLabelSelectorRequirement(sr unversioned.LabelSelectorRequirement, f
// ValidateLabelName validates that the label name is correctly defined.
func ValidateLabelName(labelName string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, err := range validation.IsQualifiedName(labelName) {
allErrs = append(allErrs, field.Invalid(fldPath, labelName, err))
for _, msg := range validation.IsQualifiedName(labelName) {
allErrs = append(allErrs, field.Invalid(fldPath, labelName, msg))
}
return allErrs
}
@@ -66,8 +66,8 @@ func ValidateLabels(labels map[string]string, fldPath *field.Path) field.ErrorLi
allErrs := field.ErrorList{}
for k, v := range labels {
allErrs = append(allErrs, ValidateLabelName(k, fldPath)...)
for _, err := range validation.IsValidLabelValue(v) {
allErrs = append(allErrs, field.Invalid(fldPath, v, err))
for _, msg := range validation.IsValidLabelValue(v) {
allErrs = append(allErrs, field.Invalid(fldPath, v, msg))
}
}
return allErrs

View File

@@ -28,36 +28,36 @@ var NameMayNotBe = []string{".", ".."}
var NameMayNotContain = []string{"/", "%"}
// IsValidPathSegmentName validates the name can be safely encoded as a path segment
func IsValidPathSegmentName(name string) (bool, string) {
func IsValidPathSegmentName(name string) []string {
for _, illegalName := range NameMayNotBe {
if name == illegalName {
return false, fmt.Sprintf(`name may not be %q`, illegalName)
return []string{fmt.Sprintf(`may not be '%s'`, illegalName)}
}
}
for _, illegalContent := range NameMayNotContain {
if strings.Contains(name, illegalContent) {
return false, fmt.Sprintf(`name may not contain %q`, illegalContent)
return []string{fmt.Sprintf(`may not contain '%s'`, illegalContent)}
}
}
return true, ""
return nil
}
// IsValidPathSegmentPrefix validates the name can be used as a prefix for a name which will be encoded as a path segment
// It does not check for exact matches with disallowed names, since an arbitrary suffix might make the name valid
func IsValidPathSegmentPrefix(name string) (bool, string) {
func IsValidPathSegmentPrefix(name string) []string {
for _, illegalContent := range NameMayNotContain {
if strings.Contains(name, illegalContent) {
return false, fmt.Sprintf(`name may not contain %q`, illegalContent)
return []string{fmt.Sprintf(`may not contain '%s'`, illegalContent)}
}
}
return true, ""
return nil
}
// ValidatePathSegmentName validates the name can be safely encoded as a path segment
func ValidatePathSegmentName(name string, prefix bool) (bool, string) {
func ValidatePathSegmentName(name string, prefix bool) []string {
if prefix {
return IsValidPathSegmentPrefix(name)
} else {

View File

@@ -25,32 +25,27 @@ func TestValidatePathSegmentName(t *testing.T) {
testcases := map[string]struct {
Name string
Prefix bool
ExpectedOK bool
ExpectedMsg string
}{
"empty": {
Name: "",
Prefix: false,
ExpectedOK: true,
ExpectedMsg: "",
},
"empty,prefix": {
Name: "",
Prefix: true,
ExpectedOK: true,
ExpectedMsg: "",
},
"valid": {
Name: "foo.bar.baz",
Prefix: false,
ExpectedOK: true,
ExpectedMsg: "",
},
"valid,prefix": {
Name: "foo.bar.baz",
Prefix: true,
ExpectedOK: true,
ExpectedMsg: "",
},
@@ -58,92 +53,80 @@ func TestValidatePathSegmentName(t *testing.T) {
"valid complex": {
Name: "sha256:ABCDEF012345@ABCDEF012345",
Prefix: false,
ExpectedOK: true,
ExpectedMsg: "",
},
// Make sure non-ascii characters are tolerated
"valid extended charset": {
Name: "Iñtërnâtiônàlizætiøn",
Prefix: false,
ExpectedOK: true,
ExpectedMsg: "",
},
"dot": {
Name: ".",
Prefix: false,
ExpectedOK: false,
ExpectedMsg: ".",
},
"dot leading": {
Name: ".test",
Prefix: false,
ExpectedOK: true,
ExpectedMsg: "",
},
"dot,prefix": {
Name: ".",
Prefix: true,
ExpectedOK: true, // allowed because a suffix could make it valid
ExpectedMsg: "",
},
"dot dot": {
Name: "..",
Prefix: false,
ExpectedOK: false,
ExpectedMsg: "..",
},
"dot dot leading": {
Name: "..test",
Prefix: false,
ExpectedOK: true,
ExpectedMsg: "",
},
"dot dot,prefix": {
Name: "..",
Prefix: true,
ExpectedOK: true, // allowed because a suffix could make it valid
ExpectedMsg: "",
},
"slash": {
Name: "foo/bar",
Prefix: false,
ExpectedOK: false,
ExpectedMsg: "/",
},
"slash,prefix": {
Name: "foo/bar",
Prefix: true,
ExpectedOK: false,
ExpectedMsg: "/",
},
"percent": {
Name: "foo%bar",
Prefix: false,
ExpectedOK: false,
ExpectedMsg: "%",
},
"percent,prefix": {
Name: "foo%bar",
Prefix: true,
ExpectedOK: false,
ExpectedMsg: "%",
},
}
for k, tc := range testcases {
ok, msg := ValidatePathSegmentName(tc.Name, tc.Prefix)
if ok != tc.ExpectedOK {
t.Errorf("%s: expected ok=%v, got %v", k, tc.ExpectedOK, ok)
msgs := ValidatePathSegmentName(tc.Name, tc.Prefix)
if len(tc.ExpectedMsg) == 0 && len(msgs) > 0 {
t.Errorf("%s: expected no message, got %v", k, msgs)
}
if len(tc.ExpectedMsg) == 0 && len(msg) > 0 {
t.Errorf("%s: expected no message, got %v", k, msg)
if len(tc.ExpectedMsg) > 0 && len(msgs) == 0 {
t.Errorf("%s: expected error message, got none", k)
}
if len(tc.ExpectedMsg) > 0 && !strings.Contains(msg, tc.ExpectedMsg) {
t.Errorf("%s: expected message containing %q, got %v", k, tc.ExpectedMsg, msg)
if len(tc.ExpectedMsg) > 0 && !strings.Contains(msgs[0], tc.ExpectedMsg) {
t.Errorf("%s: expected message containing %q, got %v", k, tc.ExpectedMsg, msgs[0])
}
}
}

View File

@@ -92,8 +92,8 @@ func ValidateAnnotations(annotations map[string]string, fldPath *field.Path) fie
allErrs := field.ErrorList{}
var totalSize int64
for k, v := range annotations {
for _, err := range validation.IsQualifiedName(strings.ToLower(k)) {
allErrs = append(allErrs, field.Invalid(fldPath, k, err))
for _, msg := range validation.IsQualifiedName(strings.ToLower(k)) {
allErrs = append(allErrs, field.Invalid(fldPath, k, msg))
}
totalSize += (int64)(len(k)) + (int64)(len(v))
}
@@ -162,9 +162,11 @@ func ValidateOwnerReferences(ownerReferences []api.OwnerReference, fldPath *fiel
}
// 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.
type ValidateNameFunc func(name string, prefix bool) (bool, string)
// 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 names 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
// maskTrailingDash replaces the final character of a string with a subdomain safe
// value if is a dash.
@@ -178,106 +180,86 @@ func maskTrailingDash(name string) string {
// ValidatePodName can be used to check whether the given pod name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidatePodName(name string, prefix bool) (bool, string) {
return NameIsDNSSubdomain(name, prefix)
}
var ValidatePodName = NameIsDNSSubdomain
// ValidateReplicationControllerName can be used to check whether the given replication
// controller name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidateReplicationControllerName(name string, prefix bool) (bool, string) {
return NameIsDNSSubdomain(name, prefix)
}
var ValidateReplicationControllerName = NameIsDNSSubdomain
// ValidateServiceName can be used to check whether the given service name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidateServiceName(name string, prefix bool) (bool, string) {
return NameIsDNS952Label(name, prefix)
}
var ValidateServiceName = NameIsDNS952Label
// ValidateNodeName can be used to check whether the given node name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidateNodeName(name string, prefix bool) (bool, string) {
return NameIsDNSSubdomain(name, prefix)
}
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.
func ValidateNamespaceName(name string, prefix bool) (bool, string) {
return NameIsDNSLabel(name, prefix)
}
var ValidateNamespaceName = NameIsDNSLabel
// 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
// trailing dashes are allowed.
func ValidateLimitRangeName(name string, prefix bool) (bool, string) {
return NameIsDNSSubdomain(name, prefix)
}
var ValidateLimitRangeName = NameIsDNSSubdomain
// ValidateResourceQuotaName can be used to check whether the given
// resource quota name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidateResourceQuotaName(name string, prefix bool) (bool, string) {
return NameIsDNSSubdomain(name, prefix)
}
var ValidateResourceQuotaName = NameIsDNSSubdomain
// ValidateSecretName can be used to check whether the given secret name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidateSecretName(name string, prefix bool) (bool, string) {
return NameIsDNSSubdomain(name, prefix)
}
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.
func ValidateServiceAccountName(name string, prefix bool) (bool, string) {
return NameIsDNSSubdomain(name, prefix)
}
var ValidateServiceAccountName = NameIsDNSSubdomain
// 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
// trailing dashes are allowed.
func ValidateEndpointsName(name string, prefix bool) (bool, string) {
return NameIsDNSSubdomain(name, prefix)
}
var ValidateEndpointsName = NameIsDNSSubdomain
// NameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain.
func NameIsDNSSubdomain(name string, prefix bool) (bool, string) {
func NameIsDNSSubdomain(name string, prefix bool) []string {
if prefix {
name = maskTrailingDash(name)
}
if validation.IsDNS1123Subdomain(name) {
return true, ""
return nil
}
return false, DNSSubdomainErrorMsg
return []string{DNSSubdomainErrorMsg}
}
// NameIsDNSLabel is a ValidateNameFunc for names that must be a DNS 1123 label.
func NameIsDNSLabel(name string, prefix bool) (bool, string) {
func NameIsDNSLabel(name string, prefix bool) []string {
if prefix {
name = maskTrailingDash(name)
}
if validation.IsDNS1123Label(name) {
return true, ""
return nil
}
return false, DNS1123LabelErrorMsg
return []string{DNS1123LabelErrorMsg}
}
// 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) []string {
if prefix {
name = maskTrailingDash(name)
}
if validation.IsDNS952Label(name) {
return true, ""
return nil
}
return false, DNS952LabelErrorMsg
return []string{DNS952LabelErrorMsg}
}
// Validates that given value is not negative.
@@ -314,8 +296,8 @@ func ValidateObjectMeta(meta *api.ObjectMeta, requiresNamespace bool, nameFn Val
allErrs := field.ErrorList{}
if len(meta.GenerateName) != 0 {
if ok, qualifier := nameFn(meta.GenerateName, true); !ok {
allErrs = append(allErrs, field.Invalid(fldPath.Child("generateName"), meta.GenerateName, qualifier))
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
@@ -324,15 +306,17 @@ func ValidateObjectMeta(meta *api.ObjectMeta, requiresNamespace bool, nameFn Val
if len(meta.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name or generateName is required"))
} else {
if ok, qualifier := nameFn(meta.Name, false); !ok {
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), meta.Name, qualifier))
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 if ok, _ := ValidateNamespaceName(meta.Namespace, false); !ok {
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), meta.Namespace, DNS1123LabelErrorMsg))
} 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 {
@@ -816,9 +800,9 @@ func validateAzureFile(azure *api.AzureFileVolumeSource, fldPath *field.Path) fi
return allErrs
}
func ValidatePersistentVolumeName(name string, prefix bool) (bool, string) {
return NameIsDNSSubdomain(name, prefix)
}
// ValidatePersistentVolumeName checks that a name is appropriate for a
// PersistentVolumeName object.
var ValidatePersistentVolumeName = NameIsDNSSubdomain
var supportedAccessModes = sets.NewString(string(api.ReadWriteOnce), string(api.ReadOnlyMany), string(api.ReadWriteMany))
@@ -1456,13 +1440,13 @@ func ValidatePodSpec(spec *api.PodSpec, fldPath *field.Path) field.ErrorList {
allErrs = append(allErrs, ValidatePodSecurityContext(spec.SecurityContext, spec, fldPath, fldPath.Child("securityContext"))...)
allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets, fldPath.Child("imagePullSecrets"))...)
if len(spec.ServiceAccountName) > 0 {
if ok, msg := ValidateServiceAccountName(spec.ServiceAccountName, false); !ok {
for _, msg := range ValidateServiceAccountName(spec.ServiceAccountName, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceAccountName"), spec.ServiceAccountName, msg))
}
}
if len(spec.NodeName) > 0 {
if ok, msg := ValidateNodeName(spec.NodeName, false); !ok {
for _, msg := range ValidateNodeName(spec.NodeName, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("nodeName"), spec.NodeName, msg))
}
}
@@ -1556,8 +1540,8 @@ func validatePodAffinityTerm(podAffinityTerm api.PodAffinityTerm, allowEmptyTopo
allErrs := field.ErrorList{}
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(podAffinityTerm.LabelSelector, fldPath.Child("matchExpressions"))...)
for _, name := range podAffinityTerm.Namespaces {
if ok, _ := ValidateNamespaceName(name, false); !ok {
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), name, DNS1123LabelErrorMsg))
for _, msg := range ValidateNamespaceName(name, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), name, msg))
}
}
if !allowEmptyTopologyKey && len(podAffinityTerm.TopologyKey) == 0 {
@@ -2146,8 +2130,8 @@ func ValidateNodeUpdate(node, oldNode *api.Node) field.ErrorList {
// Refer to docs/design/resources.md for more details.
func validateResourceName(value string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, err := range validation.IsQualifiedName(value) {
allErrs = append(allErrs, field.Invalid(fldPath, value, err))
for _, msg := range validation.IsQualifiedName(value) {
allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
}
if len(allErrs) != 0 {
return allErrs
@@ -2189,8 +2173,8 @@ func validateResourceQuotaResourceName(value string, fldPath *field.Path) field.
// Validate limit range types
func validateLimitRangeTypeName(value string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, err := range validation.IsQualifiedName(value) {
allErrs = append(allErrs, field.Invalid(fldPath, value, err))
for _, msg := range validation.IsQualifiedName(value) {
allErrs = append(allErrs, field.Invalid(fldPath, value, msg))
}
if len(allErrs) != 0 {
return allErrs
@@ -2449,9 +2433,7 @@ func ValidateSecretUpdate(newSecret, oldSecret *api.Secret) field.ErrorList {
// ValidateConfigMapName can be used to check whether the given ConfigMap name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
func ValidateConfigMapName(name string, prefix bool) (bool, string) {
return NameIsDNSSubdomain(name, prefix)
}
var ValidateConfigMapName = NameIsDNSSubdomain
// ValidateConfigMap tests whether required fields in the ConfigMap are set.
func ValidateConfigMap(cfg *api.ConfigMap) field.ErrorList {
@@ -2663,8 +2645,8 @@ func ValidateNamespace(namespace *api.Namespace) field.ErrorList {
// Validate finalizer names
func validateFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, err := range validation.IsQualifiedName(stringValue) {
allErrs = append(allErrs, field.Invalid(fldPath, stringValue, err))
for _, msg := range validation.IsQualifiedName(stringValue) {
allErrs = append(allErrs, field.Invalid(fldPath, stringValue, msg))
}
if len(allErrs) != 0 {
return allErrs
@@ -2870,8 +2852,8 @@ func ValidateLoadBalancerStatus(status *api.LoadBalancerStatus, fldPath *field.P
}
}
if len(ingress.Hostname) > 0 {
if valid, errMsg := NameIsDNSSubdomain(ingress.Hostname, false); !valid {
allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, errMsg))
for _, msg := range NameIsDNSSubdomain(ingress.Hostname, false) {
allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, msg))
}
if isIP := (net.ParseIP(ingress.Hostname) != nil); isIP {
allErrs = append(allErrs, field.Invalid(idxPath.Child("hostname"), ingress.Hostname, "must be a DNS name, not an IP address"))

View File

@@ -47,11 +47,11 @@ func TestValidateObjectMetaCustomName(t *testing.T) {
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", GenerateName: "foo"},
false,
func(s string, prefix bool) (bool, string) {
func(s string, prefix bool) []string {
if s == "test" {
return true, ""
return nil
}
return false, "name-gen"
return []string{"name-gen"}
},
field.NewPath("field"))
if len(errs) != 1 {
@@ -67,8 +67,8 @@ func TestValidateObjectMetaNamespaces(t *testing.T) {
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", Namespace: "foo.bar"},
true,
func(s string, prefix bool) (bool, string) {
return true, ""
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
if len(errs) != 1 {
@@ -86,8 +86,8 @@ func TestValidateObjectMetaNamespaces(t *testing.T) {
errs = ValidateObjectMeta(
&api.ObjectMeta{Name: "test", Namespace: string(b)},
true,
func(s string, prefix bool) (bool, string) {
return true, ""
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
if len(errs) != 1 {
@@ -132,8 +132,8 @@ func TestValidateObjectMetaOwnerReferences(t *testing.T) {
errs := ValidateObjectMeta(
&api.ObjectMeta{Name: "test", Namespace: "test", OwnerReferences: tc.ownerReferences},
true,
func(s string, prefix bool) (bool, string) {
return true, ""
func(s string, prefix bool) []string {
return nil
},
field.NewPath("field"))
if len(errs) != 0 && !tc.expectError {