rskit_workload/
manager.rs1use async_trait::async_trait;
8use futures::stream::BoxStream;
9use rskit_errors::AppResult;
10
11use crate::report::{
12 DeployResult, DiskUsage, ExecResult, ImageDetail, ImageEvent, SystemInfo, WaitResult,
13 WorkloadEvent, WorkloadInfo, WorkloadStats, WorkloadStatus,
14};
15use crate::spec::{DeployRequest, ImageEventFilter, ListFilter, LogOptions};
16
17#[async_trait]
23pub trait Manager: Send + Sync {
24 async fn deploy(&self, request: DeployRequest) -> AppResult<DeployResult>;
26
27 async fn stop(&self, id: &str) -> AppResult<()>;
29
30 async fn remove(&self, id: &str) -> AppResult<()>;
32
33 async fn restart(&self, id: &str) -> AppResult<()>;
35
36 async fn status(&self, id: &str) -> AppResult<WorkloadStatus>;
38
39 async fn wait(&self, id: &str) -> AppResult<WaitResult>;
41
42 async fn logs(&self, id: &str, options: LogOptions) -> AppResult<Vec<String>>;
44
45 async fn list(&self, filter: ListFilter) -> AppResult<Vec<WorkloadInfo>>;
47
48 async fn health_check(&self) -> AppResult<()>;
50}
51
52#[async_trait]
54pub trait ExecCapable: Send + Sync {
55 async fn exec(&self, id: &str, command: &[String]) -> AppResult<ExecResult>;
57}
58
59#[async_trait]
61pub trait StatsCapable: Send + Sync {
62 async fn stats(&self, id: &str) -> AppResult<WorkloadStats>;
64}
65
66#[async_trait]
68pub trait LogStreamer: Send + Sync {
69 async fn stream_logs(
71 &self,
72 id: &str,
73 options: LogOptions,
74 ) -> AppResult<BoxStream<'static, AppResult<String>>>;
75}
76
77#[async_trait]
79pub trait EventWatcher: Send + Sync {
80 async fn watch_events(
82 &self,
83 filter: ListFilter,
84 ) -> AppResult<BoxStream<'static, AppResult<WorkloadEvent>>>;
85}
86
87#[async_trait]
89pub trait SystemInfoCapable: Send + Sync {
90 async fn system_info(&self) -> AppResult<SystemInfo>;
92}
93
94#[async_trait]
96pub trait DiskUsageCapable: Send + Sync {
97 async fn disk_usage(&self) -> AppResult<DiskUsage>;
99}
100
101#[async_trait]
103pub trait ImageInspector: Send + Sync {
104 async fn image_inspect(&self, reference: &str) -> AppResult<ImageDetail>;
106}
107
108#[async_trait]
110pub trait ImageEventWatcher: Send + Sync {
111 async fn watch_image_events(
113 &self,
114 filter: ImageEventFilter,
115 ) -> AppResult<BoxStream<'static, AppResult<ImageEvent>>>;
116}
117
118#[cfg(test)]
119mod tests {
120 use std::sync::Arc;
121
122 use futures::StreamExt;
123
124 use super::*;
125 use crate::test_support::FakeManager;
126
127 #[tokio::test]
128 async fn manager_is_object_safe_and_drives_every_method() {
129 let manager: Arc<dyn Manager> = Arc::new(FakeManager);
130 let result = manager
131 .deploy(DeployRequest::new("api", "nginx"))
132 .await
133 .unwrap();
134 assert_eq!(result.id, "api-1");
135 assert!(manager.status(&result.id).await.unwrap().running);
136 assert!(manager.health_check().await.is_ok());
137 assert_eq!(
138 manager
139 .logs(&result.id, LogOptions::default())
140 .await
141 .unwrap()
142 .len(),
143 1
144 );
145 assert!(manager.wait(&result.id).await.is_ok());
146 assert!(
147 manager
148 .list(ListFilter::default())
149 .await
150 .unwrap()
151 .is_empty()
152 );
153 manager.restart(&result.id).await.unwrap();
154 manager.stop(&result.id).await.unwrap();
155 manager.remove(&result.id).await.unwrap();
156 }
157
158 #[tokio::test]
159 async fn capability_traits_are_usable_as_trait_objects() {
160 let exec: Arc<dyn ExecCapable> = Arc::new(FakeManager);
161 assert_eq!(
162 exec.exec("id", &["ls".to_string()])
163 .await
164 .unwrap()
165 .exit_code,
166 0
167 );
168
169 let stats: Arc<dyn StatsCapable> = Arc::new(FakeManager);
170 assert_eq!(stats.stats("id").await.unwrap().pids, 0);
171
172 let streamer: Arc<dyn LogStreamer> = Arc::new(FakeManager);
173 let lines: Vec<_> = streamer
174 .stream_logs("id", LogOptions::default())
175 .await
176 .unwrap()
177 .filter_map(|line| async move { line.ok() })
178 .collect()
179 .await;
180 assert_eq!(lines, vec!["a".to_string(), "b".to_string()]);
181
182 let watcher: Arc<dyn EventWatcher> = Arc::new(FakeManager);
183 let events: Vec<_> = watcher
184 .watch_events(ListFilter::default())
185 .await
186 .unwrap()
187 .collect()
188 .await;
189 assert_eq!(events.len(), 1);
190 }
191
192 #[tokio::test]
193 async fn optional_runtime_capabilities_are_usable_as_trait_objects() {
194 let sysinfo: Arc<dyn SystemInfoCapable> = Arc::new(FakeManager);
195 assert_eq!(sysinfo.system_info().await.unwrap().cpus, 8);
196
197 let disk: Arc<dyn DiskUsageCapable> = Arc::new(FakeManager);
198 assert_eq!(disk.disk_usage().await.unwrap().images_size, 1024);
199
200 let inspector: Arc<dyn ImageInspector> = Arc::new(FakeManager);
201 assert_eq!(
202 inspector.image_inspect("nginx:latest").await.unwrap().id,
203 "nginx:latest"
204 );
205
206 let img_watcher: Arc<dyn ImageEventWatcher> = Arc::new(FakeManager);
207 let events: Vec<_> = img_watcher
208 .watch_image_events(ImageEventFilter::default())
209 .await
210 .unwrap()
211 .collect()
212 .await;
213 assert_eq!(events.len(), 1);
214 }
215}