use super::types::Severity;
use serde::{Deserialize, Serialize};
use std::path::Path;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct K8sOptimizeConfig {
pub min_severity: Severity,
pub waste_threshold_percent: u8,
pub safety_margin_percent: u8,
pub include_info: bool,
pub ignore_rules: Vec<String>,
pub exclude_namespaces: Vec<String>,
pub exclude_patterns: Vec<String>,
pub include_system: bool,
pub max_cpu_request_millicores: u32,
pub max_memory_request_mi: u32,
pub max_cpu_limit_ratio: f32,
pub max_memory_limit_ratio: f32,
pub generate_fixes: bool,
}
impl Default for K8sOptimizeConfig {
fn default() -> Self {
Self {
min_severity: Severity::Info,
waste_threshold_percent: 10,
safety_margin_percent: 20,
include_info: false,
ignore_rules: Vec::new(),
exclude_namespaces: Vec::new(),
exclude_patterns: Vec::new(),
include_system: false,
max_cpu_request_millicores: 1000, max_memory_request_mi: 2048, max_cpu_limit_ratio: 10.0,
max_memory_limit_ratio: 4.0,
generate_fixes: true,
}
}
}
impl K8sOptimizeConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_severity(mut self, severity: Severity) -> Self {
self.min_severity = severity;
self
}
pub fn with_threshold(mut self, threshold: u8) -> Self {
self.waste_threshold_percent = threshold;
self
}
pub fn with_safety_margin(mut self, margin: u8) -> Self {
self.safety_margin_percent = margin;
self
}
pub fn with_info(mut self) -> Self {
self.include_info = true;
self.min_severity = Severity::Info;
self
}
pub fn ignore_rule(mut self, rule: impl Into<String>) -> Self {
self.ignore_rules.push(rule.into());
self
}
pub fn exclude_namespace(mut self, namespace: impl Into<String>) -> Self {
self.exclude_namespaces.push(namespace.into());
self
}
pub fn with_system(mut self) -> Self {
self.include_system = true;
self
}
pub fn should_ignore_rule(&self, rule: &str) -> bool {
self.ignore_rules.iter().any(|r| r == rule)
}
pub fn should_exclude_namespace(&self, namespace: &str) -> bool {
if !self.include_system {
const SYSTEM_NAMESPACES: &[&str] =
&["kube-system", "kube-public", "kube-node-lease", "default"];
if SYSTEM_NAMESPACES.contains(&namespace) {
return true;
}
}
self.exclude_namespaces.iter().any(|n| n == namespace)
}
pub fn should_ignore_path(&self, path: &Path) -> bool {
let path_str = path.to_string_lossy();
for pattern in &self.exclude_patterns {
if path_str.contains(pattern) {
return true;
}
}
false
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = K8sOptimizeConfig::default();
assert_eq!(config.waste_threshold_percent, 10);
assert_eq!(config.safety_margin_percent, 20);
assert!(!config.include_system);
}
#[test]
fn test_exclude_system_namespaces() {
let config = K8sOptimizeConfig::default();
assert!(config.should_exclude_namespace("kube-system"));
assert!(!config.should_exclude_namespace("production"));
let config = config.with_system();
assert!(!config.should_exclude_namespace("kube-system"));
}
#[test]
fn test_builder_pattern() {
let config = K8sOptimizeConfig::new()
.with_threshold(20)
.with_safety_margin(30)
.ignore_rule("K8S-OPT-001")
.exclude_namespace("test");
assert_eq!(config.waste_threshold_percent, 20);
assert_eq!(config.safety_margin_percent, 30);
assert!(config.should_ignore_rule("K8S-OPT-001"));
assert!(config.should_exclude_namespace("test"));
}
}