Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/cli/alpha/internal/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
6 changes: 6 additions & 0 deletions pkg/model/resource/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}

Expand Down
5 changes: 5 additions & 0 deletions pkg/model/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 6 additions & 2 deletions pkg/plugins/golang/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
}
}

Expand Down
1 change: 1 addition & 0 deletions pkg/plugins/golang/v4/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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", true, "if set, generate applyconfiguration code for the resource")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it be done per API or the whole project need to be changed to adopted this?
I mean, can I have 3 APIs that uses applyconfiguration and 3 that do not use them in the same project?
Is that possible?


fs.BoolVar(&p.options.DoController, "controller", true,
"if set, generate the controller without prompting the user")
Expand Down
1 change: 1 addition & 0 deletions pkg/plugins/golang/v4/scaffolds/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func (s *apiScaffolder) Scaffold() error {
if err := scaffold.Execute(
&api.Types{Force: s.force},
&api.Group{},
&api.Doc{},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only generate the new files if the/when the flag is informed

); err != nil {
return fmt.Errorf("error scaffolding APIs: %w", err)
}
Expand Down
59 changes: 59 additions & 0 deletions pkg/plugins/golang/v4/scaffolds/internal/templates/api/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
Copy link
Member

@camilamacedo86 camilamacedo86 Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for looking into that. 🎉

If it turns out to be a breaking change, we need to find a way to make it backwards compatible. We can’t introduce it right now.

However, if you want to go ahead and implement the changes based on how you think it should work, and then share your findings with us, we can refine it together in a follow-up.

Because of that, I wouldn’t worry about the e2e tests at this stage. Instead, focus on making the changes in a way that, when you run make generate, the testdata samples are regenerated—or at least show how a project would look with the new behavior.

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 }}
`
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -67,8 +66,11 @@ var (
// 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: GroupVersion}
SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion}

// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ func (f *Types) SetTemplateDefaults() error {
//nolint:lll
const typesTemplate = `{{ .Boilerplate }}

{{ if .Resource.HasApplyConfiguration }}
// +kubebuilder:ac:generate=true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the feature must be optional.
We should not promote it by default, right?
By default, people should still be working as they do today.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I'll make it optional

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if you want to add it in a way that we can first see it working, then we can evaluate it.

Do you have a project that uses this configuration? I’ve never tried to use it myself, so looking at an example would be helpful and could give us some ideas on how we might add it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a project that uses this configuration, however I ran the generate testdata using it: 537f54e

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added the flag, made it true by default (for now so we can see the generated code) and also generated the code 👍

{{ else }}
// +kubebuilder:ac:generate=false
{{ end }}
package {{ .Resource.Version }}

import (
Expand Down Expand Up @@ -116,6 +121,8 @@ type {{ .Resource.Kind }}Status struct {
// +kubebuilder:resource:scope=Cluster
{{- else if not .Resource.IsRegularPlural }}
// +kubebuilder:resource:path={{ .Resource.Plural }}
{{- else }}
// +kubebuilder:resource
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this marker do?
Have you an example of a project using the option for we give a look?
Could you share?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{{- end }}

// {{ .Resource.Kind }} is the Schema for the {{ .Resource.Plural }} API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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="./..."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, here we should not do this change as well.
We should not change the default scaffold to be applyconfiguration

That would mean all would only work with.

So, the first question is:

In a project, can I have both?

Apis created with applyconfiguration and others not?
OR if we use applyconfiguration all APIs will need to generate with it?

{{- end }}

.PHONY: fmt
Expand Down