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 format_gvk_name(gvk: &GVK, ns_name: &str) -> String {
76 format!("{gvk}:{ns_name}")
77}
78
79
80pub fn sanitize_obj(obj: &mut DynamicObject, api_version: &str, kind: &str) {
81 obj.metadata.creation_timestamp = None;
82 obj.metadata.deletion_timestamp = None;
83 obj.metadata.deletion_grace_period_seconds = None;
84 obj.metadata.generation = None;
85 obj.metadata.managed_fields = None;
86 obj.metadata.owner_references = None;
87 obj.metadata.resource_version = None;
88 obj.metadata.uid = None;
89
90 if let Some(a) = obj.metadata.annotations.as_mut() {
91 a.remove(LAST_APPLIED_CONFIG_LABEL_KEY);
92 a.remove(DEPL_REVISION_LABEL_KEY);
93 }
94
95 obj.types = Some(TypeMeta { api_version: api_version.into(), kind: kind.into() });
96}
97
98pub fn split_namespaced_name(name: &str) -> (String, String) {
99 match name.split_once('/') {
100 Some((namespace, name)) => (namespace.into(), name.into()),
101 None => ("".into(), name.into()),
102 }
103}
104
105impl<T: Resource> KubeResourceExt for T {
106 fn namespaced_name(&self) -> String {
107 match self.namespace() {
108 Some(ns) => format!("{}/{}", ns, self.name_any()),
109 None => self.name_any().clone(),
110 }
111 }
112
113 fn matches(&self, sel: &metav1::LabelSelector) -> anyhow::Result<bool> {
114 if let Some(exprs) = &sel.match_expressions {
115 for expr in exprs {
116 if !label_expr_match(self.labels(), expr)? {
117 return Ok(false);
118 }
119 }
120 }
121
122 if let Some(labels) = &sel.match_labels {
123 for (k, v) in labels {
124 if self.labels().get(k) != Some(v) {
125 return Ok(false);
126 }
127 }
128 }
129 Ok(true)
130 }
131}
132
133fn build_object_meta_helper<K>(namespace: Option<String>, name: &str, sim_name: &str, owner: &K) -> metav1::ObjectMeta
134where
135 K: Resource<DynamicType = ()>,
136{
137 let mut meta = metav1::ObjectMeta {
138 namespace,
139 name: Some(name.into()),
140 ..Default::default()
141 };
142
143 add_common_metadata(sim_name, owner, &mut meta);
144 meta
145}
146
147pub(super) const OPERATOR_IN: &str = "In";
150pub(super) const OPERATOR_NOT_IN: &str = "NotIn";
151pub(super) const OPERATOR_EXISTS: &str = "Exists";
152pub(super) const OPERATOR_DOES_NOT_EXIST: &str = "DoesNotExist";
153
154fn label_expr_match(
155 obj_labels: &BTreeMap<String, String>,
156 expr: &metav1::LabelSelectorRequirement,
157) -> anyhow::Result<bool> {
158 match expr.operator.as_str() {
161 OPERATOR_IN => match obj_labels.get(&expr.key) {
162 Some(v) => match &expr.values {
163 Some(values) if !values.is_empty() => Ok(values.contains(v)),
164 _ => bail!(KubernetesError::malformed_label_selector(expr)),
165 },
166 None => Ok(false),
167 },
168 OPERATOR_NOT_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(true),
174 },
175 OPERATOR_EXISTS => match &expr.values {
176 Some(values) if !values.is_empty() => bail!(KubernetesError::malformed_label_selector(expr)),
177 _ => Ok(obj_labels.contains_key(&expr.key)),
178 },
179 OPERATOR_DOES_NOT_EXIST => match &expr.values {
180 Some(values) if !values.is_empty() => {
181 bail!(KubernetesError::malformed_label_selector(expr));
182 },
183 _ => Ok(!obj_labels.contains_key(&expr.key)),
184 },
185 _ => bail!("malformed label selector expression: {:?}", expr),
186 }
187}