syncable_cli/analyzer/k8s_optimize/rules/
mod.rs

1//! Individual optimization rules for Kubernetes resources.
2//!
3//! Each rule is implemented as a separate module with a consistent interface.
4//! Rules are identified by codes like K8S-OPT-001, K8S-OPT-002, etc.
5
6mod 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
23// ============================================================================
24// Rule Trait
25// ============================================================================
26
27/// Trait for optimization rules.
28pub trait OptimizationRule: Send + Sync {
29    /// Get the rule code (e.g., "K8S-OPT-001").
30    fn code(&self) -> &'static str;
31
32    /// Get the rule description.
33    fn description(&self) -> &'static str;
34
35    /// Get the default severity for this rule.
36    fn default_severity(&self) -> Severity;
37
38    /// Check if this rule applies and generate a recommendation if so.
39    fn check(
40        &self,
41        ctx: &RuleContext,
42        config: &K8sOptimizeConfig,
43    ) -> Option<ResourceRecommendation>;
44}
45
46/// Context for rule evaluation.
47pub 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
58// ============================================================================
59// Rule Codes
60// ============================================================================
61
62/// Rule code constants.
63pub 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
76// ============================================================================
77// Rule Registry
78// ============================================================================
79
80/// Get all available optimization rules.
81pub 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
96/// Get rule description by code.
97pub 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
113// ============================================================================
114// Recommendation Generation
115// ============================================================================
116
117/// Container context for generating recommendations (backward compatibility).
118pub type ContainerContext = RuleContext;
119
120/// Generate recommendations for a container using all applicable rules.
121pub fn generate_recommendations(
122    ctx: &RuleContext,
123    config: &K8sOptimizeConfig,
124) -> Vec<ResourceRecommendation> {
125    let mut recommendations = Vec::new();
126
127    // Special case: If no resources are defined at all, generate a single critical recommendation
128    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    // Run all rules
160    for rule in all_rules() {
161        // Skip if rule is ignored
162        if config.should_ignore_rule(rule.code()) {
163            continue;
164        }
165
166        // Check if rule applies
167        if let Some(rec) = rule.check(ctx, config) {
168            // Filter by severity
169            if rec.severity >= config.min_severity {
170                recommendations.push(rec);
171            }
172        }
173    }
174
175    recommendations
176}
177
178// Re-export rule implementations for direct access
179pub 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;