Skip to content
Open
Show file tree
Hide file tree
Changes from 146 commits
Commits
Show all changes
147 commits
Select commit Hold shift + click to select a range
913855b
Initial empty commit
Keno Jun 23, 2018
e883599
Split some functionality off into a file.
maleadt Nov 20, 2019
25e85d0
Multi-registry bugfix.
maleadt Nov 20, 2019
7a8e22f
Fix stream redirection, and block reading from stdin.
maleadt Nov 20, 2019
78ef1dd
Keep track if timeouts.
maleadt Nov 20, 2019
5cfeaa1
Use compact printing on CI.
maleadt Nov 20, 2019
faf9539
Make it possible to download without hash.
maleadt Nov 20, 2019
4387296
Refer to versions by string to avoid potential confusion.
maleadt Nov 20, 2019
797a2e8
Merge pull request #25 from JuliaComputing/tb
maleadt Nov 21, 2019
ed3d8e1
Create separate method for downloading builds that don't map to speci…
maleadt Nov 21, 2019
2a268c5
Provide a higher-level run function for simplified use.
maleadt Nov 21, 2019
71d96c6
Replace result dict argument with a callback.
maleadt Nov 21, 2019
6d3792f
Parse additional failure statusses from the log.
maleadt Nov 21, 2019
05ebea8
Log package version and run number in the database.
maleadt Nov 21, 2019
04cc397
Use and propagate missing to get NULL values in the db.
maleadt Nov 21, 2019
b339f22
Separate the error mode from the reason.
maleadt Nov 21, 2019
0096a8b
Report killed jobs.
maleadt Nov 21, 2019
892a4aa
Classify some more failure reasons.
maleadt Nov 21, 2019
d138d5b
Merge pull request #28 from JuliaComputing/tb/download
maleadt Nov 21, 2019
baf5c85
Whitespace fix.
maleadt Nov 22, 2019
134e992
Use Docker for sandboxing.
maleadt Nov 22, 2019
76997e4
Bugfix in output counts.
maleadt Nov 22, 2019
62979bc
Use prepare instead of obtain/get for similar methods.
maleadt Nov 22, 2019
7358ee9
Don't unconditionally use a colored prompt.
maleadt Nov 22, 2019
6f1d1e1
Squeeze two worker statuses on a single line of output.
maleadt Nov 22, 2019
94d2e0a
Add a progress bar.
maleadt Nov 22, 2019
900e62f
Tweak output.
maleadt Nov 22, 2019
12b7457
Kill the container instead of the process.
maleadt Nov 22, 2019
00c28f5
Simplify test script.
maleadt Nov 22, 2019
3e1245f
Use a container name.
maleadt Nov 22, 2019
8ad2cb0
Merge pull request #29 from JuliaComputing/tb/docker
maleadt Nov 22, 2019
77d24f7
Simplify run methods, use Pkg API to update registry.
maleadt Nov 23, 2019
5cd542a
Separate logic in different run functions, and check Julia version co…
maleadt Nov 23, 2019
450581f
Use empty array of names for sentinal of all packages.
maleadt Nov 23, 2019
8f88445
Extend the run interface to accept a list of Julia versions.
maleadt Nov 23, 2019
e4e65aa
Better version compatibility check.
maleadt Nov 24, 2019
0f1bb2b
Don't write a missing log.
maleadt Nov 24, 2019
9338e76
Skip jll packages.
maleadt Nov 24, 2019
cb918e2
Merge pull request #30 from JuliaComputing/tb/run
maleadt Nov 25, 2019
2b52836
Simplify by using DataFrames.
maleadt Nov 25, 2019
911d0d1
Reporting functionality.
maleadt Nov 25, 2019
c30acde
Merge pull request #31 from JuliaComputing/tb/report
maleadt Dec 2, 2019
d58d08e
Rephrase.
maleadt Dec 3, 2019
29e486b
Bump time limit.
maleadt Dec 3, 2019
70df891
Report the test environment.
maleadt Dec 3, 2019
c7f6471
Merge pull request #32 from JuliaComputing/tb/work
maleadt Dec 3, 2019
f3bc5db
Quiet container build.
maleadt Dec 4, 2019
5e8ee25
Kill containers using docker stop.
maleadt Dec 4, 2019
007b6c3
Don't report the Docker hostname (container ID).
maleadt Dec 4, 2019
d3a77ad
Put the UUID in the results, and leave the registry out.
maleadt Dec 6, 2019
c67fa86
Report the full version info.
maleadt Dec 6, 2019
6b8298f
Add packages and detect more failures.
maleadt Dec 6, 2019
0ca5a27
Remove assertion to avoid racy behavior.
maleadt Dec 10, 2019
75dec29
Lower log file limit to 1MB.
maleadt Dec 10, 2019
236200a
Fix the registry mount point, make it user independent.
maleadt Dec 11, 2019
e518601
Preserve read-only mounts, not touching the host system.
maleadt Dec 11, 2019
d243603
Support for retrying packages.
maleadt Dec 20, 2019
e2d1ca2
Detect SIGABRT.
maleadt Dec 20, 2019
bd2e870
Detect GC corruption.
maleadt Dec 20, 2019
635b261
Don't write logs to disk.
maleadt Dec 20, 2019
ec9c211
Merge pull request #43 from JuliaComputing/tb/retry
maleadt Dec 20, 2019
64e1fa3
Merge pull request #45 from JuliaComputing/tb/rm_logs
maleadt Dec 20, 2019
3afe363
Merge pull request #44 from JuliaComputing/tb/sigabrt
maleadt Dec 20, 2019
b9b6ca3
Add some randomness to the container name to avoid conflicts.
maleadt Dec 21, 2019
7837068
Merge pull request #49 from JuliaComputing/tb/rand_name
maleadt Dec 21, 2019
1afca23
Set the environment variable "JULIAPKGEVAL" to "true"
DilumAluthge Dec 22, 2019
333b454
Rename `JULIAPKGEVAL` to `JULIA_PKGEVAL`
DilumAluthge Dec 31, 2019
a1530ae
Guard against bad version number parsing.
maleadt Jan 3, 2020
c1ba1d2
Merge pull request #56 from JuliaComputing/tb/build
maleadt Jan 3, 2020
9b99666
Run Julia under `xvfb-run`
DilumAluthge Dec 31, 2019
843a1a9
Use tmpfs for all container operations.
maleadt Jan 13, 2020
3bf53a6
Restrict to a single CPU per job.
maleadt Jan 13, 2020
249ab1e
Make configurable, and limit to 2 CPUs instead.
maleadt Jan 14, 2020
917b55c
Merge pull request #62 from JuliaComputing/tb/tmpfs
maleadt Jan 14, 2020
ff8cad3
Remove some latency between finishing tests and displaying results.
maleadt Jan 14, 2020
ec028e6
Merge pull request #63 from JuliaComputing/tb/latency
maleadt Jan 14, 2020
c06b44a
Make the retry count configurable.
maleadt Jan 15, 2020
353b6ff
Merge pull request #65 from JuliaComputing/tb/precompile
maleadt Jan 15, 2020
d9e0cf2
Add GPU support.
maleadt Mar 6, 2020
1c37e63
Show Docker output when running with debug output.
maleadt Mar 23, 2020
c9aafcd
Merge pull request #69 from JuliaComputing/tb/ci
maleadt Mar 23, 2020
1e8ec03
Put mutable state in the cache dir, and use it to better prune old bu…
maleadt Mar 20, 2020
64d5095
Cache artifacts across runs.
maleadt Mar 20, 2020
ef00113
Fix artifact cache permissions.
maleadt Mar 24, 2020
6e34f45
Merge pull request #68 from JuliaComputing/tb/cache
maleadt Mar 24, 2020
b1e7af5
Extract Julia in temporary dir, not the cache.
maleadt May 13, 2020
1bf6bb6
Merge pull request #73 from JuliaComputing/tb/extract_tempdir
maleadt May 13, 2020
91c2e73
Set `ENV["PYTHON"] = ""` and `ENV["R_HOME"] = "*"`
DilumAluthge Aug 27, 2020
46cdcd7
Merge pull request #74 from DilumAluthge/patch-1
maleadt Aug 28, 2020
c6f6f65
Fix use of Pkg API on Julia 1.5
maleadt Sep 1, 2020
3bf4858
Simplify GPU check.
maleadt Oct 2, 2020
74841cf
Silence container stop commands.
maleadt Oct 2, 2020
c1810cc
Explicitly stop work after workers finish.
maleadt Oct 2, 2020
873234e
Save the compilation cache between runs.
maleadt Oct 9, 2020
72a0f4d
Generalize handling of compilecache/artifacts into global storage and…
maleadt Oct 9, 2020
6d82265
Merge pull request #77 from JuliaComputing/tb/compilecache
maleadt Oct 20, 2020
96278a2
Query docker container statistics.
maleadt Oct 27, 2020
5fce5b1
Timestamp the log.
maleadt Oct 27, 2020
fc1e748
Verify stats API code.
maleadt Oct 27, 2020
f8c3b2b
Disable compile caching.
maleadt Oct 27, 2020
00166a7
Forward host PKG server.
maleadt Oct 27, 2020
fd528ef
Compat with 1.3.
maleadt Oct 27, 2020
455384a
Protect against missing stats keys.
maleadt Oct 28, 2020
691c13e
Simplify container start-up.
maleadt Oct 28, 2020
914e2cd
Don't continuously monitor stats, only collect at the end.
maleadt Oct 28, 2020
bf4924c
Simplify, and always show raw statistics.
maleadt Oct 28, 2020
70dcf0d
NFC clean-ups.
maleadt Oct 28, 2020
51b5eb0
Don't automatically precompile all packages.
maleadt Oct 28, 2020
d0cfa80
Note absence of stats.
maleadt Oct 28, 2020
5eb85e6
Have the container sleep so that we can more reliably pick up stats.
maleadt Oct 28, 2020
26f99c9
Factor-out container stats function.
maleadt Oct 28, 2020
4fdb76e
Detect container inactivity.
maleadt Oct 28, 2020
4f02530
Try closing the output stream to work around docker run blocking.
maleadt Oct 28, 2020
c980cf7
More robust inactivity check.
maleadt Oct 28, 2020
8fb870b
Specify allowed CPUs using cpuset.
maleadt Oct 28, 2020
75e5a4d
Be less aggressive about killing 'inactive' tests.
maleadt Oct 28, 2020
44bd5f6
Properly close the input pipe.
maleadt Nov 3, 2020
face2ee
Fix error matching regexes.
maleadt Nov 3, 2020
deaa40e
Lock container operations to prevent races.
maleadt Nov 12, 2020
67650b9
Try closing the output stream before stopping the container.
maleadt Nov 12, 2020
8f9fbe8
Rename package and update gitignore.
maleadt Dec 2, 2020
f416a31
Simplify the test script.
maleadt Dec 2, 2020
819f3c7
Remove an unused option.
maleadt Dec 2, 2020
e681c28
Read the log tail without fail
vtjnash Feb 17, 2021
3eda918
Don't forget to close input end of output after connecting to stdout and
vtjnash Feb 20, 2021
211b471
Make failure to stop a container non-fatal.
maleadt Apr 14, 2021
e6f5582
Update for Julia 1.6.
maleadt Apr 14, 2021
7f825ce
Merge pull request #89 from JuliaCI/tb/julia_1.6
maleadt Apr 15, 2021
1c920e4
Introduce a Config object, and other clean-ups.
maleadt Dec 2, 2020
a71a5a3
Merge pull request #90 from JuliaCI/tb/config
maleadt Apr 15, 2021
088ad71
Replace Docker with Sandbox.jl.
maleadt Apr 16, 2021
29f33b0
Graceful process terminator that doesn't just kill the sandbox.
maleadt Apr 16, 2021
fdf7c08
Work around misdetection of CPU threads.
maleadt Apr 16, 2021
ca3a8ce
Ensure the artifacts storage exist.
maleadt Apr 16, 2021
eba7fe0
Don't put the actual Configuration object in the output dataframe.
maleadt Apr 16, 2021
9b2d37a
Fixes for package compatibility.
maleadt Apr 19, 2021
7817749
Add a user to the rootfs.
maleadt Apr 20, 2021
49da8d4
Use systemd-run for resource management.
maleadt Apr 20, 2021
fac18f8
Revert "Use systemd-run for resource management."
maleadt Apr 21, 2021
0902923
Merge pull request #91 from JuliaCI/tb/sandbox
maleadt Apr 21, 2021
90f2c28
Merge branch 'main' of https://github.com/JuliaContainerization/Sandb…
ericphanson May 5, 2021
340bfd8
rm stuff, put into a new package
ericphanson May 5, 2021
830ce58
rename
ericphanson May 5, 2021
1954c42
make more standalone
ericphanson May 5, 2021
572fb74
get example running
ericphanson May 6, 2021
b54d22b
add simple test; add CI from PkgEval
ericphanson May 6, 2021
c3b07e0
Update ci.yml
ericphanson May 6, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: CI
on:
push:
branches:
- main
pull_request:
- main
jobs:
test:
name: Julia ${{ matrix.version }} - ${{ matrix.build_spec }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
version:
- '1.6'
os:
- ubuntu-latest
arch:
- x64
env:
JULIA_DEBUG: SandboxJulia
steps:
- uses: actions/checkout@v2
- uses: julia-actions/setup-julia@v1
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: actions/cache@v1
env:
cache-name: cache-artifacts
with:
path: ~/.julia/artifacts
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
restore-keys: |
${{ runner.os }}-test-${{ env.cache-name }}-
${{ runner.os }}-test-
${{ runner.os }}-
- uses: julia-actions/julia-buildpkg@latest
- run: |
git config --global user.name Tester
git config --global user.email [email protected]
Comment on lines +41 to +43
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if we needed this; copied from PkgEval

- uses: julia-actions/julia-runtest@latest
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Manifest.toml
15 changes: 15 additions & 0 deletions Artifacts.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[alpine]
git-tree-sha1 = "562768a40e93d27b79fbedf9cfa7883409d494ea"
lazy = true

[[alpine.download]]
sha256 = "5a588162779446d8e5235bf6fc97588d1e197f44cf64e3a1f88ae828270456a7"
url = "https://github.com/alpinelinux/docker-alpine/raw/2f3c3015951938c521e752899a92ecd618e0c05b/x86_64/alpine-minirootfs-3.12.4-x86_64.tar.gz"

[debian]
git-tree-sha1 = "629416ae3d28494fea097fedc57d0fdd748731e3"
lazy = true

[[debian.download]]
sha256 = "3c3c78a1b15490bfdc29cfd10a72f5f16195ccf64ffc4cdfb445cad387ea5b50"
url = "https://github.com/JuliaCI/PkgEval.jl/releases/download/v0.1/debian-buster-20210420.tar.xz"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still points to PkgEval, I guess we should upload our own?

13 changes: 13 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name = "SandboxJulia"
uuid = "00b7dd00-052f-4550-80aa-b019f336a5ea"
version = "0.1.0"

[deps]
LazyArtifacts = "4af54fe1-eca0-43a8-85a7-787d91b784e3"
Sandbox = "9307e30f-c43e-9ca7-d17c-c2dc59df670d"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]
180 changes: 180 additions & 0 deletions src/SandboxJulia.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
module SandboxJulia

using LazyArtifacts, Sandbox
export run_sandboxed_julia

isdebug(group) = Base.CoreLogging.current_logger_for_env(Base.CoreLogging.Debug, group, SandboxJulia) !== nothing

lazy_artifact(x) = @artifact_str(x)

const rootfs_lock = ReentrantLock()
const rootfs_cache = Dict()
function prepare_rootfs(distro="debian"; uid=1000, user="sandboxjulia", gid=1000, group="sandboxjulia", home="/home/$user")
lock(rootfs_lock) do
get!(rootfs_cache, (distro, uid, user, gid, group, home)) do
base = lazy_artifact(distro)

# a bare rootfs isn't usable out-of-the-box
derived = mktempdir()
cp(base, derived; force=true)

# add a user and group
chmod(joinpath(derived, "etc/passwd"), 0o644)
open(joinpath(derived, "etc/passwd"), "a") do io
println(io, "$user:x:$uid:$gid::$home:/bin/bash")
end
chmod(joinpath(derived, "etc/group"), 0o644)
open(joinpath(derived, "etc/group"), "a") do io
println(io, "$group:x:$gid:")
end
chmod(joinpath(derived, "etc/shadow"), 0o640)
open(joinpath(derived, "etc/shadow"), "a") do io
println(io, "$user:*:::::::")
end

# replace resolv.conf
rm(joinpath(derived, "etc/resolv.conf"); force=true)
write(joinpath(derived, "etc/resolv.conf"), read("/etc/resolv.conf"))

return (path=derived, uid, user, gid, group, home)
end
end
end

"""
run_sandboxed_julia(install::String, args=``; env=Dict(), mounts=Dict(),
wait=true, stdin=stdin, stdout=stdout, stderr=stderr,
install_dir="/opt/julia", kwargs...)
Comment on lines +45 to +47
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

out of date


Run Julia inside of a sandbox, passing the given arguments `args` to it. The argument `wait`
determines if the process will be waited on. Streams can be connected using the `stdin`,
`stdout` and `sterr` arguments. Returns a `Process` object.

Further customization is possible using the `env` arg, to set environment variables, and the
`mounts` argument to mount additional directories. With `install_dir`, the directory where
Julia is installed can be chosen.
"""
function run_sandboxed_julia(args=``; wait=true,
registries_dir=joinpath(first(DEPOT_PATH), "registries"),
storage_dir=mktempdir(),
install::String=dirname(dirname(Sys.which("julia"))),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a positional argument without a default in PkgEval. Probably I should move it back to that? This was convenient though

mounts::Dict{String,String}=Dict{String,String}(),
privileged=false,
kwargs...)
config, cmd = runner_sandboxed_julia(install, args; storage_dir, registries_dir, kwargs...)

# XXX: even when preferred_executor() returns UnprivilegedUserNamespacesExecutor,
# sometimes a stray sudo happens at run time? no idea how.
exe_typ = privileged ? PrivilegedUserNamespacesExecutor : UnprivilegedUserNamespacesExecutor
exe = exe_typ()
proc = Base.run(exe, config, cmd; wait)

# TODO: introduce a --stats flag that has the sandbox trace and report on CPU, network, ... usage

if wait
cleanup(exe)
else
@async begin
try
Base.wait(proc)
cleanup(exe)
catch err
@error "Unexpected error while cleaning up process" exception=(err, catch_backtrace())
end
end
end

return proc
end

# global Xvfb process for use by all containers
const xvfb_lock = ReentrantLock()
const xvfb_proc = Ref{Union{Base.Process,Nothing}}(nothing)


function installed_julia_dir(jp)
jp_contents = readdir(jp)
# Allow the unpacked directory to either be insider another directory (as produced by
# the buildbots) or directly inside the mapped directory (as produced by the BB script)
if length(jp_contents) == 1
jp = joinpath(jp, first(jp_contents))
end
jp
end

function runner_sandboxed_julia(install::String, args=``; registries_dir,
storage_dir,
dot_julia_path = mktempdir(),
install_dir="/opt/julia",
stdin=stdin, stdout=stdout, stderr=stderr,
env::Dict{String,String}=Dict{String,String}(),
mounts::Dict{String,String}=Dict{String,String}(),
xvfb::Bool=true, cpus::Vector{Int}=Int[])
julia_path = installed_julia_dir(install)
rootfs = prepare_rootfs()
read_only_maps = Dict(
"/" => rootfs.path,
install_dir => julia_path,
"/usr/local/share/julia/registries" => registries_dir,
)

artifacts_path = joinpath(storage_dir, "artifacts")
mkpath(artifacts_path)
read_write_maps = merge(mounts, Dict(
joinpath(rootfs.home, ".julia/artifacts") => artifacts_path,
joinpath(rootfs.home, ".julia") => dot_julia_path
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we want to map over the whole .julia but I would get permissions errors otherwise. It's not clear to me if read-write means the rootfs is read-only except for the write-mapped bits, or if it means these are the only directories that the code in the sandbox can write to on the host filesystem.

))

env = merge(env, Dict(
# use the provided registry
# NOTE: putting a registry in a non-primary depot entry makes Pkg use it as-is,
# without needingb to set Pkg.UPDATED_REGISTRY_THIS_SESSION.
"JULIA_DEPOT_PATH" => "::/usr/local/share/julia",

# some essential env vars (since we don't run from a shell)
"PATH" => "/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin",
"HOME" => rootfs.home,
))
if haskey(ENV, "TERM")
env["TERM"] = ENV["TERM"]
end

if xvfb
lock(xvfb_lock) do
if xvfb_proc[] === nothing || !process_running(xvfb_proc[])
proc = Base.run(`Xvfb :1 -screen 0 1024x768x16`; wait=false)
sleep(1)
process_running(proc) || error("Could not start Xvfb")

xvfb_proc[] === nothing && atexit() do
kill(xvfb_proc[])
wait(xvfb_proc[])
end
xvfb_proc[] = proc
end
end

env["DISPLAY"] = ":1"
read_write_maps["/tmp/.X11-unix"] = "/tmp/.X11-unix"
end

cmd = `$install_dir/bin/julia`

# restrict resource usage
if !isempty(cpus)
cmd = `/usr/bin/taskset --cpu-list $(join(cpus, ',')) $cmd`
env["JULIA_CPU_THREADS"] = string(length(cpus)) # JuliaLang/julia#35787
end

# NOTE: we use persist=true so that modifications to the rootfs are backed by
# actual storage on the host, and not just the (1G hard-coded) tmpfs,
# because some packages like to generate a lot of data during testing.

config = SandboxConfig(read_only_maps, read_write_maps, env;
rootfs.uid, rootfs.gid, pwd=rootfs.home, persist=true,
stdin, stdout, stderr, verbose=isdebug(:sandbox))

return config, `$cmd $args`
end

end # module
9 changes: 9 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Test, SandboxJulia

script = """
using Pkg;
Pkg.add("Example")
using Example
"""

run_sandboxed_julia(`-e $script`; privileged=true)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I needed privileged=true to run this on my EC2 instance