1use std::collections::HashMap;
2
3use async_recursion::async_recursion;
4use kube::api::ListParams;
5use kube::discovery::{
6 ApiCapabilities,
7 Scope,
8};
9use kube::Resource;
10use tracing::*;
11
12use super::*;
13use crate::k8s::DynamicApiSet;
14use crate::prelude::*;
15
16pub struct OwnersCache {
17 apiset: DynamicApiSet,
18 owners: HashMap<String, Vec<metav1::OwnerReference>>,
19}
20
21impl OwnersCache {
22 pub fn new(apiset: DynamicApiSet) -> OwnersCache {
23 OwnersCache { apiset, owners: HashMap::new() }
24 }
25
26 pub fn new_from_parts(apiset: DynamicApiSet, owners: HashMap<String, Vec<metav1::OwnerReference>>) -> OwnersCache {
27 OwnersCache { apiset, owners }
28 }
29
30 #[async_recursion]
32 pub async fn compute_owner_chain(
33 &mut self,
34 obj: &(impl Resource + Sync),
35 ) -> anyhow::Result<Vec<metav1::OwnerReference>> {
36 let ns_name = obj.namespaced_name();
37 debug!("computing owner references for {ns_name}");
38
39 if let Some(owners) = self.owners.get(&ns_name) {
40 debug!("found owners {owners:?} for {ns_name} in cache");
41 return Ok(owners.clone());
42 }
43
44 let mut owners = Vec::from(obj.owner_references());
45
46 for rf in obj.owner_references() {
47 let owner_gvk = GVK::from_owner_ref(rf)?;
48 let (api, cap) = self.apiset.unnamespaced_api_by_gvk(&owner_gvk).await?;
49 let sel = build_owner_selector(&rf.name, obj, cap);
50 let resp = api.list(&sel).await?;
51 if resp.items.len() != 1 {
52 bail!("could not find single owner for {}, found {:?}", obj.namespaced_name(), resp.items);
53 }
54
55 let owner = &resp.items[0];
56 owners.extend(self.compute_owner_chain(owner).await?);
57 }
58
59 self.owners.insert(ns_name.clone(), owners.clone());
60
61 debug!("computed owners {owners:?} for {ns_name}");
62 Ok(owners)
63 }
64
65 pub fn lookup(&mut self, ns_name: &str) -> Option<&Vec<metav1::OwnerReference>> {
66 self.owners.get(ns_name)
67 }
68}
69
70fn build_owner_selector(owner_name: &str, obj: &(impl Resource + Sync), owner_cap: ApiCapabilities) -> ListParams {
71 let sel = match owner_cap.scope {
72 Scope::Cluster => Some(format!("metadata.name={owner_name}")),
73 Scope::Namespaced => {
74 Some(format!("metadata.namespace={},metadata.name={}", obj.namespace().unwrap(), owner_name))
77 },
78 };
79 ListParams { field_selector: sel, ..Default::default() }
80}