mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-18 04:54:54 +00:00
Add alpha and beta tests
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package atomicslice
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type AtomicSliceStruct struct {
|
||||
TypeMeta int
|
||||
|
||||
// Case: Standard eachVal
|
||||
// +k8s:listType=atomic
|
||||
// +k8s:eachVal=+k8s:minimum=10
|
||||
Standard []int `json:"standard"`
|
||||
|
||||
// Case: Alpha eachVal
|
||||
// +k8s:listType=atomic
|
||||
// +k8s:alpha=+k8s:eachVal=+k8s:minimum=10
|
||||
Alpha []int `json:"Alpha"`
|
||||
|
||||
// Case: Beta eachVal
|
||||
// +k8s:listType=atomic
|
||||
// +k8s:beta=+k8s:eachVal=+k8s:minimum=10
|
||||
Beta []int `json:"Beta"`
|
||||
|
||||
// Case: Standard eachVal, Alpha validation
|
||||
// +k8s:listType=atomic
|
||||
// +k8s:eachVal=+k8s:alpha=+k8s:minimum=10
|
||||
AlphaValidation []int `json:"AlphaValidation"`
|
||||
|
||||
// Case: Standard eachVal, Beta validation
|
||||
// +k8s:listType=atomic
|
||||
// +k8s:eachVal=+k8s:beta=+k8s:minimum=10
|
||||
BetaValidation []int `json:"BetaValidation"`
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
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 atomicslice
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestAtomicSlice(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&AtomicSliceStruct{
|
||||
Standard: []int{5},
|
||||
Alpha: []int{5},
|
||||
Beta: []int{5},
|
||||
AlphaValidation: []int{5},
|
||||
BetaValidation: []int{5},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
// Case: Standard -> Normal Error
|
||||
field.Invalid(field.NewPath("standard").Index(0), 5, "").WithOrigin("minimum"),
|
||||
|
||||
// Case: Alpha eachVal -> Alpha Error
|
||||
field.Invalid(field.NewPath("Alpha").Index(0), 5, "").WithOrigin("minimum").MarkAlpha(),
|
||||
|
||||
// Case: Beta eachVal -> Beta Error
|
||||
field.Invalid(field.NewPath("Beta").Index(0), 5, "").WithOrigin("minimum").MarkBeta(),
|
||||
|
||||
// Case: Standard eachVal, Alpha validation -> Alpha Error
|
||||
field.Invalid(field.NewPath("AlphaValidation").Index(0), 5, "").WithOrigin("minimum").MarkAlpha(),
|
||||
|
||||
// Case: Standard eachVal, Beta validation -> Beta Error
|
||||
field.Invalid(field.NewPath("BetaValidation").Index(0), 5, "").WithOrigin("minimum").MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
//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 atomicslice
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
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 AtomicSliceStruct
|
||||
scheme.AddValidationFunc((*AtomicSliceStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_AtomicSliceStruct(ctx, op, nil /* fldPath */, obj.(*AtomicSliceStruct), safe.Cast[*AtomicSliceStruct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_AtomicSliceStruct validates an instance of AtomicSliceStruct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_AtomicSliceStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *AtomicSliceStruct) (errs field.ErrorList) {
|
||||
// field AtomicSliceStruct.TypeMeta has no validation
|
||||
|
||||
// field AtomicSliceStruct.Standard
|
||||
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 && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10)
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("standard"), obj.Standard, safe.Field(oldObj, func(oldObj *AtomicSliceStruct) []int { return oldObj.Standard }), oldObj != nil)...)
|
||||
|
||||
// field AtomicSliceStruct.Alpha
|
||||
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 && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10)
|
||||
}).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("Alpha"), obj.Alpha, safe.Field(oldObj, func(oldObj *AtomicSliceStruct) []int { return oldObj.Alpha }), oldObj != nil)...)
|
||||
|
||||
// field AtomicSliceStruct.Beta
|
||||
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 && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10)
|
||||
}).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("Beta"), obj.Beta, safe.Field(oldObj, func(oldObj *AtomicSliceStruct) []int { return oldObj.Beta }), oldObj != nil)...)
|
||||
|
||||
// field AtomicSliceStruct.AlphaValidation
|
||||
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 && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkAlpha()
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("AlphaValidation"), obj.AlphaValidation, safe.Field(oldObj, func(oldObj *AtomicSliceStruct) []int { return oldObj.AlphaValidation }), oldObj != nil)...)
|
||||
|
||||
// field AtomicSliceStruct.BetaValidation
|
||||
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 && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkBeta()
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("BetaValidation"), obj.BetaValidation, safe.Field(oldObj, func(oldObj *AtomicSliceStruct) []int { return oldObj.BetaValidation }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package enums
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type Struct struct {
|
||||
TypeMeta int
|
||||
|
||||
EnumField Enum `json:"enumField"`
|
||||
|
||||
EnumFieldBeta BetaEnum `json:"enumFieldBeta"`
|
||||
}
|
||||
|
||||
// +k8s:alpha=+k8s:enum
|
||||
type Enum string
|
||||
|
||||
const (
|
||||
EnumA Enum = "A"
|
||||
)
|
||||
|
||||
// +k8s:beta=+k8s:enum
|
||||
type BetaEnum string
|
||||
|
||||
const (
|
||||
BetaEnumA BetaEnum = "A"
|
||||
)
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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 enums
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestAlpha(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
EnumField: "A",
|
||||
EnumFieldBeta: "A",
|
||||
}).ExpectValid()
|
||||
|
||||
// Test failures marked as alpha
|
||||
st.Value(&Struct{
|
||||
EnumField: "B",
|
||||
EnumFieldBeta: "A",
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.NotSupported(field.NewPath("enumField"), Enum("B"), []string{"A"}).MarkAlpha(),
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeta(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
EnumField: "A",
|
||||
EnumFieldBeta: "A",
|
||||
}).ExpectValid()
|
||||
|
||||
// Test failures marked as beta
|
||||
st.Value(&Struct{
|
||||
EnumField: "A",
|
||||
EnumFieldBeta: "B",
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.NotSupported(field.NewPath("enumFieldBeta"), BetaEnum("B"), []string{"A"}).MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
//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 enums
|
||||
|
||||
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"
|
||||
sets "k8s.io/apimachinery/pkg/util/sets"
|
||||
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
|
||||
}
|
||||
|
||||
var symbolsForBetaEnum = sets.New(BetaEnumA)
|
||||
|
||||
// Validate_BetaEnum validates an instance of BetaEnum according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_BetaEnum(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *BetaEnum) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForBetaEnum, nil).MarkBeta()...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
var symbolsForEnum = sets.New(EnumA)
|
||||
|
||||
// Validate_Enum validates an instance of Enum according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_Enum(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Enum) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.Enum(ctx, op, fldPath, obj, oldObj, symbolsForEnum, nil).MarkAlpha()...)
|
||||
|
||||
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.EnumField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *Enum, 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_Enum(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("enumField"), &obj.EnumField, safe.Field(oldObj, func(oldObj *Struct) *Enum { return &oldObj.EnumField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.EnumFieldBeta
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *BetaEnum, 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_BetaEnum(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("enumFieldBeta"), &obj.EnumFieldBeta, safe.Field(oldObj, func(oldObj *Struct) *BetaEnum { return &oldObj.EnumFieldBeta }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package listkeys
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type MapItem struct {
|
||||
Key string `json:"key"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
type MultiKeyItem struct {
|
||||
Key1 string `json:"key1"`
|
||||
Key2 int `json:"key2"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
type ListKeyStruct struct {
|
||||
TypeMeta int
|
||||
|
||||
// Case: Alpha listType, Standard Key
|
||||
// +k8s:alpha=+k8s:listType=map
|
||||
// +k8s:listMapKey=key
|
||||
AlphaListTypeStandardKey []MapItem `json:"alphaListTypeStandardKey"`
|
||||
|
||||
// Case: Standard listType, Alpha Key
|
||||
// +k8s:listType=map
|
||||
// +k8s:alpha=+k8s:listMapKey=key
|
||||
StandardListTypeAlphaKey []MapItem `json:"standardListTypeAlphaKey"`
|
||||
|
||||
// Case: Alpha listType, Alpha Key
|
||||
// +k8s:alpha=+k8s:listType=map
|
||||
// +k8s:alpha=+k8s:listMapKey=key
|
||||
AlphaListTypeAlphaKey []MapItem `json:"alphaListTypeAlphaKey"`
|
||||
|
||||
// Case: Standard listType, Alpha Key1, Standard Key2
|
||||
// +k8s:listType=map
|
||||
// +k8s:alpha=+k8s:listMapKey=key1
|
||||
// +k8s:listMapKey=key2
|
||||
StandardListTypeMixedKeys1 []MultiKeyItem `json:"standardListTypeMixedKeys1"`
|
||||
|
||||
// Case: Standard listType, Standard Key1, Alpha Key2
|
||||
// +k8s:listType=map
|
||||
// +k8s:listMapKey=key1
|
||||
// +k8s:alpha=+k8s:listMapKey=key2
|
||||
StandardListTypeMixedKeys2 []MultiKeyItem `json:"standardListTypeMixedKeys2"`
|
||||
|
||||
// Case: Alpha listType, Alpha Key1, Standard Key2
|
||||
// +k8s:alpha=+k8s:listType=map
|
||||
// +k8s:alpha=+k8s:listMapKey=key1
|
||||
// +k8s:listMapKey=key2
|
||||
AlphaListTypeMixedKeys1 []MultiKeyItem `json:"alphaListTypeMixedKeys1"`
|
||||
|
||||
// Case: Alpha listType, Standard Key1, Alpha Key2
|
||||
// +k8s:alpha=+k8s:listType=map
|
||||
// +k8s:listMapKey=key1
|
||||
// +k8s:alpha=+k8s:listMapKey=key2
|
||||
AlphaListTypeMixedKeys2 []MultiKeyItem `json:"alphaListTypeMixedKeys2"`
|
||||
|
||||
// Case: Standard listType, Beta Key1, Standard Key2
|
||||
// +k8s:listType=map
|
||||
// +k8s:beta=+k8s:listMapKey=key1
|
||||
// +k8s:listMapKey=key2
|
||||
StandardListTypeMixedKeysBeta1 []MultiKeyItem `json:"standardListTypeMixedKeysBeta1"`
|
||||
|
||||
// Case: Standard listType, Standard Key1, Beta Key2
|
||||
// +k8s:listType=map
|
||||
// +k8s:listMapKey=key1
|
||||
// +k8s:beta=+k8s:listMapKey=key2
|
||||
StandardListTypeMixedKeysBeta2 []MultiKeyItem `json:"standardListTypeMixedKeysBeta2"`
|
||||
|
||||
// Case: Beta listType, Beta Key1, Standard Key2
|
||||
// +k8s:beta=+k8s:listType=map
|
||||
// +k8s:beta=+k8s:listMapKey=key1
|
||||
// +k8s:listMapKey=key2
|
||||
BetaListTypeMixedKeys1 []MultiKeyItem `json:"betaListTypeMixedKeys1"`
|
||||
|
||||
// Case: Beta listType, Standard Key1, Beta Key2
|
||||
// +k8s:beta=+k8s:listType=map
|
||||
// +k8s:listMapKey=key1
|
||||
// +k8s:beta=+k8s:listMapKey=key2
|
||||
BetaListTypeMixedKeys2 []MultiKeyItem `json:"betaListTypeMixedKeys2"`
|
||||
|
||||
// Case: Beta listType, Standard Key
|
||||
// +k8s:beta=+k8s:listType=map
|
||||
// +k8s:listMapKey=key
|
||||
BetaListTypeStandardKey []MapItem `json:"betaListTypeStandardKey"`
|
||||
|
||||
// Case: Standard listType, Beta Key
|
||||
// +k8s:listType=map
|
||||
// +k8s:beta=+k8s:listMapKey=key
|
||||
StandardListTypeBetaKey []MapItem `json:"standardListTypeBetaKey"`
|
||||
|
||||
// Case: Beta listType, Beta Key
|
||||
// +k8s:beta=+k8s:listType=map
|
||||
// +k8s:beta=+k8s:listMapKey=key
|
||||
BetaListTypeBetaKey []MapItem `json:"betaListTypeBetaKey"`
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
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 listkeys
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestListKeys(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&ListKeyStruct{
|
||||
// Case 1: Alpha listType, Standard Key
|
||||
AlphaListTypeStandardKey: []MapItem{
|
||||
{Key: "a", Value: 1},
|
||||
{Key: "a", Value: 2},
|
||||
},
|
||||
// Case 2: Standard listType, Alpha Key
|
||||
StandardListTypeAlphaKey: []MapItem{
|
||||
{Key: "b", Value: 1},
|
||||
{Key: "b", Value: 2},
|
||||
},
|
||||
// Case 3: Alpha listType, Alpha Key
|
||||
AlphaListTypeAlphaKey: []MapItem{
|
||||
{Key: "c", Value: 1},
|
||||
{Key: "c", Value: 2},
|
||||
},
|
||||
// Case 4: Standard listType, Alpha Key1, Standard Key2
|
||||
StandardListTypeMixedKeys1: []MultiKeyItem{
|
||||
{Key1: "d", Key2: 1, Value: 1},
|
||||
{Key1: "d", Key2: 1, Value: 2},
|
||||
},
|
||||
// Case 5: Standard listType, Standard Key1, Alpha Key2
|
||||
StandardListTypeMixedKeys2: []MultiKeyItem{
|
||||
{Key1: "e", Key2: 1, Value: 1},
|
||||
{Key1: "e", Key2: 1, Value: 2},
|
||||
},
|
||||
// Case 6: Alpha listType, Alpha Key1, Standard Key2
|
||||
AlphaListTypeMixedKeys1: []MultiKeyItem{
|
||||
{Key1: "f", Key2: 1, Value: 1},
|
||||
{Key1: "f", Key2: 1, Value: 2},
|
||||
},
|
||||
// Case 7: Alpha listType, Standard Key1, Alpha Key2
|
||||
AlphaListTypeMixedKeys2: []MultiKeyItem{
|
||||
{Key1: "g", Key2: 1, Value: 1},
|
||||
{Key1: "g", Key2: 1, Value: 2},
|
||||
},
|
||||
|
||||
// Case 11: Standard listType, Beta Key1, Standard Key2
|
||||
StandardListTypeMixedKeysBeta1: []MultiKeyItem{
|
||||
{Key1: "k", Key2: 1, Value: 1},
|
||||
{Key1: "k", Key2: 1, Value: 2},
|
||||
},
|
||||
// Case 12: Standard listType, Standard Key1, Beta Key2
|
||||
StandardListTypeMixedKeysBeta2: []MultiKeyItem{
|
||||
{Key1: "l", Key2: 1, Value: 1},
|
||||
{Key1: "l", Key2: 1, Value: 2},
|
||||
},
|
||||
// Case 13: Beta listType, Beta Key1, Standard Key2
|
||||
BetaListTypeMixedKeys1: []MultiKeyItem{
|
||||
{Key1: "m", Key2: 1, Value: 1},
|
||||
{Key1: "m", Key2: 1, Value: 2},
|
||||
},
|
||||
// Case 14: Beta listType, Standard Key1, Beta Key2
|
||||
BetaListTypeMixedKeys2: []MultiKeyItem{
|
||||
{Key1: "n", Key2: 1, Value: 1},
|
||||
{Key1: "n", Key2: 1, Value: 2},
|
||||
},
|
||||
|
||||
// Case 8: Beta listType, Standard Key
|
||||
BetaListTypeStandardKey: []MapItem{
|
||||
{Key: "h", Value: 1},
|
||||
{Key: "h", Value: 2},
|
||||
},
|
||||
// Case 9: Standard listType, Beta Key
|
||||
StandardListTypeBetaKey: []MapItem{
|
||||
{Key: "i", Value: 1},
|
||||
{Key: "i", Value: 2},
|
||||
},
|
||||
// Case 10: Beta listType, Beta Key
|
||||
BetaListTypeBetaKey: []MapItem{
|
||||
{Key: "j", Value: 1},
|
||||
{Key: "j", Value: 2},
|
||||
},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
// Case 1: Alpha listType, Standard Key -> Alpha Error
|
||||
field.Duplicate(field.NewPath("alphaListTypeStandardKey").Index(1), MapItem{Key: "a", Value: 2}).MarkAlpha(),
|
||||
|
||||
// Case 2: Standard listType, Alpha Key -> Normal Error
|
||||
// The alpha key is treated as a functional key for the list validation.
|
||||
field.Duplicate(field.NewPath("standardListTypeAlphaKey").Index(1), MapItem{Key: "b", Value: 2}),
|
||||
|
||||
// Case 3: Alpha listType, Alpha Key -> Alpha Error
|
||||
field.Duplicate(field.NewPath("alphaListTypeAlphaKey").Index(1), MapItem{Key: "c", Value: 2}).MarkAlpha(),
|
||||
|
||||
// Case 4: Standard listType, Alpha Key1, Standard Key2 -> Normal Error
|
||||
// Both keys participate in the uniqueness check.
|
||||
field.Duplicate(field.NewPath("standardListTypeMixedKeys1").Index(1), MultiKeyItem{Key1: "d", Key2: 1, Value: 2}),
|
||||
|
||||
// Case 5: Standard listType, Standard Key1, Alpha Key2 -> Normal Error
|
||||
field.Duplicate(field.NewPath("standardListTypeMixedKeys2").Index(1), MultiKeyItem{Key1: "e", Key2: 1, Value: 2}),
|
||||
|
||||
// Case 6: Alpha listType, Alpha Key1, Standard Key2 -> Alpha Error
|
||||
field.Duplicate(field.NewPath("alphaListTypeMixedKeys1").Index(1), MultiKeyItem{Key1: "f", Key2: 1, Value: 2}).MarkAlpha(),
|
||||
|
||||
// Case 7: Alpha listType, Standard Key1, Alpha Key2 -> Alpha Error
|
||||
field.Duplicate(field.NewPath("alphaListTypeMixedKeys2").Index(1), MultiKeyItem{Key1: "g", Key2: 1, Value: 2}).MarkAlpha(),
|
||||
|
||||
// Case 11: Standard listType, Beta Key1, Standard Key2 -> Normal Error
|
||||
field.Duplicate(field.NewPath("standardListTypeMixedKeysBeta1").Index(1), MultiKeyItem{Key1: "k", Key2: 1, Value: 2}),
|
||||
|
||||
// Case 12: Standard listType, Standard Key1, Beta Key2 -> Normal Error
|
||||
field.Duplicate(field.NewPath("standardListTypeMixedKeysBeta2").Index(1), MultiKeyItem{Key1: "l", Key2: 1, Value: 2}),
|
||||
|
||||
// Case 13: Beta listType, Beta Key1, Standard Key2 -> Beta Error
|
||||
field.Duplicate(field.NewPath("betaListTypeMixedKeys1").Index(1), MultiKeyItem{Key1: "m", Key2: 1, Value: 2}).MarkBeta(),
|
||||
|
||||
// Case 14: Beta listType, Standard Key1, Beta Key2 -> Beta Error
|
||||
field.Duplicate(field.NewPath("betaListTypeMixedKeys2").Index(1), MultiKeyItem{Key1: "n", Key2: 1, Value: 2}).MarkBeta(),
|
||||
|
||||
// Case 8: Beta listType, Standard Key -> Beta Error
|
||||
field.Duplicate(field.NewPath("betaListTypeStandardKey").Index(1), MapItem{Key: "h", Value: 2}).MarkBeta(),
|
||||
|
||||
// Case 9: Standard listType, Beta Key -> Normal Error
|
||||
// The beta key is treated as a functional key for the list validation.
|
||||
field.Duplicate(field.NewPath("standardListTypeBetaKey").Index(1), MapItem{Key: "i", Value: 2}),
|
||||
|
||||
// Case 10: Beta listType, Beta Key -> Beta Error
|
||||
field.Duplicate(field.NewPath("betaListTypeBetaKey").Index(1), MapItem{Key: "j", Value: 2}).MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
//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 listkeys
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
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 ListKeyStruct
|
||||
scheme.AddValidationFunc((*ListKeyStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_ListKeyStruct(ctx, op, nil /* fldPath */, obj.(*ListKeyStruct), safe.Cast[*ListKeyStruct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_ListKeyStruct validates an instance of ListKeyStruct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ListKeyStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ListKeyStruct) (errs field.ErrorList) {
|
||||
// field ListKeyStruct.TypeMeta has no validation
|
||||
|
||||
// field ListKeyStruct.AlphaListTypeStandardKey
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key }).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("alphaListTypeStandardKey"), obj.AlphaListTypeStandardKey, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MapItem { return oldObj.AlphaListTypeStandardKey }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.StandardListTypeAlphaKey
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key })...)
|
||||
return
|
||||
}(fldPath.Child("standardListTypeAlphaKey"), obj.StandardListTypeAlphaKey, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MapItem { return oldObj.StandardListTypeAlphaKey }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.AlphaListTypeAlphaKey
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key }).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("alphaListTypeAlphaKey"), obj.AlphaListTypeAlphaKey, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MapItem { return oldObj.AlphaListTypeAlphaKey }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.StandardListTypeMixedKeys1
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MultiKeyItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MultiKeyItem, b MultiKeyItem) bool { return a.Key2 == b.Key2 && a.Key1 == b.Key1 })...)
|
||||
return
|
||||
}(fldPath.Child("standardListTypeMixedKeys1"), obj.StandardListTypeMixedKeys1, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MultiKeyItem { return oldObj.StandardListTypeMixedKeys1 }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.StandardListTypeMixedKeys2
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MultiKeyItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MultiKeyItem, b MultiKeyItem) bool { return a.Key1 == b.Key1 && a.Key2 == b.Key2 })...)
|
||||
return
|
||||
}(fldPath.Child("standardListTypeMixedKeys2"), obj.StandardListTypeMixedKeys2, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MultiKeyItem { return oldObj.StandardListTypeMixedKeys2 }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.AlphaListTypeMixedKeys1
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MultiKeyItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MultiKeyItem, b MultiKeyItem) bool { return a.Key2 == b.Key2 && a.Key1 == b.Key1 }).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("alphaListTypeMixedKeys1"), obj.AlphaListTypeMixedKeys1, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MultiKeyItem { return oldObj.AlphaListTypeMixedKeys1 }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.AlphaListTypeMixedKeys2
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MultiKeyItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MultiKeyItem, b MultiKeyItem) bool { return a.Key1 == b.Key1 && a.Key2 == b.Key2 }).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("alphaListTypeMixedKeys2"), obj.AlphaListTypeMixedKeys2, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MultiKeyItem { return oldObj.AlphaListTypeMixedKeys2 }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.StandardListTypeMixedKeysBeta1
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MultiKeyItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MultiKeyItem, b MultiKeyItem) bool { return a.Key2 == b.Key2 && a.Key1 == b.Key1 })...)
|
||||
return
|
||||
}(fldPath.Child("standardListTypeMixedKeysBeta1"), obj.StandardListTypeMixedKeysBeta1, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MultiKeyItem { return oldObj.StandardListTypeMixedKeysBeta1 }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.StandardListTypeMixedKeysBeta2
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MultiKeyItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MultiKeyItem, b MultiKeyItem) bool { return a.Key1 == b.Key1 && a.Key2 == b.Key2 })...)
|
||||
return
|
||||
}(fldPath.Child("standardListTypeMixedKeysBeta2"), obj.StandardListTypeMixedKeysBeta2, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MultiKeyItem { return oldObj.StandardListTypeMixedKeysBeta2 }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.BetaListTypeMixedKeys1
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MultiKeyItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MultiKeyItem, b MultiKeyItem) bool { return a.Key2 == b.Key2 && a.Key1 == b.Key1 }).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("betaListTypeMixedKeys1"), obj.BetaListTypeMixedKeys1, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MultiKeyItem { return oldObj.BetaListTypeMixedKeys1 }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.BetaListTypeMixedKeys2
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MultiKeyItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MultiKeyItem, b MultiKeyItem) bool { return a.Key1 == b.Key1 && a.Key2 == b.Key2 }).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("betaListTypeMixedKeys2"), obj.BetaListTypeMixedKeys2, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MultiKeyItem { return oldObj.BetaListTypeMixedKeys2 }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.BetaListTypeStandardKey
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key }).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("betaListTypeStandardKey"), obj.BetaListTypeStandardKey, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MapItem { return oldObj.BetaListTypeStandardKey }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.StandardListTypeBetaKey
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key })...)
|
||||
return
|
||||
}(fldPath.Child("standardListTypeBetaKey"), obj.StandardListTypeBetaKey, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MapItem { return oldObj.StandardListTypeBetaKey }), oldObj != nil)...)
|
||||
|
||||
// field ListKeyStruct.BetaListTypeBetaKey
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key }).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("betaListTypeBetaKey"), obj.BetaListTypeBetaKey, safe.Field(oldObj, func(oldObj *ListKeyStruct) []MapItem { return oldObj.BetaListTypeBetaKey }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package listmapitem
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type MapItem struct {
|
||||
Key string `json:"key"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
type InnerItem struct {
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
type ListMapItemStruct struct {
|
||||
TypeMeta int
|
||||
|
||||
// Case: Standard Item
|
||||
// +k8s:listType=map
|
||||
// +k8s:listMapKey=key
|
||||
// +k8s:item(key: "foo")=+k8s:subfield(value)=+k8s:minimum=10
|
||||
StandardItem []MapItem `json:"standardItem"`
|
||||
|
||||
// Case: Alpha Item tag
|
||||
// +k8s:listType=map
|
||||
// +k8s:listMapKey=key
|
||||
// +k8s:alpha=+k8s:item(key: "foo")=+k8s:subfield(value)=+k8s:minimum=10
|
||||
AlphaItemTag []MapItem `json:"alphaItemTag"`
|
||||
|
||||
// Case: Alpha validation
|
||||
// +k8s:listType=map
|
||||
// +k8s:listMapKey=key
|
||||
// +k8s:item(key: "foo")=+k8s:subfield(value)=+k8s:alpha=+k8s:minimum=10
|
||||
AlphaValidation []MapItem `json:"alphaValidation"`
|
||||
|
||||
// Case: Double Alpha
|
||||
// +k8s:listType=map
|
||||
// +k8s:listMapKey=key
|
||||
// +k8s:alpha=+k8s:item(key: "foo")=+k8s:subfield(value)=+k8s:alpha=+k8s:minimum=10
|
||||
DoubleAlpha []MapItem `json:"doubleAlpha"`
|
||||
|
||||
// Case: Beta Item tag
|
||||
// +k8s:listType=map
|
||||
// +k8s:listMapKey=key
|
||||
// +k8s:beta=+k8s:item(key: "foo")=+k8s:subfield(value)=+k8s:minimum=10
|
||||
BetaItemTag []MapItem `json:"betaItemTag"`
|
||||
|
||||
// Case: Beta validation
|
||||
// +k8s:listType=map
|
||||
// +k8s:listMapKey=key
|
||||
// +k8s:item(key: "foo")=+k8s:subfield(value)=+k8s:beta=+k8s:minimum=10
|
||||
BetaValidation []MapItem `json:"betaValidation"`
|
||||
|
||||
// Case: Double Beta
|
||||
// +k8s:listType=map
|
||||
// +k8s:listMapKey=key
|
||||
// +k8s:beta=+k8s:item(key: "foo")=+k8s:subfield(value)=+k8s:beta=+k8s:minimum=10
|
||||
DoubleBeta []MapItem `json:"doubleBeta"`
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
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 listmapitem
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestListMapItem(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&ListMapItemStruct{
|
||||
StandardItem: []MapItem{{Key: "foo", Value: 5}},
|
||||
AlphaItemTag: []MapItem{{Key: "foo", Value: 5}},
|
||||
AlphaValidation: []MapItem{{Key: "foo", Value: 5}},
|
||||
DoubleAlpha: []MapItem{{Key: "foo", Value: 5}},
|
||||
|
||||
BetaItemTag: []MapItem{{Key: "foo", Value: 5}},
|
||||
BetaValidation: []MapItem{{Key: "foo", Value: 5}},
|
||||
DoubleBeta: []MapItem{{Key: "foo", Value: 5}},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
// Case 1: Standard Item -> Normal Error
|
||||
field.Invalid(field.NewPath("standardItem").Index(0).Child("value"), 5, "").WithOrigin("minimum"),
|
||||
|
||||
// Case 2: Alpha Item tag -> Alpha Error
|
||||
field.Invalid(field.NewPath("alphaItemTag").Index(0).Child("value"), 5, "").WithOrigin("minimum").MarkAlpha(),
|
||||
|
||||
// Case 3: Alpha validation -> Alpha Error
|
||||
field.Invalid(field.NewPath("alphaValidation").Index(0).Child("value"), 5, "").WithOrigin("minimum").MarkAlpha(),
|
||||
|
||||
// Case 4: Double Alpha -> Alpha Error
|
||||
field.Invalid(field.NewPath("doubleAlpha").Index(0).Child("value"), 5, "").WithOrigin("minimum").MarkAlpha(),
|
||||
|
||||
// Case 5: Beta Item tag -> Beta Error
|
||||
field.Invalid(field.NewPath("betaItemTag").Index(0).Child("value"), 5, "").WithOrigin("minimum").MarkBeta(),
|
||||
|
||||
// Case 6: Beta validation -> Beta Error
|
||||
field.Invalid(field.NewPath("betaValidation").Index(0).Child("value"), 5, "").WithOrigin("minimum").MarkBeta(),
|
||||
|
||||
// Case 7: Double Beta -> Beta Error
|
||||
field.Invalid(field.NewPath("doubleBeta").Index(0).Child("value"), 5, "").WithOrigin("minimum").MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
//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 listmapitem
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
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 ListMapItemStruct
|
||||
scheme.AddValidationFunc((*ListMapItemStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_ListMapItemStruct(ctx, op, nil /* fldPath */, obj.(*ListMapItemStruct), safe.Cast[*ListMapItemStruct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_ListMapItemStruct validates an instance of ListMapItemStruct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ListMapItemStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ListMapItemStruct) (errs field.ErrorList) {
|
||||
// field ListMapItemStruct.TypeMeta has no validation
|
||||
|
||||
// field ListMapItemStruct.StandardItem
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key })...)
|
||||
func() { // cohort {"key": "foo"}
|
||||
errs = append(errs, validate.SliceItem(ctx, op, fldPath, obj, oldObj, func(item *MapItem) bool { return item.Key == "foo" }, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MapItem) field.ErrorList {
|
||||
return validate.Subfield(ctx, op, fldPath, obj, oldObj, "value", func(o *MapItem) *int { return &o.Value }, validate.DirectEqualPtr, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10)
|
||||
})
|
||||
})...)
|
||||
}()
|
||||
return
|
||||
}(fldPath.Child("standardItem"), obj.StandardItem, safe.Field(oldObj, func(oldObj *ListMapItemStruct) []MapItem { return oldObj.StandardItem }), oldObj != nil)...)
|
||||
|
||||
// field ListMapItemStruct.AlphaItemTag
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key })...)
|
||||
func() { // cohort {"key": "foo"}
|
||||
errs = append(errs, validate.SliceItem(ctx, op, fldPath, obj, oldObj, func(item *MapItem) bool { return item.Key == "foo" }, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MapItem) field.ErrorList {
|
||||
return validate.Subfield(ctx, op, fldPath, obj, oldObj, "value", func(o *MapItem) *int { return &o.Value }, validate.DirectEqualPtr, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10)
|
||||
})
|
||||
}).MarkAlpha()...)
|
||||
}()
|
||||
return
|
||||
}(fldPath.Child("alphaItemTag"), obj.AlphaItemTag, safe.Field(oldObj, func(oldObj *ListMapItemStruct) []MapItem { return oldObj.AlphaItemTag }), oldObj != nil)...)
|
||||
|
||||
// field ListMapItemStruct.AlphaValidation
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key })...)
|
||||
func() { // cohort {"key": "foo"}
|
||||
errs = append(errs, validate.SliceItem(ctx, op, fldPath, obj, oldObj, func(item *MapItem) bool { return item.Key == "foo" }, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MapItem) field.ErrorList {
|
||||
return validate.Subfield(ctx, op, fldPath, obj, oldObj, "value", func(o *MapItem) *int { return &o.Value }, validate.DirectEqualPtr, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkAlpha()
|
||||
})
|
||||
})...)
|
||||
}()
|
||||
return
|
||||
}(fldPath.Child("alphaValidation"), obj.AlphaValidation, safe.Field(oldObj, func(oldObj *ListMapItemStruct) []MapItem { return oldObj.AlphaValidation }), oldObj != nil)...)
|
||||
|
||||
// field ListMapItemStruct.DoubleAlpha
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key })...)
|
||||
func() { // cohort {"key": "foo"}
|
||||
errs = append(errs, validate.SliceItem(ctx, op, fldPath, obj, oldObj, func(item *MapItem) bool { return item.Key == "foo" }, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MapItem) field.ErrorList {
|
||||
return validate.Subfield(ctx, op, fldPath, obj, oldObj, "value", func(o *MapItem) *int { return &o.Value }, validate.DirectEqualPtr, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkAlpha()
|
||||
})
|
||||
}).MarkAlpha()...)
|
||||
}()
|
||||
return
|
||||
}(fldPath.Child("doubleAlpha"), obj.DoubleAlpha, safe.Field(oldObj, func(oldObj *ListMapItemStruct) []MapItem { return oldObj.DoubleAlpha }), oldObj != nil)...)
|
||||
|
||||
// field ListMapItemStruct.BetaItemTag
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key })...)
|
||||
func() { // cohort {"key": "foo"}
|
||||
errs = append(errs, validate.SliceItem(ctx, op, fldPath, obj, oldObj, func(item *MapItem) bool { return item.Key == "foo" }, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MapItem) field.ErrorList {
|
||||
return validate.Subfield(ctx, op, fldPath, obj, oldObj, "value", func(o *MapItem) *int { return &o.Value }, validate.DirectEqualPtr, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10)
|
||||
})
|
||||
}).MarkBeta()...)
|
||||
}()
|
||||
return
|
||||
}(fldPath.Child("betaItemTag"), obj.BetaItemTag, safe.Field(oldObj, func(oldObj *ListMapItemStruct) []MapItem { return oldObj.BetaItemTag }), oldObj != nil)...)
|
||||
|
||||
// field ListMapItemStruct.BetaValidation
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key })...)
|
||||
func() { // cohort {"key": "foo"}
|
||||
errs = append(errs, validate.SliceItem(ctx, op, fldPath, obj, oldObj, func(item *MapItem) bool { return item.Key == "foo" }, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MapItem) field.ErrorList {
|
||||
return validate.Subfield(ctx, op, fldPath, obj, oldObj, "value", func(o *MapItem) *int { return &o.Value }, validate.DirectEqualPtr, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkBeta()
|
||||
})
|
||||
})...)
|
||||
}()
|
||||
return
|
||||
}(fldPath.Child("betaValidation"), obj.BetaValidation, safe.Field(oldObj, func(oldObj *ListMapItemStruct) []MapItem { return oldObj.BetaValidation }), oldObj != nil)...)
|
||||
|
||||
// field ListMapItemStruct.DoubleBeta
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []MapItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with map semantics require unique keys
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a MapItem, b MapItem) bool { return a.Key == b.Key })...)
|
||||
func() { // cohort {"key": "foo"}
|
||||
errs = append(errs, validate.SliceItem(ctx, op, fldPath, obj, oldObj, func(item *MapItem) bool { return item.Key == "foo" }, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MapItem) field.ErrorList {
|
||||
return validate.Subfield(ctx, op, fldPath, obj, oldObj, "value", func(o *MapItem) *int { return &o.Value }, validate.DirectEqualPtr, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkBeta()
|
||||
})
|
||||
}).MarkBeta()...)
|
||||
}()
|
||||
return
|
||||
}(fldPath.Child("doubleBeta"), obj.DoubleBeta, safe.Field(oldObj, func(oldObj *ListMapItemStruct) []MapItem { return oldObj.DoubleBeta }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package listset
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type ListSetStruct struct {
|
||||
TypeMeta int
|
||||
|
||||
// Case: Alpha listType=set
|
||||
// +k8s:alpha=+k8s:listType=set
|
||||
Set []ComplexSetItem `json:"set"`
|
||||
|
||||
// Case: Beta listType=set
|
||||
// +k8s:beta=+k8s:listType=set
|
||||
BetaSet []ComplexSetItem `json:"betaSet"`
|
||||
|
||||
// Case: Chained subfield validation
|
||||
// +k8s:listType=set
|
||||
// +k8s:eachVal=+k8s:subfield(value)=+k8s:alpha=+k8s:minimum=10
|
||||
ChainedSubfieldSet []SimpleSetItem `json:"chainedSubfieldSet"`
|
||||
|
||||
// Case: Alpha listType=set, Beta item validation
|
||||
// +k8s:alpha=+k8s:listType=set
|
||||
SetBetaItem []ComplexSetItemBeta `json:"setBetaItem"`
|
||||
|
||||
// Case: Beta listType=set, Beta item validation
|
||||
// +k8s:beta=+k8s:listType=set
|
||||
BetaSetBetaItem []ComplexSetItemBeta `json:"betaSetBetaItem"`
|
||||
|
||||
// Case: Chained subfield validation (Beta)
|
||||
// +k8s:listType=set
|
||||
// +k8s:eachVal=+k8s:subfield(value)=+k8s:beta=+k8s:minimum=10
|
||||
ChainedSubfieldSetBeta []SimpleSetItem `json:"chainedSubfieldSetBeta"`
|
||||
}
|
||||
|
||||
type ComplexSetItem struct {
|
||||
// +k8s:alpha=+k8s:minimum=10
|
||||
Value int `json:"value"`
|
||||
StringVal string `json:"stringVal"`
|
||||
}
|
||||
|
||||
type ComplexSetItemBeta struct {
|
||||
// +k8s:beta=+k8s:minimum=10
|
||||
Value int `json:"value"`
|
||||
StringVal string `json:"stringVal"`
|
||||
}
|
||||
|
||||
type SimpleSetItem struct {
|
||||
Value int `json:"value"`
|
||||
StringVal string `json:"stringVal"`
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
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 listset
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestListSet(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&ListSetStruct{
|
||||
Set: []ComplexSetItem{
|
||||
{StringVal: "abc", Value: 10},
|
||||
{StringVal: "abc", Value: 10}, // Duplicate value (alpha)
|
||||
},
|
||||
BetaSet: []ComplexSetItem{
|
||||
{StringVal: "abc", Value: 10},
|
||||
{StringVal: "abc", Value: 10}, // Duplicate value (beta)
|
||||
},
|
||||
ChainedSubfieldSet: []SimpleSetItem{
|
||||
{StringVal: "def", Value: 9}, // Value 9 < 10 (alpha)
|
||||
},
|
||||
SetBetaItem: []ComplexSetItemBeta{
|
||||
{StringVal: "ghi", Value: 10},
|
||||
{StringVal: "ghi", Value: 10}, // Duplicate value (alpha)
|
||||
},
|
||||
BetaSetBetaItem: []ComplexSetItemBeta{
|
||||
{StringVal: "jkl", Value: 10},
|
||||
{StringVal: "jkl", Value: 10}, // Duplicate value (beta)
|
||||
},
|
||||
ChainedSubfieldSetBeta: []SimpleSetItem{
|
||||
{StringVal: "mno", Value: 9}, // Value 9 < 10 (beta)
|
||||
},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
// Set: Duplicate value is SHADOWED
|
||||
field.Duplicate(field.NewPath("set").Index(1), ComplexSetItem{StringVal: "abc", Value: 10}).MarkAlpha(),
|
||||
|
||||
// BetaSet: Duplicate value is SHADOWED
|
||||
field.Duplicate(field.NewPath("betaSet").Index(1), ComplexSetItem{StringVal: "abc", Value: 10}).MarkBeta(),
|
||||
|
||||
// ChainedSubfieldSet: Value 9 < 10 is SHADOWED
|
||||
field.Invalid(field.NewPath("chainedSubfieldSet").Index(0).Child("value"), 9, "").WithOrigin("minimum").MarkAlpha(),
|
||||
|
||||
// SetBetaItem: Duplicate value is SHADOWED (Alpha list)
|
||||
field.Duplicate(field.NewPath("setBetaItem").Index(1), ComplexSetItemBeta{StringVal: "ghi", Value: 10}).MarkAlpha(),
|
||||
|
||||
// BetaSetBetaItem: Duplicate value is SHADOWED (Beta list)
|
||||
field.Duplicate(field.NewPath("betaSetBetaItem").Index(1), ComplexSetItemBeta{StringVal: "jkl", Value: 10}).MarkBeta(),
|
||||
|
||||
// ChainedSubfieldSetBeta: Value 9 < 10 is SHADOWED (Beta item)
|
||||
field.Invalid(field.NewPath("chainedSubfieldSetBeta").Index(0).Child("value"), 9, "").WithOrigin("minimum").MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
//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 listset
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
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 ListSetStruct
|
||||
scheme.AddValidationFunc((*ListSetStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_ListSetStruct(ctx, op, nil /* fldPath */, obj.(*ListSetStruct), safe.Cast[*ListSetStruct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_ComplexSetItem validates an instance of ComplexSetItem according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ComplexSetItem(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ComplexSetItem) (errs field.ErrorList) {
|
||||
// field ComplexSetItem.Value
|
||||
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.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("value"), &obj.Value, safe.Field(oldObj, func(oldObj *ComplexSetItem) *int { return &oldObj.Value }), oldObj != nil)...)
|
||||
|
||||
// field ComplexSetItem.StringVal has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_ComplexSetItemBeta validates an instance of ComplexSetItemBeta according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ComplexSetItemBeta(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ComplexSetItemBeta) (errs field.ErrorList) {
|
||||
// field ComplexSetItemBeta.Value
|
||||
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.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("value"), &obj.Value, safe.Field(oldObj, func(oldObj *ComplexSetItemBeta) *int { return &oldObj.Value }), oldObj != nil)...)
|
||||
|
||||
// field ComplexSetItemBeta.StringVal has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_ListSetStruct validates an instance of ListSetStruct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ListSetStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ListSetStruct) (errs field.ErrorList) {
|
||||
// field ListSetStruct.TypeMeta has no validation
|
||||
|
||||
// field ListSetStruct.Set
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []ComplexSetItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with set semantics require unique values
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual).MarkAlpha()...)
|
||||
// iterate the list and call the type's validation function
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, nil, Validate_ComplexSetItem)...)
|
||||
return
|
||||
}(fldPath.Child("set"), obj.Set, safe.Field(oldObj, func(oldObj *ListSetStruct) []ComplexSetItem { return oldObj.Set }), oldObj != nil)...)
|
||||
|
||||
// field ListSetStruct.BetaSet
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []ComplexSetItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with set semantics require unique values
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual).MarkBeta()...)
|
||||
// iterate the list and call the type's validation function
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, nil, Validate_ComplexSetItem)...)
|
||||
return
|
||||
}(fldPath.Child("betaSet"), obj.BetaSet, safe.Field(oldObj, func(oldObj *ListSetStruct) []ComplexSetItem { return oldObj.BetaSet }), oldObj != nil)...)
|
||||
|
||||
// field ListSetStruct.ChainedSubfieldSet
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []SimpleSetItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *SimpleSetItem) field.ErrorList {
|
||||
return validate.Subfield(ctx, op, fldPath, obj, oldObj, "value", func(o *SimpleSetItem) *int { return &o.Value }, validate.DirectEqualPtr, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkAlpha()
|
||||
})
|
||||
})...)
|
||||
// lists with set semantics require unique values
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...)
|
||||
return
|
||||
}(fldPath.Child("chainedSubfieldSet"), obj.ChainedSubfieldSet, safe.Field(oldObj, func(oldObj *ListSetStruct) []SimpleSetItem { return oldObj.ChainedSubfieldSet }), oldObj != nil)...)
|
||||
|
||||
// field ListSetStruct.SetBetaItem
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []ComplexSetItemBeta, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with set semantics require unique values
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual).MarkAlpha()...)
|
||||
// iterate the list and call the type's validation function
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, nil, Validate_ComplexSetItemBeta)...)
|
||||
return
|
||||
}(fldPath.Child("setBetaItem"), obj.SetBetaItem, safe.Field(oldObj, func(oldObj *ListSetStruct) []ComplexSetItemBeta { return oldObj.SetBetaItem }), oldObj != nil)...)
|
||||
|
||||
// field ListSetStruct.BetaSetBetaItem
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []ComplexSetItemBeta, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with set semantics require unique values
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual).MarkBeta()...)
|
||||
// iterate the list and call the type's validation function
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, nil, Validate_ComplexSetItemBeta)...)
|
||||
return
|
||||
}(fldPath.Child("betaSetBetaItem"), obj.BetaSetBetaItem, safe.Field(oldObj, func(oldObj *ListSetStruct) []ComplexSetItemBeta { return oldObj.BetaSetBetaItem }), oldObj != nil)...)
|
||||
|
||||
// field ListSetStruct.ChainedSubfieldSetBeta
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []SimpleSetItem, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *SimpleSetItem) field.ErrorList {
|
||||
return validate.Subfield(ctx, op, fldPath, obj, oldObj, "value", func(o *SimpleSetItem) *int { return &o.Value }, validate.DirectEqualPtr, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkBeta()
|
||||
})
|
||||
})...)
|
||||
// lists with set semantics require unique values
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)...)
|
||||
return
|
||||
}(fldPath.Child("chainedSubfieldSetBeta"), obj.ChainedSubfieldSetBeta, safe.Field(oldObj, func(oldObj *ListSetStruct) []SimpleSetItem { return oldObj.ChainedSubfieldSetBeta }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package mapvalidation
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type MapValidationStruct struct {
|
||||
TypeMeta int
|
||||
|
||||
// Case: Standard eachVal
|
||||
// +k8s:eachVal=+k8s:maxLength=2
|
||||
StandardEachVal map[string]string `json:"standardEachVal"`
|
||||
|
||||
// Case: Alpha eachVal
|
||||
// +k8s:alpha=+k8s:eachVal=+k8s:maxLength=2
|
||||
AlphaEachVal map[string]string `json:"AlphaEachVal"`
|
||||
|
||||
// Case: Beta eachVal
|
||||
// +k8s:beta=+k8s:eachVal=+k8s:maxLength=2
|
||||
BetaEachVal map[string]string `json:"BetaEachVal"`
|
||||
|
||||
// Case: Standard eachKey
|
||||
// +k8s:eachKey=+k8s:maxLength=2
|
||||
StandardEachKey map[string]string `json:"standardEachKey"`
|
||||
|
||||
// Case: Alpha eachKey
|
||||
// +k8s:alpha=+k8s:eachKey=+k8s:maxLength=2
|
||||
AlphaEachKey map[string]string `json:"AlphaEachKey"`
|
||||
|
||||
// Case: Beta eachKey
|
||||
// +k8s:beta=+k8s:eachKey=+k8s:maxLength=2
|
||||
BetaEachKey map[string]string `json:"BetaEachKey"`
|
||||
|
||||
// Case: Alpha Validation
|
||||
// +k8s:eachVal=+k8s:alpha=+k8s:maxLength=2
|
||||
AlphaValidation map[string]string `json:"AlphaValidation"`
|
||||
|
||||
// Case: Beta Validation
|
||||
// +k8s:eachVal=+k8s:beta=+k8s:maxLength=2
|
||||
BetaValidation map[string]string `json:"BetaValidation"`
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
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 mapvalidation
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestMapValidation(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&MapValidationStruct{
|
||||
StandardEachVal: map[string]string{"a": "foo"},
|
||||
AlphaEachVal: map[string]string{"a": "foo"},
|
||||
BetaEachVal: map[string]string{"a": "foo"},
|
||||
StandardEachKey: map[string]string{"foo": "a"},
|
||||
AlphaEachKey: map[string]string{"foo": "a"},
|
||||
BetaEachKey: map[string]string{"foo": "a"},
|
||||
AlphaValidation: map[string]string{"a": "foo"},
|
||||
BetaValidation: map[string]string{"a": "foo"},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
// Case: Standard eachVal -> Normal Error
|
||||
field.TooLong(field.NewPath("standardEachVal").Key("a"), "foo", 2).WithOrigin("maxLength"),
|
||||
|
||||
// Case: Alpha eachVal -> Alpha Error
|
||||
field.TooLong(field.NewPath("AlphaEachVal").Key("a"), "foo", 2).WithOrigin("maxLength").MarkAlpha(),
|
||||
|
||||
// Case: Beta eachVal -> Beta Error
|
||||
field.TooLong(field.NewPath("BetaEachVal").Key("a"), "foo", 2).WithOrigin("maxLength").MarkBeta(),
|
||||
|
||||
// Case: Standard eachKey -> Normal Error
|
||||
field.TooLong(field.NewPath("standardEachKey"), "foo", 2).WithOrigin("maxLength"),
|
||||
|
||||
// Case: Alpha eachKey -> Alpha Error
|
||||
field.TooLong(field.NewPath("AlphaEachKey"), "foo", 2).WithOrigin("maxLength").MarkAlpha(),
|
||||
|
||||
// Case: Beta eachKey -> Beta Error
|
||||
field.TooLong(field.NewPath("BetaEachKey"), "foo", 2).WithOrigin("maxLength").MarkBeta(),
|
||||
|
||||
// Case: Alpha Validation -> Alpha Error
|
||||
field.TooLong(field.NewPath("AlphaValidation").Key("a"), "foo", 2).WithOrigin("maxLength").MarkAlpha(),
|
||||
|
||||
// Case: Beta Validation -> Beta Error
|
||||
field.TooLong(field.NewPath("BetaValidation").Key("a"), "foo", 2).WithOrigin("maxLength").MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
//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 mapvalidation
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
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 MapValidationStruct
|
||||
scheme.AddValidationFunc((*MapValidationStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_MapValidationStruct(ctx, op, nil /* fldPath */, obj.(*MapValidationStruct), safe.Cast[*MapValidationStruct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_MapValidationStruct validates an instance of MapValidationStruct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_MapValidationStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MapValidationStruct) (errs field.ErrorList) {
|
||||
// field MapValidationStruct.TypeMeta has no validation
|
||||
|
||||
// field MapValidationStruct.StandardEachVal
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj map[string]string, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 2)
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("standardEachVal"), obj.StandardEachVal, safe.Field(oldObj, func(oldObj *MapValidationStruct) map[string]string { return oldObj.StandardEachVal }), oldObj != nil)...)
|
||||
|
||||
// field MapValidationStruct.AlphaEachVal
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj map[string]string, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 2)
|
||||
}).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("AlphaEachVal"), obj.AlphaEachVal, safe.Field(oldObj, func(oldObj *MapValidationStruct) map[string]string { return oldObj.AlphaEachVal }), oldObj != nil)...)
|
||||
|
||||
// field MapValidationStruct.BetaEachVal
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj map[string]string, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 2)
|
||||
}).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("BetaEachVal"), obj.BetaEachVal, safe.Field(oldObj, func(oldObj *MapValidationStruct) map[string]string { return oldObj.BetaEachVal }), oldObj != nil)...)
|
||||
|
||||
// field MapValidationStruct.StandardEachKey
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj map[string]string, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 2)
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("standardEachKey"), obj.StandardEachKey, safe.Field(oldObj, func(oldObj *MapValidationStruct) map[string]string { return oldObj.StandardEachKey }), oldObj != nil)...)
|
||||
|
||||
// field MapValidationStruct.AlphaEachKey
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj map[string]string, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 2)
|
||||
}).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("AlphaEachKey"), obj.AlphaEachKey, safe.Field(oldObj, func(oldObj *MapValidationStruct) map[string]string { return oldObj.AlphaEachKey }), oldObj != nil)...)
|
||||
|
||||
// field MapValidationStruct.BetaEachKey
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj map[string]string, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 2)
|
||||
}).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("BetaEachKey"), obj.BetaEachKey, safe.Field(oldObj, func(oldObj *MapValidationStruct) map[string]string { return oldObj.BetaEachKey }), oldObj != nil)...)
|
||||
|
||||
// field MapValidationStruct.AlphaValidation
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj map[string]string, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 2).MarkAlpha()
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("AlphaValidation"), obj.AlphaValidation, safe.Field(oldObj, func(oldObj *MapValidationStruct) map[string]string { return oldObj.AlphaValidation }), oldObj != nil)...)
|
||||
|
||||
// field MapValidationStruct.BetaValidation
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj map[string]string, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 2).MarkBeta()
|
||||
})...)
|
||||
return
|
||||
}(fldPath.Child("BetaValidation"), obj.BetaValidation, safe.Field(oldObj, func(oldObj *MapValidationStruct) map[string]string { return oldObj.BetaValidation }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package optionalrequired
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type Struct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:alpha=+k8s:required
|
||||
RequiredField *string `json:"requiredField"`
|
||||
|
||||
// +k8s:beta=+k8s:required
|
||||
RequiredFieldBeta *string `json:"requiredFieldBeta"`
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
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 optionalrequired
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
func TestAlpha(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
RequiredField: ptr.To("val"),
|
||||
RequiredFieldBeta: ptr.To("val"),
|
||||
}).ExpectValid()
|
||||
|
||||
// Test failures marked as alpha
|
||||
st.Value(&Struct{
|
||||
RequiredField: nil,
|
||||
RequiredFieldBeta: ptr.To("val"),
|
||||
}).OldValue(&Struct{
|
||||
RequiredField: ptr.To("old"),
|
||||
RequiredFieldBeta: ptr.To("old"),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Required(field.NewPath("requiredField"), "").MarkAlpha(),
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeta(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
RequiredField: ptr.To("val"),
|
||||
RequiredFieldBeta: ptr.To("val"),
|
||||
}).ExpectValid()
|
||||
|
||||
// Test failures marked as beta
|
||||
st.Value(&Struct{
|
||||
RequiredField: ptr.To("val"),
|
||||
RequiredFieldBeta: nil,
|
||||
}).OldValue(&Struct{
|
||||
RequiredField: ptr.To("old"),
|
||||
RequiredFieldBeta: ptr.To("old"),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Required(field.NewPath("requiredFieldBeta"), "").MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
//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 optionalrequired
|
||||
|
||||
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_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.RequiredField
|
||||
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
|
||||
earlyReturn := false
|
||||
if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj).MarkAlpha(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("requiredField"), obj.RequiredField, safe.Field(oldObj, func(oldObj *Struct) *string { return oldObj.RequiredField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.RequiredFieldBeta
|
||||
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
|
||||
earlyReturn := false
|
||||
if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj).MarkBeta(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("requiredFieldBeta"), obj.RequiredFieldBeta, safe.Field(oldObj, func(oldObj *Struct) *string { return oldObj.RequiredFieldBeta }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package simple
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type Struct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:alpha=+k8s:minimum=10
|
||||
IntField int `json:"intField"`
|
||||
|
||||
// +k8s:beta=+k8s:minimum=10
|
||||
IntFieldBeta int `json:"intFieldBeta"`
|
||||
|
||||
// +k8s:alpha=+k8s:maxLength=5
|
||||
StringField string `json:"stringField"`
|
||||
|
||||
// +k8s:beta=+k8s:maxLength=5
|
||||
StringFieldBeta string `json:"stringFieldBeta"`
|
||||
|
||||
// +k8s:alpha=+k8s:maxItems=2
|
||||
SliceField []string `json:"sliceField"`
|
||||
|
||||
// +k8s:beta=+k8s:maxItems=2
|
||||
SliceFieldBeta []string `json:"sliceFieldBeta"`
|
||||
|
||||
// +k8s:alpha=+k8s:format=k8s-uuid
|
||||
UUIDField string `json:"uuidField"`
|
||||
|
||||
// +k8s:beta=+k8s:format=k8s-uuid
|
||||
UUIDFieldBeta string `json:"uuidFieldBeta"`
|
||||
|
||||
// +k8s:alpha=+k8s:immutable
|
||||
ImmutableField string `json:"immutableField"`
|
||||
|
||||
// +k8s:beta=+k8s:immutable
|
||||
ImmutableFieldBeta string `json:"immutableFieldBeta"`
|
||||
}
|
||||
|
||||
type SpecialValidationStruct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:alpha=+k8s:neq=5
|
||||
NEQField int `json:"neqField"`
|
||||
|
||||
// +k8s:beta=+k8s:neq=5
|
||||
NEQFieldBeta int `json:"neqFieldBeta"`
|
||||
|
||||
// +k8s:alpha=+k8s:forbidden
|
||||
ForbiddenField *string `json:"forbiddenField"`
|
||||
|
||||
// +k8s:beta=+k8s:forbidden
|
||||
ForbiddenFieldBeta *string `json:"forbiddenFieldBeta"`
|
||||
|
||||
// +k8s:alpha=+k8s:update=NoModify
|
||||
UpdateField string `json:"updateField"`
|
||||
|
||||
// +k8s:beta=+k8s:update=NoModify
|
||||
UpdateFieldBeta string `json:"updateFieldBeta"`
|
||||
}
|
||||
|
||||
type StructWithValidateFalse struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:alpha=+k8s:validateFalse="always fails"
|
||||
ValidateFalse *string `json:"validateFalse"`
|
||||
|
||||
// +k8s:beta=+k8s:validateFalse="always fails"
|
||||
ValidateFalseBeta *string `json:"validateFalseBeta"`
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
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 simple
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
func TestAlpha(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
IntField: 10,
|
||||
StringField: "abc",
|
||||
SliceField: []string{"a", "b"},
|
||||
UUIDField: "a0a2a2d2-0b87-4964-a123-78d00a8787a6",
|
||||
ImmutableField: "foo",
|
||||
IntFieldBeta: 10,
|
||||
StringFieldBeta: "abc",
|
||||
SliceFieldBeta: []string{"a", "b"},
|
||||
UUIDFieldBeta: "a0a2a2d2-0b87-4964-a123-78d00a8787a6",
|
||||
ImmutableFieldBeta: "foo",
|
||||
}).ExpectValid()
|
||||
|
||||
// Test failures marked as alpha
|
||||
st.Value(&Struct{
|
||||
IntField: 5,
|
||||
StringField: "too-long",
|
||||
SliceField: []string{"a", "b", "c"},
|
||||
UUIDField: "not-a-uuid",
|
||||
ImmutableField: "bar",
|
||||
IntFieldBeta: 10,
|
||||
StringFieldBeta: "abc",
|
||||
SliceFieldBeta: []string{"a", "b"},
|
||||
UUIDFieldBeta: "a0a2a2d2-0b87-4964-a123-78d00a8787a6",
|
||||
ImmutableFieldBeta: "foo",
|
||||
}).OldValue(&Struct{
|
||||
ImmutableField: "foo",
|
||||
ImmutableFieldBeta: "foo",
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("intField"), 5, "").WithOrigin("minimum").MarkAlpha(),
|
||||
field.TooLong(field.NewPath("stringField"), "too-long", 5).WithOrigin("maxLength").MarkAlpha(),
|
||||
field.TooMany(field.NewPath("sliceField"), 3, 2).WithOrigin("maxItems").MarkAlpha(),
|
||||
field.Invalid(field.NewPath("uuidField"), "not-a-uuid", "").WithOrigin("format=k8s-uuid").MarkAlpha(),
|
||||
field.Invalid(field.NewPath("immutableField"), "bar", "").WithOrigin("immutable").MarkAlpha(),
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeta(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
IntField: 10,
|
||||
StringField: "abc",
|
||||
SliceField: []string{"a", "b"},
|
||||
UUIDField: "a0a2a2d2-0b87-4964-a123-78d00a8787a6",
|
||||
ImmutableField: "foo",
|
||||
IntFieldBeta: 10,
|
||||
StringFieldBeta: "abc",
|
||||
SliceFieldBeta: []string{"a", "b"},
|
||||
UUIDFieldBeta: "a0a2a2d2-0b87-4964-a123-78d00a8787a6",
|
||||
ImmutableFieldBeta: "foo",
|
||||
}).ExpectValid()
|
||||
|
||||
// Test failures marked as beta
|
||||
st.Value(&Struct{
|
||||
IntField: 10,
|
||||
StringField: "abc",
|
||||
SliceField: []string{"a", "b"},
|
||||
UUIDField: "a0a2a2d2-0b87-4964-a123-78d00a8787a6",
|
||||
ImmutableField: "foo",
|
||||
IntFieldBeta: 5,
|
||||
StringFieldBeta: "too-long",
|
||||
SliceFieldBeta: []string{"a", "b", "c"},
|
||||
UUIDFieldBeta: "not-a-uuid",
|
||||
ImmutableFieldBeta: "bar",
|
||||
}).OldValue(&Struct{
|
||||
ImmutableField: "foo",
|
||||
ImmutableFieldBeta: "foo",
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("intFieldBeta"), 5, "").WithOrigin("minimum").MarkBeta(),
|
||||
field.TooLong(field.NewPath("stringFieldBeta"), "too-long", 5).WithOrigin("maxLength").MarkBeta(),
|
||||
field.TooMany(field.NewPath("sliceFieldBeta"), 3, 2).WithOrigin("maxItems").MarkBeta(),
|
||||
field.Invalid(field.NewPath("uuidFieldBeta"), "not-a-uuid", "").WithOrigin("format=k8s-uuid").MarkBeta(),
|
||||
field.Invalid(field.NewPath("immutableFieldBeta"), "bar", "").WithOrigin("immutable").MarkBeta(),
|
||||
})
|
||||
}
|
||||
|
||||
func TestSpecialValidationStruct(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&SpecialValidationStruct{
|
||||
NEQField: 10,
|
||||
NEQFieldBeta: 10,
|
||||
}).ExpectValid()
|
||||
|
||||
st.Value(&SpecialValidationStruct{
|
||||
NEQField: 5,
|
||||
NEQFieldBeta: 5,
|
||||
ForbiddenField: ptr.To("val"),
|
||||
ForbiddenFieldBeta: ptr.To("val"),
|
||||
UpdateField: "new",
|
||||
UpdateFieldBeta: "new",
|
||||
}).OldValue(&SpecialValidationStruct{
|
||||
UpdateField: "old",
|
||||
UpdateFieldBeta: "old",
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("neqField"), 5, "must not be 5").WithOrigin("neq").MarkAlpha(),
|
||||
field.Invalid(field.NewPath("neqFieldBeta"), 5, "must not be 5").WithOrigin("neq").MarkBeta(),
|
||||
field.Forbidden(field.NewPath("forbiddenField"), "").MarkAlpha(),
|
||||
field.Forbidden(field.NewPath("forbiddenFieldBeta"), "").MarkBeta(),
|
||||
field.Invalid(field.NewPath("updateField"), "new", "field cannot be modified once set").WithOrigin("update").MarkAlpha(),
|
||||
field.Invalid(field.NewPath("updateFieldBeta"), "new", "field cannot be modified once set").WithOrigin("update").MarkBeta(),
|
||||
})
|
||||
|
||||
st.Value(&StructWithValidateFalse{
|
||||
ValidateFalse: ptr.To("val"),
|
||||
ValidateFalseBeta: ptr.To("val"),
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("validateFalse"), "val", "always fails").MarkAlpha(),
|
||||
field.Invalid(field.NewPath("validateFalseBeta"), "val", "always fails").MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
//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 simple
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
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 SpecialValidationStruct
|
||||
scheme.AddValidationFunc((*SpecialValidationStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_SpecialValidationStruct(ctx, op, nil /* fldPath */, obj.(*SpecialValidationStruct), safe.Cast[*SpecialValidationStruct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// 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()))}
|
||||
})
|
||||
// type StructWithValidateFalse
|
||||
scheme.AddValidationFunc((*StructWithValidateFalse)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_StructWithValidateFalse(ctx, op, nil /* fldPath */, obj.(*StructWithValidateFalse), safe.Cast[*StructWithValidateFalse](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_SpecialValidationStruct validates an instance of SpecialValidationStruct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_SpecialValidationStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *SpecialValidationStruct) (errs field.ErrorList) {
|
||||
// field SpecialValidationStruct.TypeMeta has no validation
|
||||
|
||||
// field SpecialValidationStruct.NEQField
|
||||
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.NEQ(ctx, op, fldPath, obj, oldObj, 5).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("neqField"), &obj.NEQField, safe.Field(oldObj, func(oldObj *SpecialValidationStruct) *int { return &oldObj.NEQField }), oldObj != nil)...)
|
||||
|
||||
// field SpecialValidationStruct.NEQFieldBeta
|
||||
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.NEQ(ctx, op, fldPath, obj, oldObj, 5).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("neqFieldBeta"), &obj.NEQFieldBeta, safe.Field(oldObj, func(oldObj *SpecialValidationStruct) *int { return &oldObj.NEQFieldBeta }), oldObj != nil)...)
|
||||
|
||||
// field SpecialValidationStruct.ForbiddenField
|
||||
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
|
||||
earlyReturn := false
|
||||
if e := validate.ForbiddenPointer(ctx, op, fldPath, obj, oldObj).MarkAlpha(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkAlpha(); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("forbiddenField"), obj.ForbiddenField, safe.Field(oldObj, func(oldObj *SpecialValidationStruct) *string { return oldObj.ForbiddenField }), oldObj != nil)...)
|
||||
|
||||
// field SpecialValidationStruct.ForbiddenFieldBeta
|
||||
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
|
||||
earlyReturn := false
|
||||
if e := validate.ForbiddenPointer(ctx, op, fldPath, obj, oldObj).MarkBeta(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkBeta(); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("forbiddenFieldBeta"), obj.ForbiddenFieldBeta, safe.Field(oldObj, func(oldObj *SpecialValidationStruct) *string { return oldObj.ForbiddenFieldBeta }), oldObj != nil)...)
|
||||
|
||||
// field SpecialValidationStruct.UpdateField
|
||||
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
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkAlpha(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("updateField"), &obj.UpdateField, safe.Field(oldObj, func(oldObj *SpecialValidationStruct) *string { return &oldObj.UpdateField }), oldObj != nil)...)
|
||||
|
||||
// field SpecialValidationStruct.UpdateFieldBeta
|
||||
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
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkBeta(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("updateFieldBeta"), &obj.UpdateFieldBeta, safe.Field(oldObj, func(oldObj *SpecialValidationStruct) *string { return &oldObj.UpdateFieldBeta }), oldObj != nil)...)
|
||||
|
||||
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.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *Struct) *int { return &oldObj.IntField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.IntFieldBeta
|
||||
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.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("intFieldBeta"), &obj.IntFieldBeta, safe.Field(oldObj, func(oldObj *Struct) *int { return &oldObj.IntFieldBeta }), oldObj != nil)...)
|
||||
|
||||
// field Struct.StringField
|
||||
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.MaxLength(ctx, op, fldPath, obj, oldObj, 5).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.StringField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.StringFieldBeta
|
||||
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.MaxLength(ctx, op, fldPath, obj, oldObj, 5).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("stringFieldBeta"), &obj.StringFieldBeta, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.StringFieldBeta }), oldObj != nil)...)
|
||||
|
||||
// field Struct.SliceField
|
||||
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 && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 2).MarkAlpha(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("sliceField"), obj.SliceField, safe.Field(oldObj, func(oldObj *Struct) []string { return oldObj.SliceField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.SliceFieldBeta
|
||||
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 && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 2).MarkBeta(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("sliceFieldBeta"), obj.SliceFieldBeta, safe.Field(oldObj, func(oldObj *Struct) []string { return oldObj.SliceFieldBeta }), oldObj != nil)...)
|
||||
|
||||
// field Struct.UUIDField
|
||||
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.UUID(ctx, op, fldPath, obj, oldObj).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("uuidField"), &obj.UUIDField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.UUIDField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.UUIDFieldBeta
|
||||
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.UUID(ctx, op, fldPath, obj, oldObj).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("uuidFieldBeta"), &obj.UUIDFieldBeta, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.UUIDFieldBeta }), oldObj != nil)...)
|
||||
|
||||
// field Struct.ImmutableField
|
||||
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
|
||||
earlyReturn := false
|
||||
if e := validate.Immutable(ctx, op, fldPath, obj, oldObj).MarkAlpha(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("immutableField"), &obj.ImmutableField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.ImmutableField }), oldObj != nil)...)
|
||||
|
||||
// field Struct.ImmutableFieldBeta
|
||||
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
|
||||
earlyReturn := false
|
||||
if e := validate.Immutable(ctx, op, fldPath, obj, oldObj).MarkBeta(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("immutableFieldBeta"), &obj.ImmutableFieldBeta, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.ImmutableFieldBeta }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_StructWithValidateFalse validates an instance of StructWithValidateFalse according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_StructWithValidateFalse(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StructWithValidateFalse) (errs field.ErrorList) {
|
||||
// field StructWithValidateFalse.TypeMeta has no validation
|
||||
|
||||
// field StructWithValidateFalse.ValidateFalse
|
||||
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.FixedResult(ctx, op, fldPath, obj, oldObj, false, "always fails").MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("validateFalse"), obj.ValidateFalse, safe.Field(oldObj, func(oldObj *StructWithValidateFalse) *string { return oldObj.ValidateFalse }), oldObj != nil)...)
|
||||
|
||||
// field StructWithValidateFalse.ValidateFalseBeta
|
||||
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.FixedResult(ctx, op, fldPath, obj, oldObj, false, "always fails").MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("validateFalseBeta"), obj.ValidateFalseBeta, safe.Field(oldObj, func(oldObj *StructWithValidateFalse) *string { return oldObj.ValidateFalseBeta }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package structs
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type MixedStruct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:minimum=5
|
||||
// +k8s:alpha=+k8s:minimum=10
|
||||
IntField int `json:"intField"`
|
||||
|
||||
// +k8s:minimum=5
|
||||
// +k8s:beta=+k8s:minimum=10
|
||||
IntFieldBeta int `json:"intFieldBeta"`
|
||||
|
||||
// +k8s:maxItems=5
|
||||
// +k8s:alpha=+k8s:maxItems=3
|
||||
ListField []string `json:"listField"`
|
||||
|
||||
// +k8s:maxItems=5
|
||||
// +k8s:beta=+k8s:maxItems=3
|
||||
ListFieldBeta []string `json:"listFieldBeta"`
|
||||
}
|
||||
|
||||
type ConditionalStruct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:alpha=+k8s:ifEnabled(MyFeature)=+k8s:minimum=10
|
||||
ConditionalField int `json:"conditionalField"`
|
||||
|
||||
// +k8s:beta=+k8s:ifEnabled(MyFeature)=+k8s:minimum=10
|
||||
ConditionalFieldBeta int `json:"conditionalFieldBeta"`
|
||||
|
||||
// +k8s:alpha=+k8s:alpha=+k8s:minimum=20
|
||||
RecursiveAlpha int `json:"recursiveAlpha"`
|
||||
|
||||
// +k8s:beta=+k8s:beta=+k8s:minimum=20
|
||||
RecursiveBeta int `json:"recursiveBeta"`
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
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 structs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestMixed(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
// Valid case (meets both normal and alpha requirements)
|
||||
st.Value(&MixedStruct{
|
||||
IntField: 15,
|
||||
IntFieldBeta: 15,
|
||||
ListField: []string{"a", "b", "c"},
|
||||
ListFieldBeta: []string{"a", "b", "c"},
|
||||
}).ExpectValid()
|
||||
|
||||
// Fails alpha validation but passes normal validation
|
||||
// IntField: 5 <= 8 < 10 (alpha fails)
|
||||
// ListField: 3 < 4 <= 5 (alpha fails)
|
||||
st.Value(&MixedStruct{
|
||||
IntField: 8,
|
||||
IntFieldBeta: 8,
|
||||
ListField: []string{"a", "b", "c", "d"},
|
||||
ListFieldBeta: []string{"a", "b", "c", "d"},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("intField"), 8, "").WithOrigin("minimum").MarkAlpha(),
|
||||
field.Invalid(field.NewPath("intFieldBeta"), 8, "").WithOrigin("minimum").MarkBeta(),
|
||||
field.TooMany(field.NewPath("listField"), 4, 3).WithOrigin("maxItems").MarkAlpha(),
|
||||
field.TooMany(field.NewPath("listFieldBeta"), 4, 3).WithOrigin("maxItems").MarkBeta(),
|
||||
})
|
||||
|
||||
// Fails both normal and alpha validation
|
||||
// IntField: 4 < 5 (normal fails) AND 4 < 10 (alpha fails)
|
||||
// ListField: 6 > 5 (normal fails) AND 6 > 3 (alpha fails)
|
||||
st.Value(&MixedStruct{
|
||||
IntField: 4,
|
||||
IntFieldBeta: 4,
|
||||
ListField: []string{"a", "b", "c", "d", "e", "f"},
|
||||
ListFieldBeta: []string{"a", "b", "c", "d", "e", "f"},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("intField"), 4, "").WithOrigin("minimum"),
|
||||
field.Invalid(field.NewPath("intField"), 4, "").WithOrigin("minimum").MarkAlpha(),
|
||||
field.Invalid(field.NewPath("intFieldBeta"), 4, "").WithOrigin("minimum"),
|
||||
field.Invalid(field.NewPath("intFieldBeta"), 4, "").WithOrigin("minimum").MarkBeta(),
|
||||
field.TooMany(field.NewPath("listField"), 6, 5).WithOrigin("maxItems"),
|
||||
field.TooMany(field.NewPath("listField"), 6, 3).WithOrigin("maxItems").MarkAlpha(),
|
||||
field.TooMany(field.NewPath("listFieldBeta"), 6, 5).WithOrigin("maxItems"),
|
||||
field.TooMany(field.NewPath("listFieldBeta"), 6, 3).WithOrigin("maxItems").MarkBeta(),
|
||||
})
|
||||
}
|
||||
|
||||
func TestConditionalStruct(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&ConditionalStruct{
|
||||
ConditionalField: 15,
|
||||
ConditionalFieldBeta: 15,
|
||||
RecursiveAlpha: 25,
|
||||
RecursiveBeta: 25,
|
||||
}).Opts([]string{"MyFeature"}).ExpectValid()
|
||||
|
||||
st.Value(&ConditionalStruct{
|
||||
ConditionalField: 5,
|
||||
ConditionalFieldBeta: 5,
|
||||
RecursiveAlpha: 10,
|
||||
RecursiveBeta: 10,
|
||||
}).Opts([]string{"MyFeature"}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("conditionalField"), 5, "").WithOrigin("minimum").MarkAlpha(),
|
||||
field.Invalid(field.NewPath("conditionalFieldBeta"), 5, "").WithOrigin("minimum").MarkBeta(),
|
||||
field.Invalid(field.NewPath("recursiveAlpha"), 10, "").WithOrigin("minimum").MarkAlpha(),
|
||||
field.Invalid(field.NewPath("recursiveBeta"), 10, "").WithOrigin("minimum").MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
//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 structs
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
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 ConditionalStruct
|
||||
scheme.AddValidationFunc((*ConditionalStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_ConditionalStruct(ctx, op, nil /* fldPath */, obj.(*ConditionalStruct), safe.Cast[*ConditionalStruct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// type MixedStruct
|
||||
scheme.AddValidationFunc((*MixedStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_MixedStruct(ctx, op, nil /* fldPath */, obj.(*MixedStruct), safe.Cast[*MixedStruct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_ConditionalStruct validates an instance of ConditionalStruct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ConditionalStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ConditionalStruct) (errs field.ErrorList) {
|
||||
// field ConditionalStruct.TypeMeta has no validation
|
||||
|
||||
// field ConditionalStruct.ConditionalField
|
||||
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.IfOption(ctx, op, fldPath, obj, oldObj, "MyFeature", true, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10)
|
||||
}).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("conditionalField"), &obj.ConditionalField, safe.Field(oldObj, func(oldObj *ConditionalStruct) *int { return &oldObj.ConditionalField }), oldObj != nil)...)
|
||||
|
||||
// field ConditionalStruct.ConditionalFieldBeta
|
||||
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.IfOption(ctx, op, fldPath, obj, oldObj, "MyFeature", true, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 10)
|
||||
}).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("conditionalFieldBeta"), &obj.ConditionalFieldBeta, safe.Field(oldObj, func(oldObj *ConditionalStruct) *int { return &oldObj.ConditionalFieldBeta }), oldObj != nil)...)
|
||||
|
||||
// field ConditionalStruct.RecursiveAlpha
|
||||
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.Minimum(ctx, op, fldPath, obj, oldObj, 20).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("recursiveAlpha"), &obj.RecursiveAlpha, safe.Field(oldObj, func(oldObj *ConditionalStruct) *int { return &oldObj.RecursiveAlpha }), oldObj != nil)...)
|
||||
|
||||
// field ConditionalStruct.RecursiveBeta
|
||||
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.Minimum(ctx, op, fldPath, obj, oldObj, 20).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("recursiveBeta"), &obj.RecursiveBeta, safe.Field(oldObj, func(oldObj *ConditionalStruct) *int { return &oldObj.RecursiveBeta }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_MixedStruct validates an instance of MixedStruct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_MixedStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MixedStruct) (errs field.ErrorList) {
|
||||
// field MixedStruct.TypeMeta has no validation
|
||||
|
||||
// field MixedStruct.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.Minimum(ctx, op, fldPath, obj, oldObj, 5)...)
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *MixedStruct) *int { return &oldObj.IntField }), oldObj != nil)...)
|
||||
|
||||
// field MixedStruct.IntFieldBeta
|
||||
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.Minimum(ctx, op, fldPath, obj, oldObj, 5)...)
|
||||
errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 10).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("intFieldBeta"), &obj.IntFieldBeta, safe.Field(oldObj, func(oldObj *MixedStruct) *int { return &oldObj.IntFieldBeta }), oldObj != nil)...)
|
||||
|
||||
// field MixedStruct.ListField
|
||||
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 && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 5); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 3).MarkAlpha(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("listField"), obj.ListField, safe.Field(oldObj, func(oldObj *MixedStruct) []string { return oldObj.ListField }), oldObj != nil)...)
|
||||
|
||||
// field MixedStruct.ListFieldBeta
|
||||
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 && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 5); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 3).MarkBeta(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("listFieldBeta"), obj.ListFieldBeta, safe.Field(oldObj, func(oldObj *MixedStruct) []string { return oldObj.ListFieldBeta }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package subfields
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type Struct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:alpha=+k8s:subfield(inner)=+k8s:minimum=5
|
||||
Subfield SubStruct `json:"subfield"`
|
||||
|
||||
// +k8s:beta=+k8s:subfield(inner)=+k8s:minimum=5
|
||||
SubfieldBeta SubStruct `json:"subfieldBeta"`
|
||||
}
|
||||
|
||||
type SubStruct struct {
|
||||
Inner int `json:"inner"`
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
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 subfields
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestAlpha(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
Subfield: SubStruct{Inner: 5},
|
||||
SubfieldBeta: SubStruct{Inner: 5},
|
||||
}).ExpectValid()
|
||||
|
||||
// Test failures marked as alpha
|
||||
st.Value(&Struct{
|
||||
Subfield: SubStruct{Inner: 1},
|
||||
SubfieldBeta: SubStruct{Inner: 5},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("subfield", "inner"), 1, "").WithOrigin("minimum").MarkAlpha(),
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeta(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
Subfield: SubStruct{Inner: 5},
|
||||
SubfieldBeta: SubStruct{Inner: 5},
|
||||
}).ExpectValid()
|
||||
|
||||
// Test failures marked as beta
|
||||
st.Value(&Struct{
|
||||
Subfield: SubStruct{Inner: 5},
|
||||
SubfieldBeta: SubStruct{Inner: 1},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("subfieldBeta", "inner"), 1, "").WithOrigin("minimum").MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
//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 subfields
|
||||
|
||||
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_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.Subfield
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *SubStruct, 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
|
||||
func() { // cohort inner
|
||||
errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "inner", func(o *SubStruct) *int { return &o.Inner }, validate.DirectEqualPtr, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 5)
|
||||
}).MarkAlpha()...)
|
||||
}()
|
||||
return
|
||||
}(fldPath.Child("subfield"), &obj.Subfield, safe.Field(oldObj, func(oldObj *Struct) *SubStruct { return &oldObj.Subfield }), oldObj != nil)...)
|
||||
|
||||
// field Struct.SubfieldBeta
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *SubStruct, 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
|
||||
func() { // cohort inner
|
||||
errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "inner", func(o *SubStruct) *int { return &o.Inner }, validate.DirectEqualPtr, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *int) field.ErrorList {
|
||||
return validate.Minimum(ctx, op, fldPath, obj, oldObj, 5)
|
||||
}).MarkBeta()...)
|
||||
}()
|
||||
return
|
||||
}(fldPath.Child("subfieldBeta"), &obj.SubfieldBeta, safe.Field(oldObj, func(oldObj *Struct) *SubStruct { return &oldObj.SubfieldBeta }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package unions
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type Struct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:alpha=+k8s:unionDiscriminator
|
||||
D D `json:"d"`
|
||||
|
||||
// +k8s:alpha=+k8s:unionMember
|
||||
// +k8s:optional
|
||||
M1 *M1 `json:"m1"`
|
||||
|
||||
// +k8s:alpha(since:"1.35")=+k8s:unionMember
|
||||
// +k8s:optional
|
||||
M2 *M2 `json:"m2"`
|
||||
}
|
||||
|
||||
type UnionStructBeta struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:beta=+k8s:unionDiscriminator
|
||||
DBeta BetaD `json:"dBeta"`
|
||||
|
||||
// +k8s:beta=+k8s:unionMember
|
||||
// +k8s:optional
|
||||
M1Beta *BetaM1 `json:"m1Beta"`
|
||||
|
||||
// +k8s:beta(since:"1.35")=+k8s:unionMember
|
||||
// +k8s:optional
|
||||
M2Beta *BetaM2 `json:"m2Beta"`
|
||||
}
|
||||
|
||||
type MyStruct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:alpha=+k8s:zeroOrOneOfMember
|
||||
// +k8s:optional
|
||||
Z1 *Z1 `json:"z1"`
|
||||
|
||||
// +k8s:alpha=+k8s:zeroOrOneOfMember
|
||||
// +k8s:optional
|
||||
Z2 *Z2 `json:"z2"`
|
||||
}
|
||||
|
||||
type MyStructBeta struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:beta=+k8s:zeroOrOneOfMember
|
||||
// +k8s:optional
|
||||
Z1Beta *BetaZ1 `json:"z1Beta"`
|
||||
|
||||
// +k8s:beta=+k8s:zeroOrOneOfMember
|
||||
// +k8s:optional
|
||||
Z2Beta *BetaZ2 `json:"z2Beta"`
|
||||
}
|
||||
|
||||
type D string
|
||||
|
||||
const (
|
||||
DM1 D = "M1"
|
||||
DM2 D = "M2"
|
||||
)
|
||||
|
||||
type M1 struct{}
|
||||
type M2 struct{}
|
||||
|
||||
type BetaD string
|
||||
|
||||
const (
|
||||
BetaDM1 BetaD = "M1Beta"
|
||||
BetaDM2 BetaD = "M2Beta"
|
||||
)
|
||||
|
||||
type BetaM1 struct{}
|
||||
type BetaM2 struct{}
|
||||
|
||||
type Z1 struct{}
|
||||
type Z2 struct{}
|
||||
|
||||
type BetaZ1 struct{}
|
||||
type BetaZ2 struct{}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 unions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestAlpha(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
D: DM1,
|
||||
M1: &M1{},
|
||||
}).ExpectValid()
|
||||
|
||||
st.Value(&Struct{
|
||||
D: DM1,
|
||||
M1: nil, // required by discriminator
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("m1"), nil, "").WithOrigin("union").MarkAlpha(),
|
||||
})
|
||||
|
||||
st.Value(&MyStruct{
|
||||
Z1: &Z1{},
|
||||
Z2: &Z2{},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(nil, &MyStruct{
|
||||
Z1: &Z1{}, Z2: &Z2{},
|
||||
}, "only one of z1, z2 may be specified").WithOrigin("zeroOrOneOf").MarkAlpha(),
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeta(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&UnionStructBeta{
|
||||
DBeta: BetaDM1,
|
||||
M1Beta: &BetaM1{},
|
||||
}).ExpectValid()
|
||||
|
||||
st.Value(&UnionStructBeta{
|
||||
DBeta: BetaDM1,
|
||||
M1Beta: nil, // required by discriminator
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("m1Beta"), nil, "").WithOrigin("union").MarkBeta(),
|
||||
})
|
||||
|
||||
st.Value(&MyStructBeta{
|
||||
Z1Beta: &BetaZ1{},
|
||||
Z2Beta: &BetaZ2{},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Invalid(nil, &MyStructBeta{
|
||||
Z1Beta: &BetaZ1{}, Z2Beta: &BetaZ2{},
|
||||
}, "only one of z1Beta, z2Beta may be specified").WithOrigin("zeroOrOneOf").MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
//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 unions
|
||||
|
||||
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 MyStruct
|
||||
scheme.AddValidationFunc((*MyStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_MyStruct(ctx, op, nil /* fldPath */, obj.(*MyStruct), safe.Cast[*MyStruct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// type MyStructBeta
|
||||
scheme.AddValidationFunc((*MyStructBeta)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_MyStructBeta(ctx, op, nil /* fldPath */, obj.(*MyStructBeta), safe.Cast[*MyStructBeta](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// 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()))}
|
||||
})
|
||||
// type UnionStructBeta
|
||||
scheme.AddValidationFunc((*UnionStructBeta)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_UnionStructBeta(ctx, op, nil /* fldPath */, obj.(*UnionStructBeta), safe.Cast[*UnionStructBeta](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
var zeroOrOneOfMembershipFor_k8s_io_code_generator_cmd_validation_gen_output_tests_tags_levels_unions_MyStruct_ = validate.NewUnionMembership(validate.NewUnionMember("z1"), validate.NewUnionMember("z2"))
|
||||
|
||||
// Validate_MyStruct validates an instance of MyStruct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_MyStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MyStruct) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.ZeroOrOneOfUnion(ctx, op, fldPath, obj, oldObj, zeroOrOneOfMembershipFor_k8s_io_code_generator_cmd_validation_gen_output_tests_tags_levels_unions_MyStruct_, func(obj *MyStruct) bool {
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
return obj.Z1 != nil
|
||||
}, func(obj *MyStruct) bool {
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
return obj.Z2 != nil
|
||||
}).MarkAlpha()...)
|
||||
|
||||
// field MyStruct.TypeMeta has no validation
|
||||
|
||||
// field MyStruct.Z1
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *Z1, 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
|
||||
earlyReturn := false
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("z1"), obj.Z1, safe.Field(oldObj, func(oldObj *MyStruct) *Z1 { return oldObj.Z1 }), oldObj != nil)...)
|
||||
|
||||
// field MyStruct.Z2
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *Z2, 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
|
||||
earlyReturn := false
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("z2"), obj.Z2, safe.Field(oldObj, func(oldObj *MyStruct) *Z2 { return oldObj.Z2 }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
var zeroOrOneOfMembershipFor_k8s_io_code_generator_cmd_validation_gen_output_tests_tags_levels_unions_MyStructBeta_ = validate.NewUnionMembership(validate.NewUnionMember("z1Beta"), validate.NewUnionMember("z2Beta"))
|
||||
|
||||
// Validate_MyStructBeta validates an instance of MyStructBeta according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_MyStructBeta(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MyStructBeta) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.ZeroOrOneOfUnion(ctx, op, fldPath, obj, oldObj, zeroOrOneOfMembershipFor_k8s_io_code_generator_cmd_validation_gen_output_tests_tags_levels_unions_MyStructBeta_, func(obj *MyStructBeta) bool {
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
return obj.Z1Beta != nil
|
||||
}, func(obj *MyStructBeta) bool {
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
return obj.Z2Beta != nil
|
||||
}).MarkBeta()...)
|
||||
|
||||
// field MyStructBeta.TypeMeta has no validation
|
||||
|
||||
// field MyStructBeta.Z1Beta
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *BetaZ1, 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
|
||||
earlyReturn := false
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("z1Beta"), obj.Z1Beta, safe.Field(oldObj, func(oldObj *MyStructBeta) *BetaZ1 { return oldObj.Z1Beta }), oldObj != nil)...)
|
||||
|
||||
// field MyStructBeta.Z2Beta
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *BetaZ2, 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
|
||||
earlyReturn := false
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("z2Beta"), obj.Z2Beta, safe.Field(oldObj, func(oldObj *MyStructBeta) *BetaZ2 { return oldObj.Z2Beta }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
var unionMembershipFor_k8s_io_code_generator_cmd_validation_gen_output_tests_tags_levels_unions_Struct_ = validate.NewDiscriminatedUnionMembership("d", validate.NewDiscriminatedUnionMember("m1", "M1"), validate.NewDiscriminatedUnionMember("m2", "M2"))
|
||||
|
||||
// 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) {
|
||||
errs = append(errs, validate.DiscriminatedUnion(ctx, op, fldPath, obj, oldObj, unionMembershipFor_k8s_io_code_generator_cmd_validation_gen_output_tests_tags_levels_unions_Struct_, func(obj *Struct) string {
|
||||
if obj == nil {
|
||||
return ""
|
||||
}
|
||||
return string(obj.D)
|
||||
}, func(obj *Struct) bool {
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
return obj.M1 != nil
|
||||
}, func(obj *Struct) bool {
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
return obj.M2 != nil
|
||||
}).MarkAlpha()...)
|
||||
|
||||
// field Struct.TypeMeta has no validation
|
||||
// field Struct.D has no validation
|
||||
|
||||
// field Struct.M1
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *M1, 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
|
||||
earlyReturn := false
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("m1"), obj.M1, safe.Field(oldObj, func(oldObj *Struct) *M1 { return oldObj.M1 }), oldObj != nil)...)
|
||||
|
||||
// field Struct.M2
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *M2, 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
|
||||
earlyReturn := false
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("m2"), obj.M2, safe.Field(oldObj, func(oldObj *Struct) *M2 { return oldObj.M2 }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
var unionMembershipFor_k8s_io_code_generator_cmd_validation_gen_output_tests_tags_levels_unions_UnionStructBeta_ = validate.NewDiscriminatedUnionMembership("dBeta", validate.NewDiscriminatedUnionMember("m1Beta", "M1Beta"), validate.NewDiscriminatedUnionMember("m2Beta", "M2Beta"))
|
||||
|
||||
// Validate_UnionStructBeta validates an instance of UnionStructBeta according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_UnionStructBeta(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *UnionStructBeta) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.DiscriminatedUnion(ctx, op, fldPath, obj, oldObj, unionMembershipFor_k8s_io_code_generator_cmd_validation_gen_output_tests_tags_levels_unions_UnionStructBeta_, func(obj *UnionStructBeta) string {
|
||||
if obj == nil {
|
||||
return ""
|
||||
}
|
||||
return string(obj.DBeta)
|
||||
}, func(obj *UnionStructBeta) bool {
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
return obj.M1Beta != nil
|
||||
}, func(obj *UnionStructBeta) bool {
|
||||
if obj == nil {
|
||||
return false
|
||||
}
|
||||
return obj.M2Beta != nil
|
||||
}).MarkBeta()...)
|
||||
|
||||
// field UnionStructBeta.TypeMeta has no validation
|
||||
// field UnionStructBeta.DBeta has no validation
|
||||
|
||||
// field UnionStructBeta.M1Beta
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *BetaM1, 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
|
||||
earlyReturn := false
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("m1Beta"), obj.M1Beta, safe.Field(oldObj, func(oldObj *UnionStructBeta) *BetaM1 { return oldObj.M1Beta }), oldObj != nil)...)
|
||||
|
||||
// field UnionStructBeta.M2Beta
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *BetaM2, 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
|
||||
earlyReturn := false
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("m2Beta"), obj.M2Beta, safe.Field(oldObj, func(oldObj *UnionStructBeta) *BetaM2 { return oldObj.M2Beta }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
// +k8s:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
package uniquetag
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type UniqueStruct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:listType=atomic
|
||||
// +k8s:alpha=+k8s:unique=set
|
||||
AlphaUniqueSet []string `json:"alphaUniqueSet"`
|
||||
|
||||
// +k8s:listType=atomic
|
||||
// +k8s:beta=+k8s:unique=set
|
||||
BetaUniqueSet []string `json:"betaUniqueSet"`
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
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 uniquetag
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func TestMisc(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&UniqueStruct{
|
||||
AlphaUniqueSet: []string{"a", "a"},
|
||||
BetaUniqueSet: []string{"a", "a"},
|
||||
}).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByValidationStabilityLevel(), field.ErrorList{
|
||||
field.Duplicate(field.NewPath("alphaUniqueSet").Index(1), "a").MarkAlpha(),
|
||||
field.Duplicate(field.NewPath("betaUniqueSet").Index(1), "a").MarkBeta(),
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
//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 uniquetag
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
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 UniqueStruct
|
||||
scheme.AddValidationFunc((*UniqueStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_UniqueStruct(ctx, op, nil /* fldPath */, obj.(*UniqueStruct), safe.Cast[*UniqueStruct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_UniqueStruct validates an instance of UniqueStruct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_UniqueStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *UniqueStruct) (errs field.ErrorList) {
|
||||
// field UniqueStruct.TypeMeta has no validation
|
||||
|
||||
// field UniqueStruct.AlphaUniqueSet
|
||||
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 && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with set semantics require unique values
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual).MarkAlpha()...)
|
||||
return
|
||||
}(fldPath.Child("alphaUniqueSet"), obj.AlphaUniqueSet, safe.Field(oldObj, func(oldObj *UniqueStruct) []string { return oldObj.AlphaUniqueSet }), oldObj != nil)...)
|
||||
|
||||
// field UniqueStruct.BetaUniqueSet
|
||||
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 && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
// lists with set semantics require unique values
|
||||
errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual).MarkBeta()...)
|
||||
return
|
||||
}(fldPath.Child("betaUniqueSet"), obj.BetaUniqueSet, safe.Field(oldObj, func(oldObj *UniqueStruct) []string { return oldObj.BetaUniqueSet }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
Reference in New Issue
Block a user