1use std::{
2 collections::HashMap,
3 path::{Path, PathBuf},
4 sync::Arc,
5};
6
7use crate::{
8 config::{self, AutoschematicConfig},
9 connector::{
10 Connector, ConnectorInbox, FilterResponse,
11 handle::{ConnectorHandle, ConnectorHandleStatus},
12 spawn::spawn_connector,
13 },
14 connector_util::check_connector_host_version_match,
15 error::AutoschematicError,
16 keystore::KeyStore,
17 util::parse_env_file,
18};
19
20use anyhow::Context;
21use dashmap::DashMap;
22use serde::Serialize;
23use tokio::task::JoinSet;
24
25#[derive(Debug, Clone, Serialize)]
26pub enum InitStatus {
27 Offline,
28 Spawning,
29 Initializing,
30 Error(String),
31 Running,
32}
33
34#[derive(Debug, Serialize)]
35pub struct TopResponse {
36 handle_status: ConnectorHandleStatus,
37 init_status: InitStatus,
38}
39
40#[derive(Clone, PartialEq, Eq, Hash, Serialize)]
41pub struct ConnectorCacheKey {
42 pub prefix: PathBuf,
43 pub shortname: String,
44}
45
46pub type ConnectorCacheValue = (Arc<dyn ConnectorHandle>, ConnectorInbox);
47
48#[derive(Default)]
49pub struct ConnectorCache {
50 cache: Arc<DashMap<ConnectorCacheKey, ConnectorCacheValue>>,
51 init_status: Arc<DashMap<ConnectorCacheKey, InitStatus>>,
52 filter_cache: Arc<DashMap<ConnectorCacheKey, HashMap<PathBuf, FilterResponse>>>,
56 }
59
60impl ConnectorCache {
63 pub async fn top(&self) -> HashMap<ConnectorCacheKey, TopResponse> {
64 let mut res = HashMap::new();
65
66 let keys: Vec<ConnectorCacheKey> = self.cache.iter().map(|kv| kv.key().clone()).collect();
68
69 for key in keys {
70 if let Some(connector) = self.cache.get(&key).map(|kv| kv.0.clone()) {
71 let init_status = match self.init_status.get(&key) {
72 Some(init_status) => init_status.value().clone(),
73 None => InitStatus::Initializing,
74 };
75
76 res.insert(
77 key,
78 TopResponse {
79 handle_status: connector.status().await,
80 init_status,
81 },
82 );
83 }
84 }
85
86 res
87 }
88
89 pub async fn get_connector(&self, name: &str, prefix: &Path) -> Option<(Arc<dyn Connector>, ConnectorInbox)> {
90 let key = ConnectorCacheKey {
91 shortname: name.into(),
92 prefix: prefix.into(),
93 };
94
95 if let Some(entry) = self.cache.get(&key) {
96 let (connector, inbox) = &*entry;
97 Some((connector.clone(), inbox.resubscribe()))
98 } else {
99 None
100 }
101 }
102
103 pub async fn get_or_spawn_connector(
104 &self,
105 config: &AutoschematicConfig,
106 prefix: &str,
107 connector_def: &config::Connector,
108 keystore: Option<Arc<dyn KeyStore>>,
109 do_init: bool,
110 ) -> Result<(Arc<dyn Connector>, ConnectorInbox), AutoschematicError> {
111 let key = ConnectorCacheKey {
112 shortname: connector_def.shortname.clone(),
113 prefix: prefix.into(),
114 };
115
116 let Some(prefix_def) = config.prefixes.get(prefix) else {
117 return Err(anyhow::anyhow!(format!("No such prefix {}", prefix)).into());
118 };
119
120 let spec = &connector_def.spec;
121
122 let mut env = HashMap::new();
123
124 if let Some(ref env_file) = prefix_def.env_file {
125 for (k, v) in parse_env_file(&std::fs::read_to_string(env_file).context(format!("Reading env file {}", env_file))?)
126 {
127 env.insert(k, v);
128 }
129 }
130
131 for (k, v) in &prefix_def.env {
132 env.insert(k.into(), v.into());
133 }
134
135 if let Some(ref env_file) = connector_def.env_file {
136 for (k, v) in parse_env_file(&std::fs::read_to_string(env_file).context(format!("Reading env file {}", env_file))?)
137 {
138 env.insert(k, v);
139 }
140 }
141
142 for (k, v) in &connector_def.env {
143 env.insert(k.into(), v.into());
144 }
145
146 if let Some((connector, inbox)) = self.cache.get(&key).map(|entry| {
147 let (connector, inbox) = &*entry;
148 (connector.clone(), inbox.resubscribe())
149 }) {
150 let need_init = match self.init_status.entry(key.clone()) {
151 dashmap::Entry::Occupied(status_ref) => match status_ref.get() {
152 InitStatus::Offline => do_init,
153 InitStatus::Spawning => do_init,
154 InitStatus::Initializing => false,
155 InitStatus::Error(_) => do_init,
156 InitStatus::Running => false,
157 },
158 dashmap::Entry::Vacant(vacant_entry) => {
159 vacant_entry.insert(InitStatus::Offline);
160 do_init
161 }
162 };
163
164 if need_init {
165 self.init_status.insert(key.clone(), InitStatus::Initializing);
166 if let Err(e) = connector.init().await {
167 tracing::error!(
168 "In prefix {}: failed to init connector {}: {:#?}",
169 prefix,
170 connector_def.shortname,
171 e
172 );
173
174 self.init_status.insert(key.clone(), InitStatus::Error(format!("{:#?}", e)));
175 } else {
176 self.init_status.insert(key.clone(), InitStatus::Running);
177 }
178 }
179
180 Ok((connector.clone(), inbox.resubscribe()))
181 } else {
182 self.init_status.insert(key.clone(), InitStatus::Spawning);
183 let (connector, inbox) = spawn_connector(&connector_def.shortname, spec, &PathBuf::from(prefix), &env, keystore)
187 .await
188 .context("spawn_connector()")?;
189
190 check_connector_host_version_match(&connector_def.shortname, &connector).await?;
191
192 if do_init {
193 self.init_status.insert(key.clone(), InitStatus::Initializing);
194 if let Err(e) = connector.init().await {
195 tracing::error!(
196 "In prefix {}: failed to init connector {}: {:#?}",
197 prefix,
198 connector_def.shortname,
199 e
200 );
201 self.init_status.insert(key.clone(), InitStatus::Error(format!("{:#?}", e)));
202 } else {
203 self.init_status.insert(key.clone(), InitStatus::Running);
204 }
205 }
206
207 let connector_arc = Arc::new(connector);
208
209 self.cache.insert(key, (connector_arc.clone(), inbox.resubscribe()));
210
211 Ok((connector_arc, inbox))
212 }
213 }
215
216 pub async fn init_connector(&self, name: &str, prefix: &Path) -> Option<anyhow::Result<()>> {
217 let key = ConnectorCacheKey {
218 shortname: name.into(),
219 prefix: prefix.into(),
220 };
221
222 if let Some(entry) = self.cache.get(&key) {
223 let (connector, _inbox) = &*entry;
224 self.clear_filter_cache(name, prefix).await;
225 Some(connector.init().await)
226 } else {
227 None
228 }
229 }
230
231 pub async fn filter_cached(&self, name: &str, prefix: &Path, addr: &Path) -> anyhow::Result<FilterResponse> {
236 let key = ConnectorCacheKey {
237 shortname: name.into(),
238 prefix: prefix.into(),
239 };
240
241 if let Some(value) = self.filter_cache.get(&key).and_then(|cache| cache.get(addr).copied()) {
243 Ok(value)
244 } else if let Some(connector) = self.cache.get(&key).map(|entry| entry.0.clone()) {
245 let res = connector.filter(addr).await?;
246 self.filter_cache.entry(key.clone()).or_default().insert(addr.into(), res);
247 Ok(res)
248 } else {
249 Ok(FilterResponse::none())
250 }
251 }
252
253 pub async fn filter_all_cached(
254 &self,
255 autoschematic_config: &AutoschematicConfig,
256 addr: &Path,
257 ) -> anyhow::Result<FilterResponse> {
258 for (prefix_name, prefix_def) in &autoschematic_config.prefixes {
259 for connector_def in &prefix_def.connectors {
260 match self
261 .filter_cached(&connector_def.shortname, &PathBuf::from(prefix_name), addr)
262 .await?
263 {
264 FilterResponse::None => continue,
265 resp => return Ok(resp),
266 }
267 }
268 }
269 Ok(FilterResponse::None)
270 }
271
272 pub async fn clear_filter_cache(&self, name: &str, prefix: &Path) {
273 let key = ConnectorCacheKey {
274 shortname: name.into(),
275 prefix: prefix.into(),
276 };
277
278 self.filter_cache.remove(&key);
279 }
280
281 pub async fn clear(&self) {
285 let keys: Vec<ConnectorCacheKey> = self.cache.iter().map(|kv| kv.key().clone()).collect();
286
287 let mut joinset = JoinSet::new();
288
289 for key in keys {
290 if let Some(kv) = self.cache.get(&key) {
291 let connector = kv.0.clone();
292 joinset.spawn(async move { connector.kill().await });
293 }
294 }
295
296 joinset.join_all().await;
297
298 self.cache.clear();
299 self.filter_cache.clear();
300 }
301}
302
303