Open clux opened 1 year ago
Just chiming it to say this looks really useful and addresses exactly some of the problems we're looking to solve in our own usage of KubeRS. The third option looks the best to me from the ones presented above.
I'm playing around with a store factory at the moment with a pattern that could address some of the concerns, e.g.
// very very rough example of the API I'm considering
fn example(pod_store_factory: StoreFactory<Pod>) {
let example_pod_store = pod_store_factory::all(ListParams::default().labels("example=test"));
}
The aim is to keep declaration of selectors close to their point of ownership, but if another piece of code asks for the same store (e.g. same scope and list params) the factory will serve a clone of the first one created so that a 2nd stream isn't created.
As an update to the people subscribed to this issue, this is now at a point where it should have a functional start under an unstable feature in 0.91: https://github.com/kube-rs/kube/releases/tag/0.91.0
Would you like to work on this feature?
maybe
What problem are you trying to solve?
Currently, our
Controller
machinery creates watch streams and a singlereflector
for the main watch stream only, and these streams/caches are fully managed and internal (except for the single reflector reader).This means there is no good way to share streams between other controllers (because the other controller would similarly set up its own watches). This means currently
kube_runtime
is best suited for smaller controllers and not larger controller manager style ones we see in go.I would like to let users configure the watch stream(s) themselves so the streams and caches can be shared between controller instances as a minimum, and try to do this with the least amount of ergonomic pain points in the existing Controller Api. It is currently possible to do this with the
applier
, but theapplier
is certified hard mode for most users.This has come up before in #824
Progress
Controller
#1187 + #1173Controller
#1189 via #1449Controller
interfaces improvements - #1472NB: there was an early experiment for managing multiple
watcher
streams and caches in #1147, and parts of it has merged in #1131.Original Idea Sketches
Collapsible Set of Ideas
This issue aims to start the conversation with 3 ideas: 1. lift the creation of the controller's `QueueStream` into a separate builder that can take arbitrary watches 2. create a full StreamCache container for various watch-listparam pairs 2. change `Controller::watches` and `Controller::owns` to take a watchstream rather than an `Api` **NB:** A non-goal of **this** issue is solving the much harder problem of two controllers watching the same api with different `ListParams` (one might be less strict, so one watch **could** be a subset of another). This is very hard to do because the `ListParam` is intrinsic to the watch, and you can have totally orthogonal `ListParams` that watches only certain labels. It is potentially possible to analytically figure out the largest subset of a full watch, and then somehow filter events down the relevant events locally (using some kind of watcher interface), but that would be quite hard to do, so not going to talk about that at all here - feel free to write an issue for it! ### 1. QueueStream Abstraction A first (bad) idea I had; take the `QueueStream` builder and make it top-level to try to re-use it between controllers (pseudo-code): ```rust let qs = QueueStream::for(mainapi, lp1).owns(ownedapi, lp2).watches(watchedapi, lp3, mapper); let ctrl = Controller::from(&qs).run(...) // re-using let stream1 = qs.get_stream(typemeta_for_main_api); let stream2 = qs.get_stream(typemeta_for_owned_api); // create another queuestream from these underlying streams let qs2 = QueueStream::for_stream(steam2).watches_stream(stream1, mapper) ``` This feels very awkward. Internally it needs to know the params, and relations to apply to each api before it can be passed to the Controller, but the stream it needs to have exposed is the stream __before__ it applies any mapping relations. It also needs two sets of constructors (one for streams and one for api-lp pairs). ### 2. StreamCache Abstraction A possibly more direct translation of what we have in go? We make a literal map of streams that can be used in many controllers: ```rust // init with api-lp pairs let scache = StreamCache::new().add(api1, lp1).add(api2, lp2).add(api3, lp3); // grab one watch stream let crdstream: impl Stream = scache.get(typemeta_for_api1).unwrap(); // get a cache for it let (crdreader, crdwriter) = reflector::store::Documentation, Adoption, Migration Strategy
Will write a guide for this on kube.rs once we have something.
Target crate for feature
kube-runtime