syncable_cli/analyzer/k8s_optimize/rules/
k8s_opt_003.rs

1//! K8S-OPT-003: No CPU limit defined.
2
3use super::{OptimizationRule, RuleContext, codes};
4use crate::analyzer::k8s_optimize::config::K8sOptimizeConfig;
5use crate::analyzer::k8s_optimize::parser::parse_cpu_to_millicores;
6use crate::analyzer::k8s_optimize::types::{
7    OptimizationIssue, ResourceRecommendation, ResourceSpec, RuleCode, Severity,
8};
9
10/// Rule: No CPU limit defined.
11pub struct NoCpuLimitRule;
12
13impl OptimizationRule for NoCpuLimitRule {
14    fn code(&self) -> &'static str {
15        codes::NO_CPU_LIMIT
16    }
17
18    fn description(&self) -> &'static str {
19        "No CPU limit defined"
20    }
21
22    fn default_severity(&self) -> Severity {
23        Severity::Info
24    }
25
26    fn check(
27        &self,
28        ctx: &RuleContext,
29        config: &K8sOptimizeConfig,
30    ) -> Option<ResourceRecommendation> {
31        // Only report if include_info is set (CPU limits are optional)
32        if !config.include_info {
33            return None;
34        }
35
36        // Skip if CPU limit is defined
37        if ctx.current.cpu_limit.is_some() {
38            return None;
39        }
40
41        let defaults = ctx.workload_type.default_resources();
42
43        // Calculate CPU limit based on request if available
44        let cpu_limit = if let Some(ref cpu_request) = ctx.current.cpu_request {
45            if let Some(millicores) = parse_cpu_to_millicores(cpu_request) {
46                let limit_millicores = millicores * defaults.typical_cpu_ratio as u64;
47                crate::analyzer::k8s_optimize::parser::millicores_to_cpu_string(limit_millicores)
48            } else {
49                defaults.cpu_limit.to_string()
50            }
51        } else {
52            defaults.cpu_limit.to_string()
53        };
54
55        let recommended = ResourceSpec {
56            cpu_request: ctx.current.cpu_request.clone(),
57            cpu_limit: Some(cpu_limit),
58            memory_request: ctx.current.memory_request.clone(),
59            memory_limit: ctx.current.memory_limit.clone(),
60        };
61
62        Some(ResourceRecommendation {
63            resource_kind: ctx.resource_kind.clone(),
64            resource_name: ctx.resource_name.clone(),
65            namespace: ctx.namespace.clone(),
66            container: ctx.container_name.clone(),
67            file_path: ctx.file_path.clone(),
68            line: ctx.line,
69            issue: OptimizationIssue::NoLimitsDefined,
70            severity: self.default_severity(),
71            message: "No CPU limit defined. Consider adding one if you want to prevent CPU starvation on the node.".to_string(),
72            workload_type: ctx.workload_type,
73            current: ctx.current.clone(),
74            actual_usage: None,
75            recommended: recommended.clone(),
76            savings: None,
77            fix_yaml: recommended.to_yaml(),
78            rule_code: RuleCode::new(self.code()),
79        })
80    }
81}