-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 webhooks to register custom validators/defaulter types #1676
Conversation
/hold |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: vincepri The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
bc58c4b
to
9fd7870
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see two aspect for this PR
- Allowing registering webhook implemented outside of the API types
- Improving validator and default interface with context and always returning error.
For the first part, I have the impression that what we need is just somethings that implements the two methods defined in runtime.Object, but I still need to investigate this a little bit.
For the second part, might be worth also considering a breaking change, given that those seems best practices to me
9fd7870
to
1f1fd9c
Compare
Would that be a preferred solution to this problem? It seems like that overwriting the deep copy methods is a hacky way to force an external type into the current webhooks builder logic. |
This is something we can consider breaking in a future release, once we can streamline the interface as well |
That's right, I'm trying to understand if we can be explicit using HandlerFor in the builder but avoiding to duplicate the Handle code in validation/defaulter handler |
pkg/builder/webhook.go
Outdated
// | ||
// If the given object implements the admission.DefaulterFor interface, a MutatingWebhook will be wired for this type. | ||
// If the given object implements the admission.ValidatorFor interface, a ValidatingWebhook will be wired for this type. | ||
func (blder *WebhookBuilder) HandlerFor(forType admission.For) *WebhookBuilder { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would something like WebhookFor
be better? I worry that Handler
is such a generic term.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if that's clear either, wouldn't the WebhookBuilder
be in charge of returning the webhook?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't document or advertise that approach, but its definitely possible to do this external to the api package with the existing interface by embedding the api type into the webhook implementation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@alvaroaleman I tried to do that, but I couldn't get it to work with the DeepCopy and subsequent Decode. (because the webhook struct is not registered as type for the decoding). Do you have a link to an existing implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah ok, I didn't test it I just assumed that it should work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay, I just thought maybe there is a way and I just couldn't find it :)
pkg/builder/webhook.go
Outdated
// | ||
// If the given object implements the admission.DefaulterFor interface, a MutatingWebhook will be wired for this type. | ||
// If the given object implements the admission.ValidatorFor interface, a ValidatingWebhook will be wired for this type. | ||
func (blder *WebhookBuilder) HandlerFor(forType admission.For) *WebhookBuilder { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't document or advertise that approach, but its definitely possible to do this external to the api package with the existing interface by embedding the api type into the webhook implementation
bbaed32
to
cd7d01a
Compare
cd7d01a
to
08aca1c
Compare
|
||
// WithValidator defines functions for validating an operation. | ||
type WithValidator interface { | ||
ValidateCreate(ctx context.Context, obj runtime.Object) error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use client.Object
for these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really, unless we make it a breaking change? I was trying to change any interface until 0.11, so we can backport this change
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are new interfaces, how would using client.Object
here be breaking?
I was thinking that this would make usage slightly better, i.E. you wouldn't be able to pass a List object in here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While the interfaces are new, the object passed in comes from the webhook builder, we'd have to cast it before passing creating the webhook. Moreover, when we use DeepCopyObject() that returns a runtime.Object
, which we'd have to cast again if we want to use client.Object everywhere
|
||
// Get the object in the request | ||
obj := h.object.DeepCopyObject() | ||
if req.Operation == v1.Create { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: We could switch on the operation to be clear that these are mutually excluse (and maybe have a erorring default
clause). Also, the error final error handling can be moved out of the conditions and deduplicated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! ptal
9844bd4
to
6b01655
Compare
6052973
to
f99b738
Compare
} | ||
|
||
err = h.validator.ValidateDelete(ctx, obj) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to return an error in the default case?
f99b738
to
e855958
Compare
This changeset allows our webhook builder to take in a handler any other struct other than a runtime.Object. Today having an object as the primary source of truth for both Defaulting and Validators makes API types carry a lot of information and business logic alongside their definitions. Moreover, lots of folks in the past have asked for ways to have an external type to handle these operations and use a controller runtime client for validations. This change brings a new way to register webhooks, which admission.For handler any type (struct) can be a defaulting or validating handler for a runtime Object. Signed-off-by: Vince Prignano <[email protected]>
e855958
to
e133413
Compare
/hold cancel |
No more nits from my side :) Thank you very much for implementing this!! |
/lgtm |
/hold |
/hold for @alvaroaleman |
/hold cancel |
/cherrypick release-0.10 |
@vincepri: new pull request created: #1679 In response to this:
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/test-infra repository. |
This changeset allows our webhook builder to take in a handler any other
struct other than a runtime.Object.
Today having an object as the primary source of truth for both
Defaulting and Validators makes API types carry a lot of information and
business logic alongside their definitions.
Moreover, lots of folks in the past have asked for ways to have an
external type to handle these operations and use a controller runtime
client for validations.
This change brings a new way to register webhooks, which admission.For
handler any type (struct) can be a defaulting or validating handler for
a runtime Object.
Signed-off-by: Vince Prignano [email protected]