mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 02:08:13 +00:00 
			
		
		
		
	Merge pull request #130730 from jpbetz/minimum-tag
Add +k8s:minimum validation tag
This commit is contained in:
		| @@ -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.", | ||||
| 		}}, | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Kubernetes Prow Robot
					Kubernetes Prow Robot