sk_core/k8s/
apiset.rs

1use std::collections::hash_map::Entry;
2use std::collections::HashMap;
3
4use kube::api::{
5    ApiResource,
6    DynamicObject,
7};
8use kube::discovery::ApiCapabilities;
9
10use crate::k8s::GVK;
11
12// An ApiSet object caches a list of ApiResources returned by the k8s server so that we don't have
13// to repeatedly make "discovery" calls against the apiserver.
14pub 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}