From e9eabb2007dd294edc2c4b361e6d9ee5da1a8439 Mon Sep 17 00:00:00 2001 From: yongruilin Date: Wed, 11 Jun 2025 20:03:35 +0000 Subject: [PATCH 1/5] feat(validation-gen): Refactor type handling and introduce utility functions This commit introduces a new utility package for the validation generator, encapsulating functions for type handling, including `GetMemberByJSON`, `IsNilableType`, `NativeType`, and `NonPointer`. The existing code has been refactored to utilize these new utility functions, improving code clarity and maintainability. Additionally, the previous unaliasing logic has been removed in favor of the new utility methods, streamlining type validation processes across the codebase. Co-authored-by: Tim Hockin --- .../cmd/validation-gen/util/util.go | 116 +++++ .../cmd/validation-gen/util/util_test.go | 454 ++++++++++++++++++ .../cmd/validation-gen/validation.go | 41 +- .../cmd/validation-gen/validators/common.go | 70 --- .../cmd/validation-gen/validators/each.go | 18 +- .../validation-gen/validators/immutable.go | 4 +- .../cmd/validation-gen/validators/limits.go | 4 +- .../cmd/validation-gen/validators/required.go | 12 +- .../cmd/validation-gen/validators/subfield.go | 10 +- 9 files changed, 611 insertions(+), 118 deletions(-) create mode 100644 staging/src/k8s.io/code-generator/cmd/validation-gen/util/util.go create mode 100644 staging/src/k8s.io/code-generator/cmd/validation-gen/util/util_test.go diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util.go new file mode 100644 index 00000000000..25165586610 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util.go @@ -0,0 +1,116 @@ +/* +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 util + +import ( + "k8s.io/gengo/v2/parser/tags" + "k8s.io/gengo/v2/types" +) + +// GetMemberByJSON returns the child member of the type that has the given JSON +// name. It returns nil if no such member exists. +func GetMemberByJSON(t *types.Type, jsonName string) *types.Member { + for i := range t.Members { + if jsonTag, ok := tags.LookupJSON(t.Members[i]); ok { + if jsonTag.Name == jsonName { + return &t.Members[i] + } + } + } + return nil +} + +// IsNilableType returns true if the argument type can be compared to nil. +func IsNilableType(t *types.Type) bool { + t = NativeType(t) + + switch t.Kind { + case types.Pointer, types.Map, types.Slice, types.Interface: // Note: Arrays are not nilable + return true + } + return false +} + +// NativeType returns the Go native type of the argument type, with any +// intermediate typedefs removed. Go itself already flattens typedefs, but this +// handles it in the unlikely event that we ever fix that. +// +// Examples: +// * Trivial: +// - given `int`, returns `int` +// - given `*int`, returns `*int` +// - given `[]int`, returns `[]int` +// +// * Typedefs +// - given `type X int; X`, returns `int` +// - given `type X int; []X`, returns `[]X` +// +// * Typedefs and pointers: +// - given `type X int; *X`, returns `*int` +// - given `type X *int; *X`, returns `**int` +// - given `type X []int; X`, returns `[]int` +// - given `type X []int; *X`, returns `*[]int` +func NativeType(t *types.Type) *types.Type { + ptrs := 0 + for { + switch t.Kind { + case types.Alias: + t = t.Underlying + case types.Pointer: + ptrs++ + t = t.Elem + default: + goto done + } + } +done: + for range ptrs { + t = types.PointerTo(t) + } + return t +} + +// NonPointer returns the value-type of a possibly pointer type. If type is not +// a pointer, it returns the input type. +func NonPointer(t *types.Type) *types.Type { + for t.Kind == types.Pointer { + t = t.Elem + } + return t +} + +// IsDirectComparable returns true if the type is safe to compare using "==". +// It is similar to gengo.IsComparable, but it doesn't consider Pointers to be +// comparable (we don't want shallow compare). +func IsDirectComparable(t *types.Type) bool { + switch t.Kind { + case types.Builtin: + return true + case types.Struct: + for _, f := range t.Members { + if !IsDirectComparable(f.Type) { + return false + } + } + return true + case types.Array: + return IsDirectComparable(t.Elem) + case types.Alias: + return IsDirectComparable(t.Underlying) + } + return false +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util_test.go new file mode 100644 index 00000000000..5ff3ae6cb8c --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util_test.go @@ -0,0 +1,454 @@ +/* +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 util + +import ( + "reflect" + "testing" + + "k8s.io/gengo/v2/types" +) + +func TestGetMemberByJSON(t *testing.T) { + tests := []struct { + name string + t *types.Type + jsonTag string + want *types.Member + wantBool bool + }{{ + name: "exact match", + t: &types.Type{ + Members: []types.Member{ + {Name: "Field0", Tags: `json:"field0"`}, + {Name: "Field1", Tags: `json:"field1"`}, + {Name: "Field2", Tags: `json:"field2"`}, + }, + }, + jsonTag: "field1", + want: &types.Member{Name: "Field1", Tags: `json:"field1"`}, + wantBool: true, + }, { + name: "no match", + t: &types.Type{ + Members: []types.Member{ + {Name: "Field0", Tags: `json:"field0"`}, + {Name: "Field1", Tags: `json:"field1"`}, + }, + }, + jsonTag: "field2", + want: nil, + wantBool: false, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := GetMemberByJSON(tt.t, tt.jsonTag) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetMemberByJSON() got %v, want %v", got, tt.want) + } + }) + } +} + +func TestIsNilableType(t *testing.T) { + tStruct := &types.Type{ + Name: types.Name{Name: "MyStruct"}, + Kind: types.Struct, + } + + tests := []struct { + name string + t *types.Type + want bool + }{{ + name: "pointer", + t: &types.Type{ + Kind: types.Pointer, + Elem: tStruct, + }, + want: true, + }, { + name: "alias to pointer", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Pointer, + Elem: tStruct, + }, + }, + want: true, + }, { + name: "map", + t: &types.Type{ + Kind: types.Map, + }, + want: true, + }, { + name: "alias to map", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Map, + }, + }, + want: true, + }, { + name: "slice", + t: &types.Type{ + Kind: types.Slice, + }, + want: true, + }, { + name: "alias to slice", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Slice, + }, + }, + want: true, + }, { + name: "interface", + t: &types.Type{ + Kind: types.Interface, + }, + want: true, + }, { + name: "alias to interface", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Interface, + }, + }, + want: true, + }, { + name: "struct", + t: &types.Type{ + Kind: types.Struct, + }, + want: false, + }, { + name: "alias to struct", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Struct, + }, + }, + want: false, + }, { + name: "builtin", + t: &types.Type{ + Kind: types.Builtin, + }, + want: false, + }, { + name: "alias to builtin", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Builtin, + }, + }, + want: false, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsNilableType(tt.t); got != tt.want { + t.Errorf("IsNilableType() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNativeType(t *testing.T) { + tStruct := &types.Type{ + Name: types.Name{Name: "MyStruct"}, + Kind: types.Struct, + } + pStruct := &types.Type{ + Name: types.Name{Name: "*MyStruct"}, + Kind: types.Pointer, + Elem: tStruct, + } + + tests := []struct { + name string + t *types.Type + want *types.Type + }{{ + name: "struct", + t: tStruct, + want: tStruct, + }, { + name: "pointer to struct", + t: pStruct, + want: pStruct, + }, { + name: "alias to struct", + t: &types.Type{ + Name: types.Name{Name: "Alias"}, + Kind: types.Alias, + Underlying: tStruct, + }, + want: tStruct, + }, { + name: "pointer to alias to struct", + t: &types.Type{ + Name: types.Name{Name: "*Alias"}, + Kind: types.Pointer, + Elem: &types.Type{ + Kind: types.Alias, + Underlying: tStruct, + }, + }, + want: pStruct, + }, { + name: "alias of pointer to struct", + t: &types.Type{ + Name: types.Name{Name: "AliasP"}, + Kind: types.Alias, + Underlying: pStruct, + }, + want: pStruct, + }, { + name: "pointer to alias of pointer to struct", + t: &types.Type{ + Name: types.Name{Name: "*AliasP"}, + Kind: types.Pointer, + Elem: &types.Type{ + Name: types.Name{Name: "AliasP"}, + Kind: types.Alias, + Underlying: pStruct, + }, + }, + want: &types.Type{ + Name: types.Name{Name: "**MyStruct"}, + Kind: types.Pointer, + Elem: pStruct, + }, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if want, got := tt.want.String(), NativeType(tt.t).String(); want != got { + t.Errorf("NativeType() = %q, want %q", got, tt.want) + } + }) + } +} + +func TestNonPointer(t *testing.T) { + tStruct := &types.Type{ + Name: types.Name{Name: "MyStruct"}, + Kind: types.Struct, + } + + tests := []struct { + name string + t *types.Type + want *types.Type + }{{ + name: "value", + t: tStruct, + want: tStruct, + }, { + name: "pointer", + t: &types.Type{ + Name: types.Name{Name: "*MyStruct"}, + Kind: types.Pointer, + Elem: tStruct, + }, + want: tStruct, + }, { + name: "pointer pointer", + t: &types.Type{ + Name: types.Name{Name: "**MyStruct"}, + Kind: types.Pointer, + Elem: &types.Type{ + Kind: types.Pointer, + Elem: tStruct, + }, + }, + want: tStruct, + }, { + name: "pointer alias pointer", + t: &types.Type{ + Name: types.Name{Name: "*AliasP"}, + Kind: types.Pointer, + Elem: &types.Type{ + Name: types.Name{Name: "AliasP"}, + Kind: types.Alias, + Underlying: &types.Type{ + Name: types.Name{Name: "*MyStruct"}, + Kind: types.Pointer, + Elem: tStruct, + }, + }, + }, + want: &types.Type{ + Name: types.Name{Name: "AliasP"}, + Kind: types.Alias, + Underlying: &types.Type{ + Name: types.Name{Name: "*MyStruct"}, + Kind: types.Pointer, + Elem: tStruct, + }, + }, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NonPointer(tt.t); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NonPointer() = %v, want %v", got, tt.want) + } + }) + } +} + +// gengo has `PointerTo()` but not the rest, so keep this here for consistency. +func ptrTo(t *types.Type) *types.Type { + return &types.Type{ + Name: types.Name{ + Package: "", + Name: "*" + t.Name.String(), + }, + Kind: types.Pointer, + Elem: t, + } +} + +func sliceOf(t *types.Type) *types.Type { + return &types.Type{ + Name: types.Name{ + Package: "", + Name: "[]" + t.Name.String(), + }, + Kind: types.Slice, + Elem: t, + } +} + +func mapOf(t *types.Type) *types.Type { + return &types.Type{ + Name: types.Name{ + Package: "", + Name: "map[string]" + t.Name.String(), + }, + Kind: types.Map, + Key: types.String, + Elem: t, + } +} + +func arrayOf(t *types.Type) *types.Type { + return &types.Type{ + Name: types.Name{ + Package: "", + Name: "[2]" + t.Name.String(), + }, + Kind: types.Array, + Len: 2, + Elem: t, + } +} + +func aliasOf(name string, t *types.Type) *types.Type { + return &types.Type{ + Name: types.Name{ + Package: "", + Name: "Alias_" + name, + }, + Kind: types.Alias, + Underlying: t, + } +} + +func TestIsDirectComparable(t *testing.T) { + cases := []struct { + in *types.Type + expect bool + }{ + { + in: types.String, + expect: true, + }, { + in: ptrTo(types.String), + expect: false, + }, { + in: sliceOf(types.String), + expect: false, + }, { + in: mapOf(types.String), + expect: false, + }, { + in: aliasOf("s", types.String), + expect: true, + }, { + in: &types.Type{ + Name: types.Name{ + Package: "", + Name: "struct_comparable_member", + }, + Kind: types.Struct, + Members: []types.Member{ + { + Name: "s", + Type: types.String, + }, + }, + }, + expect: true, + }, { + in: &types.Type{ + Name: types.Name{ + Package: "", + Name: "struct_uncomparable_member", + }, + Kind: types.Struct, + Members: []types.Member{ + { + Name: "s", + Type: ptrTo(types.String), + }, + }, + }, + expect: false, + }, { + in: arrayOf(types.String), + expect: true, + }, { + in: arrayOf(aliasOf("s", types.String)), + expect: true, + }, { + in: arrayOf(ptrTo(types.String)), + expect: false, + }, { + in: arrayOf(mapOf(types.String)), + expect: false, + }, + } + + for _, tc := range cases { + if got, want := IsDirectComparable(tc.in), tc.expect; got != want { + t.Errorf("%q: expected %v, got %v", tc.in, want, got) + } + } +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go index dc8f966fd5a..b7f86545c38 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/code-generator/cmd/validation-gen/util" "k8s.io/code-generator/cmd/validation-gen/validators" "k8s.io/gengo/v2/generator" "k8s.io/gengo/v2/namer" @@ -219,14 +220,6 @@ func (td *typeDiscoverer) DiscoverType(t *types.Type) error { return nil } -// unalias returns the unaliased type. -func unalias(t *types.Type) *types.Type { - for t.Kind == types.Alias { - t = t.Underlying - } - return t -} - // discoverType walks the given type recursively and returns a typeNode // representing it. This does not distinguish between discovering a type // definition and discovering a field of a struct. The first time it @@ -255,7 +248,7 @@ func (td *typeDiscoverer) discoverType(t *types.Type, fldPath *field.Path) (*typ return nil, fmt.Errorf("field %s (%s): typedefs to pointers are not supported", fldPath.String(), t) } case types.Pointer: - pointee := unalias(t.Elem) + pointee := util.NativeType(t.Elem) switch pointee.Kind { case types.Pointer: return nil, fmt.Errorf("field %s (%s): pointers to pointers are not supported", fldPath.String(), t) @@ -267,23 +260,23 @@ func (td *typeDiscoverer) discoverType(t *types.Type, fldPath *field.Path) (*typ case types.Array: return nil, fmt.Errorf("field %s (%s): fixed-size arrays are not supported", fldPath.String(), t) case types.Slice: - elem := unalias(t.Elem) + elem := util.NativeType(t.Elem) switch elem.Kind { case types.Pointer: return nil, fmt.Errorf("field %s (%s): lists of pointers are not supported", fldPath.String(), t) case types.Slice: - if unalias(elem.Elem) != types.Byte { + if util.NativeType(elem.Elem) != types.Byte { return nil, fmt.Errorf("field %s (%s): lists of lists are not supported", fldPath.String(), t) } case types.Map: return nil, fmt.Errorf("field %s (%s): lists of maps are not supported", fldPath.String(), t) } case types.Map: - key := unalias(t.Key) + key := util.NativeType(t.Key) if key != types.String { return nil, fmt.Errorf("field %s (%s): maps with non-string keys are not supported", fldPath.String(), t) } - elem := unalias(t.Elem) + elem := util.NativeType(t.Elem) switch elem.Kind { case types.Pointer: return nil, fmt.Errorf("field %s (%s): maps of pointers are not supported", fldPath.String(), t) @@ -416,7 +409,7 @@ func (td *typeDiscoverer) discoverType(t *types.Type, fldPath *field.Path) (*typ // system, while preventing validation on them since that functionality is not yet // implemented. Once validation support for map-of-slices is added, this check should // be removed. - if unalias(t).Kind == types.Map && unalias(t).Elem.Kind == types.Slice { + if util.NativeType(t).Kind == types.Map && util.NativeType(t).Elem.Kind == types.Slice { return nil, fmt.Errorf("field %s: validation for map of slices is not supported", fldPath) } klog.V(5).InfoS("found type-attached validations", "n", validations.Len(), "type", t) @@ -595,7 +588,7 @@ func (td *typeDiscoverer) discoverStruct(thisNode *typeNode, fldPath *field.Path // system, while preventing validation on them since that functionality is not yet // implemented. Once validation support for map-of-slices is added, this check should // be removed. - if unalias(childType).Kind == types.Map && unalias(childType).Elem.Kind == types.Slice { + if util.NativeType(childType).Kind == types.Map && util.NativeType(childType).Elem.Kind == types.Slice { return fmt.Errorf("field %s: validation for map of slices is not supported", childPath) } child.fieldValidations.Add(validations) @@ -838,7 +831,7 @@ func (g *genValidations) emitRegisterFunction(c *generator.Context, schemeRegist "safe": mkSymbolArgs(c, safePkgSymbols), "context": mkSymbolArgs(c, contextPkgSymbols), } - if !isNilableType(rootType) { + if !util.IsNilableType(rootType) { targs["typePfx"] = "*" } @@ -905,7 +898,7 @@ func (g *genValidations) emitValidationFunction(c *generator.Context, t *types.T "context": mkSymbolArgs(c, contextPkgSymbols), "objTypePfx": "*", } - if isNilableType(t) { + if util.IsNilableType(t) { targs["objTypePfx"] = "" } @@ -1334,7 +1327,7 @@ func toGolangSourceDataLiteral(sw *generator.SnippetWriter, c *generator.Context "objType": v.ObjType, "objTypePfx": "*", } - if isNilableType(v.ObjType) { + if util.IsNilableType(v.ObjType) { targs["objTypePfx"] = "" } @@ -1431,16 +1424,6 @@ func toGolangSourceDataLiteral(sw *generator.SnippetWriter, c *generator.Context } } -// isNilableType returns true if the argument type can be compared to nil. -func isNilableType(t *types.Type) bool { - t = unalias(t) - switch t.Kind { - case types.Pointer, types.Map, types.Slice, types.Interface: // Note: Arrays are not nilable - return true - } - return false -} - // getLeafTypeAndPrefixes returns the "leaf value type" for a given type, as // well as type and expression prefix strings for the input type. The type // prefix can be prepended to the given type's name to produce the nilable form @@ -1466,7 +1449,7 @@ func getLeafTypeAndPrefixes(inType *types.Type) (*types.Type, string, string) { nPtrs++ leafType = leafType.Elem } - if !isNilableType(leafType) { + if !util.IsNilableType(leafType) { typePfx = "*" if nPtrs == 0 { exprPfx = "&" diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/common.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/common.go index 145262edc61..93cea458e84 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/common.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/common.go @@ -17,7 +17,6 @@ limitations under the License. package validators import ( - "k8s.io/gengo/v2/parser/tags" "k8s.io/gengo/v2/types" ) @@ -27,75 +26,6 @@ const ( libValidationPkg = "k8s.io/apimachinery/pkg/api/validate" ) -func getMemberByJSON(t *types.Type, jsonName string) *types.Member { - for i := range t.Members { - if jsonTag, ok := tags.LookupJSON(t.Members[i]); ok { - if jsonTag.Name == jsonName { - return &t.Members[i] - } - } - } - return nil -} - -// isNilableType returns true if the argument type can be compared to nil. -func isNilableType(t *types.Type) bool { - for t.Kind == types.Alias { - t = t.Underlying - } - switch t.Kind { - case types.Pointer, types.Map, types.Slice, types.Interface: // Note: Arrays are not nilable - return true - } - return false -} - -// nativeType returns the Go native type of the argument type, with any -// intermediate typedefs removed. Go itself already flattens typedefs, but this -// handles it in the unlikely event that we ever fix that. -// -// Examples: -// * Trivial: -// - given `int`, returns `int` -// - given `*int`, returns `*int` -// - given `[]int`, returns `[]int` -// -// * Typedefs -// - given `type X int; X`, returns `int` -// - given `type X int; []X`, returns `[]X` -// -// * Typedefs and pointers: -// - given `type X int; *X`, returns `*int` -// - given `type X *int; *X`, returns `**int` -// - given `type X []int; X`, returns `[]int` -// - given `type X []int; *X`, returns `*[]int` -func nativeType(t *types.Type) *types.Type { - ptrs := 0 - for { - if t.Kind == types.Alias { - t = t.Underlying - } else if t.Kind == types.Pointer { - ptrs++ - t = t.Elem - } else { - break - } - } - for range ptrs { - t = types.PointerTo(t) - } - return t -} - -// nonPointer returns the value-type of a possibly pointer type. If type is not -// a pointer, it returns the input type. -func nonPointer(t *types.Type) *types.Type { - for t.Kind == types.Pointer { - t = t.Elem - } - return t -} - // rootTypeString returns a string representation of the relationship between // src and dst types, for use in error messages. func rootTypeString(src, dst *types.Type) string { diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go index c3e20de6fbb..d17e574ddc0 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go @@ -24,6 +24,8 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/gengo/v2/codetags" "k8s.io/gengo/v2/types" + + "k8s.io/code-generator/cmd/validation-gen/util" ) const ( @@ -78,7 +80,7 @@ func (listTypeTagValidator) ValidScopes() sets.Set[Scope] { func (lttv listTypeTagValidator) GetValidations(context Context, tag codetags.Tag) (Validations, error) { // NOTE: pointers to lists are not supported, so we should never see a pointer here. - t := nativeType(context.Type) + t := util.NativeType(context.Type) if t.Kind != types.Slice && t.Kind != types.Array { return Validations{}, fmt.Errorf("can only be used on list types") } @@ -88,7 +90,7 @@ func (lttv listTypeTagValidator) GetValidations(context Context, tag codetags.Ta // Allowed but no special handling. case "map": // NOTE: maps of pointers are not supported, so we should never see a pointer here. - if nativeType(t.Elem).Kind != types.Struct { + if util.NativeType(t.Elem).Kind != types.Struct { return Validations{}, fmt.Errorf("only lists of structs can be list-maps") } @@ -138,19 +140,19 @@ func (listMapKeyTagValidator) ValidScopes() sets.Set[Scope] { func (lmktv listMapKeyTagValidator) GetValidations(context Context, tag codetags.Tag) (Validations, error) { // NOTE: pointers to lists are not supported, so we should never see a pointer here. - t := nativeType(context.Type) + t := util.NativeType(context.Type) if t.Kind != types.Slice && t.Kind != types.Array { return Validations{}, fmt.Errorf("can only be used on list types") } // NOTE: lists of pointers are not supported, so we should never see a pointer here. - if nativeType(t.Elem).Kind != types.Struct { + if util.NativeType(t.Elem).Kind != types.Struct { return Validations{}, fmt.Errorf("only lists of structs can be list-maps") } var fieldName string - if memb := getMemberByJSON(nativeType(t.Elem), tag.Value); memb == nil { + if memb := util.GetMemberByJSON(util.NativeType(t.Elem), tag.Value); memb == nil { return Validations{}, fmt.Errorf("no field for JSON name %q", tag.Value) - } else if k := nativeType(memb.Type).Kind; k != types.Builtin { + } else if k := util.NativeType(memb.Type).Kind; k != types.Builtin { return Validations{}, fmt.Errorf("only primitive types can be list-map keys (%s)", k) } else { fieldName = memb.Name @@ -210,7 +212,7 @@ var ( func (evtv eachValTagValidator) GetValidations(context Context, tag codetags.Tag) (Validations, error) { // NOTE: pointers to lists and maps are not supported, so we should never see a pointer here. - t := nativeType(context.Type) + t := util.NativeType(context.Type) switch t.Kind { case types.Slice, types.Array, types.Map: default: @@ -350,7 +352,7 @@ var ( func (ektv eachKeyTagValidator) GetValidations(context Context, tag codetags.Tag) (Validations, error) { // NOTE: pointers to lists are not supported, so we should never see a pointer here. - t := nativeType(context.Type) + t := util.NativeType(context.Type) if t.Kind != types.Map { return Validations{}, fmt.Errorf("can only be used on map types") } diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/immutable.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/immutable.go index 519484e67e2..12be03c7924 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/immutable.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/immutable.go @@ -20,6 +20,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/gengo/v2/codetags" "k8s.io/gengo/v2/types" + + "k8s.io/code-generator/cmd/validation-gen/util" ) const ( @@ -52,7 +54,7 @@ var ( func (immutableTagValidator) GetValidations(context Context, _ codetags.Tag) (Validations, error) { var result Validations - if nonPointer(nativeType(context.Type)).Kind == types.Builtin { + if util.NonPointer(util.NativeType(context.Type)).Kind == types.Builtin { // This is a minor optimization to just compare primitive values when // possible. Slices and maps are not comparable, and structs might hold // pointer fields, which are directly comparable but not what we need. diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go index 47f52aa2473..57a79920070 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go @@ -23,6 +23,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/gengo/v2/codetags" "k8s.io/gengo/v2/types" + + "k8s.io/code-generator/cmd/validation-gen/util" ) const ( @@ -58,7 +60,7 @@ func (minimumTagValidator) GetValidations(context Context, tag codetags.Tag) (Va // This tag can apply to value and pointer fields, as well as typedefs // (which should never be pointers). We need to check the concrete type. - if t := nonPointer(nativeType(context.Type)); !types.IsInteger(t) { + if t := util.NonPointer(util.NativeType(context.Type)); !types.IsInteger(t) { return result, fmt.Errorf("can only be used on integer types (%s)", rootTypeString(context.Type, t)) } diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go index 11e05eef047..b52acdd2e17 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go @@ -25,6 +25,8 @@ import ( "k8s.io/gengo/v2" "k8s.io/gengo/v2/codetags" "k8s.io/gengo/v2/types" + + "k8s.io/code-generator/cmd/validation-gen/util" ) const ( @@ -91,7 +93,7 @@ func (rtv requirednessTagValidator) doRequired(context Context) (Validations, er // originally defined as a value-type or a pointer-type in the API. This // one does. Since Go doesn't do partial specialization of templates, we // do manual dispatch here. - switch nativeType(context.Type).Kind { + switch util.NativeType(context.Type).Kind { case types.Slice: return Validations{Functions: []FunctionGen{Function(requiredTagName, ShortCircuit, requiredSliceValidator)}}, nil case types.Map: @@ -145,7 +147,7 @@ func (rtv requirednessTagValidator) doOptional(context Context) (Validations, er if hasDefault, zeroDefault, err := rtv.hasZeroDefault(context); err != nil { return Validations{}, err } else if hasDefault { - if !isNilableType(context.Type) && zeroDefault { + if !util.IsNilableType(context.Type) && zeroDefault { return Validations{Comments: []string{"optional value-type fields with zero-value defaults are purely documentation"}}, nil } validations, err := rtv.doRequired(context) @@ -162,7 +164,7 @@ func (rtv requirednessTagValidator) doOptional(context Context) (Validations, er // originally defined as a value-type or a pointer-type in the API. This // one does. Since Go doesn't do partial specialization of templates, we // do manual dispatch here. - switch nativeType(context.Type).Kind { + switch util.NativeType(context.Type).Kind { case types.Slice: return Validations{Functions: []FunctionGen{Function(optionalTagName, ShortCircuit|NonError, optionalSliceValidator)}}, nil case types.Map: @@ -183,7 +185,7 @@ func (rtv requirednessTagValidator) doOptional(context Context) (Validations, er // hasZeroDefault returns whether the field has a default value and whether // that default value is the zero value for the field's type. func (rtv requirednessTagValidator) hasZeroDefault(context Context) (bool, bool, error) { - t := nonPointer(nativeType(context.Type)) + t := util.NonPointer(util.NativeType(context.Type)) // This validator only applies to fields, so Member must be valid. tagsByName, err := gengo.ExtractFunctionStyleCommentTags("+", []string{defaultTagName}, context.Member.CommentLines) if err != nil { @@ -262,7 +264,7 @@ func (requirednessTagValidator) doForbidden(context Context) (Validations, error // optional check and short-circuit (but without error). Why? For // example, this prevents any further validation from trying to run on a // nil pointer. - switch nativeType(context.Type).Kind { + switch util.NativeType(context.Type).Kind { case types.Slice: return Validations{ Functions: []FunctionGen{ diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go index 7643dd364d2..f9f154fde6b 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go @@ -22,6 +22,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/gengo/v2/codetags" "k8s.io/gengo/v2/types" + + "k8s.io/code-generator/cmd/validation-gen/util" ) const ( @@ -58,12 +60,12 @@ func (stv subfieldTagValidator) GetValidations(context Context, tag codetags.Tag args := tag.Args // This tag can apply to value and pointer fields, as well as typedefs // (which should never be pointers). We need to check the concrete type. - t := nonPointer(nativeType(context.Type)) + t := util.NonPointer(util.NativeType(context.Type)) if t.Kind != types.Struct { return Validations{}, fmt.Errorf("can only be used on struct types") } subname := args[0].Value - submemb := getMemberByJSON(t, subname) + submemb := util.GetMemberByJSON(t, subname) if submemb == nil { return Validations{}, fmt.Errorf("no field for json name %q", subname) } @@ -83,12 +85,12 @@ func (stv subfieldTagValidator) GetValidations(context Context, tag codetags.Tag for _, vfn := range validations.Functions { nilableStructType := context.Type - if !isNilableType(nilableStructType) { + if !util.IsNilableType(nilableStructType) { nilableStructType = types.PointerTo(nilableStructType) } nilableFieldType := submemb.Type fieldExprPrefix := "" - if !isNilableType(nilableFieldType) { + if !util.IsNilableType(nilableFieldType) { nilableFieldType = types.PointerTo(nilableFieldType) fieldExprPrefix = "&" } From f574115f14f037187a4195cbd2701afd50893a7e Mon Sep 17 00:00:00 2001 From: yongruilin Date: Mon, 12 May 2025 22:47:45 +0000 Subject: [PATCH 2/5] feat: add default ratcheting support Emit equivalence check before emit validators when needed. --- .../ratcheting/default_behavior/doc.go | 144 ++++++ .../ratcheting/default_behavior/doc_test.go | 209 +++++++++ .../zz_generated.validations.go | 438 ++++++++++++++++++ .../cmd/validation-gen/validation.go | 43 +- 4 files changed, 833 insertions(+), 1 deletion(-) create mode 100644 staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc.go create mode 100644 staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc_test.go create mode 100644 staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/zz_generated.validations.go diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc.go new file mode 100644 index 00000000000..b5514658ec9 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc.go @@ -0,0 +1,144 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme + +// This is a test package. +package defaultbehavior + +import "k8s.io/code-generator/cmd/validation-gen/testscheme" + +var localSchemeBuilder = testscheme.New() + +type StructPrimitive struct { + TypeMeta int + + // +k8s:validateFalse="field intField" + IntField int `json:"intField"` + + // +k8s:optional + // +k8s:validateFalse="field intPtrField" + IntPtrField *int `json:"intPtrField"` +} + +type StructSlice struct { + TypeMeta int + + // +k8s:validateFalse="field sliceField" + SliceField []S `json:"sliceField"` + + // +k8s:validateFalse="field typedefSliceField" + TypeDefSliceField MySlice `json:"typedefSliceField"` +} + +type StructMap struct { + TypeMeta int + + // +k8s:validateFalse="field mapKeyField" + MapKeyField map[S]string `json:"mapKeyField"` + + // +k8s:validateFalse="field mapValueField" + MapValueField map[string]S `json:"mapValueField"` + + // +k8s:validateFalse="field aliasMapKeyTypeField" + AliasMapKeyTypeField AliasMapKeyType `json:"aliasMapKeyTypeField"` + + // +k8s:validateFalse="field aliasMapValueTypeField" + AliasMapValueTypeField AliasMapValueType `json:"aliasMapValueTypeField"` +} + +type StructStruct struct { + TypeMeta int + + // +k8s:validateFalse="field directComparableStructField" + DirectComparableStructField DirectComparableStruct `json:"directComparableStructField"` + + // +k8s:validateFalse="field nonDirectComparableStructField" + NonDirectComparableStructField NonDirectComparableStruct `json:"nonDirectComparableStructField"` + + // +k8s:validateFalse="field directComparableStructPtrField" + DirectComparableStructPtr *DirectComparableStruct `json:"directComparableStructPtrField"` + + // +k8s:validateFalse="field nonDirectComparableStructPtrField" + NonDirectComparableStructPtr *NonDirectComparableStruct `json:"nonDirectComparableStructPtrField"` + + // +k8s:validateFalse="field DirectComparableStruct" + DirectComparableStruct + + // +k8s:validateFalse="field NonDirectComparableStruct" + NonDirectComparableStruct +} + +type StructEmbedded struct { + TypeMeta int + // +k8s:validateFalse="field DirectComparableStruct" + DirectComparableStruct `json:"directComparableStruct"` + + // +k8s:validateFalse="field NonDirectComparableStruct" + NonDirectComparableStruct `json:"nonDirectComparableStruct"` + + // +k8s:validateFalse="field NestedDirectComparableStructField" + NestedDirectComparableStructField NestedDirectComparableStruct `json:"nestedDirectComparableStructField"` + + // +k8s:validateFalse="field NestedNonDirectComparableStructField" + NestedNonDirectComparableStructField NestedNonDirectComparableStruct `json:"nestedNonDirectComparableStructField"` +} + +// +k8s:validateFalse="type TypeDefStruct" +type TypeDefStruct struct{} + +// +k8s:validateFalse="type MySlice" +type MySlice []int + +// +k8s:validateFalse="type S" +type S string + +// +k8s:validateFalse="type MapKeyType" +type AliasMapKeyType MapKeyType + +// +k8s:validateFalse="type MapValueType" +type AliasMapValueType MapValueType + +// no validation +type MapKeyType map[S]string + +// no validation +type MapValueType map[string]S + +// +k8s:validateFalse="type DirectComparableStruct" +type DirectComparableStruct struct { + // +k8s:validateFalse="field intField" + IntField int `json:"intField"` +} + +// +k8s:validateFalse="type NonDirectComparableStruct" +type NonDirectComparableStruct struct { + // +k8s:validateFalse="field intField" + IntPtrField *int `json:"intPtrField"` +} + +// +k8s:validateFalse="type NestedDirectComparableStruct" +type NestedDirectComparableStruct struct { + // +k8s:validateFalse="field intField" + DirectComparableStructField DirectComparableStruct `json:"directComparableStructField"` +} + +// +k8s:validateFalse="type NestedNonDirectComparableStruct" +type NestedNonDirectComparableStruct struct { + // +k8s:validateFalse="field intField" + NonDirectComparableStructField NonDirectComparableStruct `json:"nonDirectComparableStructField"` +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc_test.go new file mode 100644 index 00000000000..972049ed2c0 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc_test.go @@ -0,0 +1,209 @@ +/* +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 defaultbehavior + +import ( + "testing" + + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/utils/ptr" +) + +func Test_StructPrimitive(t *testing.T) { + st := localSchemeBuilder.Test(t) + st.Value(&StructPrimitive{ + IntField: 1, + IntPtrField: ptr.To(1), + }).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{ + field.Invalid(field.NewPath("intField"), "", ""), + field.Invalid(field.NewPath("intPtrField"), "", ""), + }) + st.Value(&StructPrimitive{ + IntField: 1, + IntPtrField: ptr.To(1), + }).OldValue(&StructPrimitive{ + IntField: 1, + IntPtrField: ptr.To(1), // Different pointers but value unchanged. + }).ExpectValid() +} + +func Test_StructSlice(t *testing.T) { + st := localSchemeBuilder.Test(t) + st.Value(&StructSlice{ + SliceField: []S{""}, + TypeDefSliceField: MySlice{1}, + }).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{ + field.Invalid(field.NewPath("sliceField"), "", ""), + field.Invalid(field.NewPath("sliceField[0]"), "", ""), + field.Invalid(field.NewPath("typedefSliceField"), "", ""), + }) + + st.Value(&StructSlice{ + SliceField: []S{""}, + TypeDefSliceField: MySlice{1}, + }).OldValue(&StructSlice{ + SliceField: []S{""}, + TypeDefSliceField: MySlice{1}, + }).ExpectValid() +} + +func Test_StructMap(t *testing.T) { + st := localSchemeBuilder.Test(t) + st.Value(&StructMap{ + MapKeyField: map[S]string{S("k"): "v"}, + MapValueField: map[string]S{"k": "v"}, + AliasMapKeyTypeField: AliasMapKeyType{"k": "v"}, + AliasMapValueTypeField: AliasMapValueType{"k": "v"}, + }).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{ + field.Invalid(field.NewPath("mapKeyField"), "", ""), + field.Invalid(field.NewPath("mapValueField"), "", ""), + field.Invalid(field.NewPath("mapValueField[k]"), "", ""), + field.Invalid(field.NewPath("aliasMapKeyTypeField"), "", ""), + field.Invalid(field.NewPath("aliasMapValueTypeField"), "", ""), + field.Invalid(field.NewPath("aliasMapValueTypeField[k]"), "", ""), + }) + + st.Value(&StructMap{ + MapKeyField: map[S]string{S("k"): "v"}, + MapValueField: map[string]S{"k": "v"}, + AliasMapKeyTypeField: AliasMapKeyType{"k": "v"}, + AliasMapValueTypeField: AliasMapValueType{"k": "v"}, + }).OldValue(&StructMap{ + MapKeyField: map[S]string{S("k"): "v"}, + MapValueField: map[string]S{"k": "v"}, + AliasMapKeyTypeField: AliasMapKeyType{"k": "v"}, + AliasMapValueTypeField: AliasMapValueType{"k": "v"}, + }).ExpectValid() +} + +func Test_StructStruct(t *testing.T) { + st := localSchemeBuilder.Test(t) + st.Value(&StructStruct{ + DirectComparableStructField: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructField: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + DirectComparableStructPtr: &DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructPtr: &NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{ + field.Invalid(field.NewPath("directComparableStructField"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStructField"), "", ""), + field.Invalid(field.NewPath("directComparableStructField").Child("intField"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStructField").Child("intPtrField"), "", ""), + field.Invalid(field.NewPath("directComparableStructPtrField"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStructPtrField"), "", ""), + field.Invalid(field.NewPath("directComparableStructPtrField").Child("intField"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStructPtrField").Child("intPtrField"), "", ""), + field.Invalid(field.NewPath("DirectComparableStruct"), "", ""), + field.Invalid(field.NewPath("NonDirectComparableStruct"), "", ""), + field.Invalid(field.NewPath("DirectComparableStruct").Child("intField"), "", ""), + field.Invalid(field.NewPath("NonDirectComparableStruct").Child("intPtrField"), "", ""), + }) + + st.Value(&StructStruct{ + DirectComparableStructField: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructField: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + DirectComparableStructPtr: &DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructPtr: &NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }).OldValue(&StructStruct{ + DirectComparableStructField: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructField: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + DirectComparableStructPtr: &DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructPtr: &NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }).ExpectValid() +} + +func Test_StructEmbedded(t *testing.T) { + st := localSchemeBuilder.Test(t) + st.Value(&StructEmbedded{ + DirectComparableStruct: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStruct: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{ + field.Invalid(field.NewPath("directComparableStruct"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStruct"), "", ""), + field.Invalid(field.NewPath("directComparableStruct").Child("intField"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStruct").Child("intPtrField"), "", ""), + field.Invalid(field.NewPath("nestedDirectComparableStructField"), "", ""), + field.Invalid(field.NewPath("nestedDirectComparableStructField").Child("directComparableStructField"), "", ""), + field.Invalid(field.NewPath("nestedDirectComparableStructField").Child("directComparableStructField").Child("intField"), "", ""), + field.Invalid(field.NewPath("nestedNonDirectComparableStructField"), "", ""), + field.Invalid(field.NewPath("nestedNonDirectComparableStructField").Child("nonDirectComparableStructField"), "", ""), + field.Invalid(field.NewPath("nestedNonDirectComparableStructField").Child("nonDirectComparableStructField").Child("intPtrField"), "", ""), + }) + + st.Value(&StructEmbedded{ + DirectComparableStruct: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStruct: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + NestedDirectComparableStructField: NestedDirectComparableStruct{ + DirectComparableStructField: DirectComparableStruct{ + IntField: 1, + }, + }, + NestedNonDirectComparableStructField: NestedNonDirectComparableStruct{ + NonDirectComparableStructField: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }, + }).OldValue(&StructEmbedded{ + DirectComparableStruct: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStruct: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + NestedDirectComparableStructField: NestedDirectComparableStruct{ + DirectComparableStructField: DirectComparableStruct{ + IntField: 1, + }, + }, + NestedNonDirectComparableStructField: NestedNonDirectComparableStruct{ + NonDirectComparableStructField: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }, + }).ExpectValid() +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/zz_generated.validations.go new file mode 100644 index 00000000000..26a3efd54d2 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/zz_generated.validations.go @@ -0,0 +1,438 @@ +//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 defaultbehavior + +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 { + scheme.AddValidationFunc((*StructEmbedded)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StructEmbedded(ctx, op, nil /* fldPath */, obj.(*StructEmbedded), safe.Cast[*StructEmbedded](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + scheme.AddValidationFunc((*StructMap)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StructMap(ctx, op, nil /* fldPath */, obj.(*StructMap), safe.Cast[*StructMap](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + scheme.AddValidationFunc((*StructPrimitive)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StructPrimitive(ctx, op, nil /* fldPath */, obj.(*StructPrimitive), safe.Cast[*StructPrimitive](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + scheme.AddValidationFunc((*StructSlice)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StructSlice(ctx, op, nil /* fldPath */, obj.(*StructSlice), safe.Cast[*StructSlice](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + scheme.AddValidationFunc((*StructStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StructStruct(ctx, op, nil /* fldPath */, obj.(*StructStruct), safe.Cast[*StructStruct](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + return nil +} + +func Validate_AliasMapKeyType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj AliasMapKeyType) (errs field.ErrorList) { + // type AliasMapKeyType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapKeyType")...) + errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, Validate_S)...) + + return errs +} + +func Validate_AliasMapValueType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj AliasMapValueType) (errs field.ErrorList) { + // type AliasMapValueType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapValueType")...) + errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, Validate_S)...) + + return errs +} + +func Validate_DirectComparableStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *DirectComparableStruct) (errs field.ErrorList) { + // type DirectComparableStruct + 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, "type DirectComparableStruct")...) + + // field DirectComparableStruct.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, "field intField")...) + return + }(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *DirectComparableStruct) *int { return &oldObj.IntField }))...) + + return errs +} + +func Validate_MySlice(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MySlice) (errs field.ErrorList) { + // type MySlice + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MySlice")...) + + return errs +} + +func Validate_NestedDirectComparableStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NestedDirectComparableStruct) (errs field.ErrorList) { + // type NestedDirectComparableStruct + 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, "type NestedDirectComparableStruct")...) + + // field NestedDirectComparableStruct.DirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *DirectComparableStruct) (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, "field intField")...) + errs = append(errs, Validate_DirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("directComparableStructField"), &obj.DirectComparableStructField, safe.Field(oldObj, func(oldObj *NestedDirectComparableStruct) *DirectComparableStruct { + return &oldObj.DirectComparableStructField + }))...) + + return errs +} + +func Validate_NestedNonDirectComparableStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NestedNonDirectComparableStruct) (errs field.ErrorList) { + // type NestedNonDirectComparableStruct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type NestedNonDirectComparableStruct")...) + + // field NestedNonDirectComparableStruct.NonDirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field intField")...) + errs = append(errs, Validate_NonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nonDirectComparableStructField"), &obj.NonDirectComparableStructField, safe.Field(oldObj, func(oldObj *NestedNonDirectComparableStruct) *NonDirectComparableStruct { + return &oldObj.NonDirectComparableStructField + }))...) + + return errs +} + +func Validate_NonDirectComparableStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + // type NonDirectComparableStruct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type NonDirectComparableStruct")...) + + // field NonDirectComparableStruct.IntPtrField + 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, "field intField")...) + return + }(fldPath.Child("intPtrField"), obj.IntPtrField, safe.Field(oldObj, func(oldObj *NonDirectComparableStruct) *int { return oldObj.IntPtrField }))...) + + return errs +} + +func Validate_S(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *S) (errs field.ErrorList) { + // type S + 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, "type S")...) + + return errs +} + +func Validate_StructEmbedded(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StructEmbedded) (errs field.ErrorList) { + // field StructEmbedded.TypeMeta has no validation + + // field StructEmbedded.DirectComparableStruct + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *DirectComparableStruct) (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, "field DirectComparableStruct")...) + errs = append(errs, Validate_DirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("directComparableStruct"), &obj.DirectComparableStruct, safe.Field(oldObj, func(oldObj *StructEmbedded) *DirectComparableStruct { return &oldObj.DirectComparableStruct }))...) + + // field StructEmbedded.NonDirectComparableStruct + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field NonDirectComparableStruct")...) + errs = append(errs, Validate_NonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nonDirectComparableStruct"), &obj.NonDirectComparableStruct, safe.Field(oldObj, func(oldObj *StructEmbedded) *NonDirectComparableStruct { return &oldObj.NonDirectComparableStruct }))...) + + // field StructEmbedded.NestedDirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NestedDirectComparableStruct) (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, "field NestedDirectComparableStructField")...) + errs = append(errs, Validate_NestedDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nestedDirectComparableStructField"), &obj.NestedDirectComparableStructField, safe.Field(oldObj, func(oldObj *StructEmbedded) *NestedDirectComparableStruct { + return &oldObj.NestedDirectComparableStructField + }))...) + + // field StructEmbedded.NestedNonDirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NestedNonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field NestedNonDirectComparableStructField")...) + errs = append(errs, Validate_NestedNonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nestedNonDirectComparableStructField"), &obj.NestedNonDirectComparableStructField, safe.Field(oldObj, func(oldObj *StructEmbedded) *NestedNonDirectComparableStruct { + return &oldObj.NestedNonDirectComparableStructField + }))...) + + return errs +} + +func Validate_StructMap(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StructMap) (errs field.ErrorList) { + // field StructMap.TypeMeta has no validation + + // field StructMap.MapKeyField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj map[S]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field mapKeyField")...) + errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, Validate_S)...) + return + }(fldPath.Child("mapKeyField"), obj.MapKeyField, safe.Field(oldObj, func(oldObj *StructMap) map[S]string { return oldObj.MapKeyField }))...) + + // field StructMap.MapValueField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj map[string]S) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field mapValueField")...) + errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, Validate_S)...) + return + }(fldPath.Child("mapValueField"), obj.MapValueField, safe.Field(oldObj, func(oldObj *StructMap) map[string]S { return oldObj.MapValueField }))...) + + // field StructMap.AliasMapKeyTypeField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj AliasMapKeyType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field aliasMapKeyTypeField")...) + errs = append(errs, Validate_AliasMapKeyType(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("aliasMapKeyTypeField"), obj.AliasMapKeyTypeField, safe.Field(oldObj, func(oldObj *StructMap) AliasMapKeyType { return oldObj.AliasMapKeyTypeField }))...) + + // field StructMap.AliasMapValueTypeField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj AliasMapValueType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field aliasMapValueTypeField")...) + errs = append(errs, Validate_AliasMapValueType(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("aliasMapValueTypeField"), obj.AliasMapValueTypeField, safe.Field(oldObj, func(oldObj *StructMap) AliasMapValueType { return oldObj.AliasMapValueTypeField }))...) + + return errs +} + +func Validate_StructPrimitive(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StructPrimitive) (errs field.ErrorList) { + // field StructPrimitive.TypeMeta has no validation + + // field StructPrimitive.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, "field intField")...) + return + }(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *StructPrimitive) *int { return &oldObj.IntField }))...) + + // field StructPrimitive.IntPtrField + 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 + } + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + return // do not proceed + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field intPtrField")...) + return + }(fldPath.Child("intPtrField"), obj.IntPtrField, safe.Field(oldObj, func(oldObj *StructPrimitive) *int { return oldObj.IntPtrField }))...) + + return errs +} + +func Validate_StructSlice(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StructSlice) (errs field.ErrorList) { + // field StructSlice.TypeMeta has no validation + + // field StructSlice.SliceField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []S) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field sliceField")...) + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_S)...) + return + }(fldPath.Child("sliceField"), obj.SliceField, safe.Field(oldObj, func(oldObj *StructSlice) []S { return oldObj.SliceField }))...) + + // field StructSlice.TypeDefSliceField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj MySlice) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field typedefSliceField")...) + errs = append(errs, Validate_MySlice(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("typedefSliceField"), obj.TypeDefSliceField, safe.Field(oldObj, func(oldObj *StructSlice) MySlice { return oldObj.TypeDefSliceField }))...) + + return errs +} + +func Validate_StructStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StructStruct) (errs field.ErrorList) { + // field StructStruct.TypeMeta has no validation + + // field StructStruct.DirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *DirectComparableStruct) (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, "field directComparableStructField")...) + errs = append(errs, Validate_DirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("directComparableStructField"), &obj.DirectComparableStructField, safe.Field(oldObj, func(oldObj *StructStruct) *DirectComparableStruct { return &oldObj.DirectComparableStructField }))...) + + // field StructStruct.NonDirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field nonDirectComparableStructField")...) + errs = append(errs, Validate_NonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nonDirectComparableStructField"), &obj.NonDirectComparableStructField, safe.Field(oldObj, func(oldObj *StructStruct) *NonDirectComparableStruct { return &oldObj.NonDirectComparableStructField }))...) + + // field StructStruct.DirectComparableStructPtr + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *DirectComparableStruct) (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, "field directComparableStructPtrField")...) + errs = append(errs, Validate_DirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("directComparableStructPtrField"), obj.DirectComparableStructPtr, safe.Field(oldObj, func(oldObj *StructStruct) *DirectComparableStruct { return oldObj.DirectComparableStructPtr }))...) + + // field StructStruct.NonDirectComparableStructPtr + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field nonDirectComparableStructPtrField")...) + errs = append(errs, Validate_NonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nonDirectComparableStructPtrField"), obj.NonDirectComparableStructPtr, safe.Field(oldObj, func(oldObj *StructStruct) *NonDirectComparableStruct { return oldObj.NonDirectComparableStructPtr }))...) + + // field StructStruct.DirectComparableStruct + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *DirectComparableStruct) (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, "field DirectComparableStruct")...) + errs = append(errs, Validate_DirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("DirectComparableStruct") }), &obj.DirectComparableStruct, safe.Field(oldObj, func(oldObj *StructStruct) *DirectComparableStruct { return &oldObj.DirectComparableStruct }))...) + + // field StructStruct.NonDirectComparableStruct + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field NonDirectComparableStruct")...) + errs = append(errs, Validate_NonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("NonDirectComparableStruct") }), &obj.NonDirectComparableStruct, safe.Field(oldObj, func(oldObj *StructStruct) *NonDirectComparableStruct { return &oldObj.NonDirectComparableStruct }))...) + + return errs +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go index b7f86545c38..38ef3b40323 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go @@ -53,9 +53,11 @@ var ( safePkg = "k8s.io/apimachinery/pkg/api/safe" safePkgSymbols = mkPkgNames(safePkg, "Field", "Cast", "Value") operationPkg = "k8s.io/apimachinery/pkg/api/operation" - operationPkgSymbols = mkPkgNames(operationPkg, "Operation", "MatchesSubresource") + operationPkgSymbols = mkPkgNames(operationPkg, "Operation", "MatchesSubresource", "Update") contextPkg = "context" contextPkgSymbols = mkPkgNames(contextPkg, "Context") + equalityPkg = "k8s.io/apimachinery/pkg/api/equality" + equalityPkgSymbols = mkPkgNames(equalityPkg, "Semantic") ) // genValidations produces a file with autogenerated validations. @@ -951,6 +953,7 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild } sw.Do("// type $.inType|raw$\n", targs) emitComments(validations.Comments, sw) + emitRatchetingCheck(c, thisNode.valueType, sw) emitCallsToValidators(c, validations.Functions, sw) if thisNode.valueType.Kind == types.Alias { underlyingNode := thisNode.underlying.node @@ -1003,9 +1006,14 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild bufsw := sw.Dup(buf) validations := fld.fieldValidations + + // fldRatchetingChecked is used to avoid emitting the ratcheting check multiple times. + fldRatchetingChecked := false if !validations.Empty() { emitComments(validations.Comments, bufsw) + emitRatchetingCheck(c, fld.childType, bufsw) emitCallsToValidators(c, validations.Functions, bufsw) + fldRatchetingChecked = true } // If the node is nil, this must be a type in a package we are not @@ -1021,6 +1029,10 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild // validations, call its validation function. if validations := fld.fieldValIterations; g.hasValidations(fld.node.elem.node) && !validations.Empty() { emitComments(validations.Comments, bufsw) + if !fldRatchetingChecked { + emitRatchetingCheck(c, fld.childType, bufsw) + fldRatchetingChecked = true + } emitCallsToValidators(c, validations.Functions, bufsw) } // Descend into this field. @@ -1030,12 +1042,20 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild // validations, call its validation function. if validations := fld.fieldKeyIterations; g.hasValidations(fld.node.key.node) && !validations.Empty() { emitComments(validations.Comments, bufsw) + if !fldRatchetingChecked { + emitRatchetingCheck(c, fld.childType, bufsw) + fldRatchetingChecked = true + } emitCallsToValidators(c, validations.Functions, bufsw) } // If this field is a map and the value-type has // validations, call its validation function. if validations := fld.fieldValIterations; g.hasValidations(fld.node.elem.node) && !validations.Empty() { emitComments(validations.Comments, bufsw) + if !fldRatchetingChecked { + emitRatchetingCheck(c, fld.childType, bufsw) + fldRatchetingChecked = true + } emitCallsToValidators(c, validations.Functions, bufsw) } // Descend into this field. @@ -1113,6 +1133,27 @@ func (g *genValidations) emitCallToOtherTypeFunc(c *generator.Context, node *typ sw.Do("errs = append(errs, $.funcName|raw$(ctx, op, fldPath, obj, oldObj)...)\n", targs) } +// emitRachetingCheck emits a equivalence check for default ratcheting. +func emitRatchetingCheck(c *generator.Context, t *types.Type, sw *generator.SnippetWriter) { + // Emit equivalence check for default ratcheting. + // TODO: Need to have a follow-up PR to handle the default behavior + // for slices and maps. + targs := generator.Args{ + "operation": mkSymbolArgs(c, operationPkgSymbols), + } + // If the type is a builtin, we can use a simpler equality check when they are not nil. + if util.IsDirectComparable(util.NonPointer(util.NativeType(t))) { + _, exprPfx, _ := getLeafTypeAndPrefixes(t) + targs["exprPfx"] = exprPfx + sw.Do("if op.Type == $.operation.Update|raw$ && (obj == oldObj || (obj != nil && oldObj != nil && $.exprPfx$obj == $.exprPfx$oldObj)) {\n", targs) + } else { + targs["equality"] = mkSymbolArgs(c, equalityPkgSymbols) + sw.Do("if op.Type == $.operation.Update|raw$ && $.equality.Semantic|raw$.DeepEqual(obj, oldObj) {\n", targs) + } + sw.Do(" return nil // no changes\n", nil) + sw.Do("}\n", nil) +} + // emitCallsToValidators emits calls to a list of validation functions for // a single field or type. validations is a list of functions to call, with // arguments. From 1574001a1cd492fc9b6806b9cea7fb0d1aed1863 Mon Sep 17 00:00:00 2001 From: yongruilin Date: Wed, 11 Jun 2025 20:08:52 +0000 Subject: [PATCH 3/5] run update-codegen.sh --- .../apps/v1beta1/zz_generated.validations.go | 3 + .../apps/v1beta2/zz_generated.validations.go | 3 + .../v1/zz_generated.validations.go | 3 + pkg/apis/core/v1/zz_generated.validations.go | 10 +++ .../v1beta1/zz_generated.validations.go | 3 + .../zz_generated.validations.go | 15 ++++ .../zz_generated.validations.go | 9 +++ .../cross_pkg/zz_generated.validations.go | 70 +++++++++++++++++++ .../zz_generated.validations.go | 9 +++ .../embedded/zz_generated.validations.go | 9 +++ .../maps/keys/zz_generated.validations.go | 25 +++++++ .../zz_generated.validations.go | 13 ++++ .../map_of_struct/zz_generated.validations.go | 16 +++++ .../zz_generated.validations.go | 7 ++ .../zz_generated.validations.go | 19 +++++ .../multiple_tags/zz_generated.validations.go | 15 ++++ .../zz_generated.validations.go | 21 ++++++ .../zz_generated.validations.go | 3 + .../structs/zz_generated.validations.go | 64 +++++++++++++++++ .../typedefs/zz_generated.validations.go | 12 ++++ .../pointers/zz_generated.validations.go | 28 ++++++++ .../primitives/zz_generated.validations.go | 27 +++++++ .../zz_generated.validations.go | 3 + .../maps/zz_generated.validations.go | 10 +++ .../pointers/zz_generated.validations.go | 19 +++++ .../slices/zz_generated.validations.go | 10 +++ .../zz_generated.validations.go | 7 ++ .../zz_generated.validations.go | 13 ++++ .../zz_generated.validations.go | 19 +++++ .../zz_generated.validations.go | 19 +++++ .../tags/eachkey/zz_generated.validations.go | 22 ++++++ .../zz_generated.validations.go | 7 ++ .../map_of_struct/zz_generated.validations.go | 7 ++ .../zz_generated.validations.go | 7 ++ .../zz_generated.validations.go | 7 ++ .../zz_generated.validations.go | 19 +++++ .../zz_generated.validations.go | 13 ++++ .../immutable/zz_generated.validations.go | 28 ++++++++ .../multiple_keys/zz_generated.validations.go | 7 ++ .../single_key/zz_generated.validations.go | 7 ++ .../tags/minimum/zz_generated.validations.go | 39 +++++++++++ .../tags/opaque/zz_generated.validations.go | 46 ++++++++++++ .../zz_generated.validations.go | 18 +++++ .../zero_defaults/zz_generated.validations.go | 18 +++++ .../tags/optional/zz_generated.validations.go | 55 +++++++++++++++ .../subfield/deep/zz_generated.validations.go | 7 ++ .../nonincluded/zz_generated.validations.go | 3 + .../shallow/zz_generated.validations.go | 7 ++ .../issubresource/zz_generated.validations.go | 3 + .../root/zz_generated.validations.go | 3 + .../subresource/zz_generated.validations.go | 3 + .../zz_generated.validations.go | 3 + .../validate_true/zz_generated.validations.go | 3 + .../type_args/zz_generated.validations.go | 21 ++++++ .../typedefs/zz_generated.validations.go | 55 +++++++++++++++ .../lists/zz_generated.validations.go | 7 ++ .../maps/zz_generated.validations.go | 7 ++ .../zz_generated.validations.go | 12 ++++ .../primitives/zz_generated.validations.go | 12 ++++ 59 files changed, 930 insertions(+) diff --git a/pkg/apis/apps/v1beta1/zz_generated.validations.go b/pkg/apis/apps/v1beta1/zz_generated.validations.go index 269fcad7424..d3e13a4f9d9 100644 --- a/pkg/apis/apps/v1beta1/zz_generated.validations.go +++ b/pkg/apis/apps/v1beta1/zz_generated.validations.go @@ -68,6 +68,9 @@ func Validate_ScaleSpec(ctx context.Context, op operation.Operation, fldPath *fi errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *appsv1beta1.ScaleSpec) *int32 { return &oldObj.Replicas }))...) diff --git a/pkg/apis/apps/v1beta2/zz_generated.validations.go b/pkg/apis/apps/v1beta2/zz_generated.validations.go index 85e3e6147b3..3c16d10b674 100644 --- a/pkg/apis/apps/v1beta2/zz_generated.validations.go +++ b/pkg/apis/apps/v1beta2/zz_generated.validations.go @@ -68,6 +68,9 @@ func Validate_ScaleSpec(ctx context.Context, op operation.Operation, fldPath *fi errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *appsv1beta2.ScaleSpec) *int32 { return &oldObj.Replicas }))...) diff --git a/pkg/apis/autoscaling/v1/zz_generated.validations.go b/pkg/apis/autoscaling/v1/zz_generated.validations.go index dfe00d930a6..1c71f7f80e5 100644 --- a/pkg/apis/autoscaling/v1/zz_generated.validations.go +++ b/pkg/apis/autoscaling/v1/zz_generated.validations.go @@ -68,6 +68,9 @@ func Validate_ScaleSpec(ctx context.Context, op operation.Operation, fldPath *fi errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *autoscalingv1.ScaleSpec) *int32 { return &oldObj.Replicas }))...) diff --git a/pkg/apis/core/v1/zz_generated.validations.go b/pkg/apis/core/v1/zz_generated.validations.go index 5ea67206150..bdde796f66b 100644 --- a/pkg/apis/core/v1/zz_generated.validations.go +++ b/pkg/apis/core/v1/zz_generated.validations.go @@ -26,6 +26,7 @@ import ( fmt "fmt" corev1 "k8s.io/api/core/v1" + 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" @@ -77,6 +78,9 @@ func Validate_ReplicationControllerList(ctx context.Context, op operation.Operat // field corev1.ReplicationControllerList.Items errs = append(errs, func(fldPath *field.Path, obj, oldObj []corev1.ReplicationController) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_ReplicationController)...) return }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *corev1.ReplicationControllerList) []corev1.ReplicationController { return oldObj.Items }))...) @@ -88,6 +92,9 @@ func Validate_ReplicationControllerSpec(ctx context.Context, op operation.Operat // field corev1.ReplicationControllerSpec.Replicas errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -101,6 +108,9 @@ func Validate_ReplicationControllerSpec(ctx context.Context, op operation.Operat errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return }(fldPath.Child("minReadySeconds"), &obj.MinReadySeconds, safe.Field(oldObj, func(oldObj *corev1.ReplicationControllerSpec) *int32 { return &oldObj.MinReadySeconds }))...) diff --git a/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.validations.go b/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.validations.go index 16bd37c4e04..6d2a1666ae3 100644 --- a/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.validations.go +++ b/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.validations.go @@ -67,6 +67,9 @@ func Validate_ScaleSpec(ctx context.Context, op operation.Operation, fldPath *fi errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *ScaleSpec) *int32 { return &oldObj.Replicas }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_field_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_field_validations/zz_generated.validations.go index 8daed8e2db8..94892383e82 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_field_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_field_validations/zz_generated.validations.go @@ -65,6 +65,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S 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, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) @@ -72,6 +75,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (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, "field T1.T2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -80,6 +86,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T3) (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, "field T1.T3")...) return }(fldPath.Child("t3"), &obj.T3, safe.Field(oldObj, func(oldObj *T1) *T3 { return &oldObj.T3 }))...) @@ -91,6 +100,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.S 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, "field T2.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T2) *string { return &oldObj.S }))...) @@ -102,6 +114,9 @@ func Validate_T4(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T4.S 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, "field T4.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T4) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_type_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_type_validations/zz_generated.validations.go index e0e380f4a99..5788f6f579f 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_type_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_type_validations/zz_generated.validations.go @@ -56,6 +56,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { // type E1 + 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, "type E1")...) return errs @@ -63,11 +66,17 @@ func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { // type T1 + 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, "type T1")...) // field T1.S 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, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/cross_pkg/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/cross_pkg/zz_generated.validations.go index 1798bfc0736..97ce08e259a 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/cross_pkg/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/cross_pkg/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -56,6 +57,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (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, true, "field T1.PrimitivesT1")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -64,6 +68,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT1Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (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, true, "field T1.PrimitivesT1Ptr")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -72,6 +79,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T2) (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, true, "field T1.PrimitivesT2")...) errs = append(errs, primitives.Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -80,6 +90,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT2Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (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, true, "field T1.PrimitivesT2Ptr")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -88,6 +101,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T3) (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, true, "field T1.PrimitivesT3")...) return }(fldPath.Child("primitivest3"), &obj.PrimitivesT3, safe.Field(oldObj, func(oldObj *T1) *primitives.T3 { return &oldObj.PrimitivesT3 }))...) @@ -95,6 +111,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT3Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (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, true, "field T1.PrimitivesT3Ptr")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -103,6 +122,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E1) (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, true, "field T1.TypedefsE1")...) errs = append(errs, typedefs.Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -111,6 +133,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE1Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E1) (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, true, "field T1.TypedefsE1Ptr")...) errs = append(errs, typedefs.Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -119,6 +144,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E2) (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, true, "field T1.TypedefsE2")...) errs = append(errs, typedefs.Validate_E2(ctx, op, fldPath, obj, oldObj)...) return @@ -127,6 +155,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE2Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E2) (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, true, "field T1.TypedefsE2Ptr")...) errs = append(errs, typedefs.Validate_E2(ctx, op, fldPath, obj, oldObj)...) return @@ -135,6 +166,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E3) (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, true, "field T1.TypedefsE3")...) errs = append(errs, typedefs.Validate_E3(ctx, op, fldPath, obj, oldObj)...) return @@ -143,6 +177,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE3Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E3) (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, true, "field T1.TypedefsE3Ptr")...) errs = append(errs, typedefs.Validate_E3(ctx, op, fldPath, obj, oldObj)...) return @@ -151,6 +188,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE4 errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E4) (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, true, "field T1.TypedefsE4")...) errs = append(errs, typedefs.Validate_E4(ctx, op, fldPath, obj, oldObj)...) return @@ -159,6 +199,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE4Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E4) (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, true, "field T1.TypedefsE4Ptr")...) errs = append(errs, typedefs.Validate_E4(ctx, op, fldPath, obj, oldObj)...) return @@ -167,6 +210,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherString errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.StringType) (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, true, "field T1.OtherString")...) return }(fldPath.Child("otherString"), &obj.OtherString, safe.Field(oldObj, func(oldObj *T1) *other.StringType { return &oldObj.OtherString }))...) @@ -174,6 +220,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherStringPtr errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.StringType) (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, true, "field T1.OtherStringPtr")...) return }(fldPath.Child("otherStringPtr"), obj.OtherStringPtr, safe.Field(oldObj, func(oldObj *T1) *other.StringType { return oldObj.OtherStringPtr }))...) @@ -181,6 +230,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherInt errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.IntType) (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, true, "field T1.OtherInt")...) return }(fldPath.Child("otherInt"), &obj.OtherInt, safe.Field(oldObj, func(oldObj *T1) *other.IntType { return &oldObj.OtherInt }))...) @@ -188,6 +240,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherIntPtr errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.IntType) (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, true, "field T1.OtherIntPtr")...) return }(fldPath.Child("otherIntPtr"), obj.OtherIntPtr, safe.Field(oldObj, func(oldObj *T1) *other.IntType { return oldObj.OtherIntPtr }))...) @@ -195,6 +250,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherStruct errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.StructType) (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, true, "field T1.OtherStruct")...) return }(fldPath.Child("otherStruct"), &obj.OtherStruct, safe.Field(oldObj, func(oldObj *T1) *other.StructType { return &oldObj.OtherStruct }))...) @@ -202,6 +260,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherStructPtr errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.StructType) (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, true, "field T1.OtherStructPtr")...) return }(fldPath.Child("otherStructPtr"), obj.OtherStructPtr, safe.Field(oldObj, func(oldObj *T1) *other.StructType { return oldObj.OtherStructPtr }))...) @@ -209,6 +270,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.SliceOfOtherStruct errs = append(errs, func(fldPath *field.Path, obj, oldObj []other.StructType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.SliceOfOtherStruct")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *other.StructType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.SliceOfOtherStruct values") @@ -219,6 +283,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.ListMapOfOtherStruct errs = append(errs, func(fldPath *field.Path, obj, oldObj []other.StructType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.ListMapOfOtherStruct")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a other.StructType, b other.StructType) bool { return a.StringField == b.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *other.StructType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.SliceOfOtherStruct values") @@ -229,6 +296,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.MapOfOtherStringToOtherStruct errs = append(errs, func(fldPath *field.Path, obj, oldObj map[other.StringType]other.StructType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *other.StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.MapOfOtherStringToOtherStruct keys") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/elide_no_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/elide_no_validations/zz_generated.validations.go index 45155dede7e..496025b23f2 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/elide_no_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/elide_no_validations/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_HasFieldVal(ctx context.Context, op operation.Operation, fldPath * // field HasFieldVal.S 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, "field HasFieldVal.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *HasFieldVal) *string { return &oldObj.S }))...) @@ -60,6 +63,9 @@ func Validate_HasFieldVal(ctx context.Context, op operation.Operation, fldPath * func Validate_HasTypeVal(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *HasTypeVal) (errs field.ErrorList) { // type HasTypeVal + 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, "type HasTypeVal")...) // field HasTypeVal.S has no validation @@ -88,6 +94,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.HasNoValFieldVal errs = append(errs, func(fldPath *field.Path, obj, oldObj *HasNoVal) (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, "field T1.HasNoValFieldVal")...) return }(fldPath.Child("hasNoValFieldVal"), &obj.HasNoValFieldVal, safe.Field(oldObj, func(oldObj *T1) *HasNoVal { return &oldObj.HasNoValFieldVal }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/embedded/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/embedded/zz_generated.validations.go index 9e8977f0f53..26dd31341a2 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/embedded/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/embedded/zz_generated.validations.go @@ -71,6 +71,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // 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 }))...) @@ -82,6 +85,9 @@ func Validate_T3(ctx context.Context, op operation.Operation, fldPath *field.Pat // 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 }))...) @@ -89,6 +95,9 @@ func Validate_T3(ctx context.Context, op operation.Operation, fldPath *field.Pat // 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 }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/keys/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/keys/zz_generated.validations.go index f74ab88224e..f21259adc77 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/keys/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/keys/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -56,6 +60,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } 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.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField(keys)") })...) @@ -66,6 +73,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[UnvalidatedStringType]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *UnvalidatedStringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField(keys)") })...) @@ -76,6 +86,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapValidatedTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[ValidatedStringType]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ValidatedStringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapValidatedTypedefField(keys)") })...) @@ -87,6 +100,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypeField errs = append(errs, func(fldPath *field.Path, obj, oldObj UnvalidatedMapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } 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.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypeField(keys)") })...) @@ -97,6 +113,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ValidatedMapTypeField errs = append(errs, func(fldPath *field.Path, obj, oldObj ValidatedMapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } 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.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ValidatedMapTypeField(keys)") })...) @@ -110,6 +129,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field func Validate_ValidatedMapType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ValidatedMapType) (errs field.ErrorList) { // type ValidatedMapType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } 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.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ValidatedMapType(keys)") })...) @@ -120,6 +142,9 @@ func Validate_ValidatedMapType(ctx context.Context, op operation.Operation, fldP func Validate_ValidatedStringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ValidatedStringType) (errs field.ErrorList) { // type ValidatedStringType + 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, "ValidatedStringType")...) return errs diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/zz_generated.validations.go index c78aeb19e35..efdf874ae89 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + 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, "type StringType")...) return errs @@ -56,6 +60,9 @@ func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *f func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -63,6 +70,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") @@ -73,6 +83,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]StringType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/zz_generated.validations.go index 3d3e0db5c2a..5d47ce0b36b 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { // type OtherStruct + 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, "type OtherStruct")...) return errs @@ -56,6 +60,9 @@ func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath * func Validate_OtherTypedefStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) (errs field.ErrorList) { // type OtherTypedefStruct + 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, "type OtherTypedefStruct")...) return errs @@ -63,6 +70,9 @@ func Validate_OtherTypedefStruct(ctx context.Context, op operation.Operation, fl func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -70,6 +80,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") @@ -81,6 +94,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/multiple_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/multiple_validations/zz_generated.validations.go index 876a5c944b7..5d4ecc6f6df 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/multiple_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/multiple_validations/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -56,6 +60,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } 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.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField(keys) #1") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/zz_generated.validations.go index b49a264142c..6695b015d11 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { // type MapType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapType")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapType[*]") @@ -59,6 +63,9 @@ func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *fiel func Validate_MapTypedefType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MapTypedefType) (errs field.ErrorList) { // type MapTypedefType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapTypedefType")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapTypedefType[*]") @@ -70,6 +77,9 @@ func Validate_MapTypedefType(ctx context.Context, op operation.Operation, fldPat func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + 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, "type StringType")...) return errs @@ -77,6 +87,9 @@ func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *f func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -84,6 +97,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") @@ -95,6 +111,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj MapTypedefType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/multiple_tags/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/multiple_tags/zz_generated.validations.go index a0a1f693cde..c8bf791b803 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/multiple_tags/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/multiple_tags/zz_generated.validations.go @@ -49,6 +49,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { // type T1 + 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, "type T1 #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T1 #2")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T1 #3")...) @@ -59,6 +62,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S 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, "field T1.S false #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.S false #2")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.S false #3")...) @@ -69,6 +75,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (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, "field T1.T2 false #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.T2 false #2")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.T2 false #3")...) @@ -82,6 +91,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { // type T2 + 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, "type T2 false #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T2 false #2")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "type T2 true")...) @@ -89,6 +101,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.S 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, "field T2.S false #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.S false #2")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T2.S true")...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_field_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_field_validations/zz_generated.validations.go index 1f35c9c8833..60f41b8c713 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_field_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_field_validations/zz_generated.validations.go @@ -49,6 +49,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { // type E1 + 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, "type E1")...) return errs @@ -60,6 +63,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S 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, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) @@ -67,6 +73,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (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, "field T1.T2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -75,6 +84,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T3) (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, "field T1.T3")...) return }(fldPath.Child("t3"), &obj.T3, safe.Field(oldObj, func(oldObj *T1) *T3 { return &oldObj.T3 }))...) @@ -82,6 +94,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E1) (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, "field T1.E1")...) errs = append(errs, Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -90,6 +105,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E2) (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, "field T1.E2")...) return }(fldPath.Child("e2"), &obj.E2, safe.Field(oldObj, func(oldObj *T1) *E2 { return &oldObj.E2 }))...) @@ -101,6 +119,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.S 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, "field T2.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T2) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_type_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_type_validations/zz_generated.validations.go index f3d8938b40d..9716b26ce97 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_type_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_type_validations/zz_generated.validations.go @@ -49,6 +49,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { // type T1 + 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, "type T1")...) // field T1.TypeMeta has no validation diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/structs/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/structs/zz_generated.validations.go index 4d788478fe4..124a93e6ece 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/structs/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/structs/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -99,6 +100,9 @@ func Validate_T00(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T01) (errs field.ErrorList) { // type T01 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T01, no flags")...) // field T01.TypeMeta has no validation @@ -106,6 +110,9 @@ func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T01.S 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, "T01.S, no flags")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T01) *string { return &oldObj.S }))...) @@ -113,6 +120,9 @@ func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T01.PS 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, "T01.PS, no flags")...) return }(fldPath.Child("ps"), obj.PS, safe.Field(oldObj, func(oldObj *T01) *string { return oldObj.PS }))...) @@ -120,6 +130,9 @@ func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T01.T errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (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, "T01.T, no flags")...) errs = append(errs, Validate_Tother(ctx, op, fldPath, obj, oldObj)...) return @@ -128,6 +141,9 @@ func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T01.PT errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (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, "T01.PT, no flags")...) errs = append(errs, Validate_Tother(ctx, op, fldPath, obj, oldObj)...) return @@ -138,6 +154,9 @@ func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T02) (errs field.ErrorList) { // type T02 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T02, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -148,6 +167,9 @@ func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T02.S 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 + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T02.S, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -158,6 +180,9 @@ func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T02.PS 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 + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T02.PS, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -168,6 +193,9 @@ func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T02.T errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T02.T, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -179,6 +207,9 @@ func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T02.PT errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T02.PT, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -192,6 +223,9 @@ func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T03) (errs field.ErrorList) { // type T03 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T03, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -203,6 +237,9 @@ func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T03.S 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 + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T03.S, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -214,6 +251,9 @@ func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T03.PS 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 + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T03.PS, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -225,6 +265,9 @@ func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T03.T errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T03.T, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -237,6 +280,9 @@ func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T03.PT errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T03.PT, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -251,6 +297,9 @@ func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_TMultiple(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *TMultiple) (errs field.ErrorList) { // type TMultiple + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "TMultiple, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -268,6 +317,9 @@ func Validate_TMultiple(ctx context.Context, op operation.Operation, fldPath *fi // field TMultiple.S 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 + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "TMultiple.S, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -285,6 +337,9 @@ func Validate_TMultiple(ctx context.Context, op operation.Operation, fldPath *fi // field TMultiple.PS 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 + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "TMultiple.PS, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -302,6 +357,9 @@ func Validate_TMultiple(ctx context.Context, op operation.Operation, fldPath *fi // field TMultiple.T errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "TMultiple.T, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -320,6 +378,9 @@ func Validate_TMultiple(ctx context.Context, op operation.Operation, fldPath *fi // field TMultiple.PT errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "TMultiple.PT, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -342,6 +403,9 @@ func Validate_Tother(ctx context.Context, op operation.Operation, fldPath *field // field Tother.OS 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, "Tother, no flags")...) return }(fldPath.Child("os"), &obj.OS, safe.Field(oldObj, func(oldObj *Tother) *string { return &oldObj.OS }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/typedefs/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/typedefs/zz_generated.validations.go index dc3be29b6a6..11fc97154af 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/typedefs/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/typedefs/zz_generated.validations.go @@ -70,6 +70,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_E01(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E01) (errs field.ErrorList) { // type E01 + 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, "E01, no flags")...) return errs @@ -77,6 +80,9 @@ func Validate_E01(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_E02(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E02) (errs field.ErrorList) { // type E02 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "E02, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -87,6 +93,9 @@ func Validate_E02(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_E03(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E03) (errs field.ErrorList) { // type E03 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "E03, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -98,6 +107,9 @@ func Validate_E03(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_EMultiple(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *EMultiple) (errs field.ErrorList) { // type EMultiple + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "EMultiple, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/pointers/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/pointers/zz_generated.validations.go index 316f8baa337..b353d293a2e 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/pointers/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/pointers/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -53,6 +54,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PS 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, "field T1.PS")...) return }(fldPath.Child("ps"), obj.PS, safe.Field(oldObj, func(oldObj *T1) *string { return oldObj.PS }))...) @@ -60,6 +64,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PI 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, "field T1.PI")...) return }(fldPath.Child("pi"), obj.PI, safe.Field(oldObj, func(oldObj *T1) *int { return oldObj.PI }))...) @@ -67,6 +74,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PB errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (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, "field T1.PB")...) return }(fldPath.Child("pb"), obj.PB, safe.Field(oldObj, func(oldObj *T1) *bool { return oldObj.PB }))...) @@ -74,6 +84,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PF errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (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, "field T1.PF")...) return }(fldPath.Child("pf"), obj.PF, safe.Field(oldObj, func(oldObj *T1) *float64 { return oldObj.PF }))...) @@ -81,6 +94,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PT2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.PT2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -97,6 +113,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PS 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, "field T2.PS")...) return }(fldPath.Child("ps"), obj.PS, safe.Field(oldObj, func(oldObj *T2) *string { return oldObj.PS }))...) @@ -104,6 +123,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PI 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, "field T2.PI")...) return }(fldPath.Child("pi"), obj.PI, safe.Field(oldObj, func(oldObj *T2) *int { return oldObj.PI }))...) @@ -111,6 +133,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PB errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (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, "field T2.PB")...) return }(fldPath.Child("pb"), obj.PB, safe.Field(oldObj, func(oldObj *T2) *bool { return oldObj.PB }))...) @@ -118,6 +143,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PF errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (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, "field T2.PF")...) return }(fldPath.Child("pf"), obj.PF, safe.Field(oldObj, func(oldObj *T2) *float64 { return oldObj.PF }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/primitives/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/primitives/zz_generated.validations.go index ceaf93bbe87..84d3819e15d 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/primitives/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/primitives/zz_generated.validations.go @@ -53,6 +53,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S 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, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) @@ -60,6 +63,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.I 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, "field T1.I")...) return }(fldPath.Child("i"), &obj.I, safe.Field(oldObj, func(oldObj *T1) *int { return &oldObj.I }))...) @@ -67,6 +73,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.B errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (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, "field T1.B")...) return }(fldPath.Child("b"), &obj.B, safe.Field(oldObj, func(oldObj *T1) *bool { return &oldObj.B }))...) @@ -74,6 +83,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.F errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (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, "field T1.F")...) return }(fldPath.Child("f"), &obj.F, safe.Field(oldObj, func(oldObj *T1) *float64 { return &oldObj.F }))...) @@ -81,6 +93,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (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, "field T1.T2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -106,6 +121,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.S 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, "field T2.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T2) *string { return &oldObj.S }))...) @@ -113,6 +131,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.I 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, "field T2.I")...) return }(fldPath.Child("i"), &obj.I, safe.Field(oldObj, func(oldObj *T2) *int { return &oldObj.I }))...) @@ -120,6 +141,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.B errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (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, "field T2.B")...) return }(fldPath.Child("b"), &obj.B, safe.Field(oldObj, func(oldObj *T2) *bool { return &oldObj.B }))...) @@ -127,6 +151,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.F errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (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, "field T2.F")...) return }(fldPath.Child("f"), &obj.F, safe.Field(oldObj, func(oldObj *T2) *float64 { return &oldObj.F }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/public_private/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/public_private/zz_generated.validations.go index e1bdaffb676..16de705f710 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/public_private/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/public_private/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.Public 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, "field T1.Public")...) return }(fldPath.Child("public"), &obj.Public, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.Public }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/maps/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/maps/zz_generated.validations.go index 0a512f519df..1fcb13d2fc0 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/maps/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/maps/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -90,6 +91,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.MT1 errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]T1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, Validate_T1)...) return }(fldPath.Child("mt1"), obj.MT1, safe.Field(oldObj, func(oldObj *T2) map[string]T1 { return oldObj.MT1 }))...) @@ -99,6 +103,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T3(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) { // type T3 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T3")...) // field T3.T4 @@ -115,6 +122,9 @@ func Validate_T4(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T4.MT3 errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]T3) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, Validate_T3)...) return }(fldPath.Child("mt3"), obj.MT3, safe.Field(oldObj, func(oldObj *T4) map[string]T3 { return oldObj.MT3 }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/pointers/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/pointers/zz_generated.validations.go index f126983098b..1ebdaf5c959 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/pointers/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/pointers/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -65,6 +66,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PT1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -82,6 +86,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PT2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -96,6 +103,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PT1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -106,6 +116,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PT2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -116,6 +129,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PT3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -128,6 +144,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T3(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) { // type T3 + 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, "type T3")...) // field T3.I has no validation diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/slices/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/slices/zz_generated.validations.go index 1e60142ba07..fb6802c71c8 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/slices/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/slices/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -90,6 +91,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.ST1 errs = append(errs, func(fldPath *field.Path, obj, oldObj []T1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_T1)...) return }(fldPath.Child("st1"), obj.ST1, safe.Field(oldObj, func(oldObj *T2) []T1 { return oldObj.ST1 }))...) @@ -99,6 +103,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T3(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) { // type T3 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T3")...) // field T3.T4 @@ -115,6 +122,9 @@ func Validate_T4(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T4.ST3 errs = append(errs, func(fldPath *field.Path, obj, oldObj []T3) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_T3)...) return }(fldPath.Child("st3"), obj.ST3, safe.Field(oldObj, func(oldObj *T4) []T3 { return oldObj.ST3 }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/multiple_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/multiple_validations/zz_generated.validations.go index 78c2e2fe6f8..6144e21a4e4 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/multiple_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/multiple_validations/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct #2")...) @@ -57,6 +61,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField #2")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_primitive/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_primitive/zz_generated.validations.go index 00f2e9649f6..0f70452efc5 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_primitive/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_primitive/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + 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, "type StringType")...) return errs @@ -56,6 +60,9 @@ func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *f func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -63,6 +70,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") @@ -73,6 +83,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []StringType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_struct/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_struct/zz_generated.validations.go index 51e32a725ae..77351fab73a 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_struct/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_struct/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { // type OtherStruct + 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, "type OtherStruct")...) return errs @@ -56,6 +60,9 @@ func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath * func Validate_OtherTypedefStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) (errs field.ErrorList) { // type OtherTypedefStruct + 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, "type OtherTypedefStruct")...) return errs @@ -63,6 +70,9 @@ func Validate_OtherTypedefStruct(ctx context.Context, op operation.Operation, fl func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -70,6 +80,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") @@ -81,6 +94,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") @@ -92,6 +108,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.UnvalidatedListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_OtherStruct)...) return }(fldPath.Child("UnvalidatedListField"), obj.UnvalidatedListField, safe.Field(oldObj, func(oldObj *Struct) []OtherStruct { return oldObj.UnvalidatedListField }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/typedef_to_slice/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/typedef_to_slice/zz_generated.validations.go index cf3c1535431..73c4b2e3ab9 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/typedef_to_slice/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/typedef_to_slice/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_ListType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ListType) (errs field.ErrorList) { // type ListType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListType")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListType[*]") @@ -59,6 +63,9 @@ func Validate_ListType(ctx context.Context, op operation.Operation, fldPath *fie func Validate_ListTypedefType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ListTypedefType) (errs field.ErrorList) { // type ListTypedefType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListTypedefType")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListTypedefType[*]") @@ -70,6 +77,9 @@ func Validate_ListTypedefType(ctx context.Context, op operation.Operation, fldPa func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + 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, "type StringType")...) return errs @@ -77,6 +87,9 @@ func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *f func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -84,6 +97,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj ListType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") @@ -95,6 +111,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj ListTypedefType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachkey/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachkey/zz_generated.validations.go index 20b7530bed5..05603483916 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachkey/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachkey/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } 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.FixedResult(ctx, op, fldPath, obj, oldObj, false, "Struct.MapField(keys)") })...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[UnvalidatedStringType]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *UnvalidatedStringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "Struct.MapTypedefField(keys)") })...) @@ -71,6 +78,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapValidatedTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[ValidatedStringType]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ValidatedStringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "Struct.MapValidatedTypedefField(keys)") })...) @@ -81,6 +91,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypeField errs = append(errs, func(fldPath *field.Path, obj, oldObj UnvalidatedMapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } 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.FixedResult(ctx, op, fldPath, obj, oldObj, false, "Struct.MapTypeField(keys)") })...) @@ -90,6 +103,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ValidatedMapTypeField errs = append(errs, func(fldPath *field.Path, obj, oldObj ValidatedMapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } 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.FixedResult(ctx, op, fldPath, obj, oldObj, false, "Struct.ValidatedMapTypeField(keys)") })...) @@ -102,6 +118,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field func Validate_ValidatedMapType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ValidatedMapType) (errs field.ErrorList) { // type ValidatedMapType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } 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.FixedResult(ctx, op, fldPath, obj, oldObj, false, "ValidatedMapType(keys)") })...) @@ -111,6 +130,9 @@ func Validate_ValidatedMapType(ctx context.Context, op operation.Operation, fldP func Validate_ValidatedStringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ValidatedStringType) (errs field.ErrorList) { // type ValidatedStringType + 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, "ValidatedStringType")...) return errs diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_primitive/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_primitive/zz_generated.validations.go index 7e8f79fe17e..fc030f63ed0 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_primitive/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_primitive/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") })...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]StringType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_struct/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_struct/zz_generated.validations.go index 91dbe96de17..604838a8768 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_struct/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_struct/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") })...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_primitive/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_primitive/zz_generated.validations.go index 1e9dfc11098..922665ca515 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_primitive/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_primitive/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") })...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []StringType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_struct/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_struct/zz_generated.validations.go index e3166b543df..f8c03528e9e 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_struct/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_struct/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") })...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_map/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_map/zz_generated.validations.go index 3f034ef725e..a7b73881763 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_map/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_map/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { // type MapType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapType[*]") })...) @@ -58,6 +62,9 @@ func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *fiel func Validate_MapTypedefType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MapTypedefType) (errs field.ErrorList) { // type MapTypedefType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapTypedefType[*]") })...) @@ -68,6 +75,9 @@ func Validate_MapTypedefType(ctx context.Context, op operation.Operation, fldPat func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + 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, "type StringType")...) return errs @@ -75,6 +85,9 @@ func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *f func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -82,6 +95,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") })...) @@ -92,6 +108,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj MapTypedefType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_slice/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_slice/zz_generated.validations.go index 5796574cbd2..df48f76ed5b 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_slice/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_slice/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_ListType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ListType) (errs field.ErrorList) { // type ListType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListType[*]") })...) @@ -58,6 +62,9 @@ func Validate_ListType(ctx context.Context, op operation.Operation, fldPath *fie func Validate_ListTypedefType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ListTypedefType) (errs field.ErrorList) { // type ListTypedefType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListTypedefType[*]") })...) @@ -71,6 +78,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj ListType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") })...) @@ -81,6 +91,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj ListTypedefType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/immutable/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/immutable/zz_generated.validations.go index 4fb6bcffbc9..b6aff0a3255 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/immutable/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/immutable/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_ImmutableType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ImmutableType) (errs field.ErrorList) { // type ImmutableType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return errs @@ -60,6 +64,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.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.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.StringField }))...) @@ -67,6 +74,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringPtrField 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.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("stringPtrField"), obj.StringPtrField, safe.Field(oldObj, func(oldObj *Struct) *string { return oldObj.StringPtrField }))...) @@ -74,6 +84,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *ComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("structField"), &obj.StructField, safe.Field(oldObj, func(oldObj *Struct) *ComparableStruct { return &oldObj.StructField }))...) @@ -81,6 +94,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *ComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("structPtrField"), obj.StructPtrField, safe.Field(oldObj, func(oldObj *Struct) *ComparableStruct { return oldObj.StructPtrField }))...) @@ -88,6 +104,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.NonComparableStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *NonComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("noncomparableStructField"), &obj.NonComparableStructField, safe.Field(oldObj, func(oldObj *Struct) *NonComparableStruct { return &oldObj.NonComparableStructField }))...) @@ -95,6 +114,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.NonComparableStructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *NonComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("noncomparableStructPtrField"), obj.NonComparableStructPtrField, safe.Field(oldObj, func(oldObj *Struct) *NonComparableStruct { return oldObj.NonComparableStructPtrField }))...) @@ -102,6 +124,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SliceField errs = append(errs, func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("sliceField"), obj.SliceField, safe.Field(oldObj, func(oldObj *Struct) []string { return oldObj.SliceField }))...) @@ -109,6 +134,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("mapField"), obj.MapField, safe.Field(oldObj, func(oldObj *Struct) map[string]string { return oldObj.MapField }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/multiple_keys/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/multiple_keys/zz_generated.validations.go index eebdcd83c76..c6805afd2cc 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/multiple_keys/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/multiple_keys/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherStruct, b OtherStruct) bool { return a.Key1Field == b.Key1Field && a.Key2Field == b.Key2Field }, validate.ImmutableByReflect)...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherTypedefStruct, b OtherTypedefStruct) bool { return a.Key1Field == b.Key1Field && a.Key2Field == b.Key2Field }, validate.ImmutableByReflect)...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/single_key/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/single_key/zz_generated.validations.go index 501e4599546..807d77de92b 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/single_key/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/single_key/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherStruct, b OtherStruct) bool { return a.KeyField == b.KeyField }, validate.ImmutableByReflect)...) return }(fldPath.Child("listField"), obj.ListField, safe.Field(oldObj, func(oldObj *Struct) []OtherStruct { return oldObj.ListField }))...) @@ -60,6 +64,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherTypedefStruct, b OtherTypedefStruct) bool { return a.KeyField == b.KeyField }, validate.ImmutableByReflect)...) return }(fldPath.Child("listTypedefField"), obj.ListTypedefField, safe.Field(oldObj, func(oldObj *Struct) []OtherTypedefStruct { return oldObj.ListTypedefField }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/zz_generated.validations.go index c8172d6675e..21495665141 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/zz_generated.validations.go @@ -49,6 +49,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_IntType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { // type IntType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return errs @@ -60,6 +63,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.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.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *Struct) *int { return &oldObj.IntField }))...) @@ -67,6 +73,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntPtrField 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.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("intPtrField"), obj.IntPtrField, safe.Field(oldObj, func(oldObj *Struct) *int { return oldObj.IntPtrField }))...) @@ -74,6 +83,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Int16Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *int16) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("int16Field"), &obj.Int16Field, safe.Field(oldObj, func(oldObj *Struct) *int16 { return &oldObj.Int16Field }))...) @@ -81,6 +93,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Int32Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("int32Field"), &obj.Int32Field, safe.Field(oldObj, func(oldObj *Struct) *int32 { return &oldObj.Int32Field }))...) @@ -88,6 +103,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Int64Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *int64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("int64Field"), &obj.Int64Field, safe.Field(oldObj, func(oldObj *Struct) *int64 { return &oldObj.Int64Field }))...) @@ -95,6 +113,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.UintField errs = append(errs, func(fldPath *field.Path, obj, oldObj *uint) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("uintField"), &obj.UintField, safe.Field(oldObj, func(oldObj *Struct) *uint { return &oldObj.UintField }))...) @@ -102,6 +123,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.UintPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *uint) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("uintPtrField"), obj.UintPtrField, safe.Field(oldObj, func(oldObj *Struct) *uint { return oldObj.UintPtrField }))...) @@ -109,6 +133,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Uint16Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *uint16) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("uint16Field"), &obj.Uint16Field, safe.Field(oldObj, func(oldObj *Struct) *uint16 { return &oldObj.Uint16Field }))...) @@ -116,6 +143,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Uint32Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *uint32) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("uint32Field"), &obj.Uint32Field, safe.Field(oldObj, func(oldObj *Struct) *uint32 { return &oldObj.Uint32Field }))...) @@ -123,6 +153,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Uint64Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *uint64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("uint64Field"), &obj.Uint64Field, safe.Field(oldObj, func(oldObj *Struct) *uint64 { return &oldObj.Uint64Field }))...) @@ -130,6 +163,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.TypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) errs = append(errs, Validate_IntType(ctx, op, fldPath, obj, oldObj)...) return @@ -138,6 +174,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.TypedefPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) errs = append(errs, Validate_IntType(ctx, op, fldPath, obj, oldObj)...) return diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/opaque/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/opaque/zz_generated.validations.go index f303cf0cf28..6210190a545 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/opaque/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/opaque/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -77,6 +78,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_OtherString(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherString) (errs field.ErrorList) { // type OtherString + 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, "type OtherString")...) return errs @@ -84,11 +88,17 @@ func Validate_OtherString(ctx context.Context, op operation.Operation, fldPath * func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { // type OtherStruct + 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, "type OtherStruct")...) // field OtherStruct.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, "field OtherStruct.StringField")...) return }(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *OtherStruct) *string { return &oldObj.StringField }))...) @@ -102,6 +112,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (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, "field Struct.StructField")...) errs = append(errs, Validate_OtherStruct(ctx, op, fldPath, obj, oldObj)...) return @@ -110,6 +123,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -122,6 +138,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.OpaqueStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (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, "field Struct.OpaqueStructField")...) return }(fldPath.Child("opaqueStructField"), &obj.OpaqueStructField, safe.Field(oldObj, func(oldObj *Struct) *OtherStruct { return &oldObj.OpaqueStructField }))...) @@ -129,6 +148,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.OpaqueStructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -140,6 +162,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SliceOfStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.SliceOfStructField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.SliceOfStructField vals") @@ -151,6 +176,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SliceOfOpaqueStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.SliceOfOpaqueStructField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.SliceOfOpaqueStructField vals") @@ -161,6 +189,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListMapOfStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListMapOfStructField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherStruct, b OtherStruct) bool { return a.StringField == b.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListMapOfStructField vals") @@ -172,6 +203,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListMapOfOpaqueStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListMapOfOpaqueStructField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherStruct, b OtherStruct) bool { return a.StringField == b.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListMapOfOpaqueStructField vals") @@ -182,6 +216,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapOfStringToStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[OtherString]OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherString) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapOfStringToStructField keys") })...) @@ -197,6 +234,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapOfStringToOpaqueStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[OtherString]OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherString) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapOfStringToOpaqueStructField keys") })...) @@ -212,6 +252,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field func Validate_TypedefMapOther(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj TypedefMapOther) (errs field.ErrorList) { // type TypedefMapOther + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "type TypedefMapOther")...) return errs @@ -219,6 +262,9 @@ func Validate_TypedefMapOther(ctx context.Context, op operation.Operation, fldPa func Validate_TypedefSliceOther(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj TypedefSliceOther) (errs field.ErrorList) { // type TypedefSliceOther + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "type TypedefSliceOther")...) return errs diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/nonzero_defaults/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/nonzero_defaults/zz_generated.validations.go index 49829b71239..996057dcd70 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/nonzero_defaults/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/nonzero_defaults/zz_generated.validations.go @@ -53,6 +53,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.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 + } // optional fields with default values are effectively required if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -64,6 +67,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringPtrField 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 + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -75,6 +81,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.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 + } // optional fields with default values are effectively required if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -86,6 +95,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntPtrField 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 + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -97,6 +109,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.BoolField errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -108,6 +123,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.BoolPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zero_defaults/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zero_defaults/zz_generated.validations.go index afdc74af490..2cbb773d7b3 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zero_defaults/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zero_defaults/zz_generated.validations.go @@ -54,12 +54,18 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } return }(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.StringField }))...) // field Struct.StringPtrField 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 + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -72,12 +78,18 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } return }(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *Struct) *int { return &oldObj.IntField }))...) // field Struct.IntPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -90,12 +102,18 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } return }(fldPath.Child("boolField"), &obj.BoolField, safe.Field(oldObj, func(oldObj *Struct) *bool { return &oldObj.BoolField }))...) // field Struct.BoolPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zz_generated.validations.go index 78dd83d2321..9f187794952 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_IntType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { // type IntType + 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, "type IntType")...) return errs @@ -56,6 +60,9 @@ func Validate_IntType(ctx context.Context, op operation.Operation, fldPath *fiel func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { // type MapType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapType")...) return errs @@ -63,6 +70,9 @@ func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *fiel func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { // type OtherStruct + 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, "type OtherStruct")...) return errs @@ -70,6 +80,9 @@ func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath * func Validate_SliceType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj SliceType) (errs field.ErrorList) { // type SliceType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type SliceType")...) return errs @@ -77,6 +90,9 @@ func Validate_SliceType(ctx context.Context, op operation.Operation, fldPath *fi func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + 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, "type StringType")...) return errs @@ -88,6 +104,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.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 + } if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -98,6 +117,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringPtrField 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 + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -108,6 +130,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -119,6 +144,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringTypedefPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -130,6 +158,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.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 + } if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -140,6 +171,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntPtrField 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 + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -150,6 +184,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -161,6 +198,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntTypedefPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -172,6 +212,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.OtherStructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -183,6 +226,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SliceField errs = append(errs, func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -193,6 +239,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SliceTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj SliceType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -204,6 +253,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalMap(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -214,6 +266,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalMap(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/deep/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/deep/zz_generated.validations.go index b1df9d4d759..f5d5d438bc8 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/deep/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/deep/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "structField", func(o *OtherStruct) *SmallStruct { return &o.StructField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *SmallStruct) field.ErrorList { return validate.Subfield(ctx, op, fldPath, obj, oldObj, "stringField", func(o *SmallStruct) *string { return &o.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "StructField.StructField") @@ -78,6 +82,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "structField", func(o *OtherStruct) *SmallStruct { return &o.StructField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *SmallStruct) field.ErrorList { return validate.Subfield(ctx, op, fldPath, obj, oldObj, "stringField", func(o *SmallStruct) *string { return &o.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "StructPtrField.StructField") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/nonincluded/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/nonincluded/zz_generated.validations.go index 07b08f3fcd8..56834253659 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/nonincluded/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/nonincluded/zz_generated.validations.go @@ -52,6 +52,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructType errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.StructType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "stringField", func(o *other.StructType) *string { return &o.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "subfield Struct.(other.StructType).StringField") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shallow/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shallow/zz_generated.validations.go index 13e6aa3f44d..00bc98497f3 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shallow/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shallow/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "stringField", func(o *OtherStruct) *string { return &o.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "subfield Struct.StructField.StringField") })...) @@ -74,6 +78,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "stringField", func(o *OtherStruct) *string { return &o.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "subfield Struct.StructPtrField.StringField") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/issubresource/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/issubresource/zz_generated.validations.go index e09a2600158..48821544698 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/issubresource/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/issubresource/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S 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, true, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/root/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/root/zz_generated.validations.go index 7874ba13ecb..20f7445020c 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/root/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/root/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S 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, true, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/subresource/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/subresource/zz_generated.validations.go index c800d54b283..3ca113b5f1a 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/subresource/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/subresource/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S 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, true, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/zz_generated.validations.go index 5183ed29095..81e19cd3c08 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/zz_generated.validations.go @@ -53,6 +53,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.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, "field Struct.StringField")...) return }(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.StringField }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_true/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_true/zz_generated.validations.go index 6e4b4712470..5a425c89556 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_true/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_true/zz_generated.validations.go @@ -53,6 +53,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.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, true, "field Struct.StringField")...) return }(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.StringField }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/type_args/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/type_args/zz_generated.validations.go index 76b94a9e383..7707e9a9005 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/type_args/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/type_args/zz_generated.validations.go @@ -50,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { // type E1 + 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, "type E1")...) return errs @@ -61,6 +64,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (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[*primitives.T1](ctx, op, fldPath, obj, oldObj, false, "T1.S1")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -69,6 +75,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PS1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (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[*primitives.T1](ctx, op, fldPath, obj, oldObj, false, "PT1.PS1")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -77,6 +86,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E1) (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[*E1](ctx, op, fldPath, obj, oldObj, false, "T1.E1")...) errs = append(errs, Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -85,6 +97,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PE1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E1) (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[*E1](ctx, op, fldPath, obj, oldObj, true, "T1.PE1")...) errs = append(errs, Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -93,6 +108,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.I1 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[*int](ctx, op, fldPath, obj, oldObj, false, "T1.I1")...) return }(fldPath.Child("i1"), &obj.I1, safe.Field(oldObj, func(oldObj *T1) *int { return &oldObj.I1 }))...) @@ -100,6 +118,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PI1 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[*int](ctx, op, fldPath, obj, oldObj, true, "T1.PI1")...) return }(fldPath.Child("pi1"), obj.PI1, safe.Field(oldObj, func(oldObj *T1) *int { return oldObj.PI1 }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/typedefs/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/typedefs/zz_generated.validations.go index 5b7fc13c04d..90ace869022 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/typedefs/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/typedefs/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { // type E1 + 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, "type E1")...) return errs @@ -56,6 +60,9 @@ func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_E2(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E2) (errs field.ErrorList) { // type E2 + 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, "type E2")...) return errs @@ -63,6 +70,9 @@ func Validate_E2(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_E3(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E3) (errs field.ErrorList) { // type E3 + 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, "type E3")...) return errs @@ -70,11 +80,17 @@ func Validate_E3(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_E4(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E4) (errs field.ErrorList) { // type E4 + 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, "type E4")...) // field E4.S 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, "field T2.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *E4) *string { return &oldObj.S }))...) @@ -84,6 +100,9 @@ func Validate_E4(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { // type T1 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T1")...) // field T1.TypeMeta has no validation @@ -91,6 +110,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E1) (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, "field T1.E1")...) errs = append(errs, Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -99,6 +121,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PE1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E1) (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, "field T1.PE1")...) errs = append(errs, Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -107,6 +132,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E2) (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, "field T1.E2")...) errs = append(errs, Validate_E2(ctx, op, fldPath, obj, oldObj)...) return @@ -115,6 +143,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PE2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E2) (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, "field T1.PE2")...) errs = append(errs, Validate_E2(ctx, op, fldPath, obj, oldObj)...) return @@ -123,6 +154,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E3) (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, "field T1.E3")...) errs = append(errs, Validate_E3(ctx, op, fldPath, obj, oldObj)...) return @@ -131,6 +165,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PE3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E3) (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, "field T1.PE3")...) errs = append(errs, Validate_E3(ctx, op, fldPath, obj, oldObj)...) return @@ -139,6 +176,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E4 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E4) (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, "field T1.E4")...) errs = append(errs, Validate_E4(ctx, op, fldPath, obj, oldObj)...) return @@ -147,6 +187,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PE4 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E4) (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, "field T1.PE4")...) errs = append(errs, Validate_E4(ctx, op, fldPath, obj, oldObj)...) return @@ -155,6 +198,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (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, "field T1.T2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -163,6 +209,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PT2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (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, "field T1.PT2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -173,11 +222,17 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { // type T2 + 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, "type T2")...) // field T2.S 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, "field T2.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T2) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/lists/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/lists/zz_generated.validations.go index 0fba0ff9c40..f586748d8f5 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/lists/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/lists/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -58,6 +59,9 @@ func Validate_M1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field M1.S 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, "M1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *M1) *string { return &oldObj.S }))...) @@ -69,6 +73,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.LM1 errs = append(errs, func(fldPath *field.Path, obj, oldObj []M1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_M1)...) return }(fldPath.Child("lm1"), obj.LM1, safe.Field(oldObj, func(oldObj *T1) []M1 { return oldObj.LM1 }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/maps/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/maps/zz_generated.validations.go index 8038e0df057..7b8e86a35a4 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/maps/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/maps/zz_generated.validations.go @@ -25,6 +25,7 @@ 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" @@ -58,6 +59,9 @@ func Validate_M1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field M1.S 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, "M1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *M1) *string { return &oldObj.S }))...) @@ -69,6 +73,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.MSM1 errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]M1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, Validate_M1)...) return }(fldPath.Child("msm1"), obj.MSM1, safe.Field(oldObj, func(oldObj *T1) map[string]M1 { return oldObj.MSM1 }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitive_pointers/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitive_pointers/zz_generated.validations.go index 5541b49597e..2b92cfb9ace 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitive_pointers/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitive_pointers/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SP 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.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("sp"), obj.SP, safe.Field(oldObj, func(oldObj *Struct) *string { return oldObj.SP }))...) @@ -58,6 +61,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IP 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.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("ip"), obj.IP, safe.Field(oldObj, func(oldObj *Struct) *int { return oldObj.IP }))...) @@ -65,6 +71,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.BP errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("bp"), obj.BP, safe.Field(oldObj, func(oldObj *Struct) *bool { return oldObj.BP }))...) @@ -72,6 +81,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.FP errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("fp"), obj.FP, safe.Field(oldObj, func(oldObj *Struct) *float64 { return oldObj.FP }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitives/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitives/zz_generated.validations.go index d3a973b1e1d..3bad101b188 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitives/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitives/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.S 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.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.S }))...) @@ -58,6 +61,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.I 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.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("i"), &obj.I, safe.Field(oldObj, func(oldObj *Struct) *int { return &oldObj.I }))...) @@ -65,6 +71,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.B errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("b"), &obj.B, safe.Field(oldObj, func(oldObj *Struct) *bool { return &oldObj.B }))...) @@ -72,6 +81,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.F errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("f"), &obj.F, safe.Field(oldObj, func(oldObj *Struct) *float64 { return &oldObj.F }))...) From 9384285c604093d108365300b6e1f8f6633a3ebd Mon Sep 17 00:00:00 2001 From: yongruilin Date: Tue, 13 May 2025 17:27:14 +0000 Subject: [PATCH 4/5] add ratcheting testcase for minimum tag --- .../output_tests/tags/minimum/doc_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/doc_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/doc_test.go index d084889683e..46d416e2f28 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/doc_test.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/doc_test.go @@ -46,6 +46,16 @@ func Test(t *testing.T) { field.Invalid(field.NewPath("typedefField"), 0, content.MinError(1)), field.Invalid(field.NewPath("typedefPtrField"), 0, content.MinError(1)), ) + // Test validation ratcheting + st.Value(&Struct{ + IntPtrField: ptr.To(0), + UintPtrField: ptr.To(uint(0)), + TypedefPtrField: ptr.To(IntType(0)), + }).OldValue(&Struct{ + IntPtrField: ptr.To(0), + UintPtrField: ptr.To(uint(0)), + TypedefPtrField: ptr.To(IntType(0)), + }).ExpectValid() st.Value(&Struct{ IntField: 1, From ac467d3aea4c4b45e0e8c6453322bcb4bccd5f39 Mon Sep 17 00:00:00 2001 From: yongruilin Date: Tue, 13 May 2025 17:47:57 +0000 Subject: [PATCH 5/5] add ratcheting testcase for validateFalse tag --- .../output_tests/tags/validate_false/doc_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/doc_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/doc_test.go index 564382d84c2..3bbc4edecca 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/doc_test.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/doc_test.go @@ -28,10 +28,18 @@ func Test(t *testing.T) { }).ExpectValidateFalseByPath(map[string][]string{ "stringField": {"field Struct.StringField"}, }) + // Test validation ratcheting + st.Value(&Struct{}).OldValue(&Struct{}).ExpectValid() st.Value(&Struct{ StringField: "abc", }).ExpectValidateFalseByPath(map[string][]string{ "stringField": {"field Struct.StringField"}, }) + // Test validation ratcheting + st.Value(&Struct{ + StringField: "abc", + }).OldValue(&Struct{ + StringField: "abc", + }).ExpectValid() }