xianyun_deploy/deployer/kustomize/
mod.rs1pub mod builtin;
9pub mod camaterial;
10pub mod files;
11pub mod hostname;
12pub mod manager;
13
14use std::path::PathBuf;
15
16pub(crate) type Cleanup = Box<dyn FnOnce() + Send>;
20
21pub(crate) fn mkdtemp(prefix: &str) -> std::io::Result<PathBuf> {
26 use std::os::unix::ffi::OsStrExt;
27
28 let template_path = std::env::temp_dir().join(format!("{prefix}XXXXXX"));
29
30 let mut template = std::ffi::CString::new(template_path.as_os_str().as_bytes())
31 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?
32 .into_bytes_with_nul();
33
34 let result = unsafe { libc::mkdtemp(template.as_mut_ptr().cast::<libc::c_char>()) };
38
39 if result.is_null() {
40 return Err(std::io::Error::last_os_error());
41 }
42
43 let path_bytes = &template[..template.len() - 1]; Ok(PathBuf::from(std::ffi::OsStr::from_bytes(path_bytes)))
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn mkdtemp_creates_a_fresh_directory() {
53 let dir = mkdtemp("caproni-mkdtemp-test-").unwrap();
54 assert!(dir.is_dir());
55 std::fs::remove_dir_all(&dir).unwrap();
56 }
57}
58
59#[cfg(test)]
68pub(crate) mod test_support {
69 use std::sync::Mutex;
70 use std::time::Duration;
71
72 use xianyun_backends::kubectl::{Backend as KubectlBackend, KubectlError, VersionResponse};
73 use xianyun_backends::kustomize::{Backend as KustomizeBackend, KustomizeError};
74
75 type BuildFn = Box<dyn FnMut(&str) -> Result<Vec<u8>, KustomizeError> + Send>;
76
77 #[derive(Default)]
81 pub(crate) struct MockKustomizeBackend {
82 build_fn: Mutex<Option<BuildFn>>,
83 }
84
85 impl MockKustomizeBackend {
86 pub(crate) fn new() -> Self {
87 Self::default()
88 }
89
90 pub(crate) fn expect_build<F>(self, f: F) -> Self
91 where
92 F: FnMut(&str) -> Result<Vec<u8>, KustomizeError> + Send + 'static,
93 {
94 *self.build_fn.lock().unwrap() = Some(Box::new(f));
95 self
96 }
97 }
98
99 impl KustomizeBackend for MockKustomizeBackend {
100 fn build(&self, dir: &str) -> Result<Vec<u8>, KustomizeError> {
101 let mut guard = self.build_fn.lock().unwrap();
102 match guard.as_mut() {
103 Some(f) => f(dir),
104 None => panic!("MockKustomizeBackend::build called without an expectation"),
105 }
106 }
107 }
108
109 type ApplyFn = Box<dyn FnMut(&[u8], &str) -> Result<(), KubectlError> + Send>;
110 type DeleteManifestFn = Box<dyn FnMut(&[u8], &str) -> Result<(), KubectlError> + Send>;
111
112 #[derive(Default)]
118 pub(crate) struct MockKubectlBackend {
119 apply_fn: Mutex<Option<ApplyFn>>,
120 delete_manifest_fn: Mutex<Option<DeleteManifestFn>>,
121 }
122
123 impl MockKubectlBackend {
124 pub(crate) fn new() -> Self {
125 Self::default()
126 }
127
128 pub(crate) fn expect_apply<F>(self, f: F) -> Self
129 where
130 F: FnMut(&[u8], &str) -> Result<(), KubectlError> + Send + 'static,
131 {
132 *self.apply_fn.lock().unwrap() = Some(Box::new(f));
133 self
134 }
135
136 pub(crate) fn expect_delete_manifest<F>(self, f: F) -> Self
137 where
138 F: FnMut(&[u8], &str) -> Result<(), KubectlError> + Send + 'static,
139 {
140 *self.delete_manifest_fn.lock().unwrap() = Some(Box::new(f));
141 self
142 }
143 }
144
145 impl KubectlBackend for MockKubectlBackend {
146 fn get(
147 &self,
148 _resource_type: &str,
149 _name: &str,
150 _namespace: &str,
151 ) -> Result<String, KubectlError> {
152 panic!("MockKubectlBackend::get: unexpected call")
153 }
154
155 fn delete_pods_by_label(
156 &self,
157 _namespace: &str,
158 _label_selector: &str,
159 ) -> Result<usize, KubectlError> {
160 panic!("MockKubectlBackend::delete_pods_by_label: unexpected call")
161 }
162
163 fn get_version(
164 &self,
165 _timeout: Duration,
166 _interval: Duration,
167 ) -> Result<VersionResponse, KubectlError> {
168 panic!("MockKubectlBackend::get_version: unexpected call")
169 }
170
171 fn scale(
172 &self,
173 _resource_type: &str,
174 _name: &str,
175 _namespace: &str,
176 _replicas: i64,
177 ) -> Result<(), KubectlError> {
178 panic!("MockKubectlBackend::scale: unexpected call")
179 }
180
181 fn apply(&self, json_data: &[u8], namespace: &str) -> Result<(), KubectlError> {
182 let mut guard = self.apply_fn.lock().unwrap();
183 match guard.as_mut() {
184 Some(f) => f(json_data, namespace),
185 None => panic!("MockKubectlBackend::apply called without an expectation"),
186 }
187 }
188
189 fn delete_manifest(&self, data: &[u8], namespace: &str) -> Result<(), KubectlError> {
190 let mut guard = self.delete_manifest_fn.lock().unwrap();
191 match guard.as_mut() {
192 Some(f) => f(data, namespace),
193 None => panic!("MockKubectlBackend::delete_manifest called without an expectation"),
194 }
195 }
196
197 fn delete(
198 &self,
199 _resource_type: &str,
200 _name: &str,
201 _namespace: &str,
202 ) -> Result<(), KubectlError> {
203 panic!("MockKubectlBackend::delete: unexpected call")
204 }
205
206 fn wait(
207 &self,
208 _resource_type: &str,
209 _name: &str,
210 _namespace: &str,
211 _condition: &str,
212 _timeout: Duration,
213 ) -> Result<(), KubectlError> {
214 panic!("MockKubectlBackend::wait: unexpected call")
215 }
216
217 fn wait_for_rollout_status(
218 &self,
219 _resource_type: &str,
220 _name: &str,
221 _namespace: &str,
222 _timeout: Duration,
223 ) -> Result<(), KubectlError> {
224 panic!("MockKubectlBackend::wait_for_rollout_status: unexpected call")
225 }
226
227 fn wait_for_pod_ready_by_label(
228 &self,
229 _namespace: &str,
230 _label_selector: &str,
231 _timeout: Duration,
232 ) -> Result<(), KubectlError> {
233 panic!("MockKubectlBackend::wait_for_pod_ready_by_label: unexpected call")
234 }
235
236 fn wait_for_job_complete(
237 &self,
238 _name: &str,
239 _namespace: &str,
240 _timeout: Duration,
241 ) -> Result<(), KubectlError> {
242 panic!("MockKubectlBackend::wait_for_job_complete: unexpected call")
243 }
244
245 fn wait_for_job_created(
246 &self,
247 _name: &str,
248 _namespace: &str,
249 _timeout: Duration,
250 ) -> Result<(), KubectlError> {
251 panic!("MockKubectlBackend::wait_for_job_created: unexpected call")
252 }
253
254 fn list_resource_names(
255 &self,
256 _kind: &str,
257 _namespace: &str,
258 _label_selector: &str,
259 ) -> Result<Vec<String>, KubectlError> {
260 panic!("MockKubectlBackend::list_resource_names: unexpected call")
261 }
262
263 fn ensure_namespace(&self, _name: &str) -> Result<(), KubectlError> {
264 panic!("MockKubectlBackend::ensure_namespace: unexpected call")
265 }
266
267 fn delete_namespace(&self, _name: &str) -> Result<(), KubectlError> {
268 panic!("MockKubectlBackend::delete_namespace: unexpected call")
269 }
270
271 fn namespace_exists(&self, _name: &str) -> Result<bool, KubectlError> {
272 panic!("MockKubectlBackend::namespace_exists: unexpected call")
273 }
274
275 fn wait_for_namespace_ready(
276 &self,
277 _name: &str,
278 _timeout: Duration,
279 ) -> Result<(), KubectlError> {
280 panic!("MockKubectlBackend::wait_for_namespace_ready: unexpected call")
281 }
282
283 fn run_job(
284 &self,
285 _image: &str,
286 _namespace: &str,
287 _timeout: Duration,
288 _command: &[&str],
289 ) -> Result<String, KubectlError> {
290 panic!("MockKubectlBackend::run_job: unexpected call")
291 }
292
293 fn get_raw(&self, _path: &str) -> Result<String, KubectlError> {
294 panic!("MockKubectlBackend::get_raw: unexpected call")
295 }
296 }
297}