kubernetes-sigs / controller-runtime

Repo for the controller-runtime subproject of kubebuilder (sig-apimachinery)
Apache License 2.0
2.44k stars 1.13k forks source link

Can't wrap existing client with WithWatch() #2967

Open 2uasimojo opened 3 days ago

2uasimojo commented 3 days ago

In some cases, one can wrap already-extant client.Clients to confer extra functionality. For example:

cWithFieldOwner := client.WithFieldOwner(cWithoutFieldOwner, "owner")

However, it is not possible to confer WithWatch on an existing client: one can only obtain a client.WithWatch via a constructor:

cWithWatch, err := client.NewWithWatch(cfg, opts)

The resulting client is "frozen" -- i.e. can't be subjected to decorators like WithFieldOwner -- because those wrappers return a client.Client, which can't Watch()!

It would be great to have a decorator function to allow an existing client to grow WithWatch functionality:

c, err := client.New(cfg, opts)
c = client.WithFieldOwner(c, "owner")
// ... chain other decorators ...
cWithAllTheThings, err := client.AsWithWatch(c)

Notes:

See #2929 where I prototyped an imperfect version of the solution that suffers from all of the above issues.

alvaroaleman commented 3 days ago

Can we use generics for this?

alvaroaleman commented 1 day ago

To expand on what I was thinking, I thought we might be able to have some sort of wrapper type for the various options that takes a client.Client or client.WithWatch through generics and returns whatever it has gotten. The main drawback is that the return will always be assertable to WithWatch even if it it isn't.

Note that this plays into https://github.com/kubernetes-sigs/controller-runtime/issues/2888, we should probably solve the two problems together