diff --git a/compiler/base/Dockerfile b/compiler/base/Dockerfile index 8ca1ba57c..7eb8af03e 100644 --- a/compiler/base/Dockerfile +++ b/compiler/base/Dockerfile @@ -41,6 +41,7 @@ RUN cd / && \ WORKDIR /playground ADD Cargo.toml /playground/Cargo.toml +ADD cargo.sh /playground/cargo.sh ADD crate-information.json /playground/crate-information.json RUN cargo fetch diff --git a/compiler/base/cargo.sh b/compiler/base/cargo.sh new file mode 100644 index 000000000..533a75fbb --- /dev/null +++ b/compiler/base/cargo.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +set -eu + +POSITIONAL=() + +while [[ $# -gt 0 ]]; do + case $1 in + --env) + export $2 + shift 2 + ;; + *) + POSITIONAL+=("$1") + shift + ;; + esac +done + +eval set -- "${POSITIONAL[@]}" + +timeout=${PLAYGROUND_TIMEOUT:-10} + +modify-cargo-toml + +timeout --signal=KILL ${timeout} cargo "$@" || pkill sleep + +pkill sleep diff --git a/compiler/base/entrypoint.sh b/compiler/base/entrypoint.sh index 9f7a06074..28546ba98 100755 --- a/compiler/base/entrypoint.sh +++ b/compiler/base/entrypoint.sh @@ -1,8 +1,3 @@ #!/bin/bash -set -eu - -timeout=${PLAYGROUND_TIMEOUT:-10} - -modify-cargo-toml -timeout --signal=KILL ${timeout} "$@" +sleep 365d diff --git a/ui/Cargo.lock b/ui/Cargo.lock index 8cd7842ee..ad93444fd 100644 --- a/ui/Cargo.lock +++ b/ui/Cargo.lock @@ -204,6 +204,15 @@ dependencies = [ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ctrlc" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dotenv" version = "0.13.0" @@ -673,6 +682,18 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nix" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nodrop" version = "0.1.13" @@ -1461,6 +1482,7 @@ version = "0.1.0" dependencies = [ "bodyparser 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "corsware 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "hubcaps 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1555,6 +1577,11 @@ name = "version_check" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "want" version = "0.0.6" @@ -1645,6 +1672,7 @@ dependencies = [ "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" "checksum csv 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7ef22b37c7a51c564a365892c012dc0271221fdcc64c69b19ba4d6fa8bd96d9c" +"checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e" "checksum dotenv 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d0a1279c96732bc6800ce6337b6a614697b0e74ae058dc03c62ebeb78b4d86" "checksum env_logger 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b61fa891024a945da30a9581546e8cfaf5602c7b3f4c137a2805cf388f92075a" "checksum error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07e791d3be96241c77c43846b665ef1384606da2cd2a48730abe606a12906e02" @@ -1695,6 +1723,7 @@ dependencies = [ "checksum mount 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e25c06012941aaf8c75f2eaf7ec5c48cf69f9fc489ab3eb3589edc107e386f0b" "checksum native-tls 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ff8e08de0070bbf4c31f452ea2a70db092f36f6f2e4d897adf5674477d488fb2" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" +"checksum nix 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d37e713a259ff641624b6cb20e3b12b2952313ba36b6823c0f16e6cfd9e5de17" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" "checksum openssl 0.10.19 (registry+https://github.com/rust-lang/crates.io-index)" = "84321fb9004c3bce5611188a644d6171f895fa2889d155927d528782edb21c5d" @@ -1794,6 +1823,7 @@ dependencies = [ "checksum utf8-ranges 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "796f7e48bef87609f7ade7e06495a87d5cd06c7866e6a5cbfceffc558a243737" "checksum vcpkg 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "def296d3eb3b12371b2c7d0e83bfe1403e4db2d7a0bba324a12b21c4ee13143d" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" diff --git a/ui/Cargo.toml b/ui/Cargo.toml index cffe835d1..3436b62c6 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -32,6 +32,7 @@ router = "0.6.0" openssl-probe = "0.1.2" dotenv = "0.13.0" snafu = "0.2.0" +ctrlc = { version = "3.1.1", features = ["termination"] } [dependencies.playground-middleware] git = "https://github.com/integer32llc/playground-middleware" diff --git a/ui/src/main.rs b/ui/src/main.rs index a00cb1ec3..02a56436d 100644 --- a/ui/src/main.rs +++ b/ui/src/main.rs @@ -22,15 +22,17 @@ use std::{ convert::{TryFrom, TryInto}, env, path::PathBuf, + process, sync::{Arc, Mutex}, time::{Duration, Instant}, }; -use crate::sandbox::Sandbox; +use crate::sandbox::{Channel, DockerContainers, Sandbox}; const DEFAULT_ADDRESS: &str = "127.0.0.1"; const DEFAULT_PORT: u16 = 5000; const DEFAULT_LOG_FILE: &str = "access-log.csv"; +const DEFAULT_DOCKER_CONTAINER_POOL_SIZE: usize = 10; mod asm_cleanup; mod gist; @@ -42,6 +44,13 @@ const ONE_YEAR_IN_SECONDS: u64 = 60 * 60 * 24 * 365; const SANDBOX_CACHE_TIME_TO_LIVE_IN_SECONDS: u64 = ONE_HOUR_IN_SECONDS as u64; +fn set_graceful_shutdown_hook(containers: Arc>) { + ctrlc::set_handler(move || { + containers.lock().unwrap().terminate(); + process::exit(0); + }).expect("Error setting Ctrl-C handler"); +} + fn main() { // Dotenv may be unable to load environment variables, but that's ok in production let _ = dotenv::dotenv(); @@ -55,6 +64,11 @@ fn main() { let port = env::var("PLAYGROUND_UI_PORT").ok().and_then(|p| p.parse().ok()).unwrap_or(DEFAULT_PORT); let logfile = env::var("PLAYGROUND_LOG_FILE").unwrap_or_else(|_| DEFAULT_LOG_FILE.to_string()); let cors_enabled = env::var_os("PLAYGROUND_CORS_ENABLED").is_some(); + let docker_containers_pool_size = env::var("DOCKER_CONTAINER_POOL_SIZE").ok().and_then(|v| v.parse().ok()).unwrap_or(DEFAULT_DOCKER_CONTAINER_POOL_SIZE); + + let containers = Arc::new(Mutex::new(DockerContainers::new(docker_containers_pool_size))); + + set_graceful_shutdown_hook(containers.clone()); let files = Staticfile::new(&root).expect("Unable to open root directory"); let mut files = Chain::new(files); @@ -71,8 +85,10 @@ fn main() { let mut mount = Mount::new(); mount.mount("/", files); - mount.mount("/compile", compile); - mount.mount("/execute", execute); + let containers_clone = containers.clone(); + mount.mount("/compile", move |req: &mut Request<'_, '_>| compile(req, &containers_clone)); + let containers_clone = containers.clone(); + mount.mount("/execute", move |req: &mut Request<'_, '_>| handle(req, &containers_clone)); mount.mount("/format", format); mount.mount("/clippy", clippy); mount.mount("/miri", miri); @@ -84,7 +100,8 @@ fn main() { mount.mount("/meta/version/clippy", meta_version_clippy); mount.mount("/meta/version/miri", meta_version_miri); mount.mount("/meta/gist", gist_router); - mount.mount("/evaluate.json", evaluate); + let containers_clone = containers.clone(); + mount.mount("/evaluate.json", move |req: &mut Request<'_, '_>| evaluate(req, &containers_clone)); let mut chain = Chain::new(mount); let file_logger = FileLogger::new(logfile).expect("Unable to create file logger"); @@ -135,21 +152,23 @@ impl iron::typemap::Key for GhToken { type Value = Self; } -fn compile(req: &mut Request<'_, '_>) -> IronResult { +fn compile(req: &mut Request<'_, '_>, containers: &Mutex) -> IronResult { with_sandbox(req, |sandbox, req: CompileRequest| { - let req = req.try_into()?; + let req: sandbox::CompileRequest = req.try_into()?; + let container = containers.lock().unwrap().pop(req.channel).unwrap(); sandbox - .compile(&req) + .compile(&req, &container) .map(CompileResponse::from) .eager_context(Compilation) }) } -fn execute(req: &mut Request<'_, '_>) -> IronResult { +fn handle(req: &mut Request<'_, '_>, containers: &Mutex) -> IronResult { with_sandbox(req, |sandbox, req: ExecuteRequest| { - let req = req.try_into()?; + let req: sandbox::ExecuteRequest = req.try_into()?; + let container = containers.lock().unwrap().pop(req.channel).unwrap(); sandbox - .execute(&req) + .execute(&req, &container) .map(ExecuteResponse::from) .eager_context(Execution) }) @@ -262,11 +281,12 @@ fn meta_gist_get(req: &mut Request<'_, '_>) -> IronResult { // This is a backwards compatibilty shim. The Rust homepage and the // documentation use this to run code in place. -fn evaluate(req: &mut Request<'_, '_>) -> IronResult { +fn evaluate(req: &mut Request<'_, '_>, containers: &Mutex) -> IronResult { with_sandbox(req, |sandbox, req: EvaluateRequest| { let req = req.try_into()?; + let container = containers.lock().unwrap().pop(Channel::Stable).unwrap(); sandbox - .execute(&req) + .execute(&req, &container) .map(EvaluateResponse::from) .eager_context(Evaluation) }) diff --git a/ui/src/sandbox.rs b/ui/src/sandbox.rs index 1dfe837a8..b8c8c954d 100644 --- a/ui/src/sandbox.rs +++ b/ui/src/sandbox.rs @@ -1,14 +1,18 @@ use serde_derive::Deserialize; use snafu::{ResultExt, Snafu}; use std::{ - collections::BTreeMap, + collections::{BTreeMap, HashMap}, ffi::OsStr, fmt, fs::{self, File}, io::{self, prelude::*, BufReader, BufWriter, ErrorKind}, path::{Path, PathBuf}, - process::Command, + process::{Command, ChildStdout, Stdio}, string, + sync::{Mutex, Arc}, + sync::mpsc::{self, Receiver, RecvError}, + time::Duration, + thread::{self, JoinHandle} }; use tempdir::TempDir; @@ -90,10 +94,10 @@ impl Sandbox { }) } - pub fn compile(&self, req: &CompileRequest) -> Result { - self.write_source_code(&req.code)?; + pub fn compile(&self, req: &CompileRequest, container: &DockerContainer) -> Result { + self.write_source_code_to_path(&container.src_dir, &req.crate_type, &req.code)?; - let mut command = self.compile_command(req.target, req.channel, req.mode, req.tests, req); + let mut command = self.compile_command(req.target, req.channel, req.mode, req.tests, req, container); let output = command.output().context(UnableToExecuteCompiler)?; @@ -101,7 +105,7 @@ impl Sandbox { // `compilation-3b75174cac3d47fb.ll`, so we just find the // first with the right extension. let file = - fs::read_dir(&self.output_dir) + fs::read_dir(&container.output_dir) .context(UnableToReadOutput)? .flat_map(|entry| entry) .map(|entry| entry.path()) @@ -143,9 +147,9 @@ impl Sandbox { }) } - pub fn execute(&self, req: &ExecuteRequest) -> Result { - self.write_source_code(&req.code)?; - let mut command = self.execute_command(req.channel, req.mode, req.tests, req); + pub fn execute(&self, req: &ExecuteRequest, container: &DockerContainer) -> Result { + self.write_source_code_to_path(&container.src_dir, &req.crate_type, &req.code)?; + let mut command = self.execute_command(req.channel, req.mode, req.tests, req, &container); let output = command.output().context(UnableToExecuteCompiler)?; @@ -279,26 +283,39 @@ impl Sandbox { Ok(()) } - fn compile_command(&self, target: CompileTarget, channel: Channel, mode: Mode, tests: bool, req: impl CrateTypeRequest + EditionRequest + BacktraceRequest) -> Command { - let mut cmd = self.docker_command(Some(req.crate_type())); - set_execution_environment(&mut cmd, Some(target), &req); + fn write_source_code_to_path(&self, src_dir: &PathBuf, crate_type: &CrateType, code: &str) -> Result<()> { + let data = code.as_bytes(); + + let input_file = src_dir.clone().join(crate_type.file_name()); + let file = File::create(&input_file).context(UnableToCreateSourceFile)?; + let mut file = BufWriter::new(file); + + file.write_all(data).context(UnableToCreateSourceFile)?; + + log::debug!("Wrote {} bytes of source to {:?}", data.len(), input_file.display()); + Ok(()) + } + + fn compile_command(&self, target: CompileTarget, channel: Channel, mode: Mode, tests: bool, req: impl CrateTypeRequest + EditionRequest + BacktraceRequest, container: &DockerContainer) -> Command { + let mut cmd = self.docker_exec_command(&container.id); let execution_cmd = build_execution_command(Some(target), channel, mode, &req, tests); + cmd.args(&execution_cmd); - cmd.arg(&channel.container_name()).args(&execution_cmd); + set_execution_environment(&mut cmd, Some(target), &req); log::debug!("Compilation command is {:?}", cmd); cmd } - fn execute_command(&self, channel: Channel, mode: Mode, tests: bool, req: impl CrateTypeRequest + EditionRequest + BacktraceRequest) -> Command { - let mut cmd = self.docker_command(Some(req.crate_type())); - set_execution_environment(&mut cmd, None, &req); + fn execute_command(&self, channel: Channel, mode: Mode, tests: bool, req: impl CrateTypeRequest + EditionRequest + BacktraceRequest, container: &DockerContainer) -> Command { + let mut cmd = self.docker_exec_command(&container.id); let execution_cmd = build_execution_command(None, channel, mode, &req, tests); + cmd.args(&execution_cmd); - cmd.arg(&channel.container_name()).args(&execution_cmd); + set_execution_environment(&mut cmd, None, &req); log::debug!("Execution command is {:?}", cmd); @@ -346,7 +363,7 @@ impl Sandbox { let mut mount_input_file = self.input_file.as_os_str().to_os_string(); mount_input_file.push(":"); - mount_input_file.push("/playground/"); + mount_input_file.push("/playground/src/"); mount_input_file.push(crate_type.file_name()); let mut mount_output_dir = self.output_dir.as_os_str().to_os_string(); @@ -361,6 +378,151 @@ impl Sandbox { cmd } + + fn docker_exec_command(&self, container_id: &str) -> Command { + let mut cmd = Command::new("docker"); + + cmd + .arg("exec") + .arg(container_id); + + cmd + } + +} + +pub struct DockerContainer { + pub id: String, + _temp_dir: TempDir, //we need to keep this, or it's dropped (= deleted) + pub src_dir: PathBuf, + pub output_dir: PathBuf, + terminated: Arc> +} + +impl DockerContainer { + + pub fn terminate(&mut self) -> JoinHandle<()> { + let mut terminated = self.terminated.lock().unwrap(); + *terminated = true; + + let container_id = self.id.clone(); + thread::spawn(move || { + let mut cmd = Command::new("docker"); + cmd + .arg("exec") + .arg(&container_id) + .args(&["pkill", "sleep"]) + .status() + .expect(&format!("Unable to kill container with id {}", container_id)); + }) + } + + // this was initially used to implement the Drop trait, keeping it for future reference + #[allow(dead_code)] + fn is_terminated(&self) -> bool { + let terminated = &self.terminated.clone(); + let terminated = terminated.lock().unwrap(); + *terminated + } + +} + +pub struct DockerContainers { + receivers: HashMap>>>, + active: Arc> +} + +impl DockerContainers { + pub fn new(pool_size: usize) -> DockerContainers { + let mut receivers = HashMap::new(); + + let active = Arc::new(Mutex::new(true)); + + for channel in [Channel::Stable, Channel::Beta, Channel::Nightly].iter() { + let (sender, receiver) = mpsc::sync_channel(pool_size); + + receivers.insert(channel.to_owned(), Arc::new(Mutex::new(receiver))); + + let active_mutex = active.clone(); + thread::spawn(move || { + let mut active = true; + while active { + sender.send(Self::start_container(channel)).unwrap_or(()); + active = *active_mutex.lock().unwrap(); + } + }); + } + + DockerContainers { receivers, active } + } + + fn container_id_from_stdout(stdout: ChildStdout) -> String { + let mut stdout = BufReader::new(stdout); + let mut container_id = String::new(); + stdout.read_line(&mut container_id).unwrap(); + + container_id.trim().to_string() + } + + fn start_container(channel: &Channel) -> DockerContainer { + let temp_dir = TempDir::new("playground_container").unwrap(); + let src_dir = temp_dir.path().to_owned(); + let output_dir = temp_dir.path().join("output"); + + let mut mount_src_dir = src_dir.as_os_str().to_os_string(); + mount_src_dir.push(":"); + mount_src_dir.push("/playground/src"); + + let mut mount_output_dir = output_dir.as_os_str().to_os_string(); + mount_output_dir.push(":"); + mount_output_dir.push("/playground-result"); + + let mut cmd = basic_secure_docker_command(); + cmd + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .arg("--volume").arg(&mount_src_dir) + .arg("--volume").arg(&mount_output_dir) + .arg("-d") + .arg(channel.container_name()); + + match cmd.spawn() { + Ok(child) => { + let id = Self::container_id_from_stdout(child.stdout.unwrap()); + + DockerContainer { id, _temp_dir: temp_dir, src_dir, output_dir, terminated: Arc::new(Mutex::new(false)) } + } + Err(err) => panic!("Error starting container {}: {}", channel.container_name(), err) + } + } + + pub fn pop(&self, channel: Channel) -> Result { + self.receivers.get(&channel).unwrap().lock().unwrap().recv() + } + + pub fn terminate(&self) { + log::info!("Shutting down docker containers pool..."); + let mut active = self.active.lock().unwrap(); + *active = false; + + let mut terminate_handles = Vec::new(); + for (_, receiver) in self.receivers.iter() { + while let Ok(mut container) = receiver.lock().unwrap().recv_timeout(Duration::new(1, 0)) { + terminate_handles.push(container.terminate()); + } + } + for handle in terminate_handles.into_iter() { + handle.join().expect("error when joining"); + } + } +} + +impl Drop for DockerContainers { + + fn drop(&mut self) { + self.terminate(); + } + } fn basic_secure_docker_command() -> Command { @@ -390,7 +552,7 @@ fn build_execution_command(target: Option, channel: Channel, mode use self::CrateType::*; use self::Mode::*; - let mut cmd = vec!["cargo"]; + let mut cmd = vec!["bash", "cargo.sh"]; match (target, req.crate_type(), tests) { (Some(Wasm), _, _) => cmd.push("wasm"), @@ -510,7 +672,7 @@ impl fmt::Display for CompileTarget { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum Channel { Stable, Beta, @@ -563,8 +725,8 @@ impl CrateType { use self::CrateType::*; match *self { - Binary => "src/main.rs", - Library(_) => "src/lib.rs", + Binary => "main.rs", + Library(_) => "lib.rs", } } } @@ -760,6 +922,8 @@ pub struct MiriResponse { #[cfg(test)] mod test { + use std::time::Duration; + use super::*; const HELLO_WORLD_CODE: &'static str = r#" @@ -812,7 +976,10 @@ mod test { let req = ExecuteRequest::default(); let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.execute(&req).expect("Unable to execute code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container).expect("Unable to execute code"); assert!(resp.stdout.contains("Hello, world!")); } @@ -837,7 +1004,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.execute(&req).expect("Unable to execute code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container).expect("Unable to execute code"); assert!(resp.stdout.contains("debug mode")); } @@ -851,7 +1021,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.execute(&req).expect("Unable to execute code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container).expect("Unable to execute code"); assert!(resp.stdout.contains("release mode")); } @@ -875,7 +1048,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.execute(&req).expect("Unable to execute code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container).expect("Unable to execute code"); assert!(resp.stdout.contains("rustc")); assert!(!resp.stdout.contains("beta")); @@ -891,7 +1067,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.execute(&req).expect("Unable to execute code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container).expect("Unable to execute code"); assert!(resp.stdout.contains("rustc")); assert!(resp.stdout.contains("beta")); @@ -907,7 +1086,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.execute(&req).expect("Unable to execute code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container).expect("Unable to execute code"); assert!(resp.stdout.contains("rustc")); assert!(!resp.stdout.contains("beta")); @@ -932,7 +1114,10 @@ mod test { }; let sb = Sandbox::new()?; - let resp = sb.execute(&req)?; + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container)?; assert!(resp.stderr.contains("`?` is not a macro repetition operator")); Ok(()) @@ -948,7 +1133,10 @@ mod test { }; let sb = Sandbox::new()?; - let resp = sb.execute(&req)?; + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container)?; assert!(resp.stderr.contains("`?` is not a macro repetition operator")); Ok(()) @@ -964,7 +1152,10 @@ mod test { }; let sb = Sandbox::new()?; - let resp = sb.execute(&req)?; + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container)?; assert!(!resp.stderr.contains("`crate` in paths is experimental")); Ok(()) @@ -989,7 +1180,10 @@ mod test { }; let sb = Sandbox::new()?; - let resp = sb.execute(&req)?; + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container)?; assert!(resp.stderr.contains("Run with `RUST_BACKTRACE=1` for a backtrace")); assert!(!resp.stderr.contains("stack backtrace:")); @@ -1006,7 +1200,10 @@ mod test { }; let sb = Sandbox::new()?; - let resp = sb.execute(&req)?; + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container)?; assert!(!resp.stderr.contains("Run with `RUST_BACKTRACE=1` for a backtrace")); assert!(resp.stderr.contains("stack backtrace:")); @@ -1022,7 +1219,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.compile(&req).expect("Unable to compile code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.compile(&req, &container).expect("Unable to compile code"); assert!(resp.code.contains("ModuleID")); assert!(resp.code.contains("target datalayout")); @@ -1037,7 +1237,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.compile(&req).expect("Unable to compile code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.compile(&req, &container).expect("Unable to compile code"); assert!(resp.code.contains(".text")); assert!(resp.code.contains(".file")); @@ -1053,7 +1256,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.compile(&req).expect("Unable to compile code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.compile(&req, &container).expect("Unable to compile code"); assert!(resp.code.contains("core::fmt::Arguments::new_v1")); assert!(resp.code.contains("std::io::stdio::_print@GOTPCREL")); @@ -1068,7 +1274,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.compile(&req).expect("Unable to compile code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.compile(&req, &container).expect("Unable to compile code"); assert!(resp.code.contains(".text")); assert!(resp.code.contains(".file")); @@ -1175,7 +1384,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.execute(&req).expect("Unable to execute code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container).expect("Unable to execute code"); assert!(resp.stdout.contains("Failed to connect")); } @@ -1196,7 +1408,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.execute(&req).expect("Unable to execute code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container).expect("Unable to execute code"); assert!(resp.stderr.contains("Killed")); } @@ -1216,7 +1431,10 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.execute(&req).expect("Unable to execute code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container).expect("Unable to execute code"); assert!(resp.stderr.contains("Killed")); } @@ -1241,8 +1459,21 @@ mod test { }; let sb = Sandbox::new().expect("Unable to create sandbox"); - let resp = sb.execute(&req).expect("Unable to execute code"); + let containers = DockerContainers::new(0); + let container = containers.pop(req.channel).unwrap(); + + let resp = sb.execute(&req, &container).expect("Unable to execute code"); assert!(resp.stderr.contains("Cannot fork")); } + + #[test] + fn docker_containers() { + let containers = DockerContainers::new(0); + thread::sleep(Duration::new(2, 0)); + + let mut container = containers.pop(Channel::Stable).unwrap(); + assert_eq!(false, container.id.is_empty()); + container.terminate().join().expect("error when joining"); + } }