From c374ab81185e59a4c3a7584c597917bf9dcc20ca Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Tue, 6 May 2025 17:29:32 +0200 Subject: [PATCH 1/3] Add a breaking example for applyconfig-gen This is based on the reproducer for https://github.com/kubernetes/kubernetes/issues/131533. Signed-off-by: Stephen Kitt --- .../examples/crd/apis/conflicting/v1/types.go | 12 ++++- .../crd/apis/gateway-api/v1/shared_types.go | 26 ++++++++++ .../apis/gateway-api/v1alpha2/policy_types.go | 47 +++++++++++++++++++ 3 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 staging/src/k8s.io/code-generator/examples/crd/apis/gateway-api/v1/shared_types.go create mode 100644 staging/src/k8s.io/code-generator/examples/crd/apis/gateway-api/v1alpha2/policy_types.go diff --git a/staging/src/k8s.io/code-generator/examples/crd/apis/conflicting/v1/types.go b/staging/src/k8s.io/code-generator/examples/crd/apis/conflicting/v1/types.go index 12e63b54ca5..4cb2945cee2 100644 --- a/staging/src/k8s.io/code-generator/examples/crd/apis/conflicting/v1/types.go +++ b/staging/src/k8s.io/code-generator/examples/crd/apis/conflicting/v1/types.go @@ -16,7 +16,10 @@ limitations under the License. package v1 -import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gwv1alpha2 "k8s.io/code-generator/examples/crd/apis/gateway-api/v1alpha2" +) // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -45,7 +48,12 @@ type TestTypeList struct { } type TestTypeStatus struct { - Blah string `json:"blah"` + Blah string `json:"blah"` + gwv1alpha2.PolicyStatus `json:",inline"` +} + +type TestTypeEmbeddedStatus struct { + Statuses []TestTypeEmbeddedStatus `json:"statuses"` } // TestEmbeddedType is a type intended to create conflicts with other embedded structs: diff --git a/staging/src/k8s.io/code-generator/examples/crd/apis/gateway-api/v1/shared_types.go b/staging/src/k8s.io/code-generator/examples/crd/apis/gateway-api/v1/shared_types.go new file mode 100644 index 00000000000..41453a69e75 --- /dev/null +++ b/staging/src/k8s.io/code-generator/examples/crd/apis/gateway-api/v1/shared_types.go @@ -0,0 +1,26 @@ +/* +Copyright 2020 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 v1 + +// Copied from Gateway API as a minimal reproducer for #131533 + +type ParentReference struct { +} + +func (in *ParentReference) DeepCopyInto(out *ParentReference) { + *out = *in +} diff --git a/staging/src/k8s.io/code-generator/examples/crd/apis/gateway-api/v1alpha2/policy_types.go b/staging/src/k8s.io/code-generator/examples/crd/apis/gateway-api/v1alpha2/policy_types.go new file mode 100644 index 00000000000..e5e1fe9e70b --- /dev/null +++ b/staging/src/k8s.io/code-generator/examples/crd/apis/gateway-api/v1alpha2/policy_types.go @@ -0,0 +1,47 @@ +/* +Copyright 2021 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 v1alpha2 + +import ( + v1 "k8s.io/code-generator/examples/crd/apis/gateway-api/v1" +) + +// Copied from Gateway API as a minimal reproducer for #131533 + +type PolicyAncestorStatus struct { + AncestorRef v1.ParentReference `json:"ancestorRef"` +} + +type PolicyStatus struct { + Ancestors []PolicyAncestorStatus `json:"ancestors"` +} + +func (in *PolicyAncestorStatus) DeepCopyInto(out *PolicyAncestorStatus) { + *out = *in + in.AncestorRef.DeepCopyInto(&out.AncestorRef) +} + +func (in *PolicyStatus) DeepCopyInto(out *PolicyStatus) { + *out = *in + if in.Ancestors != nil { + in, out := &in.Ancestors, &out.Ancestors + *out = make([]PolicyAncestorStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} From 4eb9fb21b692bedca1de9289fd8a14fa19f40b25 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Mon, 5 May 2025 18:37:21 +0200 Subject: [PATCH 2/3] applyconfig-gen: handle non-pointer embedded members Check that Elem is non-nil before dereferencing it. Also handle non-empty names for named non-pointer embedded members. Signed-off-by: Stephen Kitt --- .../generators/applyconfiguration.go | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/staging/src/k8s.io/code-generator/cmd/applyconfiguration-gen/generators/applyconfiguration.go b/staging/src/k8s.io/code-generator/cmd/applyconfiguration-gen/generators/applyconfiguration.go index 9b6aa7cec9f..4deb1771875 100644 --- a/staging/src/k8s.io/code-generator/cmd/applyconfiguration-gen/generators/applyconfiguration.go +++ b/staging/src/k8s.io/code-generator/cmd/applyconfiguration-gen/generators/applyconfiguration.go @@ -288,9 +288,9 @@ func (g *applyConfigurationGenerator) generateMemberWith(sw *generator.SnippetWr sw.Do("func (b *$.ApplyConfig.ApplyConfiguration|public$) With$.Member.Name$(value $.MemberType|raw$) *$.ApplyConfig.ApplyConfiguration|public$ {\n", memberParams) g.ensureEmbedExistsIfApplicable(sw, memberParams) if g.refGraph.isApplyConfig(memberParams.Member.Type) || isNillable(memberParams.Member.Type) { - sw.Do("b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = value\n", memberParams) + sw.Do("b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = value\n", memberParams) } else { - sw.Do("b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = &value\n", memberParams) + sw.Do("b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = &value\n", memberParams) } sw.Do(" return b\n", memberParams) sw.Do("}\n", memberParams) @@ -304,7 +304,7 @@ func (g *applyConfigurationGenerator) generateMemberGetter(sw *generator.Snippet sw.Do("func (b *$.ApplyConfig.ApplyConfiguration|public$) Get$.Member.Name$() *$.MemberType|raw$ {\n", memberParams) } g.ensureEmbedExistsIfApplicable(sw, memberParams) - sw.Do(" return b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$\n", memberParams) + sw.Do(" return b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$\n", memberParams) sw.Do("}\n", memberParams) } @@ -331,15 +331,15 @@ func (g *applyConfigurationGenerator) generateMemberWithForSlice(sw *generator.S sw.Do("}\n", memberParams) if memberIsPointerToSlice { - sw.Do("*b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = append(*b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$, *values[i])\n", memberParams) + sw.Do("*b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = append(*b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$, *values[i])\n", memberParams) } else { - sw.Do("b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = append(b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$, *values[i])\n", memberParams) + sw.Do("b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = append(b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$, *values[i])\n", memberParams) } } else { if memberIsPointerToSlice { - sw.Do("*b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = append(*b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$, values[i])\n", memberParams) + sw.Do("*b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = append(*b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$, values[i])\n", memberParams) } else { - sw.Do("b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = append(b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$, values[i])\n", memberParams) + sw.Do("b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = append(b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$, values[i])\n", memberParams) } } sw.Do(" }\n", memberParams) @@ -354,11 +354,11 @@ func (g *applyConfigurationGenerator) generateMemberWithForMap(sw *generator.Sni sw.Do("// overwriting an existing map entries in $.Member.Name$ field with the same key.\n", memberParams) sw.Do("func (b *$.ApplyConfig.ApplyConfiguration|public$) With$.Member.Name$(entries $.MemberType|raw$) *$.ApplyConfig.ApplyConfiguration|public$ {\n", memberParams) g.ensureEmbedExistsIfApplicable(sw, memberParams) - sw.Do(" if b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ == nil && len(entries) > 0 {\n", memberParams) - sw.Do(" b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$ = make($.MemberType|raw$, len(entries))\n", memberParams) + sw.Do(" if b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ == nil && len(entries) > 0 {\n", memberParams) + sw.Do(" b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$ = make($.MemberType|raw$, len(entries))\n", memberParams) sw.Do(" }\n", memberParams) sw.Do(" for k, v := range entries {\n", memberParams) - sw.Do(" b$if ne .EmbeddedIn nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$.$.Member.Name$[k] = v\n", memberParams) + sw.Do(" b$if ne .EmbeddedIn nil$$if ne .EmbeddedIn.MemberType.Name.Name \"\"$.$.EmbeddedIn.MemberType.Name.Name$$else if ne .EmbeddedIn.MemberType.Elem nil$.$.EmbeddedIn.MemberType.Elem.Name.Name$$end$$end$.$.Member.Name$[k] = v\n", memberParams) sw.Do(" }\n", memberParams) sw.Do(" return b\n", memberParams) sw.Do("}\n", memberParams) From ee94ba913ade69564674d95332f7b5474b7f3d74 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Wed, 14 May 2025 16:51:28 +0200 Subject: [PATCH 3/3] Run codegen Signed-off-by: Stephen Kitt --- .../conflicting/v1/zz_generated.deepcopy.go | 26 ++++++++++++++++++- .../conflicting/v1/testtypestatus.go | 17 +++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/staging/src/k8s.io/code-generator/examples/crd/apis/conflicting/v1/zz_generated.deepcopy.go b/staging/src/k8s.io/code-generator/examples/crd/apis/conflicting/v1/zz_generated.deepcopy.go index a73a4d908f7..36d2e1d3b2f 100644 --- a/staging/src/k8s.io/code-generator/examples/crd/apis/conflicting/v1/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/code-generator/examples/crd/apis/conflicting/v1/zz_generated.deepcopy.go @@ -46,7 +46,7 @@ func (in *TestType) DeepCopyInto(out *TestType) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) out.TestEmbeddedType = in.TestEmbeddedType return } @@ -69,6 +69,29 @@ func (in *TestType) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TestTypeEmbeddedStatus) DeepCopyInto(out *TestTypeEmbeddedStatus) { + *out = *in + if in.Statuses != nil { + in, out := &in.Statuses, &out.Statuses + *out = make([]TestTypeEmbeddedStatus, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TestTypeEmbeddedStatus. +func (in *TestTypeEmbeddedStatus) DeepCopy() *TestTypeEmbeddedStatus { + if in == nil { + return nil + } + out := new(TestTypeEmbeddedStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TestTypeList) DeepCopyInto(out *TestTypeList) { *out = *in @@ -105,6 +128,7 @@ func (in *TestTypeList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TestTypeStatus) DeepCopyInto(out *TestTypeStatus) { *out = *in + in.PolicyStatus.DeepCopyInto(&out.PolicyStatus) return } diff --git a/staging/src/k8s.io/code-generator/examples/crd/applyconfiguration/conflicting/v1/testtypestatus.go b/staging/src/k8s.io/code-generator/examples/crd/applyconfiguration/conflicting/v1/testtypestatus.go index 246b2ee1889..296f10bdf62 100644 --- a/staging/src/k8s.io/code-generator/examples/crd/applyconfiguration/conflicting/v1/testtypestatus.go +++ b/staging/src/k8s.io/code-generator/examples/crd/applyconfiguration/conflicting/v1/testtypestatus.go @@ -18,10 +18,15 @@ limitations under the License. package v1 +import ( + v1alpha2 "k8s.io/code-generator/examples/crd/apis/gateway-api/v1alpha2" +) + // TestTypeStatusApplyConfiguration represents a declarative configuration of the TestTypeStatus type for use // with apply. type TestTypeStatusApplyConfiguration struct { - Blah *string `json:"blah,omitempty"` + Blah *string `json:"blah,omitempty"` + v1alpha2.PolicyStatus `json:",inline"` } // TestTypeStatusApplyConfiguration constructs a declarative configuration of the TestTypeStatus type for use with @@ -37,3 +42,13 @@ func (b *TestTypeStatusApplyConfiguration) WithBlah(value string) *TestTypeStatu b.Blah = &value return b } + +// WithAncestors adds the given value to the Ancestors field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Ancestors field. +func (b *TestTypeStatusApplyConfiguration) WithAncestors(values ...v1alpha2.PolicyAncestorStatus) *TestTypeStatusApplyConfiguration { + for i := range values { + b.PolicyStatus.Ancestors = append(b.PolicyStatus.Ancestors, values[i]) + } + return b +}