Skip to main content

orca_core/
runtime.rs

1//! Core runtime abstraction for workload execution.
2
3use std::collections::HashMap;
4use std::pin::Pin;
5use std::time::Duration;
6
7use async_trait::async_trait;
8use tokio::io::AsyncRead;
9
10use crate::error::Result;
11use crate::types::{ResourceStats, WorkloadSpec, WorkloadStatus};
12
13/// Helper trait for downcasting trait objects to concrete types.
14pub trait AsAny: Send + Sync {
15    fn as_any(&self) -> &dyn std::any::Any;
16}
17
18/// Opaque handle returned by a runtime after creating a workload.
19#[derive(Debug, Clone)]
20pub struct WorkloadHandle {
21    /// Runtime-specific identifier (container ID, Wasm instance ID, etc.)
22    pub runtime_id: String,
23    /// Human-friendly name.
24    pub name: String,
25    /// Runtime-specific metadata (e.g., host_port, container_ip).
26    pub metadata: HashMap<String, String>,
27}
28
29/// Options for log retrieval.
30#[derive(Debug, Clone, Default)]
31pub struct LogOpts {
32    /// Follow log output (stream).
33    pub follow: bool,
34    /// Number of recent lines to return.
35    pub tail: Option<u64>,
36    /// Only return logs since this timestamp.
37    pub since: Option<chrono::DateTime<chrono::Utc>>,
38    /// Include timestamps in log output.
39    pub timestamps: bool,
40}
41
42/// Result of executing a command inside a workload.
43#[derive(Debug)]
44pub struct ExecResult {
45    /// Process exit code.
46    pub exit_code: i32,
47    /// Standard output bytes.
48    pub stdout: Vec<u8>,
49    /// Standard error bytes.
50    pub stderr: Vec<u8>,
51}
52
53/// A stream of log bytes.
54pub type LogStream = Pin<Box<dyn AsyncRead + Send>>;
55
56/// The core runtime abstraction. Both container and Wasm runtimes implement this.
57#[async_trait]
58pub trait Runtime: AsAny + Send + Sync + 'static {
59    /// Human-readable name of this runtime (e.g., "container", "wasm").
60    fn name(&self) -> &str;
61
62    /// Create a new workload from the given spec. Does not start it.
63    async fn create(&self, spec: &WorkloadSpec) -> Result<WorkloadHandle>;
64
65    /// Start a previously created workload.
66    async fn start(&self, handle: &WorkloadHandle) -> Result<()>;
67
68    /// Stop a running workload, waiting up to `timeout` for graceful shutdown.
69    async fn stop(&self, handle: &WorkloadHandle, timeout: Duration) -> Result<()>;
70
71    /// Remove a stopped workload and clean up resources.
72    async fn remove(&self, handle: &WorkloadHandle) -> Result<()>;
73
74    /// Get the current status of a workload.
75    async fn status(&self, handle: &WorkloadHandle) -> Result<WorkloadStatus>;
76
77    /// Stream logs from a workload.
78    async fn logs(&self, handle: &WorkloadHandle, opts: &LogOpts) -> Result<LogStream>;
79
80    /// Execute a command inside a running workload.
81    async fn exec(&self, handle: &WorkloadHandle, cmd: &[String]) -> Result<ExecResult>;
82
83    /// Get current resource usage stats.
84    async fn stats(&self, handle: &WorkloadHandle) -> Result<ResourceStats>;
85
86    /// Resolve the host-accessible port for a workload after it has been started.
87    ///
88    /// Returns `None` if the runtime does not expose ports or the workload has no port mapping.
89    /// Default implementation returns `None`.
90    async fn resolve_host_port(
91        &self,
92        handle: &WorkloadHandle,
93        _container_port: u16,
94    ) -> Result<Option<u16>> {
95        Ok(handle
96            .metadata
97            .get("host_port")
98            .and_then(|p| p.parse().ok()))
99    }
100
101    /// Resolve the container's network address (ip:port) on its Docker network.
102    ///
103    /// This allows the proxy to route directly to the container IP
104    /// without going through host port mappings.
105    /// Returns `None` if not applicable (e.g., Wasm workloads).
106    async fn resolve_container_address(
107        &self,
108        _handle: &WorkloadHandle,
109        _container_port: u16,
110        _network: &str,
111    ) -> Result<Option<String>> {
112        Ok(None)
113    }
114}