Controller-runtime: Is the webhook example still valid?

Created on 24 Jan 2020  路  17Comments  路  Source: kubernetes-sigs/controller-runtime

I'm trying to follow this example and building my webhook https://github.com/kubernetes-sigs/controller-runtime/blob/master/examples/builtins/validatingwebhook.go

However, having this webhook it does not work

type MyTypeValidator struct {
    client.Client
    *admission.Decoder
}

var _ inject.Client = &MyTypeValidator{}

func (v *MyTypeValidator) Handle(ctx context.Context, req admission.Request) admission.Response {
    quota := &MyType{}

    return admission.Allowed("")
}

// MyTypeValidator implements inject.Client.
// A client will be automatically injected.

// InjectClient injects the client.
func (v *MyTypeValidator) InjectClient(c client.Client) error {
    v.Client = c
    return nil
}

// MyTypeValidator implements admission.DecoderInjector.
// A decoder will be automatically injected.

// InjectDecoder injects the decoder.
func (v *MyTypeValidator) InjectDecoder(d *admission.Decoder) error {
    v.Decoder = d
    return nil
}

I get this error:

invalid field type interface{sigs.k8s.io/controller-runtime/pkg/client.Reader; sigs.k8s.io/controller-runtime/pkg/client.StatusClient; sigs.k8s.io/controller-runtime/pkg/client.Writer}

The only difference with the example is that the struct there podValidator is private while I have it public.

prioritawaiting-more-evidence

Most helpful comment

Solved this by following the exact implementation of the defaulter and validator.

func ValidatingWebhookFor() *admission.Webhook {
    return &admission.Webhook{
        Handler: &validatingHandler{},
    }
}

type validatingHandler struct {
    client client.Client
    decoder *admission.Decoder
}

var _ inject.Client = &validatingHandler{}

var _ admission.Handler = &validatingHandler{}

func (v *validatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
    quota := &MyType{}

    return admission.Allowed("")
}

// validatingHandler implements inject.Client.
// A client will be automatically injected.

// InjectClient injects the client.
func (v *validatingHandler) InjectClient(c client.Client) error {
    v.client = c
    return nil
}

// validatingHandler implements admission.DecoderInjector.
// A decoder will be automatically injected.

// InjectDecoder injects the decoder.
func (v *validatingHandler) InjectDecoder(d *admission.Decoder) error {
    v.decoder = d
    return nil
}

All 17 comments

Solved this by following the exact implementation of the defaulter and validator.

func ValidatingWebhookFor() *admission.Webhook {
    return &admission.Webhook{
        Handler: &validatingHandler{},
    }
}

type validatingHandler struct {
    client client.Client
    decoder *admission.Decoder
}

var _ inject.Client = &validatingHandler{}

var _ admission.Handler = &validatingHandler{}

func (v *validatingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
    quota := &MyType{}

    return admission.Allowed("")
}

// validatingHandler implements inject.Client.
// A client will be automatically injected.

// InjectClient injects the client.
func (v *validatingHandler) InjectClient(c client.Client) error {
    v.client = c
    return nil
}

// validatingHandler implements admission.DecoderInjector.
// A decoder will be automatically injected.

// InjectDecoder injects the decoder.
func (v *validatingHandler) InjectDecoder(d *admission.Decoder) error {
    v.decoder = d
    return nil
}

public vs private should not matter here.
I'm not able to reproduce the issue using the code in https://github.com/kubernetes-sigs/controller-runtime/issues/780#issue-554773367.

If you are using CRD, why not using the Defaulter and the Validator interfaces?

I needed to use the client and I could not see any way to use it via the defaulter or the validator

/priority awaiting-more-evidence

yeah. it does not work. refkubebuilder#1228

See the response to that issue https://github.com/kubernetes-sigs/kubebuilder/issues/1228#issuecomment-562347631

I just tested the the example checked into controller-runtime master, it works fine. Is there a more specific question you have? Or a repro for a scenario you expect to work?

i tried and it does not work,too. It is interesting that I tried first time and it worked, but after I do make docker-build and make deploy , I add more validatingwebhook (for namespace and others), then it failed.

$ make run
go: creating new go.mod: module tmp
go: found sigs.k8s.io/controller-tools/cmd/controller-gen in sigs.k8s.io/controller-tools v0.2.5
/root/mygo/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
g.hz.netease.com/k8s/scud/api/v1:-: invalid field type interface{sigs.k8s.io/controller-runtime/pkg/client.Reader; sigs.k8s.io/controller-runtime/pkg/client.StatusClient; sigs.k8s.io/controller-runtime/pkg/client.Writer}
Error: not all generators ran successfully
run `controller-gen object:headerFile=hack/boilerplate.go.txt paths=./... -w` to see all available markers, or `controller-gen object:headerFile=hack/boilerplate.go.txt paths=./... -h` for usage
Makefile:55: recipe for target 'generate' failed
make: *** [generate] Error 1

Is it possible that some generate code or files cause this failure?

There is no makefile in the example I linked. I assume this is a controller you鈥檙e writing. Can you please provide a complete, minimal repro to understand the issue?

/assign

Using the validator from https://github.com/kubernetes-sigs/controller-runtime/issues/780#issue-554773367, this also builds/starts up fine for me. Haven't validated runtime.

https://github.com/alexeldeib/hook-test

I tried again and now I'm sure that it is error when make generate because I can successfully run make manifests ; go run ./main.go

I tried again and now I'm sure that it is error when make generate because I can successfully run make manifests ; go run ./main.go

Looking at your error:

g.hz.netease.com/k8s/scud/api/v1:-: invalid field type interface{sigs.k8s.io/controller-runtime/pkg/client.Reader; sigs.k8s.io/controller-runtime/pkg/client.StatusClient; sigs.k8s.io/controller-runtime/pkg/client.Writer}

seems like you may be attempting to embed a client into a CRD? That won't work for the reason you're seeing, it's an interface.

As far as this issue is concerned, I'd say the example is still valid. If someone can produce a repro, feel free to reopen.

/close

@alexeldeib: Closing this issue.

In response to this:

As far as this issue is concerned, I'd say the example is still valid. If someone can produce a repro, feel free to reopen.

/close

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.

see the same error when trying to add a webhook for native k8s resource in a KubeBuilder project:

invalid field type interface{sigs.k8s.io/controller-runtime/pkg/client.Reader; sigs.k8s.io/controller-runtime/pkg/client.StatusClient; sigs.k8s.io/controller-runtime/pkg/client.Writer}

and figured out why.

tldr: move the webhook file out of your kubebuilder CRD dir: ./api/_your-crd-version_

The error is generated by controller-gen when running make run, simply run

controller-gen object paths="./..." output:stdout

to reproducer the error.

It tries to generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. The struct in code example contains a field which is an interface:

type validatingHandler struct {
    client client.Client // client.Client is an interface <<<<<<<<<<<<<<<---
    decoder *admission.Decoder
}

It is not supported by controller-gen, thus the error.

Since we don't need the code generation for this struct, simply moves it out of that dir, the the error will gone.

Was this page helpful?
0 / 5 - 0 ratings