1use std::collections::HashMap;
2
3use async_recursion::async_recursion;
4use kube::Resource;
5use kube::api::ListParams;
6use kube::discovery::{
7 ApiCapabilities,
8 Scope,
9};
10use tracing::*;
11
12use super::*;
13use crate::k8s::{
14 DynamicApiSet,
15 format_gvk_name,
16};
17use crate::prelude::*;
18
19pub struct OwnersCache {
28 apiset: DynamicApiSet,
29 owners: HashMap<(GVK, String), Vec<metav1::OwnerReference>>,
30}
31
32impl OwnersCache {
33 pub fn new(apiset: DynamicApiSet) -> OwnersCache {
34 OwnersCache { apiset, owners: HashMap::new() }
35 }
36
37 pub fn new_from_parts(
38 apiset: DynamicApiSet,
39 owners: HashMap<(GVK, String), Vec<metav1::OwnerReference>>,
40 ) -> OwnersCache {
41 OwnersCache { apiset, owners }
42 }
43
44 #[async_recursion]
46 pub async fn compute_owners_for(&mut self, gvk: &GVK, obj: &(impl Resource + Sync)) -> Vec<metav1::OwnerReference> {
47 let ns_name = obj.namespaced_name();
48 let gvk_name = format_gvk_name(gvk, &ns_name);
49 debug!("computing owner references for {gvk_name}");
50
51 let key = (gvk.clone(), ns_name.clone());
52 if let Some(owners) = self.owners.get(&key) {
53 debug!("found owners {owners:?} for {gvk_name} in cache");
54 return owners.clone();
55 }
56
57 let mut owners = Vec::new();
59
60 for rf in obj.owner_references() {
61 let owner_gvk = match GVK::from_owner_ref(rf) {
62 Ok(gvk) => gvk,
63 Err(err) => {
64 error!("malformed owner reference {rf:?}: {err}");
65 continue;
66 },
67 };
68 let (api, cap) = match self.apiset.unnamespaced_api_by_gvk(&owner_gvk).await {
69 Ok((a, c)) => (a, c),
70 Err(err) => {
71 warn!("could not query {owner_gvk}: {err}; skipping ownerref");
73 continue;
74 },
75 };
76 let sel = build_owner_selector(&rf.name, obj, cap);
77 let items = match api.list(&sel).await {
78 Ok(objlist) => objlist.items,
79 Err(err) => {
80 error!("Could not list {owner_gvk}: {err}; skipping ownerref");
81 continue;
82 },
83 };
84
85 if items.len() != 1 {
86 error!("could not find single owner for {gvk_name}, found {items:?}; skipping ownerref");
87 continue;
88 }
89
90 owners.push(rf.clone());
91 owners.extend(self.compute_owners_for(&owner_gvk, &items[0]).await);
92 }
93
94 debug!("computed owners {owners:?} for {gvk_name}");
95 self.owners.insert(key, owners.clone());
96 owners
97 }
98
99 pub async fn lookup_by_name_or_obj(
100 &mut self,
101 gvk: &GVK,
102 ns_name: &str,
103 maybe_obj: Option<&(impl Resource + Sync)>,
104 ) -> Vec<metav1::OwnerReference> {
105 match (self.owners.get(&(gvk.clone(), ns_name.into())), maybe_obj) {
106 (Some(o), _) => o.clone(),
107 (None, Some(obj)) => self.compute_owners_for(gvk, obj).await,
108 _ => {
109 error!("could not determine owner chain for {ns_name}");
110 vec![]
111 },
112 }
113 }
114}
115
116fn build_owner_selector(owner_name: &str, obj: &(impl Resource + Sync), owner_cap: ApiCapabilities) -> ListParams {
117 let sel = match owner_cap.scope {
118 Scope::Cluster => Some(format!("metadata.name={owner_name}")),
119 Scope::Namespaced => {
120 Some(format!("metadata.namespace={},metadata.name={}", obj.namespace().unwrap(), owner_name))
123 },
124 };
125 ListParams { field_selector: sel, ..Default::default() }
126}