Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ Allow to wire an object mutation handler #2932

Closed

Conversation

alculquicondor
Copy link
Contributor

@alculquicondor alculquicondor commented Aug 23, 2024

Proposal

Allow wiring an admission.Handler to process mutations in a webhook, as part of the builder.WebhookBuilder.

Motivation

Currently, mutations can be done through a CustomDefaulter which accepts and modifies a runtime.Object. The creation of patches is hidden from the developer. The jsonpatch is created from the difference of the request's raw yaml and the marshalled object modified by the CustomDefaulter.

This works for webhooks that are always released along with CRD changes. However, it doesn't work for webhooks written for CRDs from other projects. An example of this is Kueue, which writes webhooks for k8s Jobs, kubeflow, kuberay and others. Whenever there is a new version of the third party CRD, the hidden controller-runtime handler will produce a patch that removes all unknown fields.

A mechanism to fully override the handler allows developers to write patches that are safer against these version changes.

Goals

  • A mechanism to override jsonpatch generation for mutation webhooks.

Non Goals

  • A breaking-change in the libraries to build webhooks.
  • A breaking-change in the way patches are built when using a CustomDefaulter.
  • A mechanism to wire an admission.Handler to process validations. This could easily be added in a follow up proposal, but it doesn't seem to provide as much value as processing mutations.

Alternatives

My first proposal was #2931, which is to round-trip the raw object to get rid of unknown fields, and generate a jsondiff from this object.
However, @sbueringer pointed out that the round-trip might also add fields that didn't exist in the raw object is the go types are missing omitempty, which might lead to patches that can't be applied to the raw object in the apiserver. So the suggestion was to give full control to developers over the handler to build patches as they see fit.

@k8s-ci-robot k8s-ci-robot added do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Aug 23, 2024
@k8s-ci-robot k8s-ci-robot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Aug 23, 2024
@alculquicondor
Copy link
Contributor Author

/assign @sbueringer

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: alculquicondor
Once this PR has been reviewed and has the lgtm label, please ask for approval from sbueringer. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

pkg/builder/webhook.go Outdated Show resolved Hide resolved
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 2, 2024
@alculquicondor alculquicondor force-pushed the patch_defaulter branch 2 times, most recently from 5fd81ad to cd7afc5 Compare September 3, 2024 19:25
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Sep 3, 2024
@alculquicondor alculquicondor force-pushed the patch_defaulter branch 3 times, most recently from df4be17 to 56c3467 Compare September 3, 2024 20:03
@alculquicondor
Copy link
Contributor Author

The panic seems to be on a different suite

/retest

@k8s-ci-robot k8s-ci-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Sep 4, 2024
@alculquicondor
Copy link
Contributor Author

/hold cancel

@k8s-ci-robot k8s-ci-robot removed the do-not-merge/hold Indicates that a PR should not merge because someone has issued a /hold command. label Sep 4, 2024
@alculquicondor
Copy link
Contributor Author

@sbueringer I addressed all the comments, PTAL.

@alculquicondor
Copy link
Contributor Author

Can we move this forward? In Kueue, we are risking dropping fields all the time because we don't control the versions of the CRDs that we have webhooks for.

@alvaroaleman
Copy link
Member

Can we move this forward? In Kueue, we are risking dropping fields all the time because we don't control the versions of the CRDs that we have webhooks for.

I understand, but c-r is a widely used project and this is an external interface change. Please have some patience until we find time to properly review it.

@sbueringer
Copy link
Member

Just to be clear. This can be implemented entirely outside of controller-runtime.

@alculquicondor
Copy link
Contributor Author

This can be implemented entirely outside of controller-runtime.

I understand that, but it's a lot of repetitive code to maintain.

I was hoping that the change was not controversial, but if you don't like the high level idea, please let me know so we can propose other alternatives. Or just implement the whole thing in Kueue.

Copy link

@mimowo mimowo left a comment

Choose a reason for hiding this comment

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

LGTM

pkg/builder/webhook_test.go Outdated Show resolved Hide resolved
Copy link
Member

@alvaroaleman alvaroaleman left a comment

Choose a reason for hiding this comment

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

So if I understand it correctly, the root of the issue is a mismatch between the go types in the project and what is configured as the CRD, resulting in the patch removing fields it doesn't know about.

Can you use unstructured.Unstructured in your CustomDefaulter to avoid this?

@@ -65,6 +66,12 @@ func (blder *WebhookBuilder) For(apiType runtime.Object) *WebhookBuilder {
return blder
}

// WithMutationHandler takes an admission.Handler, a MutatingWebhook will be wired for it.
func (blder *WebhookBuilder) WithMutationHandler(h admission.Handler) *WebhookBuilder {
Copy link
Member

@alvaroaleman alvaroaleman Sep 29, 2024

Choose a reason for hiding this comment

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

Its super not obvious when and why to use this versus the defaulter and almost guaranteed to lead to questions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can we leave it as "more advanced use cases where you want control over the construction of patches"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Or we could explicitly add the use case of go types that might be in a different version than the CRD.

Copy link
Member

Choose a reason for hiding this comment

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

Please write something along the lines of that this is a low-level plug point for advanced users that need more control over how exactly the patch is being constructed and if in doubt, people should always use WithDefaulter instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added

@alvaroaleman
Copy link
Member

How about #2800 (comment) instead?

@alculquicondor
Copy link
Contributor Author

Can you use unstructured.Unstructured in your CustomDefaulter to avoid this?

I don't think so. I need to modify either the raw object or the returning patches.

How about #2800 (comment) instead?

That was my first proposal, which leads to different problems.

@sbueringer
Copy link
Member

sbueringer commented Oct 6, 2024

/lgtm
in general

We probably want some godoc around when to use WithMutationHandler vs WithDefaulter directly on these funcs + a comment on each that only one of them can be used at the same time
(cc @vincepri @alvaroaleman)

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 6, 2024
@k8s-ci-robot
Copy link
Contributor

LGTM label has been added.

Git tree hash: a9928964ed3d2fc7a109c288d3e64412ca2019ac

@mimowo
Copy link

mimowo commented Oct 7, 2024

@sbueringer thank you for reviewing as we are going to use it for Kueue webhooks to register the LossLessDefaulter ( docs, and code). We are also working on improving it to be selective.

I'm wondering what your view is on including it as an opt-in defaulter in the controller-runtime code? It would relieve Kueue from maintaining code which is below its "business-level" logic. Also, it would allow other projects to use it, in case they also have similar issues. Should we open an issue to discuss this further?

@alvaroaleman
Copy link
Member

@sbueringer thank you for reviewing as we are going to use it for Kueue webhooks to register the LossLessDefaulter ( docs, and code). We are also working on improving it to be kubernetes-sigs/kueue#3186.

This seems extreme. The tl,dr of the problem still seems to be that we don't know of a good solution, so instead we add a low-level plug point so people can work around the problem. If we were to add something like what you've built in Kueue we effectively bless it and call it a good/appropriate solution and I don't think that is what it is.

@alvaroaleman alvaroaleman added the tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges. label Oct 7, 2024
@alculquicondor
Copy link
Contributor Author

alculquicondor commented Oct 7, 2024

@alvaroaleman, I think the proposal in kubernetes-sigs/kueue#3194 is actually generic and it doesn't seem to have any of the problems of earlier proposals.

It just removes any remove operations that refer to fields not present in the go type.

@sbueringer
Copy link
Member

Maybe it would be better to first try to get the current PR merged and then we can discuss on a new issue if we want to add some built-in handlers?

Except if it's all or nothing from your side

@mimowo
Copy link

mimowo commented Oct 8, 2024

Yeah. that's for sure, we should first move it to the point where we can merge it into Kueue. I'm also perfectly ok to incubate it for some time (like a release or more) in Kueue, so that we detect potential issues.

Once it is merged into Kueue and we feel comfortable about it I would like to follow up with an issue in controller-runtime to discuss the transfer and potential gaps. WDYT?

@alculquicondor
Copy link
Contributor Author

Yes please, let's get this PR merged, as there will always be cases where people need to have fine grained control over patches.

The current way of doing that requires a lot of duplicated code from controller-runtime.

@sbueringer
Copy link
Member

Once it is merged into Kueue and we feel comfortable about it I would like to follow up with an issue in controller-runtime to discuss the transfer and potential gaps. WDYT?

No guarantees about the outcome, but sure we can absolutely open an issue and discuss

@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 10, 2024
@alculquicondor
Copy link
Contributor Author

@alvaroaleman any other comment or ask before approval?

@sbueringer
Copy link
Member

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 19, 2024
@k8s-ci-robot
Copy link
Contributor

LGTM label has been added.

Git tree hash: c3432160bf30e616ab33c71016d0a7dc91a4036e

@sbueringer
Copy link
Member

/close

Merged #2982 instead

@k8s-ci-robot
Copy link
Contributor

@sbueringer: Closed this PR.

In response to this:

/close

Merged #2982 instead

Instructions 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.

@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Nov 5, 2024
@k8s-ci-robot
Copy link
Contributor

PR needs rebase.

Instructions 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. tide/merge-method-squash Denotes a PR that should be squashed by tide when it merges.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants