syncable_cli/analyzer/kubelint/templates/
antiaffinity.rs

1//! Anti-affinity detection template.
2
3use crate::analyzer::kubelint::context::Object;
4use crate::analyzer::kubelint::context::object::K8sObject;
5use crate::analyzer::kubelint::extract;
6use crate::analyzer::kubelint::templates::{CheckFunc, ParameterDesc, Template, TemplateError};
7use crate::analyzer::kubelint::types::{Diagnostic, ObjectKindsDesc};
8
9/// Template for detecting deployments without pod anti-affinity.
10pub struct AntiAffinityTemplate;
11
12impl Template for AntiAffinityTemplate {
13    fn key(&self) -> &str {
14        "anti-affinity"
15    }
16
17    fn human_name(&self) -> &str {
18        "Anti-Affinity"
19    }
20
21    fn description(&self) -> &str {
22        "Detects deployments with multiple replicas but no pod anti-affinity"
23    }
24
25    fn supported_object_kinds(&self) -> ObjectKindsDesc {
26        ObjectKindsDesc::default()
27    }
28
29    fn parameters(&self) -> Vec<ParameterDesc> {
30        Vec::new()
31    }
32
33    fn instantiate(
34        &self,
35        _params: &serde_yaml::Value,
36    ) -> Result<Box<dyn CheckFunc>, TemplateError> {
37        Ok(Box::new(AntiAffinityCheck { min_replicas: 2 }))
38    }
39}
40
41struct AntiAffinityCheck {
42    min_replicas: i32,
43}
44
45impl CheckFunc for AntiAffinityCheck {
46    fn check(&self, object: &Object) -> Vec<Diagnostic> {
47        let mut diagnostics = Vec::new();
48
49        // Get replica count (only applicable to certain object types)
50        let replicas = match &object.k8s_object {
51            K8sObject::Deployment(d) => d.replicas.unwrap_or(1),
52            K8sObject::StatefulSet(d) => d.replicas.unwrap_or(1),
53            K8sObject::ReplicaSet(d) => d.replicas.unwrap_or(1),
54            _ => return diagnostics,
55        };
56
57        // Only check if replicas >= min_replicas
58        if replicas < self.min_replicas {
59            return diagnostics;
60        }
61
62        if let Some(pod_spec) = extract::pod_spec::extract_pod_spec(&object.k8s_object) {
63            let has_anti_affinity = pod_spec
64                .affinity
65                .as_ref()
66                .and_then(|a| a.pod_anti_affinity.as_ref())
67                .map(|paa| {
68                    !paa.required_during_scheduling_ignored_during_execution
69                        .is_empty()
70                        || !paa
71                            .preferred_during_scheduling_ignored_during_execution
72                            .is_empty()
73                })
74                .unwrap_or(false);
75
76            if !has_anti_affinity {
77                diagnostics.push(Diagnostic {
78                    message: format!(
79                        "Object '{}' has {} replicas but no pod anti-affinity rules",
80                        object.name(),
81                        replicas
82                    ),
83                    remediation: Some(
84                        "Add podAntiAffinity rules to spread replicas across nodes for high availability."
85                            .to_string(),
86                    ),
87                });
88            }
89        }
90
91        diagnostics
92    }
93}