xianyun_deploy/deployer/
registry.rs1use std::collections::HashMap;
6use std::sync::{Mutex, OnceLock};
7
8use super::{DeployerError, Manager};
9
10pub type ManagerFactory = Box<dyn Fn(&str) -> Box<dyn Manager + Send + Sync> + Send + Sync>;
12
13fn registry() -> &'static Mutex<HashMap<String, ManagerFactory>> {
14 static REGISTRY: OnceLock<Mutex<HashMap<String, ManagerFactory>>> = OnceLock::new();
15 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
16}
17
18pub fn register(deployer_type: &str, factory: ManagerFactory) {
25 let mut reg = registry()
26 .lock()
27 .unwrap_or_else(std::sync::PoisonError::into_inner);
28
29 if reg.contains_key(deployer_type) {
30 panic!("deployer type {deployer_type:?} already registered");
31 }
32
33 reg.insert(deployer_type.to_string(), factory);
34}
35
36pub fn new_manager(
39 deployer_type: &str,
40 kubeconfig: &str,
41) -> Result<Box<dyn Manager + Send + Sync>, DeployerError> {
42 let reg = registry()
43 .lock()
44 .unwrap_or_else(std::sync::PoisonError::into_inner);
45
46 let factory = reg.get(deployer_type).ok_or(DeployerError::UnknownType)?;
47
48 Ok(factory(kubeconfig))
49}
50
51#[must_use]
53pub fn list_types() -> Vec<String> {
54 let reg = registry()
55 .lock()
56 .unwrap_or_else(std::sync::PoisonError::into_inner);
57 reg.keys().cloned().collect()
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use crate::deployer::{State, Status};
64 use xianyun_core::config::latest::{CaproniConfig, DeployerConfig};
65
66 struct MockDeployer {
67 deployer_type: String,
68 }
69
70 impl Manager for MockDeployer {
71 fn deployer_type(&self) -> String {
72 self.deployer_type.clone()
73 }
74
75 fn deploy(
76 &self,
77 _deployer_config: &DeployerConfig,
78 _cfg: Option<&xianyun_core::config::container::Container>,
79 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
80 Ok(())
81 }
82
83 fn undeploy(
84 &self,
85 _deployer_config: &DeployerConfig,
86 _caproni_config: &CaproniConfig,
87 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
88 Ok(())
89 }
90
91 fn status(
92 &self,
93 _deployer_config: &DeployerConfig,
94 _caproni_config: &CaproniConfig,
95 ) -> Result<Status, Box<dyn std::error::Error + Send + Sync>> {
96 Ok(Status {
97 state: State::NotDeployed,
98 deployer_type: self.deployer_type.clone(),
99 })
100 }
101
102 fn validate(
103 &self,
104 _deployer_config: &DeployerConfig,
105 _caproni_config: &CaproniConfig,
106 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
107 Ok(())
108 }
109 }
110
111 #[test]
113 fn test_register() {
114 let deployer_type = "test-deployer";
115
116 register(
117 deployer_type,
118 Box::new(|_kubeconfig| {
119 Box::new(MockDeployer {
120 deployer_type: "test-deployer".to_string(),
121 })
122 }),
123 );
124 }
125
126 #[test]
128 fn test_new_manager() {
129 let test_type = "test-new-manager";
130 register(
131 test_type,
132 Box::new(|_kubeconfig| {
133 Box::new(MockDeployer {
134 deployer_type: "test-new-manager".to_string(),
135 })
136 }),
137 );
138
139 match new_manager(test_type, "") {
140 Ok(mgr) => assert_eq!(mgr.deployer_type(), test_type),
141 Err(e) => panic!("expected Ok, got {e}"),
142 }
143
144 match new_manager("unknown-type", "") {
145 Ok(_) => panic!("expected UnknownType error"),
146 Err(err) => assert!(matches!(err, DeployerError::UnknownType)),
147 }
148 }
149
150 #[test]
152 fn test_list_types() {
153 let type1 = "test-list-1";
154 let type2 = "test-list-2";
155
156 register(
157 type1,
158 Box::new(|_kubeconfig| {
159 Box::new(MockDeployer {
160 deployer_type: "test-list-1".to_string(),
161 })
162 }),
163 );
164 register(
165 type2,
166 Box::new(|_kubeconfig| {
167 Box::new(MockDeployer {
168 deployer_type: "test-list-2".to_string(),
169 })
170 }),
171 );
172
173 let types = list_types();
174 assert!(types.contains(&type1.to_string()));
175 assert!(types.contains(&type2.to_string()));
176 }
177}