kubernetes-sigs / kubebuilder

Kubebuilder - SDK for building Kubernetes APIs using CRDs
http://book.kubebuilder.io
Apache License 2.0
7.91k stars 1.45k forks source link

docs: controllers and webhooks for existing resources? #1270

Closed adampl closed 4 years ago

adampl commented 4 years ago

I'm going through the book, but it's all about CRDs so I feel a bit lost, because I don't need a CRD.

Is it actually possible to create a custom controller for an existing resource (like built-in Service or Pod) with Kubebuilder? Is it a good idea to use Kubebuilder for this purpose? How to do it the simple way?

I think a tutorial should be added covering this topic, or at least a short note answering these questions.

If it's not currently possible, please consider adding such feature.

/kind feature /kind documentation

gregsidelinger commented 4 years ago

I'm here because of the same thing. I was going to use kubebuilder 2.2.0 to create some mutating webhooks for Pods and Services. I've done this before in v1 and came here after I could not even run make after initializing the project. https://github.com/kubernetes-sigs/kubebuilder/issues/1213 states that native objects are not supported.

I would like to see support for native k8s objects added back to kubebuilder. They worked in v1 when it was an alpha feature.

In v1 of kubebuilder this worked.

kubebuilder init --domain example.com --license none --owner "bob"
kubebuilder alpha webhook --group core --version v1 --kind Service --type=validating --operations=create,update

I was hoping this would work in v2 but it does not.

kubebuilder init --domain example.com --license none --owner "bob"
kubebuilder create webhook --group core --version v1 --kind Service --defaulting --programmatic-validation
droot commented 4 years ago

@adampl some answers to help you.

Is it actually possible to create a custom controller for an existing resource (like built-in Service or Pod) with Kubebuilder?

Yes absolutely. Only change would be: when you run kubebuilder create api, skip creating resource when it prompts you as shown below. This will skip scaffolding API definitions etc and only create scaffolding for the controller. Rest of the instructions should work.

$ kubebuilder create api --group "core" --kind "Service" --version "v1"
Create Resource [y/n]
n
Create Controller [y/n]
y

Is it a good idea to use Kubebuilder for this purpose?

Yes.

droot commented 4 years ago

@mengqiy Can you pl. help @gregsidelinger with the webhook query ?

mengqiy commented 4 years ago

@gregsidelinger Thanks for your feedback. Sorry for the late response. We have improved our webhook library and tooling for CRD in v2. Now it's super easy for CRD.

At the moment, kubebuilder CLI doesn't provide much scaffolding for built-in types and you have to do it through the low-level library (controller-runtime). It's still doable. Here is an example.

mengqiy commented 4 years ago

We will need to add some document to at least make https://github.com/kubernetes-sigs/controller-runtime/tree/master/examples/builtins more discoverable.

adampl commented 4 years ago

Personally, this is the best example: https://godoc.org/github.com/kubernetes-sigs/controller-runtime/pkg/builder#example-Builder

gregsidelinger commented 4 years ago

I've looked over the docs links. I'm going to work on my validating hook one of these days to see how to do it in v2 of kubebuilder. The part that really bugs me for existing objects is I need to do a bunch of extra work that I did not need to do in v1 back in the alpha days. I have a co-worker who did a mutating webhook for nodes to add AWS tags as labels and he gave up on kubebuilder 2 and use 1 instead because it was much more difficult to work with existing objects in version 2.

DirectXMan12 commented 4 years ago

@gregsidelinger can you clarify what you mean? A v1 webhook should look more-or-less identical to a v2 low-level webhook:

compare https://book-v1.book.kubebuilder.io/beyond_basics/sample_webhook.html#implementing-webhook-handler

with

https://github.com/kubernetes-sigs/controller-runtime/blob/master/examples/builtins/mutatingwebhook.go

harpratap commented 4 years ago

@droot Does that method work only for core group? I'm trying to create a controller for an istio CRD and I get errors.

> kubebuilder create api --group networking.istio.io --version v1alpha3 --kind Gateway
Create Resource [y/n]
n
Create Controller [y/n]
y
Writing scaffold for you to edit...
controllers/gateway_controller.go
Running make:
$ make
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
/Users/harpratap.layal/go/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
main.go:29:2: package gateway-controller/api/v1alpha3 is not in GOROOT (/usr/local/Cellar/go/1.14.2/libexec/src/gateway-controller/api/v1alpha3)
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
make: *** [generate] Error 1
2020/06/25 18:05:30 failed to create API: exit status 2

After removing all API related code -

> 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
/Users/harpratap.layal/go/bin/controller-gen object:headerFile="hack/boilerplate.go.txt" paths="./..."
go fmt ./...
go vet ./...
package gateway-controller/controllers (test): package gateway-controller/api/v1alpha3 is not in GOROOT (/usr/local/Cellar/go/1.14.2/libexec/src/gateway-controller/api/v1alpha3)
# sigs.k8s.io/controller-runtime/pkg/metrics
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/metrics/client_go_adapter.go:120:24: too many arguments in call to metrics.Register
    have (*latencyAdapter, *resultAdapter)
    want (metrics.RegisterOpts)
# sigs.k8s.io/controller-runtime/pkg/client
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/client/typed_client.go:45:62: o.resourceMeta.Interface.Post().NamespaceIfScoped(o.Object.GetNamespace(), o.resourceMeta.isNamespaced()).Resource(o.resourceMeta.resource()).Body(obj).VersionedParams(createOpts.AsCreateOptions(), c.paramCodec).Context undefined (type *rest.Request has no field or method Context)
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/client/typed_client.go:65:62: o.resourceMeta.Interface.Put().NamespaceIfScoped(o.Object.GetNamespace(), o.resourceMeta.isNamespaced()).Resource(o.resourceMeta.resource()).Name(o.Object.GetName()).Body(obj).VersionedParams(updateOpts.AsUpdateOptions(), c.paramCodec).Context undefined (type *rest.Request has no field or method Context)
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/client/typed_client.go:85:37: o.resourceMeta.Interface.Delete().NamespaceIfScoped(o.Object.GetNamespace(), o.resourceMeta.isNamespaced()).Resource(o.resourceMeta.resource()).Name(o.Object.GetName()).Body(deleteOpts.AsDeleteOptions()).Context undefined (type *rest.Request has no field or method Context)
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/client/typed_client.go:105:42: o.resourceMeta.Interface.Delete().NamespaceIfScoped(deleteAllOfOpts.ListOptions.Namespace, o.resourceMeta.isNamespaced()).Resource(o.resourceMeta.resource()).VersionedParams(deleteAllOfOpts.ListOptions.AsListOptions(), c.paramCodec).Body(deleteAllOfOpts.DeleteOptions.AsDeleteOptions()).Context undefined (type *rest.Request has no field or method Context)
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/client/typed_client.go:129:13: o.resourceMeta.Interface.Patch(patch.Type()).NamespaceIfScoped(o.Object.GetNamespace(), o.resourceMeta.isNamespaced()).Resource(o.resourceMeta.resource()).Name(o.Object.GetName()).VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), c.paramCodec).Body(data).Context undefined (type *rest.Request has no field or method Context)
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/client/typed_client.go:143:25: r.Interface.Get().NamespaceIfScoped(key.Namespace, r.isNamespaced()).Resource(r.resource()).Context undefined (type *rest.Request has no field or method Context)
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/client/typed_client.go:159:58: r.Interface.Get().NamespaceIfScoped(listOpts.Namespace, r.isNamespaced()).Resource(r.resource()).VersionedParams(listOpts.AsListOptions(), c.paramCodec).Context undefined (type *rest.Request has no field or method Context)
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/client/typed_client.go:181:89: o.resourceMeta.Interface.Put().NamespaceIfScoped(o.Object.GetNamespace(), o.resourceMeta.isNamespaced()).Resource(o.resourceMeta.resource()).Name(o.Object.GetName()).SubResource("status").Body(obj).VersionedParams((&UpdateOptions literal).ApplyOptions(opts).AsUpdateOptions(), c.paramCodec).Context undefined (type *rest.Request has no field or method Context)
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/client/typed_client.go:206:79: o.resourceMeta.Interface.Patch(patch.Type()).NamespaceIfScoped(o.Object.GetNamespace(), o.resourceMeta.isNamespaced()).Resource(o.resourceMeta.resource()).Name(o.Object.GetName()).SubResource("status").Body(data).VersionedParams(patchOpts.ApplyOptions(opts).AsPatchOptions(), c.paramCodec).Context undefined (type *rest.Request has no field or method Context)
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/client/unstructured_client.go:51:20: not enough arguments in call to r.Create
    have (*unstructured.Unstructured, v1.CreateOptions)
    want (context.Context, *unstructured.Unstructured, v1.CreateOptions, ...string)
../../go/pkg/mod/sigs.k8s.io/controller-runtime@v0.5.0/pkg/client/unstructured_client.go:51:20: too many errors
make: *** [vet] Error 2
camilamacedo86 commented 4 years ago

Hi @harpratap,

This issue is close, could you please raise a new one with your questions and specific scenario? Also, please provide the version of kb used and etc.