1use std::time::Duration;
7
8use thiserror::Error;
9use xianyun_backends::kubectl::{self, KubectlError};
10use xianyun_core::config::latest::{CaproniConfig, DeployerConfig};
11
12const DEPLOYER_TYPE: &str = "namespace";
13
14const MAX_NAMESPACE_NAME_LENGTH: usize = 63;
17
18const NAMESPACE_READY_TIMEOUT: Duration = Duration::from_secs(60);
23
24#[derive(Debug, Error)]
26pub enum NamespaceError {
27 #[error("deployer is not a namespace deployer")]
30 NotNamespaceDeployer,
31 #[error("namespaces list must not be empty")]
33 EmptyNamespaces,
34 #[error("invalid namespace name: {0:?}")]
36 InvalidNamespaceName(String),
37 #[error("namespace name exceeds 63 characters: {0:?}")]
41 NamespaceNameTooLong(String),
42 #[error("deploying namespace {0:?}: {1}")]
44 Deploy(String, #[source] KubectlError),
45 #[error("undeploying namespace {0:?}: {1}")]
47 Undeploy(String, #[source] KubectlError),
48 #[error("checking namespace {0:?}: {1}")]
50 Status(String, #[source] KubectlError),
51}
52
53fn is_valid_dns1123_label(name: &str) -> bool {
57 let bytes = name.as_bytes();
58
59 if bytes.is_empty() {
60 return false;
61 }
62
63 let is_label_char = |c: u8| c.is_ascii_lowercase() || c.is_ascii_digit();
64
65 if !is_label_char(bytes[0]) || !is_label_char(*bytes.last().unwrap()) {
66 return false;
67 }
68
69 bytes.iter().all(|&c| is_label_char(c) || c == b'-')
70}
71
72pub struct Manager {
74 backend: Box<dyn kubectl::Backend + Send + Sync>,
75}
76
77impl Manager {
78 #[must_use]
80 pub fn new(backend: Box<dyn kubectl::Backend + Send + Sync>) -> Self {
81 Self { backend }
82 }
83
84 #[must_use]
86 pub fn deployer_type(&self) -> &'static str {
87 DEPLOYER_TYPE
88 }
89
90 pub fn validate(
92 &self,
93 deployer_config: &DeployerConfig,
94 _caproni_config: &CaproniConfig,
95 ) -> Result<(), NamespaceError> {
96 if deployer_config.deployer_type != DEPLOYER_TYPE {
97 return Err(NamespaceError::NotNamespaceDeployer);
98 }
99
100 let namespaces = configured_namespaces(deployer_config)?;
101
102 for name in namespaces {
103 if name.len() > MAX_NAMESPACE_NAME_LENGTH {
104 return Err(NamespaceError::NamespaceNameTooLong(name.clone()));
105 }
106
107 if !is_valid_dns1123_label(name) {
108 return Err(NamespaceError::InvalidNamespaceName(name.clone()));
109 }
110 }
111
112 Ok(())
113 }
114
115 pub fn deploy(&self, deployer_config: &DeployerConfig) -> Result<(), NamespaceError> {
125 if deployer_config.deployer_type != DEPLOYER_TYPE {
126 return Err(NamespaceError::NotNamespaceDeployer);
127 }
128
129 let namespaces = configured_namespaces(deployer_config)?;
130
131 for name in namespaces {
132 self.backend
133 .ensure_namespace(name)
134 .map_err(|e| NamespaceError::Deploy(name.clone(), e))?;
135
136 self.backend
137 .wait_for_namespace_ready(name, NAMESPACE_READY_TIMEOUT)
138 .map_err(|e| NamespaceError::Deploy(name.clone(), e))?;
139 }
140
141 Ok(())
142 }
143
144 pub fn undeploy(
149 &self,
150 deployer_config: &DeployerConfig,
151 _caproni_config: &CaproniConfig,
152 ) -> Result<(), NamespaceError> {
153 if deployer_config.deployer_type != DEPLOYER_TYPE {
154 return Err(NamespaceError::NotNamespaceDeployer);
155 }
156
157 let Some(namespace) = &deployer_config.namespace else {
158 return Ok(());
159 };
160
161 if !namespace.delete_on_undeploy {
162 return Ok(());
163 }
164
165 for name in &namespace.namespaces {
166 self.backend
167 .delete_namespace(name)
168 .map_err(|e| NamespaceError::Undeploy(name.clone(), e))?;
169 }
170
171 Ok(())
172 }
173
174 pub fn status(
183 &self,
184 deployer_config: &DeployerConfig,
185 _caproni_config: &CaproniConfig,
186 ) -> Result<super::super::Status, NamespaceError> {
187 if deployer_config.deployer_type != DEPLOYER_TYPE {
188 return Err(NamespaceError::NotNamespaceDeployer);
189 }
190
191 let not_deployed = || super::super::Status {
192 state: super::super::State::NotDeployed,
193 deployer_type: DEPLOYER_TYPE.to_string(),
194 };
195
196 let Some(namespace) = &deployer_config.namespace else {
197 return Ok(not_deployed());
198 };
199
200 if namespace.namespaces.is_empty() {
201 return Ok(not_deployed());
202 }
203
204 for name in &namespace.namespaces {
205 let exists = self
206 .backend
207 .namespace_exists(name)
208 .map_err(|e| NamespaceError::Status(name.clone(), e))?;
209
210 if !exists {
211 return Ok(not_deployed());
212 }
213 }
214
215 Ok(super::super::Status {
216 state: super::super::State::Deployed,
217 deployer_type: DEPLOYER_TYPE.to_string(),
218 })
219 }
220}
221
222fn configured_namespaces(deployer_config: &DeployerConfig) -> Result<&Vec<String>, NamespaceError> {
223 match &deployer_config.namespace {
224 Some(ns) if !ns.namespaces.is_empty() => Ok(&ns.namespaces),
225 _ => Err(NamespaceError::EmptyNamespaces),
226 }
227}
228
229impl crate::deployer::Manager for Manager {
231 fn deployer_type(&self) -> String {
232 DEPLOYER_TYPE.to_string()
233 }
234
235 fn deploy(
236 &self,
237 deployer_config: &DeployerConfig,
238 _cfg: Option<&xianyun_core::config::container::Container>,
239 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
240 Manager::deploy(self, deployer_config).map_err(Into::into)
241 }
242
243 fn undeploy(
244 &self,
245 deployer_config: &DeployerConfig,
246 caproni_config: &CaproniConfig,
247 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
248 Manager::undeploy(self, deployer_config, caproni_config).map_err(Into::into)
249 }
250
251 fn status(
252 &self,
253 deployer_config: &DeployerConfig,
254 caproni_config: &CaproniConfig,
255 ) -> Result<crate::deployer::Status, Box<dyn std::error::Error + Send + Sync>> {
256 Manager::status(self, deployer_config, caproni_config).map_err(Into::into)
257 }
258
259 fn validate(
260 &self,
261 deployer_config: &DeployerConfig,
262 caproni_config: &CaproniConfig,
263 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
264 Manager::validate(self, deployer_config, caproni_config).map_err(Into::into)
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use std::sync::Mutex;
271
272 use super::*;
273 use xianyun_core::config::latest::NamespaceConfig;
274
275 type WaitExpectation = (String, Duration, Result<(), KubectlError>);
283
284 #[derive(Default)]
285 struct MockKubectlBackend {
286 ensure_namespace: Mutex<Vec<(String, Result<(), KubectlError>)>>,
287 delete_namespace: Mutex<Vec<(String, Result<(), KubectlError>)>>,
288 namespace_exists: Mutex<Vec<(String, Result<bool, KubectlError>)>>,
289 wait_for_namespace_ready: Mutex<Vec<WaitExpectation>>,
290 }
291
292 impl MockKubectlBackend {
293 fn new() -> Self {
294 Self::default()
295 }
296
297 fn expect_ensure_namespace(&self, name: &str, result: Result<(), KubectlError>) {
298 self.ensure_namespace
299 .lock()
300 .unwrap()
301 .push((name.to_string(), result));
302 }
303
304 fn expect_delete_namespace(&self, name: &str, result: Result<(), KubectlError>) {
305 self.delete_namespace
306 .lock()
307 .unwrap()
308 .push((name.to_string(), result));
309 }
310
311 fn expect_namespace_exists(&self, name: &str, result: Result<bool, KubectlError>) {
312 self.namespace_exists
313 .lock()
314 .unwrap()
315 .push((name.to_string(), result));
316 }
317
318 fn expect_wait_for_namespace_ready(
319 &self,
320 name: &str,
321 timeout: Duration,
322 result: Result<(), KubectlError>,
323 ) {
324 self.wait_for_namespace_ready
325 .lock()
326 .unwrap()
327 .push((name.to_string(), timeout, result));
328 }
329 }
330
331 fn err_test_backend() -> KubectlError {
332 KubectlError::Other("kubectl backend failed".to_string())
333 }
334
335 impl kubectl::Backend for MockKubectlBackend {
336 fn get(
337 &self,
338 _resource_type: &str,
339 _name: &str,
340 _namespace: &str,
341 ) -> Result<String, KubectlError> {
342 unimplemented!("not exercised by namespace manager tests")
343 }
344
345 fn get_raw(&self, _path: &str) -> Result<String, KubectlError> {
346 unimplemented!("not exercised by namespace manager tests")
347 }
348
349 fn delete_pods_by_label(
350 &self,
351 _namespace: &str,
352 _label_selector: &str,
353 ) -> Result<usize, KubectlError> {
354 unimplemented!("not exercised by namespace manager tests")
355 }
356
357 fn get_version(
358 &self,
359 _timeout: Duration,
360 _interval: Duration,
361 ) -> Result<kubectl::VersionResponse, KubectlError> {
362 unimplemented!("not exercised by namespace manager tests")
363 }
364
365 fn scale(
366 &self,
367 _resource_type: &str,
368 _name: &str,
369 _namespace: &str,
370 _replicas: i64,
371 ) -> Result<(), KubectlError> {
372 unimplemented!("not exercised by namespace manager tests")
373 }
374
375 fn apply(&self, _json_data: &[u8], _namespace: &str) -> Result<(), KubectlError> {
376 unimplemented!("not exercised by namespace manager tests")
377 }
378
379 fn delete_manifest(&self, _data: &[u8], _namespace: &str) -> Result<(), KubectlError> {
380 unimplemented!("not exercised by namespace manager tests")
381 }
382
383 fn delete(
384 &self,
385 _resource_type: &str,
386 _name: &str,
387 _namespace: &str,
388 ) -> Result<(), KubectlError> {
389 unimplemented!("not exercised by namespace manager tests")
390 }
391
392 fn wait(
393 &self,
394 _resource_type: &str,
395 _name: &str,
396 _namespace: &str,
397 _condition: &str,
398 _timeout: Duration,
399 ) -> Result<(), KubectlError> {
400 unimplemented!("not exercised by namespace manager tests")
401 }
402
403 fn wait_for_rollout_status(
404 &self,
405 _resource_type: &str,
406 _name: &str,
407 _namespace: &str,
408 _timeout: Duration,
409 ) -> Result<(), KubectlError> {
410 unimplemented!("not exercised by namespace manager tests")
411 }
412
413 fn wait_for_pod_ready_by_label(
414 &self,
415 _namespace: &str,
416 _label_selector: &str,
417 _timeout: Duration,
418 ) -> Result<(), KubectlError> {
419 unimplemented!("not exercised by namespace manager tests")
420 }
421
422 fn wait_for_job_complete(
423 &self,
424 _name: &str,
425 _namespace: &str,
426 _timeout: Duration,
427 ) -> Result<(), KubectlError> {
428 unimplemented!("not exercised by namespace manager tests")
429 }
430
431 fn wait_for_job_created(
432 &self,
433 _name: &str,
434 _namespace: &str,
435 _timeout: Duration,
436 ) -> Result<(), KubectlError> {
437 unimplemented!("not exercised by namespace manager tests")
438 }
439
440 fn list_resource_names(
441 &self,
442 _kind: &str,
443 _namespace: &str,
444 _label_selector: &str,
445 ) -> Result<Vec<String>, KubectlError> {
446 unimplemented!("not exercised by namespace manager tests")
447 }
448
449 fn ensure_namespace(&self, name: &str) -> Result<(), KubectlError> {
450 let mut queue = self.ensure_namespace.lock().unwrap();
451 let pos = queue
452 .iter()
453 .position(|(n, _)| n == name)
454 .unwrap_or_else(|| panic!("unexpected ensure_namespace({name:?}) call"));
455 queue.remove(pos).1
456 }
457
458 fn delete_namespace(&self, name: &str) -> Result<(), KubectlError> {
459 let mut queue = self.delete_namespace.lock().unwrap();
460 let pos = queue
461 .iter()
462 .position(|(n, _)| n == name)
463 .unwrap_or_else(|| panic!("unexpected delete_namespace({name:?}) call"));
464 queue.remove(pos).1
465 }
466
467 fn namespace_exists(&self, name: &str) -> Result<bool, KubectlError> {
468 let mut queue = self.namespace_exists.lock().unwrap();
469 let pos = queue
470 .iter()
471 .position(|(n, _)| n == name)
472 .unwrap_or_else(|| panic!("unexpected namespace_exists({name:?}) call"));
473 queue.remove(pos).1
474 }
475
476 fn wait_for_namespace_ready(
477 &self,
478 name: &str,
479 timeout: Duration,
480 ) -> Result<(), KubectlError> {
481 let mut queue = self.wait_for_namespace_ready.lock().unwrap();
482 let pos = queue
483 .iter()
484 .position(|(n, t, _)| n == name && *t == timeout)
485 .unwrap_or_else(|| {
486 panic!("unexpected wait_for_namespace_ready({name:?}, {timeout:?}) call")
487 });
488 queue.remove(pos).2
489 }
490
491 fn run_job(
492 &self,
493 _image: &str,
494 _namespace: &str,
495 _timeout: Duration,
496 _command: &[&str],
497 ) -> Result<String, KubectlError> {
498 unimplemented!("not exercised by namespace manager tests")
499 }
500 }
501
502 fn deployer_config(namespaces: &[&str]) -> DeployerConfig {
503 DeployerConfig {
504 deployer_type: DEPLOYER_TYPE.to_string(),
505 namespace: Some(NamespaceConfig {
506 namespaces: namespaces.iter().map(|s| s.to_string()).collect(),
507 delete_on_undeploy: false,
508 }),
509 ..Default::default()
510 }
511 }
512
513 #[test]
515 fn test_type() {
516 let mgr = Manager::new(Box::new(MockKubectlBackend::new()));
517 assert_eq!(mgr.deployer_type(), "namespace");
518 }
519
520 #[test]
522 fn test_validate() {
523 let long_valid = format!("a{}c", "b".repeat(61)); let too_long = format!("a{}c", "b".repeat(62)); let cases: Vec<(&str, DeployerConfig, Option<&str>)> = vec![
527 ("valid single namespace", deployer_config(&["gitlab"]), None),
528 (
529 "valid multiple namespaces",
530 deployer_config(&["gitlab", "monitoring"]),
531 None,
532 ),
533 (
534 "wrong type",
535 DeployerConfig {
536 deployer_type: "helm".to_string(),
537 namespace: Some(NamespaceConfig {
538 namespaces: vec!["gitlab".to_string()],
539 ..Default::default()
540 }),
541 ..Default::default()
542 },
543 Some("not_namespace_deployer"),
544 ),
545 (
546 "missing namespace block",
547 DeployerConfig {
548 deployer_type: DEPLOYER_TYPE.to_string(),
549 ..Default::default()
550 },
551 Some("empty_namespaces"),
552 ),
553 (
554 "empty namespaces",
555 deployer_config(&[]),
556 Some("empty_namespaces"),
557 ),
558 (
559 "invalid dns name",
560 deployer_config(&["Not_Valid"]),
561 Some("invalid_namespace_name"),
562 ),
563 (
564 "name at 63 char limit accepted",
565 deployer_config(&[&long_valid]),
566 None,
567 ),
568 (
569 "name longer than 63 chars rejected",
570 deployer_config(&[&too_long]),
571 Some("namespace_name_too_long"),
572 ),
573 ];
574
575 for (name, cfg, want_err) in cases {
576 let mgr = Manager::new(Box::new(MockKubectlBackend::new()));
577 let result = mgr.validate(&cfg, &CaproniConfig::default());
578
579 match want_err {
580 None => assert!(result.is_ok(), "case {name}: expected Ok, got {result:?}"),
581 Some("not_namespace_deployer") => {
582 assert!(
583 matches!(result, Err(NamespaceError::NotNamespaceDeployer)),
584 "case {name}"
585 );
586 }
587 Some("empty_namespaces") => {
588 assert!(
589 matches!(result, Err(NamespaceError::EmptyNamespaces)),
590 "case {name}"
591 );
592 }
593 Some("invalid_namespace_name") => {
594 assert!(
595 matches!(result, Err(NamespaceError::InvalidNamespaceName(_))),
596 "case {name}"
597 );
598 }
599 Some("namespace_name_too_long") => {
600 assert!(
601 matches!(result, Err(NamespaceError::NamespaceNameTooLong(_))),
602 "case {name}"
603 );
604 }
605 Some(other) => panic!("unknown expectation {other}"),
606 }
607 }
608 }
609
610 #[test]
612 fn test_deploy() {
613 {
615 let backend = MockKubectlBackend::new();
616 backend.expect_ensure_namespace("gitlab", Ok(()));
617 backend.expect_wait_for_namespace_ready("gitlab", NAMESPACE_READY_TIMEOUT, Ok(()));
618
619 let mgr = Manager::new(Box::new(backend));
620 mgr.deploy(&deployer_config(&["gitlab"])).unwrap();
621 }
622
623 {
625 let backend = MockKubectlBackend::new();
626 backend.expect_ensure_namespace("gitlab", Ok(()));
627 backend.expect_wait_for_namespace_ready("gitlab", NAMESPACE_READY_TIMEOUT, Ok(()));
628 backend.expect_ensure_namespace("monitoring", Ok(()));
629 backend.expect_wait_for_namespace_ready("monitoring", NAMESPACE_READY_TIMEOUT, Ok(()));
630
631 let mgr = Manager::new(Box::new(backend));
632 mgr.deploy(&deployer_config(&["gitlab", "monitoring"]))
633 .unwrap();
634 }
635
636 {
638 let backend = MockKubectlBackend::new();
639 backend.expect_ensure_namespace("gitlab", Err(err_test_backend()));
640
641 let mgr = Manager::new(Box::new(backend));
642 let err = mgr.deploy(&deployer_config(&["gitlab"])).unwrap_err();
643 assert!(matches!(err, NamespaceError::Deploy(ref n, _) if n == "gitlab"));
644 assert!(err.to_string().contains("gitlab"));
645 assert!(err.to_string().contains("kubectl backend failed"));
646 }
647
648 {
650 let backend = MockKubectlBackend::new();
651 backend.expect_ensure_namespace("gitlab", Ok(()));
652 backend.expect_wait_for_namespace_ready(
653 "gitlab",
654 NAMESPACE_READY_TIMEOUT,
655 Err(err_test_backend()),
656 );
657
658 let mgr = Manager::new(Box::new(backend));
659 let err = mgr
660 .deploy(&deployer_config(&["gitlab", "monitoring"]))
661 .unwrap_err();
662 assert!(matches!(err, NamespaceError::Deploy(ref n, _) if n == "gitlab"));
663 assert!(err.to_string().contains("gitlab"));
664 }
665
666 {
668 let mgr = Manager::new(Box::new(MockKubectlBackend::new()));
669 let cfg = DeployerConfig {
670 deployer_type: "helm".to_string(),
671 namespace: Some(NamespaceConfig {
672 namespaces: vec!["gitlab".to_string()],
673 ..Default::default()
674 }),
675 ..Default::default()
676 };
677 let err = mgr.deploy(&cfg).unwrap_err();
678 assert!(matches!(err, NamespaceError::NotNamespaceDeployer));
679 }
680
681 {
683 let mgr = Manager::new(Box::new(MockKubectlBackend::new()));
684 let cfg = DeployerConfig {
685 deployer_type: DEPLOYER_TYPE.to_string(),
686 ..Default::default()
687 };
688 let err = mgr.deploy(&cfg).unwrap_err();
689 assert!(matches!(err, NamespaceError::EmptyNamespaces));
690 }
691 }
692
693 #[test]
695 fn test_undeploy() {
696 {
698 let mgr = Manager::new(Box::new(MockKubectlBackend::new()));
699 mgr.undeploy(
700 &deployer_config(&["gitlab", "monitoring"]),
701 &CaproniConfig::default(),
702 )
703 .unwrap();
704 }
705
706 {
708 let backend = MockKubectlBackend::new();
709 backend.expect_delete_namespace("gitlab", Ok(()));
710 backend.expect_delete_namespace("monitoring", Ok(()));
711
712 let mgr = Manager::new(Box::new(backend));
713 let cfg = DeployerConfig {
714 deployer_type: DEPLOYER_TYPE.to_string(),
715 namespace: Some(NamespaceConfig {
716 namespaces: vec!["gitlab".to_string(), "monitoring".to_string()],
717 delete_on_undeploy: true,
718 }),
719 ..Default::default()
720 };
721 mgr.undeploy(&cfg, &CaproniConfig::default()).unwrap();
722 }
723
724 {
726 let backend = MockKubectlBackend::new();
727 backend.expect_delete_namespace("gitlab", Err(err_test_backend()));
728
729 let mgr = Manager::new(Box::new(backend));
730 let cfg = DeployerConfig {
731 deployer_type: DEPLOYER_TYPE.to_string(),
732 namespace: Some(NamespaceConfig {
733 namespaces: vec!["gitlab".to_string(), "monitoring".to_string()],
734 delete_on_undeploy: true,
735 }),
736 ..Default::default()
737 };
738 let err = mgr.undeploy(&cfg, &CaproniConfig::default()).unwrap_err();
739 assert!(matches!(err, NamespaceError::Undeploy(ref n, _) if n == "gitlab"));
740 assert!(err.to_string().contains("gitlab"));
741 }
742
743 {
745 let mgr = Manager::new(Box::new(MockKubectlBackend::new()));
746 let cfg = DeployerConfig {
747 deployer_type: "helm".to_string(),
748 namespace: Some(NamespaceConfig {
749 namespaces: vec!["gitlab".to_string()],
750 delete_on_undeploy: true,
751 }),
752 ..Default::default()
753 };
754 let err = mgr.undeploy(&cfg, &CaproniConfig::default()).unwrap_err();
755 assert!(matches!(err, NamespaceError::NotNamespaceDeployer));
756 }
757 }
758
759 #[test]
761 fn test_status() {
762 {
764 let backend = MockKubectlBackend::new();
765 backend.expect_namespace_exists("gitlab", Ok(true));
766 backend.expect_namespace_exists("monitoring", Ok(true));
767
768 let mgr = Manager::new(Box::new(backend));
769 let status = mgr
770 .status(
771 &deployer_config(&["gitlab", "monitoring"]),
772 &CaproniConfig::default(),
773 )
774 .unwrap();
775 assert_eq!(status.state, super::super::super::State::Deployed);
776 assert_eq!(status.deployer_type, "namespace");
777 }
778
779 {
781 let backend = MockKubectlBackend::new();
782 backend.expect_namespace_exists("gitlab", Ok(true));
783 backend.expect_namespace_exists("monitoring", Ok(false));
784
785 let mgr = Manager::new(Box::new(backend));
786 let status = mgr
787 .status(
788 &deployer_config(&["gitlab", "monitoring"]),
789 &CaproniConfig::default(),
790 )
791 .unwrap();
792 assert_eq!(status.state, super::super::super::State::NotDeployed);
793 }
794
795 {
797 let backend = MockKubectlBackend::new();
798 backend.expect_namespace_exists("gitlab", Ok(false));
799
800 let mgr = Manager::new(Box::new(backend));
801 let status = mgr
802 .status(&deployer_config(&["gitlab"]), &CaproniConfig::default())
803 .unwrap();
804 assert_eq!(status.state, super::super::super::State::NotDeployed);
805 }
806
807 {
809 let backend = MockKubectlBackend::new();
810 backend.expect_namespace_exists("gitlab", Err(err_test_backend()));
811
812 let mgr = Manager::new(Box::new(backend));
813 let err = mgr
814 .status(&deployer_config(&["gitlab"]), &CaproniConfig::default())
815 .unwrap_err();
816 assert!(matches!(err, NamespaceError::Status(ref n, _) if n == "gitlab"));
817 }
818
819 {
821 let mgr = Manager::new(Box::new(MockKubectlBackend::new()));
822 let err = mgr
823 .status(
824 &DeployerConfig {
825 deployer_type: "helm".to_string(),
826 namespace: Some(NamespaceConfig {
827 namespaces: vec!["gitlab".to_string()],
828 ..Default::default()
829 }),
830 ..Default::default()
831 },
832 &CaproniConfig::default(),
833 )
834 .unwrap_err();
835 assert!(matches!(err, NamespaceError::NotNamespaceDeployer));
836 }
837
838 {
840 let mgr = Manager::new(Box::new(MockKubectlBackend::new()));
841 let status = mgr
842 .status(
843 &DeployerConfig {
844 deployer_type: DEPLOYER_TYPE.to_string(),
845 ..Default::default()
846 },
847 &CaproniConfig::default(),
848 )
849 .unwrap();
850 assert_eq!(status.state, super::super::super::State::NotDeployed);
851 assert_eq!(status.deployer_type, "namespace");
852 }
853 }
854}