controller/cloudnativepg/placement/
cnpg_node_affinity.rs1use crate::cloudnativepg::clusters::{
2 ClusterAffinityNodeAffinity,
3 ClusterAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionPreferenceMatchExpressions,
4 ClusterAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionPreferenceMatchFields,
5 ClusterAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTermsMatchExpressions,
6 ClusterAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTermsMatchFields,
7};
8use crate::cloudnativepg::poolers::{
9 PoolerTemplateSpecAffinityNodeAffinity,
10 PoolerTemplateSpecAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecution,
11 PoolerTemplateSpecAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionPreference,
12 PoolerTemplateSpecAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionPreferenceMatchExpressions,
13 PoolerTemplateSpecAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionPreferenceMatchFields,
14 PoolerTemplateSpecAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecution,
15 PoolerTemplateSpecAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTerms,
16 PoolerTemplateSpecAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTermsMatchExpressions,
17 PoolerTemplateSpecAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTermsMatchFields,
18};
19use k8s_openapi::api::core::v1::{
20 NodeAffinity, NodeSelector, NodeSelectorRequirement, NodeSelectorTerm, PreferredSchedulingTerm,
21};
22
23pub fn convert_node_affinity(ca: &ClusterAffinityNodeAffinity) -> NodeAffinity {
28 NodeAffinity {
29 preferred_during_scheduling_ignored_during_execution: Some(convert_preferred(ca)),
30 required_during_scheduling_ignored_during_execution: convert_required(ca),
31 }
32}
33
34fn convert_preferred(ca: &ClusterAffinityNodeAffinity) -> Vec<PreferredSchedulingTerm> {
36 match &ca.preferred_during_scheduling_ignored_during_execution {
37 Some(prefs) => prefs
38 .iter()
39 .map(|pref| PreferredSchedulingTerm {
40 weight: pref.weight,
41 preference: NodeSelectorTerm {
42 match_expressions: Some(convert_match_expressions(
43 &pref.preference.match_expressions,
44 )),
45 match_fields: Some(convert_match_fields(&pref.preference.match_fields)),
46 },
47 })
48 .collect(),
49 None => Vec::new(),
50 }
51}
52fn convert_required(ca: &ClusterAffinityNodeAffinity) -> Option<NodeSelector> {
54 ca.required_during_scheduling_ignored_during_execution
55 .as_ref()
56 .map(|req| NodeSelector {
57 node_selector_terms: req
58 .node_selector_terms
59 .iter()
60 .map(|term| NodeSelectorTerm {
61 match_expressions: convert_required_match_expressions(&term.match_expressions),
62 match_fields: convert_required_match_fields(&term.match_fields),
63 })
64 .collect(),
65 })
66}
67
68fn convert_required_match_expressions(
70 expressions: &Option<Vec<ClusterAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTermsMatchExpressions>>,
71) -> Option<Vec<NodeSelectorRequirement>> {
72 expressions.as_ref().map(|exprs| {
73 exprs
74 .iter()
75 .map(|expr| {
76 NodeSelectorRequirement {
77 key: expr.key.clone(),
78 operator: expr.operator.clone(),
79 values: expr.values.as_ref().cloned(), }
81 })
82 .collect()
83 })
84}
85
86fn convert_required_match_fields(
88 fields: &Option<Vec<ClusterAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTermsMatchFields>>,
89) -> Option<Vec<NodeSelectorRequirement>> {
90 fields.as_ref().map(|flds| {
91 flds.iter()
92 .map(|field| {
93 NodeSelectorRequirement {
94 key: field.key.clone(),
95 operator: field.operator.clone(),
96 values: field.values.as_ref().cloned(), }
98 })
99 .collect()
100 })
101}
102
103fn convert_match_expressions(
105 expressions: &Option<Vec<ClusterAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionPreferenceMatchExpressions>>,
106) -> Vec<NodeSelectorRequirement> {
107 expressions.as_ref().map_or(Vec::new(), |exprs| {
108 exprs
109 .iter()
110 .map(|expr| NodeSelectorRequirement {
111 key: expr.key.clone(),
112 operator: expr.operator.clone(),
113 values: Some(
114 expr.values
115 .as_ref()
116 .map_or_else(Vec::new, |vals| vals.clone()),
117 ),
118 })
119 .collect()
120 })
121}
122
123fn convert_match_fields(
125 fields: &Option<Vec<ClusterAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionPreferenceMatchFields>>,
126) -> Vec<NodeSelectorRequirement> {
127 fields.as_ref().map_or(Vec::new(), |flds| {
128 flds.iter()
129 .map(|field| NodeSelectorRequirement {
130 key: field.key.clone(),
131 operator: field.operator.clone(),
132 values: Some(
133 field
134 .values
135 .as_ref()
136 .map_or_else(Vec::new, |vals| vals.clone()),
137 ),
138 })
139 .collect()
140 })
141}
142
143pub fn convert_node_affinity_to_pooler(
146 node_affinity: &NodeAffinity,
147) -> Option<PoolerTemplateSpecAffinityNodeAffinity> {
148 if node_affinity
149 .preferred_during_scheduling_ignored_during_execution
150 .is_none()
151 && node_affinity
152 .required_during_scheduling_ignored_during_execution
153 .is_none()
154 {
155 None
156 } else {
157 Some(PoolerTemplateSpecAffinityNodeAffinity {
158 required_during_scheduling_ignored_during_execution: node_affinity.required_during_scheduling_ignored_during_execution.as_ref().map(|req| {
159 PoolerTemplateSpecAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecution {
160 node_selector_terms: convert_node_selector_terms_to_pooler(&req.node_selector_terms),
161 }
162 }),
163 preferred_during_scheduling_ignored_during_execution: node_affinity.preferred_during_scheduling_ignored_during_execution.as_ref().map(|prefs| {
164 convert_preferred_scheduling_terms_to_pooler(prefs)
165 }),
166 })
167 }
168}
169
170fn convert_node_selector_terms_to_pooler(
173 terms: &[NodeSelectorTerm],
174) -> Vec<PoolerTemplateSpecAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTerms>{
175 terms.iter().map(|term| {
176 PoolerTemplateSpecAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTerms {
177 match_expressions: term.match_expressions.as_ref().map(|expressions| {
178 expressions.iter().map(|expr| {
179 PoolerTemplateSpecAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTermsMatchExpressions {
180 key: expr.key.clone(),
181 operator: expr.operator.clone(),
182 values: expr.values.clone(),
183 }
184 }).collect()
185 }),
186 match_fields: term.match_fields.as_ref().map(|fields| {
187 fields.iter().map(|field| {
188 PoolerTemplateSpecAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTermsMatchFields {
189 key: field.key.clone(),
190 operator: field.operator.clone(),
191 values: field.values.clone(),
192 }
193 }).collect()
194 }),
195 }
196 }).collect()
197}
198
199fn convert_preferred_scheduling_terms_to_pooler(
202 terms: &[PreferredSchedulingTerm],
203) -> Vec<PoolerTemplateSpecAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecution> {
204 terms.iter().map(|term| {
205 PoolerTemplateSpecAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecution {
206 preference: PoolerTemplateSpecAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionPreference {
207 match_expressions: term.preference.match_expressions.as_ref().map(|expressions| {
208 expressions.iter().map(|expr| {
209 PoolerTemplateSpecAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionPreferenceMatchExpressions {
210 key: expr.key.clone(),
211 operator: expr.operator.clone(),
212 values: expr.values.clone(),
213 }
214 }).collect()
215 }),
216 match_fields: term.preference.match_fields.as_ref().map(|fields| {
217 fields.iter().map(|field| {
218 PoolerTemplateSpecAffinityNodeAffinityPreferredDuringSchedulingIgnoredDuringExecutionPreferenceMatchFields {
219 key: field.key.clone(),
220 operator: field.operator.clone(),
221 values: field.values.clone(),
222 }
223 }).collect()
224 }),
225 },
226 weight: term.weight,
227 }
228 }).collect()
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 fn create_sample_node_affinity() -> NodeAffinity {
236 NodeAffinity {
237 required_during_scheduling_ignored_during_execution: Some(NodeSelector {
238 node_selector_terms: vec![NodeSelectorTerm {
239 match_expressions: Some(vec![NodeSelectorRequirement {
240 key: "region".to_string(),
241 operator: "In".to_string(),
242 values: Some(vec!["us-west-1".to_string()]),
243 }]),
244 ..Default::default()
245 }],
246 }),
247 preferred_during_scheduling_ignored_during_execution: Some(vec![
248 PreferredSchedulingTerm {
249 weight: 100,
250 preference: NodeSelectorTerm {
251 match_expressions: Some(vec![NodeSelectorRequirement {
252 key: "zone".to_string(),
253 operator: "In".to_string(),
254 values: Some(vec!["us-west-1a".to_string()]),
255 }]),
256 ..Default::default()
257 },
258 },
259 ]),
260 }
261 }
262
263 #[test]
264 fn test_convert_node_affinity_empty() {
265 let ca = ClusterAffinityNodeAffinity {
266 preferred_during_scheduling_ignored_during_execution: None,
267 required_during_scheduling_ignored_during_execution: None,
268 };
269
270 let result = convert_node_affinity(&ca);
271 assert!(result
275 .required_during_scheduling_ignored_during_execution
276 .is_none());
277 }
278
279 #[test]
280 fn test_convert_required_match_expressions_empty() {
281 let expressions = None;
282 let result = convert_required_match_expressions(&expressions);
283 assert_eq!(result, None);
284 }
285
286 #[test]
287 fn test_convert_required_match_fields_empty() {
288 let fields = None;
289 let result = convert_required_match_fields(&fields);
290 assert_eq!(result, None);
291 }
292
293 #[test]
294 fn test_convert_required_match_expressions() {
295 let expressions = Some(vec![
296 ClusterAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTermsMatchExpressions {
297 key: "key1".to_string(),
298 operator: "In".to_string(),
299 values: Some(vec!["value1".to_string(), "value2".to_string()]),
300 },
301 ]);
302
303 let result = convert_required_match_expressions(&expressions).unwrap();
304 assert_eq!(result.len(), 1);
305 assert_eq!(result[0].key, "key1");
306 assert_eq!(result[0].operator, "In");
307 assert_eq!(
308 result[0].values,
309 Some(vec!["value1".to_string(), "value2".to_string()])
310 );
311 }
312
313 #[test]
314 fn test_convert_required_match_fields() {
315 let fields = Some(vec![
316 ClusterAffinityNodeAffinityRequiredDuringSchedulingIgnoredDuringExecutionNodeSelectorTermsMatchFields {
317 key: "field1".to_string(),
318 operator: "Exists".to_string(),
319 values: None,
320 },
321 ]);
322
323 let result = convert_required_match_fields(&fields).unwrap();
324 assert_eq!(result.len(), 1);
325 assert_eq!(result[0].key, "field1");
326 assert_eq!(result[0].operator, "Exists");
327 assert!(result[0].values.is_none());
328 }
329 #[test]
330 fn test_convert_node_affinity_to_pooler_non_empty() {
331 let node_affinity = create_sample_node_affinity();
332 let result = convert_node_affinity_to_pooler(&node_affinity);
333
334 assert!(result.is_some());
335 let pooler_node_affinity = result.unwrap();
336 assert!(pooler_node_affinity
337 .required_during_scheduling_ignored_during_execution
338 .is_some());
339 assert!(pooler_node_affinity
340 .preferred_during_scheduling_ignored_during_execution
341 .is_some());
342
343 let required = &pooler_node_affinity
344 .required_during_scheduling_ignored_during_execution
345 .unwrap();
346 assert_eq!(required.node_selector_terms.len(), 1);
347 assert_eq!(
348 required.node_selector_terms[0]
349 .match_expressions
350 .as_ref()
351 .unwrap()[0]
352 .key,
353 "region"
354 );
355
356 let preferred = &pooler_node_affinity
357 .preferred_during_scheduling_ignored_during_execution
358 .unwrap()[0];
359 assert_eq!(preferred.weight, 100);
360 assert_eq!(
361 preferred.preference.match_expressions.as_ref().unwrap()[0].key,
362 "zone"
363 );
364 }
365
366 #[test]
367 fn test_convert_node_affinity_to_pooler_empty() {
368 let node_affinity = NodeAffinity {
369 required_during_scheduling_ignored_during_execution: None,
370 preferred_during_scheduling_ignored_during_execution: None,
371 };
372 let result = convert_node_affinity_to_pooler(&node_affinity);
373
374 assert!(result.is_none());
375 }
376}