diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/maxbytes/doc.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/maxbytes/doc.go new file mode 100644 index 00000000000..79873c95df8 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/maxbytes/doc.go @@ -0,0 +1,81 @@ +/* +Copyright 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. +*/ + +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme + +// This is a test package. +// +k8s:validation-gen-nolint +package maxbytes + +import "k8s.io/code-generator/cmd/validation-gen/testscheme" + +var localSchemeBuilder = testscheme.New() + +type Struct struct { + TypeMeta int + + // +k8s:maxBytes=0 + Max0Field string `json:"max0Field"` + + // +k8s:maxBytes=0 + Max0PtrField *string `json:"max0PtrField"` + + // +k8s:maxBytes=10 + Max10Field string `json:"max10Field"` + + // +k8s:maxBytes=10 + Max10PtrField *string `json:"max10PtrField"` + + // +k8s:maxBytes=0 + Max0UnvalidatedTypedefField UnvalidatedStringType `json:"max0UnvalidatedTypedefField"` + + // +k8s:maxBytes=0 + Max0UnvalidatedTypedefPtrField *UnvalidatedStringType `json:"max0UnvalidatedTypedefPtrField"` + + // +k8s:maxBytes=10 + Max10UnvalidatedTypedefField UnvalidatedStringType `json:"max10UnvalidatedTypedefField"` + + // +k8s:maxBytes=10 + Max10UnvalidatedTypedefPtrField *UnvalidatedStringType `json:"max10UnvalidatedTypedefPtrField"` + + // Note: no validation here + Max0ValidatedTypedefField Max0Type `json:"max0ValidatedTypedefField"` + + // Note: no validation here + Max0ValidatedTypedefPtrField *Max0Type `json:"max0ValidatedTypedefPtrField"` + + // Note: no validation here + Max10ValidatedTypedefField Max10Type `json:"max10ValidatedTypedefField"` + + // Note: no validation here + Max10ValidatedTypedefPtrField *Max10Type `json:"max10ValidatedTypedefPtrField"` +} + +// Note: no validation here +type UnvalidatedStringType string + +// This tests that markers on type definitions +// are pulled through the validations of fields +// that use the type definition. +// +k8s:maxBytes=0 +type Max0Type string + +// This tests that markers on type definitions +// are pulled through the validations of fields +// that use the type definition. +// +k8s:maxBytes=10 +type Max10Type string diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/maxbytes/doc_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/maxbytes/doc_test.go new file mode 100644 index 00000000000..47bc3f416d9 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/maxbytes/doc_test.go @@ -0,0 +1,118 @@ +/* +Copyright 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 maxbytes + +import ( + "strings" + "testing" + + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/utils/ptr" +) + +func Test(t *testing.T) { + st := localSchemeBuilder.Test(t) + + st.Value(&Struct{ + // All zero values + }).ExpectValid() + + st.Value(&Struct{ + Max10Field: strings.Repeat("x", 1), + Max10PtrField: ptr.To(strings.Repeat("x", 1)), + Max10UnvalidatedTypedefField: UnvalidatedStringType(strings.Repeat("x", 1)), + Max10UnvalidatedTypedefPtrField: ptr.To(UnvalidatedStringType(strings.Repeat("x", 1))), + Max10ValidatedTypedefField: Max10Type(strings.Repeat("x", 1)), + Max10ValidatedTypedefPtrField: ptr.To(Max10Type(strings.Repeat("x", 1))), + }).ExpectValid() + + st.Value(&Struct{ + Max10Field: strings.Repeat("x", 9), + Max10PtrField: ptr.To(strings.Repeat("x", 9)), + Max10UnvalidatedTypedefField: UnvalidatedStringType(strings.Repeat("x", 9)), + Max10UnvalidatedTypedefPtrField: ptr.To(UnvalidatedStringType(strings.Repeat("x", 9))), + Max10ValidatedTypedefField: Max10Type(strings.Repeat("x", 9)), + Max10ValidatedTypedefPtrField: ptr.To(Max10Type(strings.Repeat("x", 9))), + }).ExpectValid() + + st.Value(&Struct{ + Max10Field: strings.Repeat("x", 10), + Max10PtrField: ptr.To(strings.Repeat("x", 10)), + Max10UnvalidatedTypedefField: UnvalidatedStringType(strings.Repeat("x", 10)), + Max10UnvalidatedTypedefPtrField: ptr.To(UnvalidatedStringType(strings.Repeat("x", 10))), + Max10ValidatedTypedefField: Max10Type(strings.Repeat("x", 10)), + Max10ValidatedTypedefPtrField: ptr.To(Max10Type(strings.Repeat("x", 10))), + }).ExpectValid() + + testVal := &Struct{ + Max0Field: strings.Repeat("x", 1), + Max0PtrField: ptr.To(strings.Repeat("x", 1)), + Max10Field: strings.Repeat("x", 11), + Max10PtrField: ptr.To(strings.Repeat("x", 11)), + Max0UnvalidatedTypedefField: UnvalidatedStringType(strings.Repeat("x", 1)), + Max0UnvalidatedTypedefPtrField: ptr.To(UnvalidatedStringType(strings.Repeat("x", 1))), + Max10UnvalidatedTypedefField: UnvalidatedStringType(strings.Repeat("x", 11)), + Max10UnvalidatedTypedefPtrField: ptr.To(UnvalidatedStringType(strings.Repeat("x", 11))), + Max0ValidatedTypedefField: Max0Type(strings.Repeat("x", 1)), + Max0ValidatedTypedefPtrField: ptr.To(Max0Type(strings.Repeat("x", 1))), + Max10ValidatedTypedefField: Max10Type(strings.Repeat("x", 11)), + Max10ValidatedTypedefPtrField: ptr.To(Max10Type(strings.Repeat("x", 11))), + } + st.Value(testVal).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{ + field.TooLong(field.NewPath("max0Field"), "", 0), + field.TooLong(field.NewPath("max0PtrField"), "", 0), + field.TooLong(field.NewPath("max10Field"), "", 10), + field.TooLong(field.NewPath("max10PtrField"), "", 10), + field.TooLong(field.NewPath("max0UnvalidatedTypedefField"), "", 0), + field.TooLong(field.NewPath("max0UnvalidatedTypedefPtrField"), "", 0), + field.TooLong(field.NewPath("max10UnvalidatedTypedefField"), "", 10), + field.TooLong(field.NewPath("max10UnvalidatedTypedefPtrField"), "", 10), + field.TooLong(field.NewPath("max0ValidatedTypedefField"), "", 0), + field.TooLong(field.NewPath("max0ValidatedTypedefPtrField"), "", 0), + field.TooLong(field.NewPath("max10ValidatedTypedefField"), "", 10), + field.TooLong(field.NewPath("max10ValidatedTypedefPtrField"), "", 10), + }) + + // Test validation ratcheting + st.Value(&Struct{ + Max0Field: strings.Repeat("x", 1), + Max0PtrField: ptr.To(strings.Repeat("x", 1)), + Max10Field: strings.Repeat("x", 11), + Max10PtrField: ptr.To(strings.Repeat("x", 11)), + Max0UnvalidatedTypedefField: UnvalidatedStringType(strings.Repeat("x", 1)), + Max0UnvalidatedTypedefPtrField: ptr.To(UnvalidatedStringType(strings.Repeat("x", 1))), + Max10UnvalidatedTypedefField: UnvalidatedStringType(strings.Repeat("x", 11)), + Max10UnvalidatedTypedefPtrField: ptr.To(UnvalidatedStringType(strings.Repeat("x", 11))), + Max0ValidatedTypedefField: Max0Type(strings.Repeat("x", 1)), + Max0ValidatedTypedefPtrField: ptr.To(Max0Type(strings.Repeat("x", 1))), + Max10ValidatedTypedefField: Max10Type(strings.Repeat("x", 11)), + Max10ValidatedTypedefPtrField: ptr.To(Max10Type(strings.Repeat("x", 11))), + }).OldValue(&Struct{ + Max0Field: strings.Repeat("x", 1), + Max0PtrField: ptr.To(strings.Repeat("x", 1)), + Max10Field: strings.Repeat("x", 11), + Max10PtrField: ptr.To(strings.Repeat("x", 11)), + Max0UnvalidatedTypedefField: UnvalidatedStringType(strings.Repeat("x", 1)), + Max0UnvalidatedTypedefPtrField: ptr.To(UnvalidatedStringType(strings.Repeat("x", 1))), + Max10UnvalidatedTypedefField: UnvalidatedStringType(strings.Repeat("x", 11)), + Max10UnvalidatedTypedefPtrField: ptr.To(UnvalidatedStringType(strings.Repeat("x", 11))), + Max0ValidatedTypedefField: Max0Type(strings.Repeat("x", 1)), + Max0ValidatedTypedefPtrField: ptr.To(Max0Type(strings.Repeat("x", 1))), + Max10ValidatedTypedefField: Max10Type(strings.Repeat("x", 11)), + Max10ValidatedTypedefPtrField: ptr.To(Max10Type(strings.Repeat("x", 11))), + }).ExpectValid() +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/maxbytes/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/maxbytes/zz_generated.validations.go new file mode 100644 index 00000000000..4f93d9945a8 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/maxbytes/zz_generated.validations.go @@ -0,0 +1,217 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright 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. +*/ + +// Code generated by validation-gen. DO NOT EDIT. + +package maxbytes + +import ( + context "context" + fmt "fmt" + + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" + field "k8s.io/apimachinery/pkg/util/validation/field" + testscheme "k8s.io/code-generator/cmd/validation-gen/testscheme" +) + +func init() { localSchemeBuilder.Register(RegisterValidations) } + +// RegisterValidations adds validation functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterValidations(scheme *testscheme.Scheme) error { + // type Struct + scheme.AddValidationFunc((*Struct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_Struct(ctx, op, nil /* fldPath */, obj.(*Struct), safe.Cast[*Struct](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + return nil +} + +// Validate_Max0Type validates an instance of Max0Type according +// to declarative validation rules in the API schema. +func Validate_Max0Type(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Max0Type) (errs field.ErrorList) { + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 0)...) + + return errs +} + +// Validate_Max10Type validates an instance of Max10Type according +// to declarative validation rules in the API schema. +func Validate_Max10Type(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Max10Type) (errs field.ErrorList) { + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 10)...) + + return errs +} + +// Validate_Struct validates an instance of Struct according +// to declarative validation rules in the API schema. +func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { + // field Struct.TypeMeta has no validation + + // field Struct.Max0Field + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 0)...) + return + }(fldPath.Child("max0Field"), &obj.Max0Field, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.Max0Field }), oldObj != nil)...) + + // field Struct.Max0PtrField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 0)...) + return + }(fldPath.Child("max0PtrField"), obj.Max0PtrField, safe.Field(oldObj, func(oldObj *Struct) *string { return oldObj.Max0PtrField }), oldObj != nil)...) + + // field Struct.Max10Field + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 10)...) + return + }(fldPath.Child("max10Field"), &obj.Max10Field, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.Max10Field }), oldObj != nil)...) + + // field Struct.Max10PtrField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *string, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 10)...) + return + }(fldPath.Child("max10PtrField"), obj.Max10PtrField, safe.Field(oldObj, func(oldObj *Struct) *string { return oldObj.Max10PtrField }), oldObj != nil)...) + + // field Struct.Max0UnvalidatedTypedefField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *UnvalidatedStringType, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 0)...) + return + }(fldPath.Child("max0UnvalidatedTypedefField"), &obj.Max0UnvalidatedTypedefField, safe.Field(oldObj, func(oldObj *Struct) *UnvalidatedStringType { return &oldObj.Max0UnvalidatedTypedefField }), oldObj != nil)...) + + // field Struct.Max0UnvalidatedTypedefPtrField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *UnvalidatedStringType, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 0)...) + return + }(fldPath.Child("max0UnvalidatedTypedefPtrField"), obj.Max0UnvalidatedTypedefPtrField, safe.Field(oldObj, func(oldObj *Struct) *UnvalidatedStringType { return oldObj.Max0UnvalidatedTypedefPtrField }), oldObj != nil)...) + + // field Struct.Max10UnvalidatedTypedefField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *UnvalidatedStringType, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 10)...) + return + }(fldPath.Child("max10UnvalidatedTypedefField"), &obj.Max10UnvalidatedTypedefField, safe.Field(oldObj, func(oldObj *Struct) *UnvalidatedStringType { return &oldObj.Max10UnvalidatedTypedefField }), oldObj != nil)...) + + // field Struct.Max10UnvalidatedTypedefPtrField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *UnvalidatedStringType, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call field-attached validations + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 10)...) + return + }(fldPath.Child("max10UnvalidatedTypedefPtrField"), obj.Max10UnvalidatedTypedefPtrField, safe.Field(oldObj, func(oldObj *Struct) *UnvalidatedStringType { return oldObj.Max10UnvalidatedTypedefPtrField }), oldObj != nil)...) + + // field Struct.Max0ValidatedTypedefField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *Max0Type, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_Max0Type(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("max0ValidatedTypedefField"), &obj.Max0ValidatedTypedefField, safe.Field(oldObj, func(oldObj *Struct) *Max0Type { return &oldObj.Max0ValidatedTypedefField }), oldObj != nil)...) + + // field Struct.Max0ValidatedTypedefPtrField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *Max0Type, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_Max0Type(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("max0ValidatedTypedefPtrField"), obj.Max0ValidatedTypedefPtrField, safe.Field(oldObj, func(oldObj *Struct) *Max0Type { return oldObj.Max0ValidatedTypedefPtrField }), oldObj != nil)...) + + // field Struct.Max10ValidatedTypedefField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *Max10Type, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_Max10Type(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("max10ValidatedTypedefField"), &obj.Max10ValidatedTypedefField, safe.Field(oldObj, func(oldObj *Struct) *Max10Type { return &oldObj.Max10ValidatedTypedefField }), oldObj != nil)...) + + // field Struct.Max10ValidatedTypedefPtrField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *Max10Type, oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil + } + // call the type's validation function + errs = append(errs, Validate_Max10Type(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("max10ValidatedTypedefPtrField"), obj.Max10ValidatedTypedefPtrField, safe.Field(oldObj, func(oldObj *Struct) *Max10Type { return oldObj.Max10ValidatedTypedefPtrField }), oldObj != nil)...) + + return errs +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go index 65d146310df..573c266307e 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go @@ -81,7 +81,7 @@ func (mltv maxLengthTagValidator) Docs() TagDoc { Tag: mltv.TagName(), StabilityLevel: TagStabilityLevelBeta, Scopes: sets.List(mltv.ValidScopes()), - Description: `Indicates that a string field has a limit on its length in characters. + Description: `Indicates that a string field has a limit on its length in characters. This could allow up to 4*N bytes if multi-byte characters are used. If you want to limit length of bytes specifically, use maxBytes.`, Payloads: []TagPayloadDoc{{ @@ -134,7 +134,7 @@ func (mltv maxBytesTagValidator) Docs() TagDoc { Tag: mltv.TagName(), StabilityLevel: TagStabilityLevelBeta, Scopes: sets.List(mltv.ValidScopes()), - Description: `Indicates that a string field has a limit on its length in bytes. + Description: `Indicates that a string field has a limit on its length in bytes. This could only allow as few as N/4 multi-byte characters. If you want to limit length of characters specifically, use maxLength.`, Payloads: []TagPayloadDoc{{