syncable_cli/analyzer/kubelint/parser/
kustomize.rs1use crate::analyzer::kubelint::context::Object;
4use crate::analyzer::kubelint::parser::yaml;
5use std::path::Path;
6use std::process::Command;
7
8pub fn render_kustomize(dir: &Path) -> Result<Vec<Object>, KustomizeError> {
13 let output = if is_kustomize_available() {
15 let mut cmd = Command::new("kustomize");
16 cmd.arg("build").arg(dir);
17 cmd.output()
18 .map_err(|e| KustomizeError::BuildError(e.to_string()))?
19 } else if is_kubectl_kustomize_available() {
20 let mut cmd = Command::new("kubectl");
21 cmd.arg("kustomize").arg(dir);
22 cmd.output()
23 .map_err(|e| KustomizeError::BuildError(e.to_string()))?
24 } else {
25 return Err(KustomizeError::KustomizeNotFound);
26 };
27
28 if !output.status.success() {
29 let stderr = String::from_utf8_lossy(&output.stderr);
30 return Err(KustomizeError::BuildError(stderr.to_string()));
31 }
32
33 let yaml_content = String::from_utf8_lossy(&output.stdout);
35 yaml::parse_yaml_with_path(&yaml_content, dir)
36 .map_err(|e| KustomizeError::BuildError(e.to_string()))
37}
38
39pub fn render_kustomize_with_options(
41 dir: &Path,
42 enable_helm: bool,
43 load_restrictors: LoadRestrictors,
44) -> Result<Vec<Object>, KustomizeError> {
45 if !is_kustomize_available() && !is_kubectl_kustomize_available() {
46 return Err(KustomizeError::KustomizeNotFound);
47 }
48
49 let output = if is_kustomize_available() {
50 let mut cmd = Command::new("kustomize");
51 cmd.arg("build").arg(dir);
52
53 if enable_helm {
54 cmd.arg("--enable-helm");
55 }
56
57 match load_restrictors {
58 LoadRestrictors::None => {
59 cmd.arg("--load-restrictor=none");
60 }
61 LoadRestrictors::RootOnly => {
62 }
64 }
65
66 cmd.output()
67 .map_err(|e| KustomizeError::BuildError(e.to_string()))?
68 } else {
69 let mut cmd = Command::new("kubectl");
71 cmd.arg("kustomize").arg(dir);
72
73 if enable_helm {
74 cmd.arg("--enable-helm");
75 }
76
77 cmd.output()
78 .map_err(|e| KustomizeError::BuildError(e.to_string()))?
79 };
80
81 if !output.status.success() {
82 let stderr = String::from_utf8_lossy(&output.stderr);
83 return Err(KustomizeError::BuildError(stderr.to_string()));
84 }
85
86 let yaml_content = String::from_utf8_lossy(&output.stdout);
87 yaml::parse_yaml_with_path(&yaml_content, dir)
88 .map_err(|e| KustomizeError::BuildError(e.to_string()))
89}
90
91#[derive(Debug, Clone, Copy, Default)]
93pub enum LoadRestrictors {
94 None,
96 #[default]
98 RootOnly,
99}
100
101pub fn is_kustomize_dir(path: &Path) -> bool {
103 path.join("kustomization.yaml").exists()
104 || path.join("kustomization.yml").exists()
105 || path.join("Kustomization").exists()
106}
107
108pub fn is_kustomize_available() -> bool {
110 Command::new("kustomize")
111 .arg("version")
112 .output()
113 .map(|o| o.status.success())
114 .unwrap_or(false)
115}
116
117pub fn is_kubectl_kustomize_available() -> bool {
119 Command::new("kubectl")
120 .arg("kustomize")
121 .arg("--help")
122 .output()
123 .map(|o| o.status.success())
124 .unwrap_or(false)
125}
126
127pub fn kustomize_version() -> Option<String> {
129 if let Some(version) = Command::new("kustomize")
131 .arg("version")
132 .output()
133 .ok()
134 .filter(|o| o.status.success())
135 .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string())
136 {
137 return Some(version);
138 }
139
140 Command::new("kubectl")
142 .arg("version")
143 .arg("--client")
144 .arg("-o")
145 .arg("json")
146 .output()
147 .ok()
148 .filter(|o| o.status.success())
149 .map(|o| {
150 let output = String::from_utf8_lossy(&o.stdout);
151 format!("kubectl ({})", output.lines().next().unwrap_or("unknown"))
152 })
153}
154
155#[derive(Debug, Clone)]
157pub enum KustomizeError {
158 KustomizeNotFound,
160 BuildError(String),
162}
163
164impl std::fmt::Display for KustomizeError {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 match self {
167 Self::KustomizeNotFound => {
168 write!(
169 f,
170 "kustomize binary not found in PATH (tried 'kustomize' and 'kubectl kustomize')"
171 )
172 }
173 Self::BuildError(msg) => write!(f, "Build error: {}", msg),
174 }
175 }
176}
177
178impl std::error::Error for KustomizeError {}
179
180#[cfg(test)]
181mod tests {
182 use super::*;
183
184 #[test]
185 fn test_is_kustomize_dir_detection() {
186 let temp_dir = std::env::temp_dir();
187 assert!(!is_kustomize_dir(&temp_dir)); }
189
190 #[test]
191 fn test_kustomize_availability() {
192 let _available = is_kustomize_available();
194 let _kubectl_available = is_kubectl_kustomize_available();
195 }
196}