1use crate::pool::{RunnerHandle, RunnerPoolKey};
2use camel_api::{Exchange, function::*};
3use std::time::Duration;
4
5mod sealed {
6 pub trait Sealed {}
7}
8
9#[derive(Debug, Clone)]
12pub enum FunctionHealthStatus {
13 Healthy,
14 Unhealthy(String),
15}
16
17#[derive(Debug, thiserror::Error)]
18pub enum ProviderError {
19 #[error("spawn failed: {0}")]
20 SpawnFailed(String),
21 #[error("health check failed: {0}")]
22 HealthFailed(String),
23 #[error("register failed: {0}")]
24 RegisterFailed(String),
25 #[error("unregister failed: {0}")]
26 UnregisterFailed(String),
27 #[error("invoke failed: {0}")]
28 InvokeFailed(String),
29 #[error("shutdown failed: {0}")]
30 ShutdownFailed(String),
31 #[error("invalid provider config: {0}")]
32 InvalidConfig(String),
33 #[error("http client construction failed: {0}")]
34 HttpClientBuildFailed(String),
35 #[error("boot timeout")]
36 BootTimeout,
37}
38
39#[async_trait::async_trait]
40pub(crate) trait FunctionProvider: Send + Sync + sealed::Sealed {
41 async fn spawn(&self, key: &RunnerPoolKey) -> Result<RunnerHandle, ProviderError>;
42 async fn shutdown(&self, handle: RunnerHandle) -> Result<(), ProviderError>;
43 async fn health(&self, handle: &RunnerHandle) -> Result<FunctionHealthStatus, ProviderError>;
44 async fn register(
45 &self,
46 handle: &RunnerHandle,
47 def: &FunctionDefinition,
48 ) -> Result<(), ProviderError>;
49 async fn unregister(&self, handle: &RunnerHandle, id: &FunctionId)
50 -> Result<(), ProviderError>;
51 async fn invoke(
52 &self,
53 handle: &RunnerHandle,
54 id: &FunctionId,
55 ex: &Exchange,
56 timeout: Duration,
57 ) -> Result<ExchangePatch, ProviderError>;
58}
59
60pub mod container;
61pub mod fake {
62 use super::*;
63 use std::collections::{HashMap, HashSet};
64 use std::sync::atomic::{AtomicUsize, Ordering};
65 use std::sync::{Arc, Mutex};
66 use tokio_util::sync::CancellationToken;
67
68 #[derive(Debug, Clone, Default)]
69 pub struct FakeProviderConfig {
70 pub fail_on_spawn: bool,
71 pub fail_on_register: usize,
72 pub fail_on_health: bool,
73 pub fail_on_shutdown: bool,
74 pub invoke_response: Option<ExchangePatch>,
75 pub invoke_delay: Option<std::time::Duration>,
76 }
77
78 #[derive(Debug, Clone)]
79 pub enum FakeCall {
80 Spawn(RunnerPoolKey),
81 Shutdown(RunnerPoolKey),
82 Health(String),
83 Register(String, FunctionId),
84 Unregister(String, FunctionId),
85 Invoke(String, FunctionId),
86 }
87
88 pub struct FakeProvider {
89 pub config: Arc<Mutex<FakeProviderConfig>>,
90 pub calls: Arc<Mutex<Vec<FakeCall>>>,
91 pub registered: Arc<Mutex<HashMap<String, HashSet<FunctionId>>>>,
92 pub spawned: Arc<Mutex<Vec<RunnerPoolKey>>>,
93 pub shutdowns: Arc<Mutex<Vec<RunnerPoolKey>>>,
94 register_ok_count: Arc<Mutex<usize>>,
95 spawn_count: AtomicUsize,
96 }
97
98 impl FakeProvider {
99 pub fn new(config: FakeProviderConfig) -> Self {
100 Self {
101 config: Arc::new(Mutex::new(config)),
102 calls: Arc::new(Mutex::new(Vec::new())),
103 registered: Arc::new(Mutex::new(HashMap::new())),
104 spawned: Arc::new(Mutex::new(Vec::new())),
105 shutdowns: Arc::new(Mutex::new(Vec::new())),
106 register_ok_count: Arc::new(Mutex::new(0)),
107 spawn_count: AtomicUsize::new(0),
108 }
109 }
110
111 pub fn spawn_count(&self) -> usize {
112 self.spawn_count.load(Ordering::SeqCst)
113 }
114 }
115
116 impl super::sealed::Sealed for FakeProvider {}
117
118 #[async_trait::async_trait]
119 impl FunctionProvider for FakeProvider {
120 async fn spawn(&self, key: &RunnerPoolKey) -> Result<RunnerHandle, ProviderError> {
121 self.spawn_count.fetch_add(1, Ordering::SeqCst);
122 self.calls
123 .lock()
124 .expect("calls") .push(FakeCall::Spawn(key.clone()));
126 self.spawned.lock().expect("spawned").push(key.clone()); if self.config.lock().expect("config").fail_on_spawn {
128 return Err(ProviderError::SpawnFailed("configured".into()));
130 }
131 Ok(RunnerHandle {
132 id: format!("fake-{}", key.runtime),
133 state: Arc::new(Mutex::new(crate::pool::RunnerState::Booting)),
134 cancel: CancellationToken::new(),
135 })
136 }
137
138 async fn shutdown(&self, handle: RunnerHandle) -> Result<(), ProviderError> {
139 self.calls
140 .lock()
141 .expect("calls") .push(FakeCall::Shutdown(RunnerPoolKey {
143 runtime: handle.id.replace("fake-", ""),
144 }));
145 self.shutdowns
146 .lock()
147 .expect("shutdowns") .push(RunnerPoolKey {
149 runtime: handle.id.replace("fake-", ""),
150 });
151 if self.config.lock().expect("config").fail_on_shutdown {
152 return Err(ProviderError::ShutdownFailed(
154 "configured shutdown failure".into(),
155 ));
156 }
157 Ok(())
158 }
159
160 async fn health(
161 &self,
162 handle: &RunnerHandle,
163 ) -> Result<FunctionHealthStatus, ProviderError> {
164 self.calls
165 .lock()
166 .expect("calls") .push(FakeCall::Health(handle.id.clone()));
168 if self.config.lock().expect("config").fail_on_health {
169 return Ok(FunctionHealthStatus::Unhealthy("configured".into()));
171 }
172 Ok(FunctionHealthStatus::Healthy)
173 }
174
175 async fn register(
176 &self,
177 handle: &RunnerHandle,
178 def: &FunctionDefinition,
179 ) -> Result<(), ProviderError> {
180 self.calls
181 .lock()
182 .expect("calls") .push(FakeCall::Register(handle.id.clone(), def.id.clone()));
184 let mut count = self.register_ok_count.lock().expect("count"); let cfg = self.config.lock().expect("config").clone(); if cfg.fail_on_register > 0 && *count >= cfg.fail_on_register {
187 return Err(ProviderError::RegisterFailed("configured".into()));
188 }
189 *count += 1;
190 self.registered
191 .lock()
192 .expect("registered") .entry(handle.id.clone())
194 .or_default()
195 .insert(def.id.clone());
196 Ok(())
197 }
198
199 async fn unregister(
200 &self,
201 handle: &RunnerHandle,
202 id: &FunctionId,
203 ) -> Result<(), ProviderError> {
204 self.calls
205 .lock()
206 .expect("calls") .push(FakeCall::Unregister(handle.id.clone(), id.clone()));
208 if let Some(set) = self
209 .registered
210 .lock()
211 .expect("registered") .get_mut(&handle.id)
213 {
214 set.remove(id);
215 }
216 Ok(())
217 }
218
219 async fn invoke(
220 &self,
221 handle: &RunnerHandle,
222 id: &FunctionId,
223 _ex: &Exchange,
224 _timeout: Duration,
225 ) -> Result<ExchangePatch, ProviderError> {
226 self.calls
227 .lock()
228 .expect("calls") .push(FakeCall::Invoke(handle.id.clone(), id.clone()));
230 let exists = self
231 .registered
232 .lock()
233 .expect("registered") .get(&handle.id)
235 .map(|s| s.contains(id))
236 .unwrap_or(false);
237 if !exists {
238 return Err(ProviderError::InvokeFailed("not registered".into()));
239 }
240 let cfg = self.config.lock().expect("config").clone(); if let Some(delay) = cfg.invoke_delay {
242 tokio::time::sleep(delay).await;
243 }
244 Ok(cfg.invoke_response.unwrap_or_default())
245 }
246 }
247}