use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::process::Stdio;
use super::docker::{DockerConfig, DockerRunner};
use super::{ExecutionResult, SandboxRunner};
#[derive(Debug, Clone, Serialize, Deserialize)]
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 fn new(mut config: GVisorConfig) -> Result<Self, anyhow::Error> {
let runsc_check = std::process::Command::new(&config.runsc_binary)
.arg("--version")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
match runsc_check {
Ok(status) if status.success() => {}
_ => {
anyhow::bail!(
"gVisor (`{}`) is not available. Install runsc \
(https://gvisor.dev/docs/user_guide/install/) and register it \
with the Docker daemon at /etc/docker/daemon.json.",
config.runsc_binary
);
}
}
config
.docker
.extra_flags
.insert(0, format!("--runtime={}", config.runtime_name));
let inner = DockerRunner::new(config.docker)?;
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");
}
#[test]
fn runtime_flag_is_prepended_to_extra_flags() {
let mut cfg = GVisorConfig::default();
cfg.docker.extra_flags = vec!["--label".into(), "test=1".into()];
cfg.docker
.extra_flags
.insert(0, format!("--runtime={}", cfg.runtime_name));
assert_eq!(cfg.docker.extra_flags[0], "--runtime=runsc");
assert_eq!(cfg.docker.extra_flags[1], "--label");
}
}