validation-gen/tests: add maxBytes tag tests

Signed-off-by: Bryce Palmer <bpalmer@redhat.com>
This commit is contained in:
Bryce Palmer
2026-02-27 14:57:29 -05:00
parent c9652760b5
commit 3deb264c91
4 changed files with 418 additions and 2 deletions

View File

@@ -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

View File

@@ -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()
}

View File

@@ -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
}

View File

@@ -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{{