syncable_cli/analyzer/kubelint/templates/
requirements.rs

1//! CPU and memory requirements detection templates.
2
3use crate::analyzer::kubelint::context::Object;
4use crate::analyzer::kubelint::extract;
5use crate::analyzer::kubelint::templates::{CheckFunc, ParameterDesc, Template, TemplateError};
6use crate::analyzer::kubelint::types::{Diagnostic, ObjectKindsDesc};
7
8/// Template for detecting containers without CPU requirements.
9pub struct CpuRequirementsTemplate;
10
11impl Template for CpuRequirementsTemplate {
12    fn key(&self) -> &str {
13        "cpu-requirements"
14    }
15
16    fn human_name(&self) -> &str {
17        "CPU Requirements"
18    }
19
20    fn description(&self) -> &str {
21        "Detects containers without CPU requests or limits"
22    }
23
24    fn supported_object_kinds(&self) -> ObjectKindsDesc {
25        ObjectKindsDesc::default()
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(CpuRequirementsCheck {
37            require_limits: false,
38        }))
39    }
40}
41
42struct CpuRequirementsCheck {
43    require_limits: bool,
44}
45
46impl CheckFunc for CpuRequirementsCheck {
47    fn check(&self, object: &Object) -> Vec<Diagnostic> {
48        let mut diagnostics = Vec::new();
49
50        if let Some(pod_spec) = extract::pod_spec::extract_pod_spec(&object.k8s_object) {
51            for container in extract::container::all_containers(pod_spec) {
52                let has_cpu_request = container
53                    .resources
54                    .as_ref()
55                    .and_then(|r| r.requests.as_ref())
56                    .map(|r| r.contains_key("cpu"))
57                    .unwrap_or(false);
58
59                let has_cpu_limit = container
60                    .resources
61                    .as_ref()
62                    .and_then(|r| r.limits.as_ref())
63                    .map(|r| r.contains_key("cpu"))
64                    .unwrap_or(false);
65
66                if !has_cpu_request {
67                    diagnostics.push(Diagnostic {
68                        message: format!(
69                            "Container '{}' does not have a CPU request",
70                            container.name
71                        ),
72                        remediation: Some(
73                            "Set resources.requests.cpu for proper scheduling.".to_string(),
74                        ),
75                    });
76                }
77
78                if self.require_limits && !has_cpu_limit {
79                    diagnostics.push(Diagnostic {
80                        message: format!(
81                            "Container '{}' does not have a CPU limit",
82                            container.name
83                        ),
84                        remediation: Some(
85                            "Set resources.limits.cpu to prevent resource exhaustion.".to_string(),
86                        ),
87                    });
88                }
89            }
90        }
91
92        diagnostics
93    }
94}
95
96/// Template for detecting containers without memory requirements.
97pub struct MemoryRequirementsTemplate;
98
99impl Template for MemoryRequirementsTemplate {
100    fn key(&self) -> &str {
101        "memory-requirements"
102    }
103
104    fn human_name(&self) -> &str {
105        "Memory Requirements"
106    }
107
108    fn description(&self) -> &str {
109        "Detects containers without memory requests or limits"
110    }
111
112    fn supported_object_kinds(&self) -> ObjectKindsDesc {
113        ObjectKindsDesc::default()
114    }
115
116    fn parameters(&self) -> Vec<ParameterDesc> {
117        Vec::new()
118    }
119
120    fn instantiate(
121        &self,
122        _params: &serde_yaml::Value,
123    ) -> Result<Box<dyn CheckFunc>, TemplateError> {
124        Ok(Box::new(MemoryRequirementsCheck {
125            require_limits: false,
126        }))
127    }
128}
129
130struct MemoryRequirementsCheck {
131    require_limits: bool,
132}
133
134impl CheckFunc for MemoryRequirementsCheck {
135    fn check(&self, object: &Object) -> Vec<Diagnostic> {
136        let mut diagnostics = Vec::new();
137
138        if let Some(pod_spec) = extract::pod_spec::extract_pod_spec(&object.k8s_object) {
139            for container in extract::container::all_containers(pod_spec) {
140                let has_memory_request = container
141                    .resources
142                    .as_ref()
143                    .and_then(|r| r.requests.as_ref())
144                    .map(|r| r.contains_key("memory"))
145                    .unwrap_or(false);
146
147                let has_memory_limit = container
148                    .resources
149                    .as_ref()
150                    .and_then(|r| r.limits.as_ref())
151                    .map(|r| r.contains_key("memory"))
152                    .unwrap_or(false);
153
154                if !has_memory_request {
155                    diagnostics.push(Diagnostic {
156                        message: format!(
157                            "Container '{}' does not have a memory request",
158                            container.name
159                        ),
160                        remediation: Some(
161                            "Set resources.requests.memory for proper scheduling.".to_string(),
162                        ),
163                    });
164                }
165
166                if self.require_limits && !has_memory_limit {
167                    diagnostics.push(Diagnostic {
168                        message: format!(
169                            "Container '{}' does not have a memory limit",
170                            container.name
171                        ),
172                        remediation: Some(
173                            "Set resources.limits.memory to prevent OOM kills.".to_string(),
174                        ),
175                    });
176                }
177            }
178        }
179
180        diagnostics
181    }
182}