mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-20 07:56:02 +00:00
Address feedbacks
This commit is contained in:
@@ -33,6 +33,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/operation"
|
||||
apiresource "k8s.io/apimachinery/pkg/api/resource"
|
||||
"k8s.io/apimachinery/pkg/api/validate"
|
||||
"k8s.io/apimachinery/pkg/api/validate/content"
|
||||
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@@ -1087,11 +1088,6 @@ func validateDeviceCounter(counter resource.Counter, fldPath *field.Path) field.
|
||||
|
||||
func validateQualifiedName(name resource.QualifiedName, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if name == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "name required"))
|
||||
return allErrs
|
||||
}
|
||||
|
||||
parts := strings.Split(string(name), "/")
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
@@ -1125,15 +1121,11 @@ func validateQualifiedName(name resource.QualifiedName, fldPath *field.Path) fie
|
||||
|
||||
func validateFullyQualifiedName(name resource.FullyQualifiedName, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if name == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "name required"))
|
||||
return allErrs
|
||||
}
|
||||
allErrs = append(allErrs, validateQualifiedName(resource.QualifiedName(name), fldPath)...)
|
||||
// validateQualifiedName checks that the name isn't empty and both parts are valid.
|
||||
// validateQualifiedName checks that both parts are valid.
|
||||
// What we need to enforce here is that there really is a domain.
|
||||
if name != "" && !strings.Contains(string(name), "/") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, name, "must include a domain"))
|
||||
if !strings.Contains(string(name), "/") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, name, "a fully qualified name must be a domain and a name separated by a slash"))
|
||||
}
|
||||
return allErrs.WithOrigin("format=k8s-resource-fully-qualified-name")
|
||||
}
|
||||
@@ -1143,7 +1135,7 @@ func validateCIdentifier(id string, fldPath *field.Path) field.ErrorList {
|
||||
if len(id) > resource.DeviceMaxIDLength {
|
||||
allErrs = append(allErrs, field.TooLong(fldPath, "" /*unused*/, resource.DeviceMaxIDLength))
|
||||
}
|
||||
for _, msg := range validation.IsCIdentifier(id) {
|
||||
for _, msg := range content.IsCIdentifier(id) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, id, msg))
|
||||
}
|
||||
return allErrs
|
||||
|
||||
@@ -263,8 +263,9 @@ func TestValidateClaim(t *testing.T) {
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("requests").Index(1), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("requests").Index(1), badName, "must be the name of a request in the claim or the name of a request and a subrequest separated by '/'"),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), "missing-domain", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')").MarkCoveredByDeclarative(),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), resource.FullyQualifiedName("missing-domain"), "must include a domain").MarkCoveredByDeclarative(),
|
||||
field.Required(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), "name required").MarkCoveredByDeclarative(),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), resource.FullyQualifiedName("missing-domain"), "a fully qualified name must be a domain and a name separated by a slash").MarkCoveredByDeclarative(),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), "", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')").MarkCoveredByDeclarative(),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), resource.FullyQualifiedName(""), "a fully qualified name must be a domain and a name separated by a slash").MarkCoveredByDeclarative(),
|
||||
field.Required(field.NewPath("spec", "devices", "constraints").Index(2).Child("matchAttribute"), ""),
|
||||
field.TooMany(field.NewPath("spec", "devices", "constraints"), resource.DeviceConstraintsMaxSize+1, resource.DeviceConstraintsMaxSize).MarkCoveredByDeclarative(),
|
||||
field.Invalid(field.NewPath("spec", "devices", "config").Index(0).Child("requests").Index(1), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"),
|
||||
@@ -322,8 +323,9 @@ func TestValidateClaim(t *testing.T) {
|
||||
"invalid-distinct-constraint": {
|
||||
wantFailures: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("distinctAttribute"), "missing-domain", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')"),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("distinctAttribute"), resource.FullyQualifiedName("missing-domain"), "must include a domain"),
|
||||
field.Required(field.NewPath("spec", "devices", "constraints").Index(1).Child("distinctAttribute"), "name required"),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("distinctAttribute"), resource.FullyQualifiedName("missing-domain"), "a fully qualified name must be a domain and a name separated by a slash"),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("distinctAttribute"), "", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')"),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("distinctAttribute"), resource.FullyQualifiedName(""), "a fully qualified name must be a domain and a name separated by a slash"),
|
||||
field.Required(field.NewPath("spec", "devices", "constraints").Index(2), `exactly one of "matchAttribute" or "distinctAttribute" is required, but multiple fields are set`)},
|
||||
claim: func() *resource.ResourceClaim {
|
||||
claim := testClaim(goodName, goodNS, validClaimSpec)
|
||||
@@ -375,8 +377,9 @@ func TestValidateClaim(t *testing.T) {
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("requests").Index(1), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("requests").Index(1), badName, "must be the name of a request in the claim or the name of a request and a subrequest separated by '/'"),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), "missing-domain", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')").MarkCoveredByDeclarative(),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), resource.FullyQualifiedName("missing-domain"), "must include a domain").MarkCoveredByDeclarative(),
|
||||
field.Required(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), "name required").MarkCoveredByDeclarative(),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), resource.FullyQualifiedName("missing-domain"), "a fully qualified name must be a domain and a name separated by a slash").MarkCoveredByDeclarative(),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), "", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')").MarkCoveredByDeclarative(),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(1).Child("matchAttribute"), resource.FullyQualifiedName(""), "a fully qualified name must be a domain and a name separated by a slash").MarkCoveredByDeclarative(),
|
||||
field.Required(field.NewPath("spec", "devices", "constraints").Index(2).Child("matchAttribute"), ""),
|
||||
field.Invalid(field.NewPath("spec", "devices", "config").Index(0).Child("requests").Index(1), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"),
|
||||
field.Invalid(field.NewPath("spec", "devices", "config").Index(0).Child("requests").Index(1), badName, "must be the name of a request in the claim or the name of a request and a subrequest separated by '/'"),
|
||||
|
||||
@@ -406,7 +406,7 @@ func testDeclarativeValidate(t *testing.T, apiVersion string) {
|
||||
tweakMatchAttribute("nodomain"),
|
||||
),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), "nodomain", "must include a domain").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), "nodomain", "a fully qualified name must be a domain and a name separated by a slash").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
},
|
||||
},
|
||||
"match attribute empty": {
|
||||
@@ -414,7 +414,7 @@ func testDeclarativeValidate(t *testing.T, apiVersion string) {
|
||||
tweakMatchAttribute(""),
|
||||
),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Required(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), "name required"),
|
||||
field.Invalid(field.NewPath("spec", "devices", "constraints").Index(0).Child("matchAttribute"), "", "").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
},
|
||||
},
|
||||
"match attribute with empty domain": {
|
||||
|
||||
@@ -568,6 +568,9 @@ const ResourceSliceMaxDeviceCountersPerSlice = 1024 // 64 * 16
|
||||
type QualifiedName string
|
||||
|
||||
// FullyQualifiedName is a QualifiedName where the domain is set.
|
||||
// Format validation cannot be added to this type because one of its usages,
|
||||
// DistinctAttribute, is validated conditionally. This conditional validation
|
||||
// cannot be expressed declaratively.
|
||||
type FullyQualifiedName string
|
||||
|
||||
// DeviceMaxDomainLength is the maximum length of the domain prefix in a fully-qualified name.
|
||||
|
||||
@@ -572,6 +572,9 @@ const ResourceSliceMaxDeviceCountersPerSlice = 1024 // 64 * 16
|
||||
type QualifiedName string
|
||||
|
||||
// FullyQualifiedName is a QualifiedName where the domain is set.
|
||||
// Format validation cannot be added to this type because one of its usages,
|
||||
// DistinctAttribute, is validated conditionally. This conditional validation
|
||||
// cannot be expressed declaratively.
|
||||
type FullyQualifiedName string
|
||||
|
||||
// DeviceMaxDomainLength is the maximum length of the domain prefix in a fully-qualified name.
|
||||
|
||||
@@ -568,6 +568,9 @@ const ResourceSliceMaxDeviceCountersPerSlice = 1024 // 64 * 16
|
||||
type QualifiedName string
|
||||
|
||||
// FullyQualifiedName is a QualifiedName where the domain is set.
|
||||
// Format validation cannot be added to this type because one of its usages,
|
||||
// DistinctAttribute, is validated conditionally. This conditional validation
|
||||
// cannot be expressed declaratively.
|
||||
type FullyQualifiedName string
|
||||
|
||||
// DeviceMaxDomainLength is the maximum length of the domain prefix in a fully-qualified name.
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2025 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 content
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2025 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 content
|
||||
|
||||
import (
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/operation"
|
||||
"k8s.io/apimachinery/pkg/api/validate/content"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
@@ -32,7 +31,7 @@ const (
|
||||
defaultResourceRequestsPrefix = "requests."
|
||||
// Default namespace prefix.
|
||||
resourceDefaultNamespacePrefix = "kubernetes.io/"
|
||||
cDeviceMaxLength = 32
|
||||
resourceDeviceMaxLength = 32
|
||||
)
|
||||
|
||||
// ShortName verifies that the specified value is a valid "short name"
|
||||
@@ -234,15 +233,13 @@ func resourcesQualifiedName[T ~string](ctx context.Context, op operation.Operati
|
||||
}
|
||||
var allErrs field.ErrorList
|
||||
s := string(*value)
|
||||
if len(s) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "name required"))
|
||||
return allErrs
|
||||
}
|
||||
|
||||
parts := strings.Split(s, "/")
|
||||
// TODO: This validation and the corresponding handwritten validation validateQualifiedName in
|
||||
// pkg/apis/resource/validation/validation.go are not validating whether there are more than 1
|
||||
// slash. This should be fixed in both places.
|
||||
switch len(parts) {
|
||||
case 1:
|
||||
allErrs = append(allErrs, validateCIdentifier(parts[0], fldPath)...)
|
||||
allErrs = append(allErrs, validateCIdentifier(parts[0], resourceDeviceMaxLength, fldPath)...)
|
||||
case 2:
|
||||
if len(parts[0]) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, "", "prefix must not be empty"))
|
||||
@@ -255,11 +252,8 @@ func resourcesQualifiedName[T ~string](ctx context.Context, op operation.Operati
|
||||
if len(parts[1]) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, "", "name must not be empty"))
|
||||
} else {
|
||||
allErrs = append(allErrs, validateCIdentifier(parts[1], fldPath)...)
|
||||
allErrs = append(allErrs, validateCIdentifier(parts[1], resourceDeviceMaxLength, fldPath)...)
|
||||
}
|
||||
// TODO: This validation and the corresponding handwritten validation validateQualifiedName in
|
||||
// pkg/apis/resource/validation/validation.go are not validating whether there are more than 1
|
||||
// slash. This should be fixed in both places.
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
@@ -276,25 +270,20 @@ func ResourceFullyQualifiedName[T ~string](ctx context.Context, op operation.Ope
|
||||
return nil
|
||||
}
|
||||
var allErrs field.ErrorList
|
||||
if len(*value) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "name can't be empty"))
|
||||
return allErrs
|
||||
}
|
||||
|
||||
s := string(*value)
|
||||
allErrs = append(allErrs, resourcesQualifiedName(ctx, op, fldPath, value, nil)...)
|
||||
allErrs = append(allErrs, resourcesQualifiedName(ctx, op, fldPath, &s, nil)...)
|
||||
if !strings.Contains(s, "/") {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, s, "a fully qualified name must be a prefix and a name separated by a slash"))
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, s, "a fully qualified name must be a domain and a name separated by a slash"))
|
||||
}
|
||||
return allErrs.WithOrigin("format=k8s-resource-fully-qualified-name")
|
||||
}
|
||||
|
||||
func validateCIdentifier(id string, fldPath *field.Path) field.ErrorList {
|
||||
func validateCIdentifier(id string, length int, fldPath *field.Path) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
if len(id) > cDeviceMaxLength {
|
||||
allErrs = append(allErrs, field.TooLong(fldPath, id, cDeviceMaxLength))
|
||||
if len(id) > length {
|
||||
allErrs = append(allErrs, field.TooLong(fldPath, id, length))
|
||||
}
|
||||
for _, msg := range validation.IsCIdentifier(id) {
|
||||
for _, msg := range content.IsCIdentifier(id) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, id, msg))
|
||||
}
|
||||
return allErrs
|
||||
|
||||
@@ -680,25 +680,26 @@ func TestResourceFullyQualifiedName(t *testing.T) {
|
||||
name: "invalid name with dots",
|
||||
input: "prefix.com/name.with.dots",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, "name.with.dots", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
field.Invalid(fldPath, "name.with.dots", "a valid C identifier must start with alphabetic character").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid name with dashes",
|
||||
input: "prefix.com/name-with-dashes",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, "name-with-dashes", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_' (e.g. 'my_name', or 'MY_NAME', or 'MyName', regex used for validation is '[A-Za-z_][A-Za-z0-9_]*')").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
field.Invalid(fldPath, "name-with-dashes", "a valid C identifier must start with alphabetic character").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: no prefix",
|
||||
input: "name",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, "name", "a fully qualified name must be a prefix and a name separated by a slash").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
field.Invalid(fldPath, "name", "a fully qualified name must be a domain and a name separated by a slash").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: empty",
|
||||
input: "",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Required(fldPath, "name can't be empty"),
|
||||
field.Invalid(fldPath, "", "a fully qualified name must be a domain and a name separated by a slash").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
field.Invalid(fldPath, "", "a valid C identifier must start with alphabetic character").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: prefix too long",
|
||||
@@ -717,7 +718,7 @@ func TestResourceFullyQualifiedName(t *testing.T) {
|
||||
name: "invalid: prefix is not a valid DNS subdomain",
|
||||
input: "Prefix.com/name",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, "Prefix.com", "prefix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
field.Invalid(fldPath, "Prefix.com", "prefix: a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: name is not a valid RFC 1123 label",
|
||||
|
||||
@@ -237,19 +237,10 @@ func IsWildcardDNS1123Subdomain(value string) []string {
|
||||
return errs
|
||||
}
|
||||
|
||||
const cIdentifierFmt string = "[A-Za-z_][A-Za-z0-9_]*"
|
||||
const identifierErrMsg string = "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_'"
|
||||
|
||||
var cIdentifierRegexp = regexp.MustCompile("^" + cIdentifierFmt + "$")
|
||||
|
||||
// IsCIdentifier tests for a string that conforms the definition of an identifier
|
||||
// in C. This checks the format, but not the length.
|
||||
func IsCIdentifier(value string) []string {
|
||||
if !cIdentifierRegexp.MatchString(value) {
|
||||
return []string{RegexError(identifierErrMsg, cIdentifierFmt, "my_name", "MY_NAME", "MyName")}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Deprecated: Use k8s.io/apimachinery/pkg/api/validate/content.IsCIdentifier instead.
|
||||
var IsCIdentifier = content.IsCIdentifier
|
||||
|
||||
// IsValidPortNum tests that the argument is a valid, non-zero port number.
|
||||
func IsValidPortNum(port int) []string {
|
||||
|
||||
@@ -116,31 +116,6 @@ func TestIsDNS1035Label(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsCIdentifier(t *testing.T) {
|
||||
goodValues := []string{
|
||||
"a", "ab", "abc", "a1", "_a", "a_", "a_b", "a_1", "a__1__2__b", "__abc_123",
|
||||
"A", "AB", "AbC", "A1", "_A", "A_", "A_B", "A_1", "A__1__2__B", "__123_ABC",
|
||||
}
|
||||
for _, val := range goodValues {
|
||||
if msgs := IsCIdentifier(val); len(msgs) != 0 {
|
||||
t.Errorf("expected true for '%s': %v", val, msgs)
|
||||
}
|
||||
}
|
||||
|
||||
badValues := []string{
|
||||
"", "1", "123", "1a",
|
||||
"-", "a-", "-a", "1-", "-1", "1_", "1_2",
|
||||
".", "a.", ".a", "a.b", "1.", ".1", "1.2",
|
||||
" ", "a ", " a", "a b", "1 ", " 1", "1 2",
|
||||
"#a#",
|
||||
}
|
||||
for _, val := range badValues {
|
||||
if msgs := IsCIdentifier(val); len(msgs) == 0 {
|
||||
t.Errorf("expected false for '%s'", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidPortNum(t *testing.T) {
|
||||
goodValues := []int{1, 2, 1000, 16384, 32768, 65535}
|
||||
for _, val := range goodValues {
|
||||
|
||||
@@ -38,9 +38,9 @@ func TestFullyQualifiedName(t *testing.T) {
|
||||
FullyQualifiedNameTypedefField: "my-prefix/",
|
||||
}
|
||||
st.Value(invalidStruct).ExpectMatches(field.ErrorMatcher{}.ByType().ByOrigin().ByField(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("fullyQualifiedNameField"), "my_name", "").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
field.Required(field.NewPath("fullyQualifiedNamePtrField"), ""),
|
||||
field.Invalid(field.NewPath("fullyQualifiedNameTypedefField"), "my-prefix/", "").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
field.Invalid(field.NewPath("fullyQualifiedNameField"), "my_name", "a fully qualified name must be a domain and a name separated by a slash").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
field.Invalid(field.NewPath("fullyQualifiedNamePtrField"), "", "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_'").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
field.Invalid(field.NewPath("fullyQualifiedNameTypedefField"), "my-prefix/", "name must not be empty").WithOrigin("format=k8s-resource-fully-qualified-name"),
|
||||
})
|
||||
// Test validation ratcheting
|
||||
st.Value(invalidStruct).OldValue(invalidStruct).ExpectValid()
|
||||
|
||||
@@ -143,7 +143,7 @@ func (ftv formatTagValidator) Docs() TagDoc {
|
||||
Docs: "Deprecated: This field holds a case-insensitive Kubernetes \"long name\", aka a \"DNS subdomain\" value.",
|
||||
}, {
|
||||
Description: "k8s-resource-fully-qualified-name",
|
||||
Docs: "This field holds a Kubernetes resource \"fully qualified name\" value.",
|
||||
Docs: "This field holds a Kubernetes resource \"fully qualified name\" value. A fully qualified name must not be empty and must be composed of a prefix and a name, separated by a slash (e.g., \"prefix/name\"). The prefix must be a DNS subdomain, and the name part must be a C identifier with no more than 32 characters.",
|
||||
}, {
|
||||
Description: "k8s-resource-pool-name",
|
||||
Docs: "This field holds value with one or more Kubernetes \"long name\" parts separated by `/` and no longer than 253 characters.",
|
||||
|
||||
Reference in New Issue
Block a user