✨ (go/v4): Add support to Server-Side Apply#5458
Conversation
0d291e1 to
60416fd
Compare
60416fd to
d24e315
Compare
10a43a3 to
0725248
Compare
971b7d3 to
fd92b49
Compare
|
|
||
| // Apply - only manages the fields you specified above | ||
| // User's custom labels/annotations are preserved! | ||
| if err := r.Patch(ctx, resource, client.Apply, client.ForceOwnership, |
There was a problem hiding this comment.
Just for my own culture, is proning client.ForceOwnership a result of "a single resource should not be managed by multiple operator" logic ?
There was a problem hiding this comment.
Updated. Good catcher !!!
7bba449 to
b4130bd
Compare
b4130bd to
00e30de
Compare
00e30de to
b206c73
Compare
This comment was marked as resolved.
This comment was marked as resolved.
b206c73 to
6fb93ef
Compare
| Use this flag when: | ||
|
|
||
| - **Multiple controllers manage the same resource**: Your controller manages some fields while other controllers or users manage others | ||
| - **Users customize your CRs**: Users add their own labels, annotations, or spec fields that your controller shouldn't overwrite | ||
| - **Partial field management**: You only want to manage specific fields and leave others alone | ||
| - **Avoiding conflicts**: You want declarative field ownership tracking to prevent accidental overwrites | ||
|
|
There was a problem hiding this comment.
I'm not quite sure on how to phrase it, but should we also say "Expect other people to automate your CRs through higher level operators" ?
As in the operator that has generated SSA types would not be the one using them but merely providing them for other operators to import ?
yyewolf
left a comment
There was a problem hiding this comment.
That's a really nice improvement, I can't wait to try it and we (at my company) will have a lot of work to do to use this as effectively as possible 🔥
| } | ||
|
|
||
| // Apply using SSA - only manages the fields specified above | ||
| if err := r.Patch(ctx, resource, client.Apply, |
There was a problem hiding this comment.
Stumbled on this PR while searching for recommendations on SSA for kubebuilder controllers. looks like client.Apply is deprecated. Is this still the way to do SSA?
| .PHONY: generate | ||
| generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. | ||
| "$(CONTROLLER_GEN)" object:headerFile="hack/boilerplate.go.txt",year=$(YEAR) paths="./..." | ||
| "$(CONTROLLER_GEN)" object:headerFile="hack/boilerplate.go.txt",year=$(YEAR) applyconfiguration:headerFile="hack/boilerplate.go.txt" paths="./..." |
There was a problem hiding this comment.
@JoelSpeed @alvaroaleman @sbueringer
It would be great if we could not need to add at all here the applyconfiguration since it will only generate the resources when the ac marker is used and a project can have CRDs that are SSA and others that not.
The need to add this does not bring a nice UX.
Could we not change the ssa marker / generator to work as any other marker in the controller-tools?
| - [Kubernetes Server-Side Apply Documentation][server-side-apply] | ||
| - [controller-gen CLI Reference](./controller-gen.md) | ||
|
|
||
| [server-side-apply]: https://kubernetes.io/docs/reference/using-api/server-side-apply/ |
There was a problem hiding this comment.
@JoelSpeed @alvaroaleman @sbueringer
Could you please give a hand on the review of this doc?
Could you please let us know if has any info here that is not accurate or can/should be shaped?
| // Try multiple patterns to handle different Makefile formats | ||
| // 1. Try with boilerplate and YEAR variable (current default) | ||
| //nolint:lll | ||
| err = util.ReplaceInFile(makefilePath, makefileOldObjectGenWithBoilerplateAndYear, makefileNewObjectGenWithBoilerplateAndYear) |
There was a problem hiding this comment.
This works, but the nested fallback structure is a little hard to scan. Since the intent is “try these known Makefile formats in order and succeed on the first match,” I think this could be made table-driven.
Something like:
func replaceObjectGenInMakefile(makefilePath string) error {
replacements := []struct {
old string
new string
}{
{
old: makefileOldObjectGenWithBoilerplateAndYear,
new: makefileNewObjectGenWithBoilerplateAndYear,
},
{
old: makefileOldObjectGenWithBoilerplate,
new: makefileNewObjectGenWithBoilerplate,
},
{
old: makefileOldObjectGenNoBoilerplate,
new: makefileNewObjectGenNoBoilerplate,
},
}
for _, replacement := range replacements {
if err := util.ReplaceInFile(makefilePath, replacement.old, replacement.new); err == nil {
return nil
}
}
return fmt.Errorf("none of the known controller-gen object generator patterns matched")
}Then the caller can stay simple:
if err := replaceObjectGenInMakefile(makefilePath); err != nil {
log.Warn("unable to find standard controller-gen object generator line in Makefile. "+
"Add applyconfiguration generation manually",
"error", err)
return
}
This avoids the nested if err != nil chain and removes the need for //nolint:lll.
Small note, it assumes ReplaceInFile returning an error means the pattern did not match in this context. If it can also fail because of read or write errors, it may be worth distinguishing those cases separately.
| "--group", kbc.Group, | ||
| "--version", kbc.Version, | ||
| "--kind", kbc.Kind, | ||
| "--namespaced", |
There was a problem hiding this comment.
Can SSA also be cluster-scoped?
There was a problem hiding this comment.
yes work with both.
There was a problem hiding this comment.
I think the test exists only for namespaced, but no test for non-nemaspaced.
|
Since SSA ownership is only claimed when the user actually calls the Apply with a field owner, maybe the flag description/docs should avoid saying the scaffolded controller uses SSA. It enables SSA support, but does not claim ownership until user code calls something like: r.Client.Status().Apply(ctx, desired, client.FieldOwner("foo-controller")) |
| makefilePath := "Makefile" | ||
|
|
||
| // Skip if already updated | ||
| hasMarker, err := util.HasFileContentWith(makefilePath, makefileApplyConfigurationMarker) |
There was a problem hiding this comment.
Just my thought
I was thinking would it be better than we return error from here on the reason why the updateMakefile failed and then callee here will handle the error and mention explicitly with log.Warn(err, "Failed to automatically update Makefile"). I feel this would make it much cleaner based on the logs on what exactly the issue is.
Note: We can still not fail the scaffolding process and let it continue
There was a problem hiding this comment.
We should not return an error because that would means fail when the Makefile is customized and does not allow move forward if the end user change their Makefile.
If we be unable to update the Makefile we warn and then end users are aware of to update it manually
| func (s *apiScaffolder) updateMakefile() { | ||
| makefilePath := "Makefile" |
There was a problem hiding this comment.
Also I was wondering, do we want to add a troubeshooting/manual steps if automatic update of Makefile failed. Currently we are just logging with warning messages.
There was a problem hiding this comment.
We warn and end users can manually update their project.
It is too simple, has not really a reason for troubeshooting.
What we can do is provide a better warn message that can be more helpful.
Add --ssa flag to create api command for scaffolding APIs with Server-Side Apply support. Uses template conditions instead of code injection where possible.
|
@camilamacedo86: The following tests failed, say
Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: camilamacedo86, mayuka-c, yyewolf The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
This PR adds a new
ssa/v1-alphaplugin that scaffolds APIs with controllers using Kubernetes Server-Side Apply (SSA) patterns. How that works: See the doc added: https://deploy-preview-5458--kubebuilder.netlify.app/reference/server-side-apply.htmlWhat is it?
Server-Side Apply helps when multiple actors (your controller, users, other controllers) need to manage different fields of the same resource. Instead of overwriting the entire object, SSA tracks field ownership and only updates the fields you explicitly manage.
Usage
What it scaffolds
After running make generate, you get type-safe apply configuration helpers:
Closes: #2514
Closes: #5237