use async_trait::async_trait;
use futures::stream::BoxStream;
use rskit_errors::AppResult;
use crate::report::{
DeployResult, DiskUsage, ExecResult, ImageDetail, ImageEvent, SystemInfo, WaitResult,
WorkloadEvent, WorkloadInfo, WorkloadStats, WorkloadStatus,
};
use crate::spec::{DeployRequest, ImageEventFilter, ListFilter, LogOptions};
#[async_trait]
pub trait Manager: Send + Sync {
async fn deploy(&self, request: DeployRequest) -> AppResult<DeployResult>;
async fn stop(&self, id: &str) -> AppResult<()>;
async fn remove(&self, id: &str) -> AppResult<()>;
async fn restart(&self, id: &str) -> AppResult<()>;
async fn status(&self, id: &str) -> AppResult<WorkloadStatus>;
async fn wait(&self, id: &str) -> AppResult<WaitResult>;
async fn logs(&self, id: &str, options: LogOptions) -> AppResult<Vec<String>>;
async fn list(&self, filter: ListFilter) -> AppResult<Vec<WorkloadInfo>>;
async fn health_check(&self) -> AppResult<()>;
}
#[async_trait]
pub trait ExecCapable: Send + Sync {
async fn exec(&self, id: &str, command: &[String]) -> AppResult<ExecResult>;
}
#[async_trait]
pub trait StatsCapable: Send + Sync {
async fn stats(&self, id: &str) -> AppResult<WorkloadStats>;
}
#[async_trait]
pub trait LogStreamer: Send + Sync {
async fn stream_logs(
&self,
id: &str,
options: LogOptions,
) -> AppResult<BoxStream<'static, AppResult<String>>>;
}
#[async_trait]
pub trait EventWatcher: Send + Sync {
async fn watch_events(
&self,
filter: ListFilter,
) -> AppResult<BoxStream<'static, AppResult<WorkloadEvent>>>;
}
#[async_trait]
pub trait SystemInfoCapable: Send + Sync {
async fn system_info(&self) -> AppResult<SystemInfo>;
}
#[async_trait]
pub trait DiskUsageCapable: Send + Sync {
async fn disk_usage(&self) -> AppResult<DiskUsage>;
}
#[async_trait]
pub trait ImageInspector: Send + Sync {
async fn image_inspect(&self, reference: &str) -> AppResult<ImageDetail>;
}
#[async_trait]
pub trait ImageEventWatcher: Send + Sync {
async fn watch_image_events(
&self,
filter: ImageEventFilter,
) -> AppResult<BoxStream<'static, AppResult<ImageEvent>>>;
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use futures::StreamExt;
use super::*;
use crate::test_support::FakeManager;
#[tokio::test]
async fn manager_is_object_safe_and_drives_every_method() {
let manager: Arc<dyn Manager> = Arc::new(FakeManager);
let result = manager
.deploy(DeployRequest::new("api", "nginx"))
.await
.unwrap();
assert_eq!(result.id, "api-1");
assert!(manager.status(&result.id).await.unwrap().running);
assert!(manager.health_check().await.is_ok());
assert_eq!(
manager
.logs(&result.id, LogOptions::default())
.await
.unwrap()
.len(),
1
);
assert!(manager.wait(&result.id).await.is_ok());
assert!(
manager
.list(ListFilter::default())
.await
.unwrap()
.is_empty()
);
manager.restart(&result.id).await.unwrap();
manager.stop(&result.id).await.unwrap();
manager.remove(&result.id).await.unwrap();
}
#[tokio::test]
async fn capability_traits_are_usable_as_trait_objects() {
let exec: Arc<dyn ExecCapable> = Arc::new(FakeManager);
assert_eq!(
exec.exec("id", &["ls".to_string()])
.await
.unwrap()
.exit_code,
0
);
let stats: Arc<dyn StatsCapable> = Arc::new(FakeManager);
assert_eq!(stats.stats("id").await.unwrap().pids, 0);
let streamer: Arc<dyn LogStreamer> = Arc::new(FakeManager);
let lines: Vec<_> = streamer
.stream_logs("id", LogOptions::default())
.await
.unwrap()
.filter_map(|line| async move { line.ok() })
.collect()
.await;
assert_eq!(lines, vec!["a".to_string(), "b".to_string()]);
let watcher: Arc<dyn EventWatcher> = Arc::new(FakeManager);
let events: Vec<_> = watcher
.watch_events(ListFilter::default())
.await
.unwrap()
.collect()
.await;
assert_eq!(events.len(), 1);
}
#[tokio::test]
async fn optional_runtime_capabilities_are_usable_as_trait_objects() {
let sysinfo: Arc<dyn SystemInfoCapable> = Arc::new(FakeManager);
assert_eq!(sysinfo.system_info().await.unwrap().cpus, 8);
let disk: Arc<dyn DiskUsageCapable> = Arc::new(FakeManager);
assert_eq!(disk.disk_usage().await.unwrap().images_size, 1024);
let inspector: Arc<dyn ImageInspector> = Arc::new(FakeManager);
assert_eq!(
inspector.image_inspect("nginx:latest").await.unwrap().id,
"nginx:latest"
);
let img_watcher: Arc<dyn ImageEventWatcher> = Arc::new(FakeManager);
let events: Vec<_> = img_watcher
.watch_image_events(ImageEventFilter::default())
.await
.unwrap()
.collect()
.await;
assert_eq!(events.len(), 1);
}
}