-
Notifications
You must be signed in to change notification settings - Fork 0
WIP: transfer functionality from PkgEval #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 146 commits
913855b
e883599
25e85d0
7a8e22f
78ef1dd
5cfeaa1
faf9539
4387296
797a2e8
ed3d8e1
2a268c5
71d96c6
6d3792f
05ebea8
04cc397
b339f22
0096a8b
892a4aa
d138d5b
baf5c85
134e992
76997e4
62979bc
7358ee9
6f1d1e1
94d2e0a
900e62f
12b7457
00c28f5
3e1245f
8ad2cb0
77d24f7
5cd542a
450581f
8f88445
e4e65aa
0f1bb2b
9338e76
cb918e2
2b52836
911d0d1
c30acde
d58d08e
29e486b
70df891
c7f6471
f3bc5db
5e8ee25
007b6c3
d3a77ad
c67fa86
6b8298f
0ca5a27
75dec29
236200a
e518601
d243603
e2d1ca2
bd2e870
635b261
ec9c211
64e1fa3
3afe363
b9b6ca3
7837068
1afca23
333b454
a1530ae
c1ba1d2
9b99666
843a1a9
3bf53a6
249ab1e
917b55c
ff8cad3
ec028e6
c06b44a
353b6ff
d9e0cf2
1c37e63
c9aafcd
1e8ec03
64d5095
ef00113
6e34f45
b1e7af5
1bf6bb6
91c2e73
46cdcd7
c6f6f65
3bf4858
74841cf
c1810cc
873234e
72a0f4d
6d82265
96278a2
5fce5b1
fc1e748
f8c3b2b
00166a7
fd528ef
455384a
691c13e
914e2cd
bf4924c
70dcf0d
51b5eb0
d0cfa80
5eb85e6
26f99c9
4fdb76e
4f02530
c980cf7
8fb870b
75e5a4d
44bd5f6
face2ee
deaa40e
67650b9
8f9fbe8
f416a31
819f3c7
e681c28
3eda918
211b471
e6f5582
7f825ce
1c920e4
a71a5a3
088ad71
29f33b0
fdf7c08
ca3a8ce
eba7fe0
9b2d37a
7817749
49da8d4
fac18f8
0902923
90f2c28
340bfd8
830ce58
1954c42
572fb74
b54d22b
c3b07e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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] | ||
- uses: julia-actions/julia-runtest@latest |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Manifest.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" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. still points to PkgEval, I guess we should upload our own? |
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"] |
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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"))), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure we want to map over the whole |
||
)) | ||
|
||
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 |
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I needed |
There was a problem hiding this comment.
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