syncable_cli/analyzer/k8s_optimize/
config.rs

1//! Configuration for Kubernetes resource optimization analysis.
2
3use super::types::Severity;
4use serde::{Deserialize, Serialize};
5use std::path::Path;
6
7/// Configuration for resource optimization analysis.
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct K8sOptimizeConfig {
10    /// Minimum severity to report (default: Info)
11    pub min_severity: Severity,
12
13    /// Minimum waste percentage to report (default: 10)
14    pub waste_threshold_percent: u8,
15
16    /// Safety margin percentage above recommended values (default: 20)
17    pub safety_margin_percent: u8,
18
19    /// Include info-level suggestions
20    pub include_info: bool,
21
22    /// Rules to ignore (by rule code)
23    pub ignore_rules: Vec<String>,
24
25    /// Namespaces to exclude
26    pub exclude_namespaces: Vec<String>,
27
28    /// Resource name patterns to exclude
29    pub exclude_patterns: Vec<String>,
30
31    /// Include system namespaces (kube-system, etc.)
32    pub include_system: bool,
33
34    /// Maximum CPU request before flagging (in millicores, default: 1000)
35    pub max_cpu_request_millicores: u32,
36
37    /// Maximum memory request before flagging (in Mi, default: 2048)
38    pub max_memory_request_mi: u32,
39
40    /// Maximum CPU limit to request ratio (default: 10)
41    pub max_cpu_limit_ratio: f32,
42
43    /// Maximum memory limit to request ratio (default: 4)
44    pub max_memory_limit_ratio: f32,
45
46    /// Generate YAML fix snippets
47    pub generate_fixes: bool,
48}
49
50impl Default for K8sOptimizeConfig {
51    fn default() -> Self {
52        Self {
53            min_severity: Severity::Info,
54            waste_threshold_percent: 10,
55            safety_margin_percent: 20,
56            include_info: false,
57            ignore_rules: Vec::new(),
58            exclude_namespaces: Vec::new(),
59            exclude_patterns: Vec::new(),
60            include_system: false,
61            max_cpu_request_millicores: 1000, // 1 core
62            max_memory_request_mi: 2048,      // 2Gi
63            max_cpu_limit_ratio: 10.0,
64            max_memory_limit_ratio: 4.0,
65            generate_fixes: true,
66        }
67    }
68}
69
70impl K8sOptimizeConfig {
71    /// Create a new default config.
72    pub fn new() -> Self {
73        Self::default()
74    }
75
76    /// Set the minimum severity threshold.
77    pub fn with_severity(mut self, severity: Severity) -> Self {
78        self.min_severity = severity;
79        self
80    }
81
82    /// Set the waste threshold percentage.
83    pub fn with_threshold(mut self, threshold: u8) -> Self {
84        self.waste_threshold_percent = threshold;
85        self
86    }
87
88    /// Set the safety margin percentage.
89    pub fn with_safety_margin(mut self, margin: u8) -> Self {
90        self.safety_margin_percent = margin;
91        self
92    }
93
94    /// Include info-level suggestions.
95    pub fn with_info(mut self) -> Self {
96        self.include_info = true;
97        self.min_severity = Severity::Info;
98        self
99    }
100
101    /// Add a rule to ignore.
102    pub fn ignore_rule(mut self, rule: impl Into<String>) -> Self {
103        self.ignore_rules.push(rule.into());
104        self
105    }
106
107    /// Add a namespace to exclude.
108    pub fn exclude_namespace(mut self, namespace: impl Into<String>) -> Self {
109        self.exclude_namespaces.push(namespace.into());
110        self
111    }
112
113    /// Include system namespaces.
114    pub fn with_system(mut self) -> Self {
115        self.include_system = true;
116        self
117    }
118
119    /// Check if a rule should be ignored.
120    pub fn should_ignore_rule(&self, rule: &str) -> bool {
121        self.ignore_rules.iter().any(|r| r == rule)
122    }
123
124    /// Check if a namespace should be excluded.
125    pub fn should_exclude_namespace(&self, namespace: &str) -> bool {
126        // Always exclude system namespaces unless include_system is true
127        if !self.include_system {
128            const SYSTEM_NAMESPACES: &[&str] =
129                &["kube-system", "kube-public", "kube-node-lease", "default"];
130            if SYSTEM_NAMESPACES.contains(&namespace) {
131                return true;
132            }
133        }
134
135        self.exclude_namespaces.iter().any(|n| n == namespace)
136    }
137
138    /// Check if a path should be ignored.
139    pub fn should_ignore_path(&self, path: &Path) -> bool {
140        let path_str = path.to_string_lossy();
141
142        for pattern in &self.exclude_patterns {
143            if path_str.contains(pattern) {
144                return true;
145            }
146        }
147
148        false
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn test_default_config() {
158        let config = K8sOptimizeConfig::default();
159        assert_eq!(config.waste_threshold_percent, 10);
160        assert_eq!(config.safety_margin_percent, 20);
161        assert!(!config.include_system);
162    }
163
164    #[test]
165    fn test_exclude_system_namespaces() {
166        let config = K8sOptimizeConfig::default();
167        assert!(config.should_exclude_namespace("kube-system"));
168        assert!(!config.should_exclude_namespace("production"));
169
170        let config = config.with_system();
171        assert!(!config.should_exclude_namespace("kube-system"));
172    }
173
174    #[test]
175    fn test_builder_pattern() {
176        let config = K8sOptimizeConfig::new()
177            .with_threshold(20)
178            .with_safety_margin(30)
179            .ignore_rule("K8S-OPT-001")
180            .exclude_namespace("test");
181
182        assert_eq!(config.waste_threshold_percent, 20);
183        assert_eq!(config.safety_margin_percent, 30);
184        assert!(config.should_ignore_rule("K8S-OPT-001"));
185        assert!(config.should_exclude_namespace("test"));
186    }
187}