syncable_cli/analyzer/k8s_optimize/rules/
mod.rs1mod k8s_opt_001;
7mod k8s_opt_002;
8mod k8s_opt_003;
9mod k8s_opt_004;
10mod k8s_opt_005;
11mod k8s_opt_006;
12mod k8s_opt_007;
13mod k8s_opt_008;
14mod k8s_opt_009;
15mod k8s_opt_010;
16
17use crate::analyzer::k8s_optimize::config::K8sOptimizeConfig;
18use crate::analyzer::k8s_optimize::types::{
19 ResourceRecommendation, ResourceSpec, Severity, WorkloadType,
20};
21use std::path::PathBuf;
22
23pub trait OptimizationRule: Send + Sync {
29 fn code(&self) -> &'static str;
31
32 fn description(&self) -> &'static str;
34
35 fn default_severity(&self) -> Severity;
37
38 fn check(
40 &self,
41 ctx: &RuleContext,
42 config: &K8sOptimizeConfig,
43 ) -> Option<ResourceRecommendation>;
44}
45
46pub struct RuleContext {
48 pub resource_kind: String,
49 pub resource_name: String,
50 pub namespace: Option<String>,
51 pub container_name: String,
52 pub file_path: PathBuf,
53 pub line: Option<u32>,
54 pub current: ResourceSpec,
55 pub workload_type: WorkloadType,
56}
57
58pub mod codes {
64 pub const NO_CPU_REQUEST: &str = "K8S-OPT-001";
65 pub const NO_MEMORY_REQUEST: &str = "K8S-OPT-002";
66 pub const NO_CPU_LIMIT: &str = "K8S-OPT-003";
67 pub const NO_MEMORY_LIMIT: &str = "K8S-OPT-004";
68 pub const HIGH_CPU_REQUEST: &str = "K8S-OPT-005";
69 pub const HIGH_MEMORY_REQUEST: &str = "K8S-OPT-006";
70 pub const EXCESSIVE_CPU_RATIO: &str = "K8S-OPT-007";
71 pub const EXCESSIVE_MEMORY_RATIO: &str = "K8S-OPT-008";
72 pub const REQUESTS_EQUAL_LIMITS: &str = "K8S-OPT-009";
73 pub const UNBALANCED_RESOURCES: &str = "K8S-OPT-010";
74}
75
76pub fn all_rules() -> Vec<Box<dyn OptimizationRule>> {
82 vec![
83 Box::new(k8s_opt_001::NoCpuRequestRule),
84 Box::new(k8s_opt_002::NoMemoryRequestRule),
85 Box::new(k8s_opt_003::NoCpuLimitRule),
86 Box::new(k8s_opt_004::NoMemoryLimitRule),
87 Box::new(k8s_opt_005::HighCpuRequestRule),
88 Box::new(k8s_opt_006::HighMemoryRequestRule),
89 Box::new(k8s_opt_007::ExcessiveCpuRatioRule),
90 Box::new(k8s_opt_008::ExcessiveMemoryRatioRule),
91 Box::new(k8s_opt_009::RequestsEqualLimitsRule),
92 Box::new(k8s_opt_010::UnbalancedResourcesRule),
93 ]
94}
95
96pub fn rule_description(code: &str) -> &'static str {
98 match code {
99 codes::NO_CPU_REQUEST => "No CPU request defined",
100 codes::NO_MEMORY_REQUEST => "No memory request defined",
101 codes::NO_CPU_LIMIT => "No CPU limit defined",
102 codes::NO_MEMORY_LIMIT => "No memory limit defined",
103 codes::HIGH_CPU_REQUEST => "CPU request exceeds threshold for workload type",
104 codes::HIGH_MEMORY_REQUEST => "Memory request exceeds threshold for workload type",
105 codes::EXCESSIVE_CPU_RATIO => "CPU limit to request ratio is excessive",
106 codes::EXCESSIVE_MEMORY_RATIO => "Memory limit to request ratio is excessive",
107 codes::REQUESTS_EQUAL_LIMITS => "Requests equal limits (no bursting allowed)",
108 codes::UNBALANCED_RESOURCES => "Resource allocation is unbalanced for workload type",
109 _ => "Unknown rule",
110 }
111}
112
113pub type ContainerContext = RuleContext;
119
120pub fn generate_recommendations(
122 ctx: &RuleContext,
123 config: &K8sOptimizeConfig,
124) -> Vec<ResourceRecommendation> {
125 let mut recommendations = Vec::new();
126
127 if !ctx.current.has_requests() && !ctx.current.has_limits() {
129 let defaults = ctx.workload_type.default_resources();
130 let recommended = ResourceSpec {
131 cpu_request: Some(defaults.cpu_request.to_string()),
132 cpu_limit: Some(defaults.cpu_limit.to_string()),
133 memory_request: Some(defaults.memory_request.to_string()),
134 memory_limit: Some(defaults.memory_limit.to_string()),
135 };
136
137 recommendations.push(ResourceRecommendation {
138 resource_kind: ctx.resource_kind.clone(),
139 resource_name: ctx.resource_name.clone(),
140 namespace: ctx.namespace.clone(),
141 container: ctx.container_name.clone(),
142 file_path: ctx.file_path.clone(),
143 line: ctx.line,
144 issue: crate::analyzer::k8s_optimize::types::OptimizationIssue::NoRequestsDefined,
145 severity: Severity::Critical,
146 message: "No resource requests defined. This can lead to resource contention, unpredictable scheduling, and OOM kills.".to_string(),
147 workload_type: ctx.workload_type,
148 current: ctx.current.clone(),
149 actual_usage: None,
150 recommended: recommended.clone(),
151 savings: None,
152 fix_yaml: recommended.to_yaml(),
153 rule_code: crate::analyzer::k8s_optimize::types::RuleCode::new(codes::NO_CPU_REQUEST),
154 });
155
156 return recommendations;
157 }
158
159 for rule in all_rules() {
161 if config.should_ignore_rule(rule.code()) {
163 continue;
164 }
165
166 if let Some(rec) = rule.check(ctx, config) {
168 if rec.severity >= config.min_severity {
170 recommendations.push(rec);
171 }
172 }
173 }
174
175 recommendations
176}
177
178pub use k8s_opt_001::NoCpuRequestRule;
180pub use k8s_opt_002::NoMemoryRequestRule;
181pub use k8s_opt_003::NoCpuLimitRule;
182pub use k8s_opt_004::NoMemoryLimitRule;
183pub use k8s_opt_005::HighCpuRequestRule;
184pub use k8s_opt_006::HighMemoryRequestRule;
185pub use k8s_opt_007::ExcessiveCpuRatioRule;
186pub use k8s_opt_008::ExcessiveMemoryRatioRule;
187pub use k8s_opt_009::RequestsEqualLimitsRule;
188pub use k8s_opt_010::UnbalancedResourcesRule;