syncable_cli/analyzer/k8s_optimize/
config.rs1use super::types::Severity;
4use serde::{Deserialize, Serialize};
5use std::path::Path;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct K8sOptimizeConfig {
10 pub min_severity: Severity,
12
13 pub waste_threshold_percent: u8,
15
16 pub safety_margin_percent: u8,
18
19 pub include_info: bool,
21
22 pub ignore_rules: Vec<String>,
24
25 pub exclude_namespaces: Vec<String>,
27
28 pub exclude_patterns: Vec<String>,
30
31 pub include_system: bool,
33
34 pub max_cpu_request_millicores: u32,
36
37 pub max_memory_request_mi: u32,
39
40 pub max_cpu_limit_ratio: f32,
42
43 pub max_memory_limit_ratio: f32,
45
46 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, max_memory_request_mi: 2048, max_cpu_limit_ratio: 10.0,
64 max_memory_limit_ratio: 4.0,
65 generate_fixes: true,
66 }
67 }
68}
69
70impl K8sOptimizeConfig {
71 pub fn new() -> Self {
73 Self::default()
74 }
75
76 pub fn with_severity(mut self, severity: Severity) -> Self {
78 self.min_severity = severity;
79 self
80 }
81
82 pub fn with_threshold(mut self, threshold: u8) -> Self {
84 self.waste_threshold_percent = threshold;
85 self
86 }
87
88 pub fn with_safety_margin(mut self, margin: u8) -> Self {
90 self.safety_margin_percent = margin;
91 self
92 }
93
94 pub fn with_info(mut self) -> Self {
96 self.include_info = true;
97 self.min_severity = Severity::Info;
98 self
99 }
100
101 pub fn ignore_rule(mut self, rule: impl Into<String>) -> Self {
103 self.ignore_rules.push(rule.into());
104 self
105 }
106
107 pub fn exclude_namespace(mut self, namespace: impl Into<String>) -> Self {
109 self.exclude_namespaces.push(namespace.into());
110 self
111 }
112
113 pub fn with_system(mut self) -> Self {
115 self.include_system = true;
116 self
117 }
118
119 pub fn should_ignore_rule(&self, rule: &str) -> bool {
121 self.ignore_rules.iter().any(|r| r == rule)
122 }
123
124 pub fn should_exclude_namespace(&self, namespace: &str) -> bool {
126 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 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}