From 7166a194f0b13abebe1df3d8e4b2b4b47243e2e5 Mon Sep 17 00:00:00 2001 From: Yewolf Date: Wed, 19 Nov 2025 14:42:51 -0500 Subject: [PATCH 1/4] (feat) add necessary changes to support applyconfiguration gen --- pkg/plugins/golang/v4/scaffolds/api.go | 1 + .../scaffolds/internal/templates/api/doc.go | 59 +++++++++++++++++++ .../scaffolds/internal/templates/api/group.go | 7 +-- .../scaffolds/internal/templates/api/types.go | 3 + 4 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 pkg/plugins/golang/v4/scaffolds/internal/templates/api/doc.go diff --git a/pkg/plugins/golang/v4/scaffolds/api.go b/pkg/plugins/golang/v4/scaffolds/api.go index aa8c708417a..e4b088914f5 100644 --- a/pkg/plugins/golang/v4/scaffolds/api.go +++ b/pkg/plugins/golang/v4/scaffolds/api.go @@ -100,6 +100,7 @@ func (s *apiScaffolder) Scaffold() error { if err := scaffold.Execute( &api.Types{Force: s.force}, &api.Group{}, + &api.Doc{}, ); err != nil { return fmt.Errorf("error scaffolding APIs: %w", err) } diff --git a/pkg/plugins/golang/v4/scaffolds/internal/templates/api/doc.go b/pkg/plugins/golang/v4/scaffolds/internal/templates/api/doc.go new file mode 100644 index 00000000000..031976b0bfc --- /dev/null +++ b/pkg/plugins/golang/v4/scaffolds/internal/templates/api/doc.go @@ -0,0 +1,59 @@ +/* +Copyright 2022 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 api + +import ( + log "log/slog" + "path/filepath" + + "sigs.k8s.io/kubebuilder/v4/pkg/machinery" +) + +var _ machinery.Template = &Doc{} + +// Doc scaffolds the doc file that defines the groupName +type Doc struct { + machinery.TemplateMixin + machinery.MultiGroupMixin + machinery.BoilerplateMixin + machinery.ResourceMixin +} + +// SetTemplateDefaults implements machinery.Template +func (f *Doc) SetTemplateDefaults() error { + if f.Path == "" { + if f.MultiGroup && f.Resource.Group != "" { + f.Path = filepath.Join("api", "%[group]", "%[version]", "doc.go") + } else { + f.Path = filepath.Join("api", "%[version]", "doc.go") + } + } + + f.Path = f.Resource.Replacer().Replace(f.Path) + log.Info(f.Path) + f.TemplateBody = docTemplate + + return nil +} + +//nolint:lll +const docTemplate = `{{ .Boilerplate }} + +// Package {{ .Resource.Version }} contains API Schema definitions for the {{ .Resource.Group }} {{ .Resource.Version }} API group. +// +groupName={{ .Resource.QualifiedGroup }} +package {{ .Resource.Version }} +` diff --git a/pkg/plugins/golang/v4/scaffolds/internal/templates/api/group.go b/pkg/plugins/golang/v4/scaffolds/internal/templates/api/group.go index 817722b461b..a2210ae0c2c 100644 --- a/pkg/plugins/golang/v4/scaffolds/internal/templates/api/group.go +++ b/pkg/plugins/golang/v4/scaffolds/internal/templates/api/group.go @@ -55,7 +55,6 @@ const groupTemplate = `{{ .Boilerplate }} // Package {{ .Resource.Version }} contains API Schema definitions for the {{ .Resource.Group }} {{ .Resource.Version }} API group. // +kubebuilder:object:generate=true -// +groupName={{ .Resource.QualifiedGroup }} package {{ .Resource.Version }} import ( @@ -64,11 +63,11 @@ import ( ) var ( - // GroupVersion is group version used to register these objects. - GroupVersion = schema.GroupVersion{Group: "{{ .Resource.QualifiedGroup }}", Version: "{{ .Resource.Version }}"} + // SchemeGroupVersion is group version used to register these objects. + SchemeGroupVersion = schema.GroupVersion{Group: "{{ .Resource.QualifiedGroup }}", Version: "{{ .Resource.Version }}"} // SchemeBuilder is used to add go types to the GroupVersionKind scheme. - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} // AddToScheme adds the types in this group-version to the given scheme. AddToScheme = SchemeBuilder.AddToScheme diff --git a/pkg/plugins/golang/v4/scaffolds/internal/templates/api/types.go b/pkg/plugins/golang/v4/scaffolds/internal/templates/api/types.go index 6996047e875..bdf1b2bbc7a 100644 --- a/pkg/plugins/golang/v4/scaffolds/internal/templates/api/types.go +++ b/pkg/plugins/golang/v4/scaffolds/internal/templates/api/types.go @@ -64,6 +64,7 @@ func (f *Types) SetTemplateDefaults() error { //nolint:lll const typesTemplate = `{{ .Boilerplate }} +// +kubebuilder:ac:generate=true package {{ .Resource.Version }} import ( @@ -116,6 +117,8 @@ type {{ .Resource.Kind }}Status struct { // +kubebuilder:resource:scope=Cluster {{- else if not .Resource.IsRegularPlural }} // +kubebuilder:resource:path={{ .Resource.Plural }} +{{- else }} +// +kubebuilder:resource {{- end }} // {{ .Resource.Kind }} is the Schema for the {{ .Resource.Plural }} API From 8d51a9fc6e12e6bbd551d155d566a1c48b4d5af7 Mon Sep 17 00:00:00 2001 From: Yewolf Date: Wed, 19 Nov 2025 15:18:58 -0500 Subject: [PATCH 2/4] (fix) reverts breaking change on GroupVersion --- .../golang/v4/scaffolds/internal/templates/api/group.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/plugins/golang/v4/scaffolds/internal/templates/api/group.go b/pkg/plugins/golang/v4/scaffolds/internal/templates/api/group.go index a2210ae0c2c..612ebe35a94 100644 --- a/pkg/plugins/golang/v4/scaffolds/internal/templates/api/group.go +++ b/pkg/plugins/golang/v4/scaffolds/internal/templates/api/group.go @@ -63,8 +63,11 @@ import ( ) var ( - // SchemeGroupVersion is group version used to register these objects. - SchemeGroupVersion = schema.GroupVersion{Group: "{{ .Resource.QualifiedGroup }}", Version: "{{ .Resource.Version }}"} + // GroupVersion is group version used to register these objects. + GroupVersion = schema.GroupVersion{Group: "{{ .Resource.QualifiedGroup }}", Version: "{{ .Resource.Version }}"} + + // SchemeGroupVersion is necessary for applyconfiguration codegen. + SchemeGroupVersion = GroupVersion // SchemeBuilder is used to add go types to the GroupVersionKind scheme. SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} From 473f3ca659cf7876745db13564f392969e9ade07 Mon Sep 17 00:00:00 2001 From: Yewolf Date: Wed, 19 Nov 2025 15:34:45 -0500 Subject: [PATCH 3/4] (feat) add an option for applyconfiguration --- pkg/cli/alpha/internal/generate.go | 5 +++++ pkg/model/resource/api.go | 6 ++++++ pkg/model/resource/resource.go | 5 +++++ pkg/plugins/golang/options.go | 8 ++++++-- pkg/plugins/golang/v4/api.go | 1 + .../golang/v4/scaffolds/internal/templates/api/types.go | 4 ++++ 6 files changed, 27 insertions(+), 2 deletions(-) diff --git a/pkg/cli/alpha/internal/generate.go b/pkg/cli/alpha/internal/generate.go index c31f88bb158..bb410c79cb8 100644 --- a/pkg/cli/alpha/internal/generate.go +++ b/pkg/cli/alpha/internal/generate.go @@ -521,6 +521,11 @@ func getAPIResourceFlags(res resource.Resource) []string { } else { args = append(args, "--namespaced=false") } + if res.API.GenerateApplyConfiguration { + args = append(args, "--generate-apply-configuration") + } else { + args = append(args, "--generate-apply-configuration=false") + } } if res.Controller { args = append(args, "--controller") diff --git a/pkg/model/resource/api.go b/pkg/model/resource/api.go index 002205161ca..c346bc24198 100644 --- a/pkg/model/resource/api.go +++ b/pkg/model/resource/api.go @@ -27,6 +27,9 @@ type API struct { // Namespaced is true if the API is namespaced. Namespaced bool `json:"namespaced,omitempty"` + + // GenerateApplyConfiguration indicates whether to generate applyconfiguration code for the resource. + GenerateApplyConfiguration bool `json:"generateApplyConfiguration,omitempty"` } // Validate checks that the API is valid. @@ -65,6 +68,9 @@ func (api *API) Update(other *API) error { // Update the namespace. api.Namespaced = api.Namespaced || other.Namespaced + // Update the generate apply configuration flag. + api.GenerateApplyConfiguration = api.GenerateApplyConfiguration || other.GenerateApplyConfiguration + return nil } diff --git a/pkg/model/resource/resource.go b/pkg/model/resource/resource.go index 4c429b3642e..197bee268c0 100644 --- a/pkg/model/resource/resource.go +++ b/pkg/model/resource/resource.go @@ -140,6 +140,11 @@ func (r Resource) IsRegularPlural() bool { return r.Plural == RegularPlural(r.Kind) } +// HasApplyConfiguration returns true if the resource has applyconfiguration generation enabled. +func (r Resource) HasApplyConfiguration() bool { + return r.API != nil && r.API.GenerateApplyConfiguration +} + // Copy returns a deep copy of the Resource that can be safely modified without affecting the original. func (r Resource) Copy() Resource { // As this function doesn't use a pointer receiver, r is already a shallow copy. diff --git a/pkg/plugins/golang/options.go b/pkg/plugins/golang/options.go index 438d245ea11..8c73d091726 100644 --- a/pkg/plugins/golang/options.go +++ b/pkg/plugins/golang/options.go @@ -68,6 +68,9 @@ type Options struct { // Namespaced is true if the resource should be namespaced. Namespaced bool + // GenerateApplyConfiguration indicates whether to generate applyconfiguration code for the resource. + GenerateApplyConfiguration bool + // Flags that define which parts should be scaffolded DoAPI bool DoController bool @@ -95,8 +98,9 @@ func (opts Options) UpdateResource(res *resource.Resource, c config.Config) { res.Path = resource.APIPackagePath(c.GetRepository(), res.Group, res.Version, c.IsMultiGroup()) res.API = &resource.API{ - CRDVersion: "v1", - Namespaced: opts.Namespaced, + CRDVersion: "v1", + Namespaced: opts.Namespaced, + GenerateApplyConfiguration: opts.GenerateApplyConfiguration, } } diff --git a/pkg/plugins/golang/v4/api.go b/pkg/plugins/golang/v4/api.go index 3d49fb10966..1ea2d11dfec 100644 --- a/pkg/plugins/golang/v4/api.go +++ b/pkg/plugins/golang/v4/api.go @@ -104,6 +104,7 @@ func (p *createAPISubcommand) BindFlags(fs *pflag.FlagSet) { "if set, generate the resource without prompting the user") p.resourceFlag = fs.Lookup("resource") fs.BoolVar(&p.options.Namespaced, "namespaced", true, "resource is namespaced") + fs.BoolVar(&p.options.GenerateApplyConfiguration, "generate-apply-configuration", false, "if set, generate applyconfiguration code for the resource") fs.BoolVar(&p.options.DoController, "controller", true, "if set, generate the controller without prompting the user") diff --git a/pkg/plugins/golang/v4/scaffolds/internal/templates/api/types.go b/pkg/plugins/golang/v4/scaffolds/internal/templates/api/types.go index bdf1b2bbc7a..65f5a147360 100644 --- a/pkg/plugins/golang/v4/scaffolds/internal/templates/api/types.go +++ b/pkg/plugins/golang/v4/scaffolds/internal/templates/api/types.go @@ -64,7 +64,11 @@ func (f *Types) SetTemplateDefaults() error { //nolint:lll const typesTemplate = `{{ .Boilerplate }} +{{ if .Resource.HasApplyConfiguration }} // +kubebuilder:ac:generate=true +{{ else }} +// +kubebuilder:ac:generate=false +{{ end }} package {{ .Resource.Version }} import ( From c91bfa9f077a3ec618c7767581e992c25baf008a Mon Sep 17 00:00:00 2001 From: Yewolf Date: Wed, 19 Nov 2025 15:39:16 -0500 Subject: [PATCH 4/4] (feat) add makefile and set generation to enabled by default --- pkg/plugins/golang/v4/api.go | 2 +- .../golang/v4/scaffolds/internal/templates/makefile.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/plugins/golang/v4/api.go b/pkg/plugins/golang/v4/api.go index 1ea2d11dfec..4aad0995e3a 100644 --- a/pkg/plugins/golang/v4/api.go +++ b/pkg/plugins/golang/v4/api.go @@ -104,7 +104,7 @@ func (p *createAPISubcommand) BindFlags(fs *pflag.FlagSet) { "if set, generate the resource without prompting the user") p.resourceFlag = fs.Lookup("resource") fs.BoolVar(&p.options.Namespaced, "namespaced", true, "resource is namespaced") - fs.BoolVar(&p.options.GenerateApplyConfiguration, "generate-apply-configuration", false, "if set, generate applyconfiguration code for the resource") + fs.BoolVar(&p.options.GenerateApplyConfiguration, "generate-apply-configuration", true, "if set, generate applyconfiguration code for the resource") fs.BoolVar(&p.options.DoController, "controller", true, "if set, generate the controller without prompting the user") diff --git a/pkg/plugins/golang/v4/scaffolds/internal/templates/makefile.go b/pkg/plugins/golang/v4/scaffolds/internal/templates/makefile.go index 4ee5fbc7ff7..eb0b1bdf0fd 100644 --- a/pkg/plugins/golang/v4/scaffolds/internal/templates/makefile.go +++ b/pkg/plugins/golang/v4/scaffolds/internal/templates/makefile.go @@ -123,9 +123,9 @@ manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and Cust .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. {{ if .BoilerplatePath -}} - "$(CONTROLLER_GEN)" object:headerFile={{printf "%q" .BoilerplatePath}} paths="./..." + "$(CONTROLLER_GEN)" applyconfiguration:headerFile="hack/boilerplate.go.txt" object:headerFile={{printf "%q" .BoilerplatePath}} paths="./..." {{- else -}} - "$(CONTROLLER_GEN)" object paths="./..." + "$(CONTROLLER_GEN)" applyconfiguration object paths="./..." {{- end }} .PHONY: fmt