| #!/usr/bin/env bats |
| |
| load helpers |
| |
| function setup() { |
| setup_busybox |
| } |
| |
| function teardown() { |
| teardown_bundle |
| } |
| |
| @test "runc exec" { |
| # run busybox detached |
| runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| [ "$status" -eq 0 ] |
| |
| runc exec test_busybox echo Hello from exec |
| [ "$status" -eq 0 ] |
| echo text echoed = "'""${output}""'" |
| [[ "${output}" == *"Hello from exec"* ]] |
| } |
| |
| @test "runc exec [exit codes]" { |
| runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| [ "$status" -eq 0 ] |
| |
| runc exec test_busybox false |
| [ "$status" -eq 1 ] |
| |
| runc exec test_busybox sh -c "exit 42" |
| [ "$status" -eq 42 ] |
| |
| runc exec --pid-file /non-existent/directory test_busybox true |
| [ "$status" -eq 255 ] |
| |
| runc exec test_busybox no-such-binary |
| [ "$status" -eq 255 ] |
| |
| runc exec no_such_container true |
| [ "$status" -eq 255 ] |
| } |
| |
| @test "runc exec --pid-file" { |
| # run busybox detached |
| runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| [ "$status" -eq 0 ] |
| |
| runc exec --pid-file pid.txt test_busybox echo Hello from exec |
| [ "$status" -eq 0 ] |
| echo text echoed = "'""${output}""'" |
| [[ "${output}" == *"Hello from exec"* ]] |
| |
| # check pid.txt was generated |
| [ -e pid.txt ] |
| |
| output=$(cat pid.txt) |
| [[ "$output" =~ [0-9]+ ]] |
| [[ "$output" != $(__runc state test_busybox | jq '.pid') ]] |
| } |
| |
| @test "runc exec --pid-file with new CWD" { |
| bundle="$(pwd)" |
| # create pid_file directory as the CWD |
| mkdir pid_file |
| cd pid_file |
| |
| # run busybox detached |
| runc run -d -b "$bundle" --console-socket "$CONSOLE_SOCKET" test_busybox |
| [ "$status" -eq 0 ] |
| |
| runc exec --pid-file pid.txt test_busybox echo Hello from exec |
| [ "$status" -eq 0 ] |
| echo text echoed = "'""${output}""'" |
| [[ "${output}" == *"Hello from exec"* ]] |
| |
| # check pid.txt was generated |
| [ -e pid.txt ] |
| |
| output=$(cat pid.txt) |
| [[ "$output" =~ [0-9]+ ]] |
| [[ "$output" != $(__runc state test_busybox | jq '.pid') ]] |
| } |
| |
| @test "runc exec ls -la" { |
| # run busybox detached |
| runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| [ "$status" -eq 0 ] |
| |
| runc exec test_busybox ls -la |
| [ "$status" -eq 0 ] |
| [[ ${lines[0]} == *"total"* ]] |
| [[ ${lines[1]} == *"."* ]] |
| [[ ${lines[2]} == *".."* ]] |
| } |
| |
| @test "runc exec ls -la with --cwd" { |
| # run busybox detached |
| runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| [ "$status" -eq 0 ] |
| |
| runc exec --cwd /bin test_busybox pwd |
| [ "$status" -eq 0 ] |
| [[ ${output} == "/bin"* ]] |
| } |
| |
| @test "runc exec --env" { |
| # run busybox detached |
| runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| [ "$status" -eq 0 ] |
| |
| runc exec --env RUNC_EXEC_TEST=true test_busybox env |
| [ "$status" -eq 0 ] |
| |
| [[ ${output} == *"RUNC_EXEC_TEST=true"* ]] |
| } |
| |
| @test "runc exec --user" { |
| # --user can't work in rootless containers that don't have idmap. |
| [ $EUID -ne 0 ] && requires rootless_idmap |
| |
| # run busybox detached |
| runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| [ "$status" -eq 0 ] |
| |
| runc exec --user 1000:1000 test_busybox id |
| [ "$status" -eq 0 ] |
| [[ "${output}" == "uid=1000 gid=1000"* ]] |
| } |
| |
| # https://github.com/opencontainers/runc/issues/3674. |
| @test "runc exec --user vs /dev/null ownership" { |
| requires root |
| |
| runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| [ "$status" -eq 0 ] |
| |
| ls -l /dev/null |
| __runc exec -d --user 1000:1000 test_busybox id </dev/null |
| ls -l /dev/null |
| UG=$(stat -c %u:%g /dev/null) |
| |
| # Host's /dev/null must be owned by root. |
| [ "$UG" = "0:0" ] |
| } |
| |
| @test "runc exec --additional-gids" { |
| requires root |
| |
| # run busybox detached |
| runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| [ "$status" -eq 0 ] |
| |
| wait_for_container 15 1 test_busybox |
| |
| runc exec --user 1000:1000 --additional-gids 100 --additional-gids 65534 test_busybox id -G |
| [ "$status" -eq 0 ] |
| [ "$output" = "1000 100 65534" ] |
| } |
| |
| @test "runc exec --preserve-fds" { |
| # run busybox detached |
| runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| [ "$status" -eq 0 ] |
| |
| echo hello >preserve-fds.test |
| # fd 3 is used by bats, so we use 4 |
| exec 4<preserve-fds.test |
| runc exec --preserve-fds=2 test_busybox cat /proc/self/fd/4 |
| [ "$status" -eq 0 ] |
| [ "${output}" = "hello" ] |
| } |
| |
| function check_exec_debug() { |
| [[ "$*" == *"nsexec container setup"* ]] |
| [[ "$*" == *"child process in init()"* ]] |
| [[ "$*" == *"setns_init: about to exec"* ]] |
| } |
| |
| @test "runc --debug exec" { |
| runc run -d --console-socket "$CONSOLE_SOCKET" test |
| [ "$status" -eq 0 ] |
| |
| runc --debug exec test true |
| [ "$status" -eq 0 ] |
| [[ "${output}" == *"level=debug"* ]] |
| check_exec_debug "$output" |
| } |
| |
| @test "runc --debug --log exec" { |
| runc run -d --console-socket "$CONSOLE_SOCKET" test |
| [ "$status" -eq 0 ] |
| |
| runc --debug --log log.out exec test true |
| # check output does not include debug info |
| [[ "${output}" != *"level=debug"* ]] |
| |
| cat log.out >&2 |
| # check expected debug output was sent to log.out |
| output=$(cat log.out) |
| [[ "${output}" == *"level=debug"* ]] |
| check_exec_debug "$output" |
| } |
| |
| @test "runc exec --cgroup sub-cgroups [v1]" { |
| requires root cgroups_v1 |
| |
| set_cgroups_path |
| set_cgroup_mount_writable |
| |
| __runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| testcontainer test_busybox running |
| |
| # Check we can't join parent cgroup. |
| runc exec --cgroup ".." test_busybox cat /proc/self/cgroup |
| [ "$status" -ne 0 ] |
| [[ "$output" == *" .. is not a sub cgroup path"* ]] |
| |
| # Check we can't join non-existing subcgroup. |
| runc exec --cgroup nonexistent test_busybox cat /proc/self/cgroup |
| [ "$status" -ne 0 ] |
| [[ "$output" == *" adding pid "*"/nonexistent/cgroup.procs: no such file "* ]] |
| |
| # Check we can't join non-existing subcgroup (for a particular controller). |
| runc exec --cgroup cpu:nonexistent test_busybox cat /proc/self/cgroup |
| [ "$status" -ne 0 ] |
| [[ "$output" == *" adding pid "*"/nonexistent/cgroup.procs: no such file "* ]] |
| |
| # Check we can't specify non-existent controller. |
| runc exec --cgroup whaaat:/ test_busybox true |
| [ "$status" -ne 0 ] |
| [[ "$output" == *"unknown controller "* ]] |
| |
| # Check we can join top-level cgroup (implicit). |
| runc exec test_busybox cat /proc/self/cgroup |
| [ "$status" -eq 0 ] |
| run ! grep -v ":$REL_CGROUPS_PATH\$" <<<"$output" |
| |
| # Check we can join top-level cgroup (explicit). |
| runc exec --cgroup / test_busybox cat /proc/self/cgroup |
| [ "$status" -eq 0 ] |
| run ! grep -v ":$REL_CGROUPS_PATH\$" <<<"$output" |
| |
| # Create a few subcgroups. |
| # Note that cpu,cpuacct may be mounted together or separate. |
| runc exec test_busybox sh -euc "mkdir -p /sys/fs/cgroup/memory/submem /sys/fs/cgroup/cpu/subcpu /sys/fs/cgroup/cpuacct/subcpu" |
| [ "$status" -eq 0 ] |
| |
| # Check that explicit --cgroup works. |
| runc exec --cgroup memory:submem --cgroup cpu,cpuacct:subcpu test_busybox cat /proc/self/cgroup |
| [ "$status" -eq 0 ] |
| [[ "$output" == *":memory:$REL_CGROUPS_PATH/submem"* ]] |
| [[ "$output" == *":cpu"*":$REL_CGROUPS_PATH/subcpu"* ]] |
| } |
| |
| @test "runc exec --cgroup subcgroup [v2]" { |
| requires root cgroups_v2 |
| |
| set_cgroups_path |
| set_cgroup_mount_writable |
| |
| __runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| testcontainer test_busybox running |
| |
| # Check we can't join parent cgroup. |
| runc exec --cgroup ".." test_busybox cat /proc/self/cgroup |
| [ "$status" -ne 0 ] |
| [[ "$output" == *" .. is not a sub cgroup path"* ]] |
| |
| # Check we can't join non-existing subcgroup. |
| runc exec --cgroup nonexistent test_busybox cat /proc/self/cgroup |
| [ "$status" -ne 0 ] |
| [[ "$output" == *" adding pid "*"/nonexistent/cgroup.procs: no such file "* ]] |
| |
| # Check we can join top-level cgroup (implicit). |
| runc exec test_busybox grep '^0::/$' /proc/self/cgroup |
| [ "$status" -eq 0 ] |
| |
| # Check we can join top-level cgroup (explicit). |
| runc exec --cgroup / test_busybox grep '^0::/$' /proc/self/cgroup |
| [ "$status" -eq 0 ] |
| |
| # Now move "init" to a subcgroup, and check it was moved. |
| runc exec test_busybox sh -euc "mkdir /sys/fs/cgroup/foobar \ |
| && echo 1 > /sys/fs/cgroup/foobar/cgroup.procs \ |
| && grep -w foobar /proc/1/cgroup" |
| [ "$status" -eq 0 ] |
| |
| # The following part is taken from |
| # @test "runc exec (cgroup v2 + init process in non-root cgroup) succeeds" |
| |
| # The init process is now in "/foo", but an exec process can still |
| # join "/" because we haven't enabled any domain controller yet. |
| runc exec test_busybox grep '^0::/$' /proc/self/cgroup |
| [ "$status" -eq 0 ] |
| |
| # Turn on a domain controller (memory). |
| runc exec test_busybox sh -euc 'echo $$ > /sys/fs/cgroup/foobar/cgroup.procs; echo +memory > /sys/fs/cgroup/cgroup.subtree_control' |
| [ "$status" -eq 0 ] |
| |
| # An exec process can no longer join "/" after turning on a domain |
| # controller. Check that cgroup v2 fallback to init cgroup works. |
| runc exec test_busybox sh -euc "cat /proc/self/cgroup && grep '^0::/foobar$' /proc/self/cgroup" |
| [ "$status" -eq 0 ] |
| |
| # Check that --cgroup / disables the init cgroup fallback. |
| runc exec --cgroup / test_busybox true |
| [ "$status" -ne 0 ] |
| [[ "$output" == *" adding pid "*" to cgroups"*"/cgroup.procs: device or resource busy"* ]] |
| |
| # Check that explicit --cgroup foobar works. |
| runc exec --cgroup foobar test_busybox grep '^0::/foobar$' /proc/self/cgroup |
| [ "$status" -eq 0 ] |
| |
| # Check all processes is in foobar (this check is redundant). |
| runc exec --cgroup foobar test_busybox sh -euc '! grep -vwH foobar /proc/*/cgroup' |
| [ "$status" -eq 0 ] |
| |
| # Add a second subcgroup, check we're in it. |
| runc exec --cgroup foobar test_busybox mkdir /sys/fs/cgroup/second |
| [ "$status" -eq 0 ] |
| runc exec --cgroup second test_busybox grep -w second /proc/self/cgroup |
| [ "$status" -eq 0 ] |
| } |
| |
| @test "runc exec [execve error]" { |
| cat <<EOF >rootfs/run.sh |
| #!/mmnnttbb foo bar |
| sh |
| EOF |
| chmod +x rootfs/run.sh |
| runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox |
| runc exec -t test_busybox /run.sh |
| [ "$status" -ne 0 ] |
| |
| # After the sync socket closed, we should not send error to parent |
| # process, or else we will get a unnecessary error log(#4171). |
| # Although we never close the sync socket when doing exec, |
| # but we need to keep this test to ensure this behavior is always right. |
| [ ${#lines[@]} -eq 1 ] |
| [[ ${lines[0]} = *"exec /run.sh: no such file or directory"* ]] |
| } |