Skip to content
This repository was archived by the owner on Jun 27, 2018. It is now read-only.

Commit 4d9f710

Browse files
committed
Swap out playpen for docker
1 parent 9f28605 commit 4d9f710

15 files changed

+282
-83
lines changed

.travis.yml

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,19 @@
11
language: rust
2-
rust:
3-
- nightly
2+
sudo: required
3+
dist: trusty
4+
rust: stable
5+
services:
6+
- docker
7+
48
install:
59
- npm install jshint
10+
- pip install pygments
611
script:
712
- find static/ -iname "*.js" | xargs jshint
813
- cargo build
14+
- sh docker/build.sh
15+
- cargo test
16+
17+
notifications:
18+
email:
19+
on_success: never

Cargo.lock

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ staticfile = "0.2.0"
1616
toml = "0.1.30"
1717
unicase = "1.4.0"
1818
url = "1.1.1"
19+
wait-timeout = "0.1"
1920

2021
[dependencies.irc]
2122
default-features = false

docker/Dockerfile-beta

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM ubuntu:16.04
2+
3+
RUN apt-get update
4+
RUN apt-get install -y --no-install-recommends \
5+
gcc libc6-dev curl file ca-certificates
6+
COPY bin/compile.sh bin/evaluate.sh /usr/local/bin/
7+
COPY install.sh /tmp
8+
RUN sh /tmp/install.sh beta
9+
USER nobody
10+
11+
WORKDIR /tmp

docker/Dockerfile-nightly

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM ubuntu:16.04
2+
3+
RUN apt-get update
4+
RUN apt-get install -y --no-install-recommends \
5+
gcc libc6-dev curl file ca-certificates
6+
COPY bin/compile.sh bin/evaluate.sh /usr/local/bin/
7+
COPY install.sh /tmp
8+
RUN sh /tmp/install.sh nightly
9+
USER nobody
10+
11+
WORKDIR /tmp

docker/Dockerfile-stable

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
FROM ubuntu:16.04
2+
3+
RUN apt-get update
4+
RUN apt-get install -y --no-install-recommends \
5+
gcc libc6-dev curl file ca-certificates
6+
COPY bin/compile.sh bin/evaluate.sh /usr/local/bin/
7+
COPY install.sh /tmp
8+
RUN sh /tmp/install.sh stable
9+
USER nobody
10+
11+
WORKDIR /tmp

bin/compile.sh renamed to docker/bin/compile.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/dash
1+
#!/bin/dash
22

33
set -o errexit
44

bin/evaluate.sh renamed to docker/bin/evaluate.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/dash
1+
#!/bin/dash
22

33
set -o errexit
44

docker/build.sh

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/sh
2+
3+
for channel in stable beta nightly; do
4+
docker build \
5+
--no-cache \
6+
--force-rm \
7+
--pull \
8+
--rm \
9+
--tag rust-playpen-$channel \
10+
--file docker/Dockerfile-$channel \
11+
docker
12+
done

docker/install.sh

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/bin/sh
2+
3+
curl https://static.rust-lang.org/rustup.sh | \
4+
sh -s -- --disable-sudo --channel=$1 -y
5+
cargo install --debug rustfmt --root /usr/local

src/bin/playbot.rs

+9-11
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
1-
extern crate rust_playpen;
2-
31
#[macro_use] extern crate log;
42
extern crate env_logger;
3+
extern crate hyper;
54
extern crate irc;
5+
extern crate rust_playpen;
6+
extern crate rustc_serialize;
67
extern crate toml;
7-
extern crate hyper;
88
extern crate url;
9-
extern crate rustc_serialize;
10-
11-
use rust_playpen::ReleaseChannel;
12-
13-
use irc::client::prelude::*;
14-
use url::form_urlencoded;
15-
use hyper::client::Client;
16-
use rustc_serialize::json;
179

1810
use std::fs::{self, File};
1911
use std::io::{self, Read};
2012
use std::str;
2113
use std::u16;
2214
use std::thread;
2315

16+
use hyper::client::Client;
17+
use irc::client::prelude::*;
18+
use rust_playpen::ReleaseChannel;
19+
use rustc_serialize::json;
20+
use url::form_urlencoded;
21+
2422
static DEFAULT_CHANNEL: ReleaseChannel = ReleaseChannel::Stable;
2523

2624
fn get_rust_versions() -> Vec<String> {

src/bin/playpen.rs

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
extern crate rust_playpen;
2-
31
#[macro_use] extern crate iron;
42
#[macro_use] extern crate log;
53
extern crate env_logger;
64
extern crate hyper;
7-
extern crate staticfile;
85
extern crate router;
9-
extern crate unicase;
6+
extern crate rust_playpen;
107
extern crate rustc_serialize;
8+
extern crate staticfile;
9+
extern crate unicase;
1110

1211
use rust_playpen::*;
1312

@@ -100,8 +99,10 @@ fn evaluate(req: &mut Request) -> IronResult<Response> {
10099
args.push(String::from("--test"));
101100
}
102101

103-
let (_status, output) = itry!(
104-
rust_playpen::exec(version, "/usr/local/bin/evaluate.sh", args, data.code));
102+
let (_status, output) = itry!(rust_playpen::exec(version,
103+
"/usr/local/bin/evaluate.sh",
104+
args,
105+
data.code));
105106

106107
let mut obj = json::Object::new();
107108
if separate_output {
@@ -200,7 +201,7 @@ fn format(req: &mut Request) -> IronResult<Response> {
200201
let version = itry!(data.version.map(|v| v.parse()).unwrap_or(Ok(ReleaseChannel::Stable)));
201202

202203
let (status, output) = itry!(
203-
rust_playpen::exec(version, "/usr/bin/rustfmt", Vec::new(), data.code));
204+
rust_playpen::exec(version, "rustfmt", Vec::new(), data.code));
204205
let output = String::from_utf8(output).unwrap();
205206
let mut response_obj = json::Object::new();
206207
if status.success() {
@@ -244,7 +245,7 @@ fn main() {
244245
let mut chain = Chain::new(router);
245246
chain.link_after(EnablePostCors);
246247

247-
let addr = ("0.0.0.0", 80);
248-
info!("listening on {:?}", addr);
248+
let addr = ("127.0.0.1", 8080);
249+
println!("listening on {:?}", addr);
249250
Iron::new(chain).http(addr).unwrap();
250251
}

src/docker.rs

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
use std::io;
2+
use std::io::prelude::*;
3+
use std::mem;
4+
use std::process::{Output, Command, Stdio, ExitStatus};
5+
use std::sync::{Arc, Mutex};
6+
use std::thread;
7+
use std::time::Duration;
8+
9+
use wait_timeout::ChildExt;
10+
11+
pub struct Container {
12+
id: String,
13+
}
14+
15+
impl Container {
16+
pub fn new(cmd: &str,
17+
args: &[String],
18+
name: &str) -> io::Result<Container> {
19+
let out = try!(run(Command::new("docker")
20+
.arg("create")
21+
.arg("--cap-drop=ALL")
22+
.arg("--memory=128m")
23+
.arg("--net=none")
24+
.arg("--pids-limit=5")
25+
.arg("--security-opt=no-new-privileges")
26+
.arg("--interactive")
27+
.arg(name)
28+
.arg(cmd)
29+
.stderr(Stdio::inherit())
30+
.args(args)));
31+
let stdout = String::from_utf8_lossy(&out.stdout);
32+
Ok(Container {
33+
id: stdout.trim().to_string(),
34+
})
35+
}
36+
37+
pub fn run(&self,
38+
input: &[u8],
39+
timeout: Duration)
40+
-> io::Result<(ExitStatus, Vec<u8>, bool)> {
41+
let mut cmd = Command::new("docker");
42+
cmd.arg("start")
43+
.arg("--attach")
44+
.arg("--interactive")
45+
.arg(&self.id)
46+
.stdin(Stdio::piped())
47+
.stdout(Stdio::piped())
48+
.stderr(Stdio::piped());
49+
debug!("attaching with {:?}", cmd);
50+
let mut cmd = try!(cmd.spawn());
51+
try!(cmd.stdin.take().unwrap().write_all(input));
52+
debug!("input written, now waiting");
53+
54+
let mut stdout = cmd.stdout.take().unwrap();
55+
let mut stderr = cmd.stderr.take().unwrap();
56+
let sink = Arc::new(Mutex::new(Vec::new()));
57+
let sink2 = sink.clone();
58+
let stdout = thread::spawn(move || append(&sink2, &mut stdout));
59+
let sink2 = sink.clone();
60+
let stderr = thread::spawn(move || append(&sink2, &mut stderr));
61+
62+
let (status, timeout) = match try!(cmd.wait_timeout(timeout)) {
63+
Some(status) => {
64+
debug!("finished before timeout");
65+
// TODO: document this
66+
(unsafe { mem::transmute(status) }, false)
67+
}
68+
None => {
69+
debug!("timeout, going to kill");
70+
try!(run(Command::new("docker").arg("kill").arg(&self.id)));
71+
(try!(cmd.wait()), true)
72+
}
73+
};
74+
stdout.join().unwrap();
75+
stderr.join().unwrap();
76+
let mut lock = sink.lock().unwrap();
77+
let output = mem::replace(&mut *lock, Vec::new());
78+
debug!("status: {}", status);
79+
debug!("output: {}", String::from_utf8_lossy(&output));
80+
Ok((status, output, timeout))
81+
}
82+
}
83+
84+
fn append(into: &Mutex<Vec<u8>>, from: &mut Read) {
85+
let mut buf = [0; 1024];
86+
while let Ok(amt) = from.read(&mut buf) {
87+
if amt == 0 {
88+
break
89+
}
90+
into.lock().unwrap().extend_from_slice(&buf[..amt]);
91+
}
92+
}
93+
94+
impl Drop for Container {
95+
fn drop(&mut self) {
96+
run(Command::new("docker")
97+
.arg("rm")
98+
.arg("--force")
99+
.arg(&self.id)).unwrap();
100+
}
101+
}
102+
103+
fn run(cmd: &mut Command) -> io::Result<Output> {
104+
debug!("spawning: {:?}", cmd);
105+
let out = try!(cmd.output());
106+
debug!("output: {:?}", out);
107+
if !out.status.success() {
108+
let msg = format!("process failed: {:?}\n{:?}", cmd, out);
109+
return Err(io::Error::new(io::ErrorKind::Other, msg))
110+
}
111+
Ok(out)
112+
}

0 commit comments

Comments
 (0)