mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-20 07:56:02 +00:00
Add DV tag '+k8s:maximum'
This commit is contained in:
@@ -29,6 +29,12 @@ func MinError[T constraints.Integer](min T) string {
|
||||
return fmt.Sprintf("must be greater than or equal to %d", min)
|
||||
}
|
||||
|
||||
// MaxError returns a string explanation of a "must be less than or equal"
|
||||
// validation failure.
|
||||
func MaxError[T constraints.Integer](max T) string {
|
||||
return fmt.Sprintf("must be less than or equal to %d", max)
|
||||
}
|
||||
|
||||
// MaxLenError returns a string explanation of a "string too long" validation
|
||||
// failure.
|
||||
func MaxLenError(length int) string {
|
||||
|
||||
@@ -89,6 +89,17 @@ func Minimum[T constraints.Integer](_ context.Context, _ operation.Operation, fl
|
||||
return nil
|
||||
}
|
||||
|
||||
// Maximum verifies that the specified value is less than or equal to max.
|
||||
func Maximum[T constraints.Integer](_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ *T, max T) field.ErrorList {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
if *value > max {
|
||||
return field.ErrorList{field.Invalid(fldPath, *value, content.MaxError(max)).WithOrigin("maximum")}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MinLength verifies that the specified value is at least min characters, if non-nil.
|
||||
func MinLength[T ~string](_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ *T, min int) field.ErrorList {
|
||||
if value == nil {
|
||||
|
||||
@@ -18,6 +18,7 @@ package validate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
@@ -31,7 +32,7 @@ func TestMaxLength(t *testing.T) {
|
||||
name string
|
||||
value string
|
||||
max int
|
||||
wantErrs field.ErrorList // regex
|
||||
wantErrs field.ErrorList
|
||||
}{{
|
||||
name: "empty string",
|
||||
value: "",
|
||||
@@ -255,12 +256,90 @@ func doTestMinimum[T constraints.Integer](t *testing.T, cases []minimumTestCase[
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaximum(t *testing.T) {
|
||||
testMaximumPositive[int](t)
|
||||
testMaximumNegative[int](t)
|
||||
testMaximumPositive[int8](t)
|
||||
testMaximumNegative[int8](t)
|
||||
testMaximumPositive[int16](t)
|
||||
testMaximumNegative[int16](t)
|
||||
testMaximumPositive[int32](t)
|
||||
testMaximumNegative[int32](t)
|
||||
testMaximumPositive[int64](t)
|
||||
testMaximumNegative[int64](t)
|
||||
|
||||
testMaximumPositive[uint](t)
|
||||
testMaximumPositive[uint8](t)
|
||||
testMaximumPositive[uint16](t)
|
||||
testMaximumPositive[uint32](t)
|
||||
testMaximumPositive[uint64](t)
|
||||
}
|
||||
|
||||
type maximumTestCase[T constraints.Integer] struct {
|
||||
max T
|
||||
value T
|
||||
wantErrs field.ErrorList
|
||||
}
|
||||
|
||||
func testMaximumPositive[T constraints.Integer](t *testing.T) {
|
||||
t.Helper()
|
||||
cases := []maximumTestCase[T]{{
|
||||
max: 0,
|
||||
value: 0,
|
||||
}, {
|
||||
max: 0,
|
||||
value: 1,
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(field.NewPath("fldpath"), nil, "must be less than or equal to").WithOrigin("maximum"),
|
||||
},
|
||||
}, {
|
||||
max: 1,
|
||||
value: 1,
|
||||
}, {
|
||||
max: 1,
|
||||
value: 2,
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(field.NewPath("fldpath"), nil, "must be less than or equal to").WithOrigin("maximum"),
|
||||
},
|
||||
}}
|
||||
doTestMaximum[T](t, cases)
|
||||
}
|
||||
|
||||
func testMaximumNegative[T constraints.Signed](t *testing.T) {
|
||||
t.Helper()
|
||||
cases := []maximumTestCase[T]{{
|
||||
max: -1,
|
||||
value: -1,
|
||||
}, {
|
||||
max: -2,
|
||||
value: -1,
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(field.NewPath("fldpath"), nil, "must be less than or equal to").WithOrigin("maximum"),
|
||||
},
|
||||
}}
|
||||
|
||||
doTestMaximum[T](t, cases)
|
||||
}
|
||||
|
||||
func doTestMaximum[T constraints.Integer](t *testing.T, cases []maximumTestCase[T]) {
|
||||
t.Helper()
|
||||
matcher := field.ErrorMatcher{}.ByOrigin().ByDetailSubstring().ByField().ByType()
|
||||
for _, tc := range cases {
|
||||
name := fmt.Sprintf("%T (%v <= %v)", tc.value, tc.value, tc.max)
|
||||
t.Run(name, func(t *testing.T) {
|
||||
v := tc.value
|
||||
gotErrs := Maximum(context.Background(), operation.Operation{}, field.NewPath("fldpath"), &v, nil, tc.max)
|
||||
matcher.Test(t, tc.wantErrs, gotErrs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxBytes(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
value string
|
||||
max int
|
||||
wantErrs field.ErrorList // regex
|
||||
wantErrs field.ErrorList
|
||||
}{{
|
||||
name: "empty string",
|
||||
value: "",
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright 2024 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 maximum
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type Struct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:maximum=1
|
||||
IntField int `json:"intField"`
|
||||
// +k8s:maximum=1
|
||||
IntPtrField *int `json:"intPtrField"`
|
||||
|
||||
// "int8" becomes "byte" somewhere in gengo. We don't need it so just skip it.
|
||||
|
||||
// +k8s:maximum=1
|
||||
Int16Field int16 `json:"int16Field"`
|
||||
// +k8s:maximum=1
|
||||
Int32Field int32 `json:"int32Field"`
|
||||
// +k8s:maximum=1
|
||||
Int64Field int64 `json:"int64Field"`
|
||||
|
||||
// +k8s:maximum=1
|
||||
UintField uint `json:"uintField"`
|
||||
// +k8s:maximum=1
|
||||
UintPtrField *uint `json:"uintPtrField"`
|
||||
|
||||
// +k8s:maximum=1
|
||||
Uint16Field uint16 `json:"uint16Field"`
|
||||
// +k8s:maximum=1
|
||||
Uint32Field uint32 `json:"uint32Field"`
|
||||
// +k8s:maximum=1
|
||||
Uint64Field uint64 `json:"uint64Field"`
|
||||
|
||||
TypedefField IntType `json:"typedefField"`
|
||||
TypedefPtrField *IntType `json:"typedefPtrField"`
|
||||
}
|
||||
|
||||
// +k8s:maximum=1
|
||||
type IntType int
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright 2024 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 maximum
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
// invalid values (greater than maximum=1)
|
||||
IntField: 2,
|
||||
IntPtrField: ptr.To(2),
|
||||
Int16Field: 2,
|
||||
Int32Field: 2,
|
||||
Int64Field: 2,
|
||||
UintField: 2,
|
||||
Uint16Field: 2,
|
||||
Uint32Field: 2,
|
||||
Uint64Field: 2,
|
||||
UintPtrField: ptr.To(uint(2)),
|
||||
TypedefField: IntType(2),
|
||||
TypedefPtrField: ptr.To(IntType(2)),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByDetailSubstring(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("intField"), nil, ""),
|
||||
field.Invalid(field.NewPath("intPtrField"), nil, ""),
|
||||
field.Invalid(field.NewPath("int16Field"), nil, ""),
|
||||
field.Invalid(field.NewPath("int32Field"), nil, ""),
|
||||
field.Invalid(field.NewPath("int64Field"), nil, ""),
|
||||
field.Invalid(field.NewPath("uintField"), nil, ""),
|
||||
field.Invalid(field.NewPath("uintPtrField"), nil, ""),
|
||||
field.Invalid(field.NewPath("uint16Field"), nil, ""),
|
||||
field.Invalid(field.NewPath("uint32Field"), nil, ""),
|
||||
field.Invalid(field.NewPath("uint64Field"), nil, ""),
|
||||
field.Invalid(field.NewPath("typedefField"), nil, ""),
|
||||
field.Invalid(field.NewPath("typedefPtrField"), nil, ""),
|
||||
})
|
||||
// Test validation ratcheting
|
||||
st.Value(&Struct{
|
||||
IntField: 2,
|
||||
IntPtrField: ptr.To(2),
|
||||
Int16Field: 2,
|
||||
Int32Field: 2,
|
||||
Int64Field: 2,
|
||||
UintField: 2,
|
||||
Uint16Field: 2,
|
||||
Uint32Field: 2,
|
||||
Uint64Field: 2,
|
||||
UintPtrField: ptr.To(uint(2)),
|
||||
TypedefField: IntType(2),
|
||||
TypedefPtrField: ptr.To(IntType(2)),
|
||||
}).OldValue(&Struct{
|
||||
IntField: 2,
|
||||
IntPtrField: ptr.To(2),
|
||||
Int16Field: 2,
|
||||
Int32Field: 2,
|
||||
Int64Field: 2,
|
||||
UintField: 2,
|
||||
Uint16Field: 2,
|
||||
Uint32Field: 2,
|
||||
Uint64Field: 2,
|
||||
UintPtrField: ptr.To(uint(2)),
|
||||
TypedefField: IntType(2),
|
||||
TypedefPtrField: ptr.To(IntType(2)),
|
||||
}).ExpectValid()
|
||||
|
||||
st.Value(&Struct{
|
||||
IntField: 1,
|
||||
IntPtrField: ptr.To(1),
|
||||
Int16Field: 1,
|
||||
Int32Field: 1,
|
||||
Int64Field: 1,
|
||||
UintField: 1,
|
||||
Uint16Field: 1,
|
||||
Uint32Field: 1,
|
||||
Uint64Field: 1,
|
||||
UintPtrField: ptr.To(uint(1)),
|
||||
TypedefField: IntType(1),
|
||||
TypedefPtrField: ptr.To(IntType(1)),
|
||||
}).ExpectValid()
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
//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 maximum
|
||||
|
||||
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_IntType validates an instance of IntType according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_IntType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Maximum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
|
||||
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.IntField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *int, 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.Maximum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *Struct) *int { return &oldObj.IntField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.IntPtrField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *int, 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.Maximum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("intPtrField"), obj.IntPtrField, safe.Field(oldObj, func(oldObj *Struct) *int { return oldObj.IntPtrField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Int16Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *int16, 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.Maximum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("int16Field"), &obj.Int16Field, safe.Field(oldObj, func(oldObj *Struct) *int16 { return &oldObj.Int16Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Int32Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *int32, 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.Maximum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("int32Field"), &obj.Int32Field, safe.Field(oldObj, func(oldObj *Struct) *int32 { return &oldObj.Int32Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Int64Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *int64, 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.Maximum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("int64Field"), &obj.Int64Field, safe.Field(oldObj, func(oldObj *Struct) *int64 { return &oldObj.Int64Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.UintField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *uint, 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.Maximum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("uintField"), &obj.UintField, safe.Field(oldObj, func(oldObj *Struct) *uint { return &oldObj.UintField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.UintPtrField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *uint, 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.Maximum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("uintPtrField"), obj.UintPtrField, safe.Field(oldObj, func(oldObj *Struct) *uint { return oldObj.UintPtrField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Uint16Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *uint16, 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.Maximum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("uint16Field"), &obj.Uint16Field, safe.Field(oldObj, func(oldObj *Struct) *uint16 { return &oldObj.Uint16Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Uint32Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *uint32, 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.Maximum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("uint32Field"), &obj.Uint32Field, safe.Field(oldObj, func(oldObj *Struct) *uint32 { return &oldObj.Uint32Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.Uint64Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *uint64, 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.Maximum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("uint64Field"), &obj.Uint64Field, safe.Field(oldObj, func(oldObj *Struct) *uint64 { return &oldObj.Uint64Field }), oldObj != nil)...)
|
||||
|
||||
// field Struct.TypedefField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *IntType, 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_IntType(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("typedefField"), &obj.TypedefField, safe.Field(oldObj, func(oldObj *Struct) *IntType { return &oldObj.TypedefField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.TypedefPtrField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *IntType, 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_IntType(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("typedefPtrField"), obj.TypedefPtrField, safe.Field(oldObj, func(oldObj *Struct) *IntType { return oldObj.TypedefPtrField }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
const (
|
||||
maxItemsTagName = "k8s:maxItems"
|
||||
minimumTagName = "k8s:minimum"
|
||||
maximumTagName = "k8s:maximum"
|
||||
minLengthTagName = "k8s:minLength"
|
||||
maxLengthTagName = "k8s:maxLength"
|
||||
maxBytesTagName = "k8s:maxBytes"
|
||||
@@ -36,6 +37,7 @@ const (
|
||||
func init() {
|
||||
RegisterTagValidator(maxItemsTagValidator{})
|
||||
RegisterTagValidator(minimumTagValidator{})
|
||||
RegisterTagValidator(maximumTagValidator{})
|
||||
RegisterTagValidator(minLengthTagValidator{})
|
||||
RegisterTagValidator(maxLengthTagValidator{})
|
||||
RegisterTagValidator(maxBytesTagValidator{})
|
||||
@@ -244,7 +246,55 @@ func (mtv minimumTagValidator) Docs() TagDoc {
|
||||
Description: "Indicates that a numeric field has a minimum value.",
|
||||
Payloads: []TagPayloadDoc{{
|
||||
Description: "<integer>",
|
||||
Docs: "This field must be greater than or equal to x.",
|
||||
Docs: "This field must be greater than or equal to X.",
|
||||
}},
|
||||
PayloadsType: codetags.ValueTypeInt,
|
||||
PayloadsRequired: true,
|
||||
}
|
||||
}
|
||||
|
||||
type maximumTagValidator struct{}
|
||||
|
||||
func (maximumTagValidator) Init(_ Config) {}
|
||||
|
||||
func (maximumTagValidator) TagName() string {
|
||||
return maximumTagName
|
||||
}
|
||||
|
||||
var maximumTagValidScopes = sets.New(ScopeType, ScopeField, ScopeListVal, ScopeMapKey, ScopeMapVal)
|
||||
|
||||
func (maximumTagValidator) ValidScopes() sets.Set[Scope] {
|
||||
return maximumTagValidScopes
|
||||
}
|
||||
|
||||
var maximumValidator = types.Name{Package: libValidationPkg, Name: "Maximum"}
|
||||
|
||||
func (maximumTagValidator) GetValidations(context Context, tag codetags.Tag) (Validations, error) {
|
||||
var result Validations
|
||||
|
||||
// This tag can apply to value and pointer fields, as well as typedefs
|
||||
// (which should never be pointers). We need to check the concrete type.
|
||||
if t := util.NonPointer(util.NativeType(context.Type)); !types.IsInteger(t) {
|
||||
return result, fmt.Errorf("can only be used on integer types (%s)", rootTypeString(context.Type, t))
|
||||
}
|
||||
|
||||
intVal, err := util.ParseInt(tag.Value)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("failed to parse tag payload as int: %w", err)
|
||||
}
|
||||
result.AddFunction(Function(maximumTagName, DefaultFlags, maximumValidator, intVal))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (mtv maximumTagValidator) Docs() TagDoc {
|
||||
return TagDoc{
|
||||
Tag: mtv.TagName(),
|
||||
StabilityLevel: TagStabilityLevelBeta,
|
||||
Scopes: mtv.ValidScopes().UnsortedList(),
|
||||
Description: "Indicates that a numeric field has a maximum value.",
|
||||
Payloads: []TagPayloadDoc{{
|
||||
Description: "<integer>",
|
||||
Docs: "This field must be less than or equal to X.",
|
||||
}},
|
||||
PayloadsType: codetags.ValueTypeInt,
|
||||
PayloadsRequired: true,
|
||||
|
||||
Reference in New Issue
Block a user