1use std::collections::HashMap;
2use std::collections::hash_map::Entry;
3
4use kube::api::{
5 ApiResource,
6 DynamicObject,
7};
8use kube::discovery::ApiCapabilities;
9
10use crate::k8s::GVK;
11
12pub struct DynamicApiSet {
15 client: kube::Client,
16 resources: HashMap<GVK, (ApiResource, ApiCapabilities)>,
17 apis: HashMap<GVK, kube::Api<DynamicObject>>,
18 namespaced_apis: HashMap<(GVK, String), kube::Api<DynamicObject>>,
19}
20
21impl DynamicApiSet {
22 pub fn new(client: kube::Client) -> DynamicApiSet {
23 DynamicApiSet {
24 client,
25 resources: HashMap::new(),
26 apis: HashMap::new(),
27 namespaced_apis: HashMap::new(),
28 }
29 }
30
31 pub async fn unnamespaced_api_by_gvk(
32 &mut self,
33 gvk: &GVK,
34 ) -> anyhow::Result<(&kube::Api<DynamicObject>, ApiCapabilities)> {
35 let (ar, cap) = self.api_meta_for(gvk).await?.clone();
36 match self.apis.entry(gvk.clone()) {
37 Entry::Occupied(e) => Ok((e.into_mut(), cap)),
38 Entry::Vacant(e) => {
39 let api = kube::Api::all_with(self.client.clone(), &ar);
40 Ok((e.insert(api), cap))
41 },
42 }
43 }
44
45 pub async fn api_for_obj(&mut self, obj: &DynamicObject) -> anyhow::Result<&kube::Api<DynamicObject>> {
46 let gvk = GVK::from_dynamic_obj(obj)?;
47 let ar = self.api_meta_for(&gvk).await?.0.clone();
48 match &obj.metadata.namespace {
49 Some(ns) => match self.namespaced_apis.entry((gvk, ns.into())) {
50 Entry::Occupied(e) => Ok(e.into_mut()),
51 Entry::Vacant(e) => {
52 let api = kube::Api::namespaced_with(self.client.clone(), &e.key().1, &ar);
53 Ok(e.insert(api))
54 },
55 },
56 None => Ok(self.unnamespaced_api_by_gvk(&gvk).await?.0),
57 }
58 }
59
60 async fn api_meta_for(&mut self, gvk: &GVK) -> anyhow::Result<&(ApiResource, ApiCapabilities)> {
61 match self.resources.entry(gvk.clone()) {
62 Entry::Occupied(e) => Ok(e.into_mut()),
63 Entry::Vacant(e) => {
64 let api_meta = kube::discovery::pinned_kind(&self.client, e.key()).await?;
65 Ok(e.insert(api_meta))
66 },
67 }
68 }
69}