mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-18 22:40:35 +00:00
Support embedded fields properly
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
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
|
||||
// +k8s:validation-gen-test-fixture=validateFalse
|
||||
|
||||
// This is a test package.
|
||||
package embedded
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type T1 struct {
|
||||
TypeMeta int
|
||||
|
||||
// NOTE: It's weird to have IntField in both, but Go allows it.
|
||||
T2 `json:",inline"`
|
||||
*T3 `json:",inline"`
|
||||
}
|
||||
|
||||
type T2 struct {
|
||||
// +k8s:validateFalse="T2.IntField"
|
||||
IntField int `json:"intField"`
|
||||
}
|
||||
|
||||
type T3 struct {
|
||||
// +k8s:validateFalse="T3.StringField"
|
||||
StringField string `json:"stringField"`
|
||||
|
||||
// +k8s:validateFalse="T3.IntField"
|
||||
IntField int `json:"intField"`
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"*embedded.T1": {
|
||||
"intField": [
|
||||
"T2.IntField",
|
||||
"T3.IntField"
|
||||
],
|
||||
"stringField": [
|
||||
"T3.StringField"
|
||||
]
|
||||
}
|
||||
}
|
||||
101
staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/embedded/zz_generated.validations.go
generated
Normal file
101
staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/embedded/zz_generated.validations.go
generated
Normal file
@@ -0,0 +1,101 @@
|
||||
//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 embedded
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
operation "k8s.io/apimachinery/pkg/api/operation"
|
||||
safe "k8s.io/apimachinery/pkg/api/safe"
|
||||
validate "k8s.io/apimachinery/pkg/api/validate"
|
||||
field "k8s.io/apimachinery/pkg/util/validation/field"
|
||||
testscheme "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
)
|
||||
|
||||
func init() { localSchemeBuilder.Register(RegisterValidations) }
|
||||
|
||||
// RegisterValidations adds validation functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterValidations(scheme *testscheme.Scheme) error {
|
||||
scheme.AddValidationFunc((*T1)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
return Validate_T1(ctx, op, nil /* fldPath */, obj.(*T1), safe.Cast[*T1](oldObj))
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) {
|
||||
// field T1.TypeMeta has no validation
|
||||
|
||||
// field T1.T2
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) {
|
||||
errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath, &obj.T2, safe.Field(oldObj, func(oldObj *T1) *T2 { return &oldObj.T2 }))...)
|
||||
|
||||
// field T1.T3
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) {
|
||||
errs = append(errs, Validate_T3(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath, obj.T3, safe.Field(oldObj, func(oldObj *T1) *T3 { return oldObj.T3 }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) {
|
||||
// field T2.IntField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) {
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil // no changes
|
||||
}
|
||||
errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T2.IntField")...)
|
||||
return
|
||||
}(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *T2) *int { return &oldObj.IntField }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func Validate_T3(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) {
|
||||
// field T3.StringField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil // no changes
|
||||
}
|
||||
errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T3.StringField")...)
|
||||
return
|
||||
}(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *T3) *string { return &oldObj.StringField }))...)
|
||||
|
||||
// field T3.IntField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) {
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil // no changes
|
||||
}
|
||||
errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T3.IntField")...)
|
||||
return
|
||||
}(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *T3) *int { return &oldObj.IntField }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//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 embedded
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidation(t *testing.T) {
|
||||
localSchemeBuilder.Test(t).ValidateFixtures()
|
||||
}
|
||||
@@ -536,13 +536,7 @@ func (td *typeDiscoverer) discoverStruct(thisNode *typeNode, fldPath *field.Path
|
||||
// Discover into each field of this struct.
|
||||
for _, memb := range thisNode.valueType.Members {
|
||||
name := memb.Name
|
||||
if len(name) == 0 { // embedded fields
|
||||
if memb.Type.Kind == types.Pointer {
|
||||
name = memb.Type.Elem.Name.Name
|
||||
} else {
|
||||
name = memb.Type.Name.Name
|
||||
}
|
||||
}
|
||||
|
||||
// Only do exported fields.
|
||||
if unicode.IsLower([]rune(name)[0]) {
|
||||
continue
|
||||
@@ -1041,10 +1035,6 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild
|
||||
}
|
||||
|
||||
if buf.Len() > 0 {
|
||||
if len(fld.jsonName) == 0 {
|
||||
continue // TODO: Embedded (inline) types are expected to be unnamed.
|
||||
}
|
||||
|
||||
leafType, typePfx, exprPfx := getLeafTypeAndPrefixes(fld.childType)
|
||||
targs := targs.WithArgs(generator.Args{
|
||||
"fieldName": fld.name,
|
||||
@@ -1064,7 +1054,12 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild
|
||||
panic(fmt.Sprintf("failed to merge buffer: %v", err))
|
||||
}
|
||||
sw.Do(" return\n", targs)
|
||||
sw.Do(" }(fldPath.Child(\"$.fieldJSON$\"), ", targs)
|
||||
sw.Do(" }(", targs)
|
||||
if len(fld.jsonName) == 0 { // embedded (inline) field
|
||||
sw.Do("fldPath, ", targs)
|
||||
} else {
|
||||
sw.Do("fldPath.Child(\"$.fieldJSON$\"), ", targs)
|
||||
}
|
||||
sw.Do(" $.fieldExprPfx$obj.$.fieldName$, ", targs)
|
||||
sw.Do(" $.safe.Field|raw$(oldObj, ", targs)
|
||||
sw.Do(" func(oldObj *$.inType|raw$) $.fieldTypePfx$$.fieldType|raw$ {", targs)
|
||||
|
||||
Reference in New Issue
Block a user