mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
Merge pull request #130730 from jpbetz/minimum-tag
Add +k8s:minimum validation tag
This commit is contained in:
commit
f3a23cfe90
@ -0,0 +1,32 @@
|
||||
/*
|
||||
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 constraints
|
||||
|
||||
// Signed is a constraint that permits any signed integer type.
|
||||
type Signed interface {
|
||||
~int | ~int8 | ~int16 | ~int32 | ~int64
|
||||
}
|
||||
|
||||
// Unsigned is a constraint that permits any unsigned integer type.
|
||||
type Unsigned interface {
|
||||
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
|
||||
}
|
||||
|
||||
// Integer is a constraint that permits any integer type.
|
||||
type Integer interface {
|
||||
Signed | Unsigned
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright 2014 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 (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/validate/constraints"
|
||||
)
|
||||
|
||||
// MinError returns a string explanation of a "must be greater than or equal"
|
||||
// validation failure.
|
||||
func MinError[T constraints.Integer](min T) string {
|
||||
return fmt.Sprintf("must be greater than or equal to %d", min)
|
||||
}
|
37
staging/src/k8s.io/apimachinery/pkg/api/validate/limits.go
Normal file
37
staging/src/k8s.io/apimachinery/pkg/api/validate/limits.go
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
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 validate
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/operation"
|
||||
"k8s.io/apimachinery/pkg/api/validate/constraints"
|
||||
"k8s.io/apimachinery/pkg/api/validate/content"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// Minimum verifies that the specified value is greater than or equal to min.
|
||||
func Minimum[T constraints.Integer](_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ *T, min T) field.ErrorList {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
if *value < min {
|
||||
return field.ErrorList{field.Invalid(fldPath, *value, content.MinError(min)).WithOrigin("minimum")}
|
||||
}
|
||||
return nil
|
||||
}
|
111
staging/src/k8s.io/apimachinery/pkg/api/validate/limits_test.go
Normal file
111
staging/src/k8s.io/apimachinery/pkg/api/validate/limits_test.go
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
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 validate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/operation"
|
||||
"k8s.io/apimachinery/pkg/api/validate/constraints"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestMinimum(t *testing.T) {
|
||||
testMinimumPositive[int](t)
|
||||
testMinimumNegative[int](t)
|
||||
testMinimumPositive[int8](t)
|
||||
testMinimumNegative[int8](t)
|
||||
testMinimumPositive[int16](t)
|
||||
testMinimumNegative[int16](t)
|
||||
testMinimumPositive[int32](t)
|
||||
testMinimumNegative[int32](t)
|
||||
testMinimumPositive[int64](t)
|
||||
testMinimumNegative[int64](t)
|
||||
|
||||
testMinimumPositive[uint](t)
|
||||
testMinimumPositive[uint8](t)
|
||||
testMinimumPositive[uint16](t)
|
||||
testMinimumPositive[uint32](t)
|
||||
testMinimumPositive[uint64](t)
|
||||
}
|
||||
|
||||
type minimumTestCase[T constraints.Integer] struct {
|
||||
value T
|
||||
min T
|
||||
err string // regex
|
||||
}
|
||||
|
||||
func testMinimumPositive[T constraints.Integer](t *testing.T) {
|
||||
t.Helper()
|
||||
cases := []minimumTestCase[T]{{
|
||||
value: 0,
|
||||
min: 0,
|
||||
}, {
|
||||
value: 0,
|
||||
min: 1,
|
||||
err: "fldpath: Invalid value.*must be greater than or equal to",
|
||||
}, {
|
||||
value: 1,
|
||||
min: 1,
|
||||
}, {
|
||||
value: 1,
|
||||
min: 2,
|
||||
err: "fldpath: Invalid value.*must be greater than or equal to",
|
||||
}}
|
||||
doTestMinimum[T](t, cases)
|
||||
}
|
||||
|
||||
func testMinimumNegative[T constraints.Signed](t *testing.T) {
|
||||
t.Helper()
|
||||
cases := []minimumTestCase[T]{{
|
||||
value: -1,
|
||||
min: -1,
|
||||
}, {
|
||||
value: -2,
|
||||
min: -1,
|
||||
err: "fldpath: Invalid value.*must be greater than or equal to",
|
||||
}}
|
||||
|
||||
doTestMinimum[T](t, cases)
|
||||
}
|
||||
|
||||
func doTestMinimum[T constraints.Integer](t *testing.T, cases []minimumTestCase[T]) {
|
||||
t.Helper()
|
||||
for i, tc := range cases {
|
||||
v := tc.value
|
||||
result := Minimum(context.Background(), operation.Operation{}, field.NewPath("fldpath"), &v, nil, tc.min)
|
||||
if len(result) > 0 && tc.err == "" {
|
||||
t.Errorf("case %d: unexpected failure: %v", i, fmtErrs(result))
|
||||
continue
|
||||
}
|
||||
if len(result) == 0 && tc.err != "" {
|
||||
t.Errorf("case %d: unexpected success: expected %q", i, tc.err)
|
||||
continue
|
||||
}
|
||||
if len(result) > 0 {
|
||||
if len(result) > 1 {
|
||||
t.Errorf("case %d: unexepected multi-error: %v", i, fmtErrs(result))
|
||||
continue
|
||||
}
|
||||
if re := regexp.MustCompile(tc.err); !re.MatchString(result[0].Error()) {
|
||||
t.Errorf("case %d: wrong error\nexpected: %q\n got: %v", i, tc.err, fmtErrs(result))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
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.
|
||||
package minimum
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type Struct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:minimum=1
|
||||
IntField int `json:"intField"`
|
||||
// +k8s:minimum=1
|
||||
IntPtrField *int `json:"intPtrField"`
|
||||
|
||||
// "int8" becomes "byte" somewhere in gengo. We don't need it so just skip it.
|
||||
|
||||
// +k8s:minimum=1
|
||||
Int16Field int16 `json:"int16Field"`
|
||||
// +k8s:minimum=1
|
||||
Int32Field int32 `json:"int32Field"`
|
||||
// +k8s:minimum=1
|
||||
Int64Field int64 `json:"int64Field"`
|
||||
|
||||
// +k8s:minimum=1
|
||||
UintField uint `json:"uintField"`
|
||||
// +k8s:minimum=1
|
||||
UintPtrField *uint `json:"uintPtrField"`
|
||||
|
||||
// +k8s:minimum=1
|
||||
Uint16Field uint16 `json:"uint16Field"`
|
||||
// +k8s:minimum=1
|
||||
Uint32Field uint32 `json:"uint32Field"`
|
||||
// +k8s:minimum=1
|
||||
Uint64Field uint64 `json:"uint64Field"`
|
||||
|
||||
// +k8s:minimum=1
|
||||
TypedefField IntType `json:"typedefField"`
|
||||
// +k8s:minimum=1
|
||||
TypedefPtrField *IntType `json:"typedefPtrField"`
|
||||
}
|
||||
|
||||
// +k8s:minimum=1
|
||||
type IntType int
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
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 minimum
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/validate/content"
|
||||
"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
|
||||
IntPtrField: ptr.To(0),
|
||||
UintPtrField: ptr.To(uint(0)),
|
||||
TypedefPtrField: ptr.To(IntType(0)),
|
||||
}).ExpectInvalid(
|
||||
field.Invalid(field.NewPath("intField"), 0, content.MinError(1)),
|
||||
field.Invalid(field.NewPath("intPtrField"), 0, content.MinError(1)),
|
||||
field.Invalid(field.NewPath("int16Field"), 0, content.MinError(1)),
|
||||
field.Invalid(field.NewPath("int32Field"), 0, content.MinError(1)),
|
||||
field.Invalid(field.NewPath("int64Field"), 0, content.MinError(1)),
|
||||
field.Invalid(field.NewPath("uintField"), uint(0), content.MinError(1)),
|
||||
field.Invalid(field.NewPath("uintPtrField"), uint(0), content.MinError(1)),
|
||||
field.Invalid(field.NewPath("uint16Field"), uint(0), content.MinError(1)),
|
||||
field.Invalid(field.NewPath("uint32Field"), uint(0), content.MinError(1)),
|
||||
field.Invalid(field.NewPath("uint64Field"), uint(0), content.MinError(1)),
|
||||
field.Invalid(field.NewPath("typedefField"), 0, content.MinError(1)),
|
||||
field.Invalid(field.NewPath("typedefPtrField"), 0, content.MinError(1)),
|
||||
)
|
||||
|
||||
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,146 @@
|
||||
//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 minimum
|
||||
|
||||
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 {
|
||||
scheme.AddValidationFunc((*Struct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}, subresources ...string) field.ErrorList {
|
||||
if len(subresources) == 0 {
|
||||
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, subresources: %v", obj, subresources))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func Validate_IntType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) {
|
||||
// type IntType
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
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) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *Struct) *int { return &oldObj.IntField }))...)
|
||||
|
||||
// field Struct.IntPtrField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("intPtrField"), obj.IntPtrField, safe.Field(oldObj, func(oldObj *Struct) *int { return oldObj.IntPtrField }))...)
|
||||
|
||||
// field Struct.Int16Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *int16) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("int16Field"), &obj.Int16Field, safe.Field(oldObj, func(oldObj *Struct) *int16 { return &oldObj.Int16Field }))...)
|
||||
|
||||
// field Struct.Int32Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("int32Field"), &obj.Int32Field, safe.Field(oldObj, func(oldObj *Struct) *int32 { return &oldObj.Int32Field }))...)
|
||||
|
||||
// field Struct.Int64Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *int64) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("int64Field"), &obj.Int64Field, safe.Field(oldObj, func(oldObj *Struct) *int64 { return &oldObj.Int64Field }))...)
|
||||
|
||||
// field Struct.UintField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *uint) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("uintField"), &obj.UintField, safe.Field(oldObj, func(oldObj *Struct) *uint { return &oldObj.UintField }))...)
|
||||
|
||||
// field Struct.UintPtrField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *uint) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("uintPtrField"), obj.UintPtrField, safe.Field(oldObj, func(oldObj *Struct) *uint { return oldObj.UintPtrField }))...)
|
||||
|
||||
// field Struct.Uint16Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *uint16) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("uint16Field"), &obj.Uint16Field, safe.Field(oldObj, func(oldObj *Struct) *uint16 { return &oldObj.Uint16Field }))...)
|
||||
|
||||
// field Struct.Uint32Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *uint32) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("uint32Field"), &obj.Uint32Field, safe.Field(oldObj, func(oldObj *Struct) *uint32 { return &oldObj.Uint32Field }))...)
|
||||
|
||||
// field Struct.Uint64Field
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *uint64) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
return
|
||||
}(fldPath.Child("uint64Field"), &obj.Uint64Field, safe.Field(oldObj, func(oldObj *Struct) *uint64 { return &oldObj.Uint64Field }))...)
|
||||
|
||||
// field Struct.TypedefField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
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 }))...)
|
||||
|
||||
// field Struct.TypedefPtrField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...)
|
||||
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 }))...)
|
||||
|
||||
return errs
|
||||
}
|
@ -49,3 +49,23 @@ func isNilableType(t *types.Type) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func realType(t *types.Type) *types.Type {
|
||||
for {
|
||||
if t.Kind == types.Alias {
|
||||
t = t.Underlying
|
||||
} else if t.Kind == types.Pointer {
|
||||
t = t.Elem
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func rootTypeString(src, dst *types.Type) string {
|
||||
if src == dst {
|
||||
return src.String()
|
||||
}
|
||||
return src.String() + " -> " + dst.String()
|
||||
}
|
||||
|
@ -107,19 +107,6 @@ func (lttv listTypeTagValidator) GetValidations(context Context, _ []string, pay
|
||||
return Validations{}, nil
|
||||
}
|
||||
|
||||
func realType(t *types.Type) *types.Type {
|
||||
for {
|
||||
if t.Kind == types.Alias {
|
||||
t = t.Underlying
|
||||
} else if t.Kind == types.Pointer {
|
||||
t = t.Elem
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (lttv listTypeTagValidator) Docs() TagDoc {
|
||||
doc := TagDoc{
|
||||
Tag: lttv.TagName(),
|
||||
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
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 validators
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/gengo/v2/types"
|
||||
)
|
||||
|
||||
const (
|
||||
minimumTagName = "k8s:minimum"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterTagValidator(minimumTagValidator{})
|
||||
}
|
||||
|
||||
type minimumTagValidator struct{}
|
||||
|
||||
func (minimumTagValidator) Init(_ Config) {}
|
||||
|
||||
func (minimumTagValidator) TagName() string {
|
||||
return minimumTagName
|
||||
}
|
||||
|
||||
var minimumTagValidScopes = sets.New(
|
||||
ScopeAny,
|
||||
)
|
||||
|
||||
func (minimumTagValidator) ValidScopes() sets.Set[Scope] {
|
||||
return minimumTagValidScopes
|
||||
}
|
||||
|
||||
var (
|
||||
minimumValidator = types.Name{Package: libValidationPkg, Name: "Minimum"}
|
||||
)
|
||||
|
||||
func (minimumTagValidator) GetValidations(context Context, _ []string, payload string) (Validations, error) {
|
||||
var result Validations
|
||||
|
||||
if t := realType(context.Type); !types.IsInteger(t) {
|
||||
return result, fmt.Errorf("can only be used on integer types (%s)", rootTypeString(context.Type, t))
|
||||
}
|
||||
|
||||
intVal, err := strconv.Atoi(payload)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("failed to parse tag payload as int: %w", err)
|
||||
}
|
||||
result.AddFunction(Function(minimumTagName, DefaultFlags, minimumValidator, intVal))
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (mtv minimumTagValidator) Docs() TagDoc {
|
||||
return TagDoc{
|
||||
Tag: mtv.TagName(),
|
||||
Scopes: mtv.ValidScopes().UnsortedList(),
|
||||
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.",
|
||||
}},
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user