use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use super::docker::{DockerConfig, DockerRunner};
use super::{ExecutionResult, SandboxRunner};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default, deny_unknown_fields)]
pub struct GVisorConfig {
pub docker: DockerConfig,
pub runtime_name: String,
pub runsc_binary: String,
}
impl Default for GVisorConfig {
fn default() -> Self {
Self {
docker: DockerConfig::default(),
runtime_name: "runsc".to_string(),
runsc_binary: "runsc".to_string(),
}
}
}
impl GVisorConfig {
pub fn for_image(image: &str) -> Self {
Self {
docker: DockerConfig::for_image(image),
..Default::default()
}
}
}
pub struct GVisorRunner {
inner: DockerRunner,
}
impl GVisorRunner {
pub(crate) fn into_docker_runner(self) -> DockerRunner {
self.inner
}
pub fn new(config: GVisorConfig) -> Result<Self, anyhow::Error> {
super::command::check_binary(
&config.runsc_binary,
"--version",
config
.docker
.max_execution_time
.min(std::time::Duration::from_secs(5)),
)?;
let inner = DockerRunner::with_runtime(config.docker, config.runtime_name.clone())?;
tracing::info!(
"gVisor sandbox initialized (runtime={})",
config.runtime_name
);
Ok(Self { inner })
}
}
#[async_trait]
impl SandboxRunner for GVisorRunner {
async fn execute(
&self,
code: &str,
env: HashMap<String, String>,
) -> Result<ExecutionResult, anyhow::Error> {
self.inner.execute(code, env).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn config_default_uses_runsc() {
let cfg = GVisorConfig::default();
assert_eq!(cfg.runtime_name, "runsc");
assert_eq!(cfg.runsc_binary, "runsc");
}
#[test]
fn config_for_image_carries_image() {
let cfg = GVisorConfig::for_image("alpine:latest");
assert_eq!(cfg.docker.image, "alpine:latest");
assert_eq!(cfg.runtime_name, "runsc");
}
}