xianyun_deploy/reloader/
registry.rs1use std::collections::HashMap;
6use std::sync::{Arc, Mutex, OnceLock};
7
8use xianyun_backends::kubectl;
9
10use super::{Manager, ReloaderError};
11
12pub type ManagerFactory = Box<
17 dyn Fn(Option<Arc<dyn kubectl::Backend + Send + Sync>>) -> Box<dyn Manager + Send + Sync>
18 + Send
19 + Sync,
20>;
21
22fn registry() -> &'static Mutex<HashMap<String, ManagerFactory>> {
23 static REGISTRY: OnceLock<Mutex<HashMap<String, ManagerFactory>>> = OnceLock::new();
24 REGISTRY.get_or_init(|| Mutex::new(HashMap::new()))
25}
26
27pub fn register(reloader_type: &str, factory: ManagerFactory) {
34 let mut reg = registry()
35 .lock()
36 .unwrap_or_else(std::sync::PoisonError::into_inner);
37
38 if reg.contains_key(reloader_type) {
39 panic!("reloader type {reloader_type:?} already registered");
40 }
41
42 reg.insert(reloader_type.to_string(), factory);
43}
44
45pub fn new_manager(
54 reloader_type: &str,
55 kubectl_backend: Option<Arc<dyn kubectl::Backend + Send + Sync>>,
56) -> Result<Box<dyn Manager + Send + Sync>, ReloaderError> {
57 let reg = registry()
58 .lock()
59 .unwrap_or_else(std::sync::PoisonError::into_inner);
60
61 let factory = reg.get(reloader_type).ok_or(ReloaderError::UnknownType)?;
62
63 Ok(factory(kubectl_backend))
64}
65
66#[must_use]
68pub fn list_types() -> Vec<String> {
69 let reg = registry()
70 .lock()
71 .unwrap_or_else(std::sync::PoisonError::into_inner);
72 reg.keys().cloned().collect()
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use crate::reloader::{State, Status};
79 use xianyun_core::config::latest::{CaproniConfig, ReloaderConfig};
80
81 struct MockReloader {
82 reloader_type: String,
83 }
84
85 impl Manager for MockReloader {
86 fn reloader_type(&self) -> String {
87 self.reloader_type.clone()
88 }
89
90 fn check_preconditions(
91 &self,
92 _reloader_config: &ReloaderConfig,
93 _caproni_config: &CaproniConfig,
94 _get_tool_version: &dyn Fn(&str) -> Option<String>,
95 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
96 Ok(())
97 }
98
99 fn run(
100 &self,
101 _reloader_config: &ReloaderConfig,
102 _caproni_config: &CaproniConfig,
103 _kubeconfig_path: &str,
104 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
105 Ok(())
106 }
107
108 fn status(
109 &self,
110 _reloader_config: &ReloaderConfig,
111 _caproni_config: &CaproniConfig,
112 ) -> Result<Status, Box<dyn std::error::Error + Send + Sync>> {
113 Ok(Status {
114 state: State::NotReady,
115 reloader_type: self.reloader_type.clone(),
116 })
117 }
118
119 fn validate(
120 &self,
121 _reloader_config: &ReloaderConfig,
122 _caproni_config: &CaproniConfig,
123 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
124 Ok(())
125 }
126 }
127
128 #[test]
130 fn test_register() {
131 let reloader_type = "test-reloader";
132
133 register(
134 reloader_type,
135 Box::new(|_kubectl_backend| {
136 Box::new(MockReloader {
137 reloader_type: "test-reloader".to_string(),
138 })
139 }),
140 );
141 }
142
143 #[test]
145 fn test_new_manager() {
146 let test_type = "test-new-manager";
147 register(
148 test_type,
149 Box::new(|_kubectl_backend| {
150 Box::new(MockReloader {
151 reloader_type: "test-new-manager".to_string(),
152 })
153 }),
154 );
155
156 match new_manager(test_type, None) {
157 Ok(mgr) => assert_eq!(mgr.reloader_type(), test_type),
158 Err(e) => panic!("expected Ok, got {e}"),
159 }
160
161 match new_manager("unknown-type", None) {
162 Ok(_) => panic!("expected UnknownType error"),
163 Err(err) => assert!(matches!(err, ReloaderError::UnknownType)),
164 }
165 }
166
167 #[test]
169 fn test_list_types() {
170 let type1 = "test-list-1";
171 let type2 = "test-list-2";
172
173 register(
174 type1,
175 Box::new(|_kubectl_backend| {
176 Box::new(MockReloader {
177 reloader_type: "test-list-1".to_string(),
178 })
179 }),
180 );
181 register(
182 type2,
183 Box::new(|_kubectl_backend| {
184 Box::new(MockReloader {
185 reloader_type: "test-list-2".to_string(),
186 })
187 }),
188 );
189
190 let types = list_types();
191 assert!(types.contains(&type1.to_string()));
192 assert!(types.contains(&type2.to_string()));
193 }
194}