use std::path::Path;
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use bytes::Bytes;
use chrono::{DateTime, Utc};
use futures::Stream;
use futures::future::BoxFuture;
use super::Backend;
use crate::MicrosandboxResult;
use crate::agent::AgentClient;
use crate::logs::{LogEntry, LogOptions, LogStreamOptions};
use crate::runtime::ProcessHandle;
use crate::sandbox::exec::{ExecHandle, ExecOptions, ExecOutput};
use crate::sandbox::fs::{FsEntry, FsMetadata, FsReadStream, FsWriteSink};
use crate::sandbox::metrics::SandboxMetrics;
use crate::sandbox::{
Sandbox, SandboxConfig, SandboxHandle, SandboxListBuilder, SandboxPage, SandboxStatus,
};
pub(crate) use super::cloud::sandbox::{
cloud_status_to_sandbox_status, sandbox_config_from_cloud_spec,
};
pub type MetricsStream =
Pin<Box<dyn Stream<Item = MicrosandboxResult<SandboxMetrics>> + Send + 'static>>;
pub type LogStream = Pin<Box<dyn Stream<Item = MicrosandboxResult<LogEntry>> + Send + 'static>>;
pub enum SandboxInner {
Local(SandboxLocalState),
Cloud(SandboxCloudState),
}
pub struct SandboxLocalState {
pub db_id: i32,
pub handle: Option<Arc<tokio::sync::Mutex<ProcessHandle>>>,
pub client: Arc<AgentClient>,
}
pub struct SandboxCloudState {
pub id: String,
pub org_id: String,
pub created_at: DateTime<Utc>,
}
pub enum SandboxHandleInner {
Local(SandboxHandleLocalState),
Cloud(SandboxHandleCloudState),
}
pub struct SandboxHandleLocalState {
pub db_id: i32,
pub status: SandboxStatus,
pub config_json: String,
pub active_config_json: Option<String>,
pub created_at: Option<DateTime<Utc>>,
pub updated_at: Option<DateTime<Utc>>,
pub pid: Option<i32>,
}
pub struct SandboxHandleCloudState {
pub id: String,
pub org_id: String,
pub status: SandboxStatus,
pub config_json: String,
pub created_at: Option<DateTime<Utc>>,
pub started_at: Option<DateTime<Utc>>,
pub stopped_at: Option<DateTime<Utc>>,
pub last_failure_message: Option<String>,
}
pub trait SandboxBackend: Send + Sync {
fn create<'a>(
&'a self,
backend: Arc<dyn Backend>,
config: SandboxConfig,
start: bool,
) -> BoxFuture<'a, MicrosandboxResult<Sandbox>>;
fn create_detached<'a>(
&'a self,
backend: Arc<dyn Backend>,
config: SandboxConfig,
) -> BoxFuture<'a, MicrosandboxResult<Sandbox>>;
fn start<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<Sandbox>>;
fn start_detached<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<Sandbox>>;
fn get<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<SandboxHandle>>;
fn list<'a>(
&'a self,
backend: Arc<dyn Backend>,
query: SandboxListBuilder,
) -> BoxFuture<'a, MicrosandboxResult<SandboxPage>>;
fn remove<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<()>>;
fn stop<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<()>>;
fn kill<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<()>>;
fn drain<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<()>>;
fn exec<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
config: &'a SandboxConfig,
cmd: String,
opts: ExecOptions,
) -> BoxFuture<'a, MicrosandboxResult<ExecOutput>> {
Box::pin(async move {
crate::sandbox::exec::agent::exec(backend.as_ref(), name, config, cmd, opts).await
})
}
fn exec_stream<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
config: &'a SandboxConfig,
cmd: String,
opts: ExecOptions,
) -> BoxFuture<'a, MicrosandboxResult<ExecHandle>> {
Box::pin(async move {
crate::sandbox::exec::agent::exec_stream(backend.as_ref(), name, config, cmd, opts)
.await
})
}
fn attach<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
config: &'a SandboxConfig,
cmd: String,
opts: crate::sandbox::AttachOptionsBuilder,
) -> BoxFuture<'a, MicrosandboxResult<i32>> {
Box::pin(async move {
crate::sandbox::attach::agent::attach(backend.as_ref(), name, config, cmd, opts).await
})
}
fn logs<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
opts: &'a LogOptions,
) -> BoxFuture<'a, MicrosandboxResult<Vec<LogEntry>>>;
fn log_stream<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
opts: &'a LogStreamOptions,
) -> BoxFuture<'a, MicrosandboxResult<LogStream>>;
fn metrics<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
config: &'a SandboxConfig,
) -> BoxFuture<'a, MicrosandboxResult<SandboxMetrics>>;
fn metrics_stream(
&self,
backend: Arc<dyn Backend>,
name: String,
config: SandboxConfig,
interval: Duration,
) -> MetricsStream;
fn fs_read<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
path: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<Bytes>> {
Box::pin(async move { crate::sandbox::fs::agent::read(backend.as_ref(), name, path).await })
}
fn fs_read_stream<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
path: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<FsReadStream>> {
Box::pin(async move {
crate::sandbox::fs::agent::read_stream(backend.as_ref(), name, path).await
})
}
fn fs_write<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
path: &'a str,
data: Vec<u8>,
) -> BoxFuture<'a, MicrosandboxResult<()>> {
Box::pin(async move {
crate::sandbox::fs::agent::write(backend.as_ref(), name, path, data).await
})
}
fn fs_write_stream<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
path: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<FsWriteSink>> {
Box::pin(async move {
crate::sandbox::fs::agent::write_stream(backend.as_ref(), name, path).await
})
}
fn fs_list<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
path: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<Vec<FsEntry>>> {
Box::pin(async move { crate::sandbox::fs::agent::list(backend.as_ref(), name, path).await })
}
fn fs_stat<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
path: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<FsMetadata>> {
Box::pin(async move { crate::sandbox::fs::agent::stat(backend.as_ref(), name, path).await })
}
fn fs_mkdir<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
path: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<()>> {
Box::pin(
async move { crate::sandbox::fs::agent::mkdir(backend.as_ref(), name, path).await },
)
}
fn fs_remove<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
path: &'a str,
recursive: bool,
) -> BoxFuture<'a, MicrosandboxResult<()>> {
Box::pin(async move {
crate::sandbox::fs::agent::remove(backend.as_ref(), name, path, recursive).await
})
}
fn fs_copy<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
from: &'a str,
to: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<()>> {
Box::pin(
async move { crate::sandbox::fs::agent::copy(backend.as_ref(), name, from, to).await },
)
}
fn fs_rename<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
from: &'a str,
to: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<()>> {
Box::pin(async move {
crate::sandbox::fs::agent::rename(backend.as_ref(), name, from, to).await
})
}
fn fs_exists<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
path: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<bool>> {
Box::pin(
async move { crate::sandbox::fs::agent::exists(backend.as_ref(), name, path).await },
)
}
fn fs_copy_from_host<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
host: &'a Path,
guest: &'a str,
) -> BoxFuture<'a, MicrosandboxResult<()>> {
Box::pin(async move {
crate::sandbox::fs::agent::copy_from_host(backend.as_ref(), name, host, guest).await
})
}
fn fs_copy_to_host<'a>(
&'a self,
backend: Arc<dyn Backend>,
name: &'a str,
guest: &'a str,
host: &'a Path,
) -> BoxFuture<'a, MicrosandboxResult<()>> {
Box::pin(async move {
crate::sandbox::fs::agent::copy_to_host(backend.as_ref(), name, guest, host).await
})
}
}