1use std::collections::BTreeMap;
2
3use kube::api::Resource;
4use serde_json as json;
5
6use super::*;
7use crate::errors::*;
8use crate::prelude::*;
9
10pub fn add_common_metadata<K>(sim_name: &str, owner: &K, meta: &mut metav1::ObjectMeta)
11where
12 K: Resource<DynamicType = ()>,
13{
14 let labels = &mut meta.labels.get_or_insert_default();
15 labels.insert(SIMULATION_LABEL_KEY.into(), sim_name.into());
16 labels.insert(APP_KUBERNETES_IO_NAME_KEY.into(), meta.name.clone().unwrap());
17
18 meta.owner_references.get_or_insert_default().push(metav1::OwnerReference {
19 api_version: K::api_version(&()).into(),
20 kind: K::kind(&()).into(),
21 name: owner.name_any(),
22
23 block_owner_deletion: Some(true),
29
30 uid: owner.uid().unwrap(),
33 ..Default::default()
34 });
35}
36
37pub fn build_deletable(gvk: &GVK, ns_name: &str) -> DynamicObject {
38 let (ns, name) = split_namespaced_name(ns_name);
39 DynamicObject {
40 metadata: metav1::ObjectMeta {
41 namespace: Some(ns),
42 name: Some(name),
43 ..Default::default()
44 },
45 types: Some(gvk.into_type_meta()),
46 data: json::Value::Null,
47 }
48}
49
50pub fn build_containment_label_selector(key: &str, labels: Vec<String>) -> metav1::LabelSelector {
51 metav1::LabelSelector {
52 match_expressions: Some(vec![metav1::LabelSelectorRequirement {
53 key: key.into(),
54 operator: "In".into(),
55 values: Some(labels),
56 }]),
57 ..Default::default()
58 }
59}
60
61pub fn build_global_object_meta<K>(name: &str, sim_name: &str, owner: &K) -> metav1::ObjectMeta
62where
63 K: Resource<DynamicType = ()>,
64{
65 build_object_meta_helper(None, name, sim_name, owner)
66}
67
68pub fn build_object_meta<K>(namespace: &str, name: &str, sim_name: &str, owner: &K) -> metav1::ObjectMeta
69where
70 K: Resource<DynamicType = ()>,
71{
72 build_object_meta_helper(Some(namespace.into()), name, sim_name, owner)
73}
74
75pub fn dyn_obj_type_str(obj: &DynamicObject) -> String {
76 obj.types
77 .as_ref()
78 .map(|tm| format!("{}.{}", tm.api_version, tm.kind))
79 .unwrap_or("<unknown type>".into())
80}
81
82pub fn format_gvk_name(gvk: &GVK, ns_name: &str) -> String {
83 format!("{gvk}:{ns_name}")
84}
85
86pub fn sanitize_obj(obj: &mut DynamicObject, api_version: &str, kind: &str) {
87 obj.metadata.creation_timestamp = None;
90 obj.metadata.deletion_timestamp = None;
91 obj.metadata.deletion_grace_period_seconds = None;
92 obj.metadata.generation = None;
93 obj.metadata.managed_fields = None;
94 obj.metadata.resource_version = None;
95 obj.metadata.uid = None;
96
97 if let Some(a) = obj.metadata.annotations.as_mut() {
98 a.remove(LAST_APPLIED_CONFIG_LABEL_KEY);
99 a.remove(DEPL_REVISION_LABEL_KEY);
100 }
101
102 obj.types = Some(TypeMeta { api_version: api_version.into(), kind: kind.into() });
103}
104
105pub fn split_namespaced_name(name: &str) -> (String, String) {
106 match name.split_once('/') {
107 Some((namespace, name)) => (namespace.into(), name.into()),
108 None => ("".into(), name.into()),
109 }
110}
111
112impl<T: Resource> KubeResourceExt for T {
113 fn namespaced_name(&self) -> String {
114 match self.namespace() {
115 Some(ns) => format!("{}/{}", ns, self.name_any()),
116 None => self.name_any().clone(),
117 }
118 }
119
120 fn matches(&self, sel: &metav1::LabelSelector) -> anyhow::Result<bool> {
121 if let Some(exprs) = &sel.match_expressions {
122 for expr in exprs {
123 if !label_expr_match(self.labels(), expr)? {
124 return Ok(false);
125 }
126 }
127 }
128
129 if let Some(labels) = &sel.match_labels {
130 for (k, v) in labels {
131 if self.labels().get(k) != Some(v) {
132 return Ok(false);
133 }
134 }
135 }
136 Ok(true)
137 }
138}
139
140fn build_object_meta_helper<K>(namespace: Option<String>, name: &str, sim_name: &str, owner: &K) -> metav1::ObjectMeta
141where
142 K: Resource<DynamicType = ()>,
143{
144 let mut meta = metav1::ObjectMeta {
145 namespace,
146 name: Some(name.into()),
147 ..Default::default()
148 };
149
150 add_common_metadata(sim_name, owner, &mut meta);
151 meta
152}
153
154pub(super) const OPERATOR_IN: &str = "In";
157pub(super) const OPERATOR_NOT_IN: &str = "NotIn";
158pub(super) const OPERATOR_EXISTS: &str = "Exists";
159pub(super) const OPERATOR_DOES_NOT_EXIST: &str = "DoesNotExist";
160
161fn label_expr_match(
162 obj_labels: &BTreeMap<String, String>,
163 expr: &metav1::LabelSelectorRequirement,
164) -> anyhow::Result<bool> {
165 match expr.operator.as_str() {
168 OPERATOR_IN => match obj_labels.get(&expr.key) {
169 Some(v) => match &expr.values {
170 Some(values) if !values.is_empty() => Ok(values.contains(v)),
171 _ => bail!(KubernetesError::malformed_label_selector(expr)),
172 },
173 None => Ok(false),
174 },
175 OPERATOR_NOT_IN => match obj_labels.get(&expr.key) {
176 Some(v) => match &expr.values {
177 Some(values) if !values.is_empty() => Ok(!values.contains(v)),
178 _ => bail!(KubernetesError::malformed_label_selector(expr)),
179 },
180 None => Ok(true),
181 },
182 OPERATOR_EXISTS => match &expr.values {
183 Some(values) if !values.is_empty() => bail!(KubernetesError::malformed_label_selector(expr)),
184 _ => Ok(obj_labels.contains_key(&expr.key)),
185 },
186 OPERATOR_DOES_NOT_EXIST => match &expr.values {
187 Some(values) if !values.is_empty() => {
188 bail!(KubernetesError::malformed_label_selector(expr));
189 },
190 _ => Ok(!obj_labels.contains_key(&expr.key)),
191 },
192 _ => bail!("malformed label selector expression: {:?}", expr),
193 }
194}