1pub mod copy;
7pub mod manager;
8
9#[cfg(test)]
22pub(crate) mod test_support {
23 use std::sync::Mutex;
24 use std::time::Duration;
25
26 use xianyun_backends::kubectl::{Backend as KubectlBackend, KubectlError, VersionResponse};
27
28 type Responder<T> = Box<dyn FnMut(&[&str]) -> T + Send>;
29
30 struct Queue<T> {
34 method: &'static str,
35 items: Mutex<Vec<Responder<T>>>,
36 }
37
38 impl<T> Queue<T> {
39 fn new(method: &'static str) -> Self {
40 Self {
41 method,
42 items: Mutex::new(Vec::new()),
43 }
44 }
45
46 fn push(&self, f: Responder<T>) {
47 self.items.lock().unwrap().push(f);
48 }
49
50 fn call(&self, args: &[&str]) -> T {
51 let mut items = self.items.lock().unwrap();
52 if items.is_empty() {
53 panic!("MockKubectlBackend::{}: unexpected call", self.method);
54 }
55 let mut f = items.remove(0);
56 drop(items);
57 f(args)
58 }
59 }
60
61 #[derive(Default)]
67 pub(crate) struct MockKubectlBackend {
68 get: Queue<Result<String, KubectlError>>,
69 scale: Queue<Result<(), KubectlError>>,
70 apply: Queue<Result<(), KubectlError>>,
71 delete: Queue<Result<(), KubectlError>>,
72 wait: Queue<Result<(), KubectlError>>,
73 wait_for_rollout_status: Queue<Result<(), KubectlError>>,
74 delete_pods_by_label: Queue<Result<usize, KubectlError>>,
75 }
76
77 impl<T> Default for Queue<T> {
78 fn default() -> Self {
79 Self::new("<unset>")
80 }
81 }
82
83 impl MockKubectlBackend {
84 pub(crate) fn new() -> Self {
85 Self {
86 get: Queue::new("get"),
87 scale: Queue::new("scale"),
88 apply: Queue::new("apply"),
89 delete: Queue::new("delete"),
90 wait: Queue::new("wait"),
91 wait_for_rollout_status: Queue::new("wait_for_rollout_status"),
92 delete_pods_by_label: Queue::new("delete_pods_by_label"),
93 }
94 }
95
96 pub(crate) fn expect_get<F>(self, mut f: F) -> Self
97 where
98 F: FnMut(&str, &str, &str) -> Result<String, KubectlError> + Send + 'static,
99 {
100 self.get
101 .push(Box::new(move |args| f(args[0], args[1], args[2])));
102 self
103 }
104
105 pub(crate) fn expect_scale<F>(self, mut f: F) -> Self
106 where
107 F: FnMut(&str, &str, &str, i64) -> Result<(), KubectlError> + Send + 'static,
108 {
109 self.scale.push(Box::new(move |args| {
110 f(args[0], args[1], args[2], args[3].parse().unwrap())
111 }));
112 self
113 }
114
115 pub(crate) fn expect_apply<F>(self, mut f: F) -> Self
116 where
117 F: FnMut(&[u8], &str) -> Result<(), KubectlError> + Send + 'static,
118 {
119 self.apply
120 .push(Box::new(move |args| f(args[0].as_bytes(), args[1])));
121 self
122 }
123
124 pub(crate) fn expect_delete<F>(self, mut f: F) -> Self
125 where
126 F: FnMut(&str, &str, &str) -> Result<(), KubectlError> + Send + 'static,
127 {
128 self.delete
129 .push(Box::new(move |args| f(args[0], args[1], args[2])));
130 self
131 }
132
133 pub(crate) fn expect_wait<F>(self, mut f: F) -> Self
134 where
135 F: FnMut(&str, &str, &str, &str) -> Result<(), KubectlError> + Send + 'static,
136 {
137 self.wait
138 .push(Box::new(move |args| f(args[0], args[1], args[2], args[3])));
139 self
140 }
141
142 pub(crate) fn expect_wait_for_rollout_status<F>(self, mut f: F) -> Self
143 where
144 F: FnMut(&str, &str, &str) -> Result<(), KubectlError> + Send + 'static,
145 {
146 self.wait_for_rollout_status
147 .push(Box::new(move |args| f(args[0], args[1], args[2])));
148 self
149 }
150
151 pub(crate) fn expect_delete_pods_by_label<F>(self, mut f: F) -> Self
152 where
153 F: FnMut(&str, &str) -> Result<usize, KubectlError> + Send + 'static,
154 {
155 self.delete_pods_by_label
156 .push(Box::new(move |args| f(args[0], args[1])));
157 self
158 }
159 }
160
161 impl KubectlBackend for MockKubectlBackend {
162 fn get(
163 &self,
164 resource_type: &str,
165 name: &str,
166 namespace: &str,
167 ) -> Result<String, KubectlError> {
168 self.get.call(&[resource_type, name, namespace])
169 }
170
171 fn delete_pods_by_label(
172 &self,
173 namespace: &str,
174 label_selector: &str,
175 ) -> Result<usize, KubectlError> {
176 self.delete_pods_by_label.call(&[namespace, label_selector])
177 }
178
179 fn get_version(
180 &self,
181 _timeout: Duration,
182 _interval: Duration,
183 ) -> Result<VersionResponse, KubectlError> {
184 panic!("MockKubectlBackend::get_version: unexpected call")
185 }
186
187 fn scale(
188 &self,
189 resource_type: &str,
190 name: &str,
191 namespace: &str,
192 replicas: i64,
193 ) -> Result<(), KubectlError> {
194 self.scale
195 .call(&[resource_type, name, namespace, &replicas.to_string()])
196 }
197
198 fn apply(&self, json_data: &[u8], namespace: &str) -> Result<(), KubectlError> {
199 self.apply.call(&[
200 std::str::from_utf8(json_data).unwrap_or_default(),
201 namespace,
202 ])
203 }
204
205 fn delete_manifest(&self, _data: &[u8], _namespace: &str) -> Result<(), KubectlError> {
206 panic!("MockKubectlBackend::delete_manifest: unexpected call")
207 }
208
209 fn delete(
210 &self,
211 resource_type: &str,
212 name: &str,
213 namespace: &str,
214 ) -> Result<(), KubectlError> {
215 self.delete.call(&[resource_type, name, namespace])
216 }
217
218 fn wait(
219 &self,
220 resource_type: &str,
221 name: &str,
222 namespace: &str,
223 condition: &str,
224 _timeout: Duration,
225 ) -> Result<(), KubectlError> {
226 self.wait.call(&[resource_type, name, namespace, condition])
227 }
228
229 fn wait_for_rollout_status(
230 &self,
231 resource_type: &str,
232 name: &str,
233 namespace: &str,
234 _timeout: Duration,
235 ) -> Result<(), KubectlError> {
236 self.wait_for_rollout_status
237 .call(&[resource_type, name, namespace])
238 }
239
240 fn wait_for_pod_ready_by_label(
241 &self,
242 _namespace: &str,
243 _label_selector: &str,
244 _timeout: Duration,
245 ) -> Result<(), KubectlError> {
246 panic!("MockKubectlBackend::wait_for_pod_ready_by_label: unexpected call")
247 }
248
249 fn wait_for_job_complete(
250 &self,
251 _name: &str,
252 _namespace: &str,
253 _timeout: Duration,
254 ) -> Result<(), KubectlError> {
255 panic!("MockKubectlBackend::wait_for_job_complete: unexpected call")
256 }
257
258 fn wait_for_job_created(
259 &self,
260 _name: &str,
261 _namespace: &str,
262 _timeout: Duration,
263 ) -> Result<(), KubectlError> {
264 panic!("MockKubectlBackend::wait_for_job_created: unexpected call")
265 }
266
267 fn list_resource_names(
268 &self,
269 _kind: &str,
270 _namespace: &str,
271 _label_selector: &str,
272 ) -> Result<Vec<String>, KubectlError> {
273 panic!("MockKubectlBackend::list_resource_names: unexpected call")
274 }
275
276 fn ensure_namespace(&self, _name: &str) -> Result<(), KubectlError> {
277 panic!("MockKubectlBackend::ensure_namespace: unexpected call")
278 }
279
280 fn delete_namespace(&self, _name: &str) -> Result<(), KubectlError> {
281 panic!("MockKubectlBackend::delete_namespace: unexpected call")
282 }
283
284 fn namespace_exists(&self, _name: &str) -> Result<bool, KubectlError> {
285 panic!("MockKubectlBackend::namespace_exists: unexpected call")
286 }
287
288 fn wait_for_namespace_ready(
289 &self,
290 _name: &str,
291 _timeout: Duration,
292 ) -> Result<(), KubectlError> {
293 panic!("MockKubectlBackend::wait_for_namespace_ready: unexpected call")
294 }
295
296 fn run_job(
297 &self,
298 _image: &str,
299 _namespace: &str,
300 _timeout: Duration,
301 _command: &[&str],
302 ) -> Result<String, KubectlError> {
303 panic!("MockKubectlBackend::run_job: unexpected call")
304 }
305
306 fn get_raw(&self, _path: &str) -> Result<String, KubectlError> {
307 panic!("MockKubectlBackend::get_raw: unexpected call")
308 }
309 }
310}