syncable_cli/analyzer/kubelint/templates/
pdb.rs1use crate::analyzer::kubelint::context::K8sObject;
4use crate::analyzer::kubelint::context::Object;
5use crate::analyzer::kubelint::templates::{CheckFunc, ParameterDesc, Template, TemplateError};
6use crate::analyzer::kubelint::types::{Diagnostic, ObjectKindsDesc};
7
8pub struct PdbMaxUnavailableTemplate;
10
11impl Template for PdbMaxUnavailableTemplate {
12 fn key(&self) -> &str {
13 "pdb-max-unavailable"
14 }
15
16 fn human_name(&self) -> &str {
17 "PDB Max Unavailable"
18 }
19
20 fn description(&self) -> &str {
21 "Checks PodDisruptionBudget maxUnavailable settings"
22 }
23
24 fn supported_object_kinds(&self) -> ObjectKindsDesc {
25 ObjectKindsDesc::new(&["PodDisruptionBudget"])
26 }
27
28 fn parameters(&self) -> Vec<ParameterDesc> {
29 Vec::new()
30 }
31
32 fn instantiate(
33 &self,
34 _params: &serde_yaml::Value,
35 ) -> Result<Box<dyn CheckFunc>, TemplateError> {
36 Ok(Box::new(PdbMaxUnavailableCheck))
37 }
38}
39
40struct PdbMaxUnavailableCheck;
41
42impl CheckFunc for PdbMaxUnavailableCheck {
43 fn check(&self, object: &Object) -> Vec<Diagnostic> {
44 let mut diagnostics = Vec::new();
45
46 if let K8sObject::PodDisruptionBudget(pdb) = &object.k8s_object {
47 if let Some(max_unavailable) = &pdb.max_unavailable {
48 if max_unavailable == "0" || max_unavailable == "0%" {
50 diagnostics.push(Diagnostic {
51 message:
52 "PDB maxUnavailable is set to 0, which blocks all voluntary disruptions"
53 .to_string(),
54 remediation: Some(
55 "Set maxUnavailable to at least 1 or a non-zero percentage to allow \
56 voluntary disruptions during cluster maintenance."
57 .to_string(),
58 ),
59 });
60 }
61 }
62 }
63
64 diagnostics
65 }
66}
67
68pub struct PdbMinAvailableTemplate;
70
71impl Template for PdbMinAvailableTemplate {
72 fn key(&self) -> &str {
73 "pdb-min-available"
74 }
75
76 fn human_name(&self) -> &str {
77 "PDB Min Available"
78 }
79
80 fn description(&self) -> &str {
81 "Checks PodDisruptionBudget minAvailable settings"
82 }
83
84 fn supported_object_kinds(&self) -> ObjectKindsDesc {
85 ObjectKindsDesc::new(&["PodDisruptionBudget"])
86 }
87
88 fn parameters(&self) -> Vec<ParameterDesc> {
89 Vec::new()
90 }
91
92 fn instantiate(
93 &self,
94 _params: &serde_yaml::Value,
95 ) -> Result<Box<dyn CheckFunc>, TemplateError> {
96 Ok(Box::new(PdbMinAvailableCheck))
97 }
98}
99
100struct PdbMinAvailableCheck;
101
102impl CheckFunc for PdbMinAvailableCheck {
103 fn check(&self, object: &Object) -> Vec<Diagnostic> {
104 let mut diagnostics = Vec::new();
105
106 if let K8sObject::PodDisruptionBudget(pdb) = &object.k8s_object {
107 if let Some(min_available) = &pdb.min_available {
108 if min_available == "100%" {
110 diagnostics.push(Diagnostic {
111 message: "PDB minAvailable is set to 100%, which blocks all voluntary disruptions".to_string(),
112 remediation: Some(
113 "Set minAvailable to less than 100% to allow voluntary disruptions \
114 during cluster maintenance."
115 .to_string(),
116 ),
117 });
118 }
119 }
120 }
121
122 diagnostics
123 }
124}
125
126pub struct PdbUnhealthyPodEvictionPolicyTemplate;
128
129impl Template for PdbUnhealthyPodEvictionPolicyTemplate {
130 fn key(&self) -> &str {
131 "pdb-unhealthy-pod-eviction-policy"
132 }
133
134 fn human_name(&self) -> &str {
135 "PDB Unhealthy Pod Eviction Policy"
136 }
137
138 fn description(&self) -> &str {
139 "Checks PodDisruptionBudget unhealthyPodEvictionPolicy settings"
140 }
141
142 fn supported_object_kinds(&self) -> ObjectKindsDesc {
143 ObjectKindsDesc::new(&["PodDisruptionBudget"])
144 }
145
146 fn parameters(&self) -> Vec<ParameterDesc> {
147 Vec::new()
148 }
149
150 fn instantiate(
151 &self,
152 _params: &serde_yaml::Value,
153 ) -> Result<Box<dyn CheckFunc>, TemplateError> {
154 Ok(Box::new(PdbUnhealthyPodEvictionPolicyCheck))
155 }
156}
157
158struct PdbUnhealthyPodEvictionPolicyCheck;
159
160impl CheckFunc for PdbUnhealthyPodEvictionPolicyCheck {
161 fn check(&self, object: &Object) -> Vec<Diagnostic> {
162 let mut diagnostics = Vec::new();
163
164 if let K8sObject::PodDisruptionBudget(pdb) = &object.k8s_object {
165 if pdb.unhealthy_pod_eviction_policy.is_none() {
167 diagnostics.push(Diagnostic {
168 message: "PDB does not specify unhealthyPodEvictionPolicy".to_string(),
169 remediation: Some(
170 "Consider setting unhealthyPodEvictionPolicy to 'AlwaysAllow' to allow \
171 eviction of unhealthy pods even when budget is violated."
172 .to_string(),
173 ),
174 });
175 }
176 }
177
178 diagnostics
179 }
180}
181
182#[cfg(test)]
183mod tests {
184 use super::*;
185 use crate::analyzer::kubelint::parser::yaml::parse_yaml;
186
187 #[test]
188 fn test_pdb_max_unavailable_zero() {
189 let yaml = r#"
190apiVersion: policy/v1
191kind: PodDisruptionBudget
192metadata:
193 name: strict-pdb
194spec:
195 maxUnavailable: 0
196 selector:
197 matchLabels:
198 app: test
199"#;
200 let objects = parse_yaml(yaml).unwrap();
201 let check = PdbMaxUnavailableCheck;
202 let diagnostics = check.check(&objects[0]);
203 assert_eq!(diagnostics.len(), 1);
204 assert!(diagnostics[0].message.contains("maxUnavailable"));
205 }
206
207 #[test]
208 fn test_pdb_min_available_100_percent() {
209 let yaml = r#"
210apiVersion: policy/v1
211kind: PodDisruptionBudget
212metadata:
213 name: strict-pdb
214spec:
215 minAvailable: "100%"
216 selector:
217 matchLabels:
218 app: test
219"#;
220 let objects = parse_yaml(yaml).unwrap();
221 let check = PdbMinAvailableCheck;
222 let diagnostics = check.check(&objects[0]);
223 assert_eq!(diagnostics.len(), 1);
224 assert!(diagnostics[0].message.contains("minAvailable"));
225 }
226}