#!/usr/bin/env bats load helpers function setup() { setup_busybox } function teardown() { teardown_bundle } # See also: "kill KILL [host pidns + init gone]" test in kill.bats. # # This needs to be placed at the top of the bats file to work around # a shellcheck bug. See . function test_runc_delete_host_pidns() { requires cgroups_freezer update_config ' .linux.namespaces -= [{"type": "pid"}]' set_cgroups_path if [ $EUID -ne 0 ]; then requires rootless_cgroup # Apparently, for rootless test, when using systemd cgroup manager, # newer versions of systemd clean up the container as soon as its init # process is gone. This is all fine and dandy, except it prevents us to # test this case, thus we skip the test. # # It is not entirely clear which systemd version got this feature: # v245 works fine, and v249 does not. if [ -v RUNC_USE_SYSTEMD ] && [ "$(systemd_version)" -gt 245 ]; then skip "rootless+systemd conflicts with systemd > 245" fi # Can't mount real /proc when rootless + no pidns, # so change it to a bind-mounted one from the host. update_config ' .mounts |= map((select(.type == "proc") | .type = "none" | .source = "/proc" | .options = ["rbind", "nosuid", "nodev", "noexec"] ) // .)' fi runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox [ "$status" -eq 0 ] cgpath=$(get_cgroup_path "pids") init_pid=$(cat "$cgpath"/cgroup.procs) # Start a few more processes. for _ in 1 2 3 4 5; do __runc exec -d test_busybox sleep 1h done # Now kill the container's init process. Since the container do # not have own PID ns, its init is no special and the container # will still be up and running. kill -9 "$init_pid" wait_pids_gone 10 0.2 "$init_pid" # Get the list of all container processes. mapfile -t pids < <(cat "$cgpath"/cgroup.procs) echo "pids:" "${pids[@]}" # Sanity check -- make sure all processes exist. for p in "${pids[@]}"; do kill -0 "$p" done # Must kill those processes and remove container. runc delete "$@" test_busybox [ "$status" -eq 0 ] runc state test_busybox [ "$status" -ne 0 ] # "Container does not exist" # Wait and check that all the processes are gone. wait_pids_gone 10 0.2 "${pids[@]}" # Make sure cgroup.procs is empty. mapfile -t pids < <(cat "$cgpath"/cgroup.procs || true) if [ ${#pids[@]} -gt 0 ]; then echo "expected empty cgroup.procs, got:" "${pids[@]}" 1>&2 return 1 fi } @test "runc delete" { # Need a permission to create a cgroup. # XXX(@kolyshkin): currently this test does not handle rootless when # fs cgroup driver is used, because in this case cgroup (with a # predefined name) is created by tests/rootless.sh, not by runc. [ $EUID -ne 0 ] && requires systemd set_resources_limit runc run -d --console-socket "$CONSOLE_SOCKET" testbusyboxdelete [ "$status" -eq 0 ] testcontainer testbusyboxdelete running # Ensure the find statement used later is correct. output=$(find /sys/fs/cgroup -name testbusyboxdelete -o -name \*-testbusyboxdelete.scope 2>/dev/null || true) if [ -z "$output" ]; then fail "expected cgroup not found" fi runc kill testbusyboxdelete KILL [ "$status" -eq 0 ] wait_for_container 10 1 testbusyboxdelete stopped runc delete testbusyboxdelete [ "$status" -eq 0 ] runc state testbusyboxdelete [ "$status" -ne 0 ] output=$(find /sys/fs/cgroup -name testbusyboxdelete -o -name \*-testbusyboxdelete.scope 2>/dev/null || true) [ "$output" = "" ] || fail "cgroup not cleaned up correctly: $output" } @test "runc delete --force" { runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox [ "$status" -eq 0 ] testcontainer test_busybox running runc delete --force test_busybox runc state test_busybox [ "$status" -ne 0 ] } @test "runc delete --force ignore not exist" { runc delete --force notexists [ "$status" -eq 0 ] } # Issue 4047, case "runc delete". @test "runc delete [host pidns + init gone]" { test_runc_delete_host_pidns } # Issue 4047, case "runc delete --force" (different code path). @test "runc delete --force [host pidns + init gone]" { test_runc_delete_host_pidns --force } @test "runc delete --force [paused container]" { requires cgroups_freezer if [ $EUID -ne 0 ]; then requires rootless_cgroup # Rootless containers have no default cgroup path. set_cgroups_path fi runc run -d --console-socket "$CONSOLE_SOCKET" ct1 [ "$status" -eq 0 ] testcontainer ct1 running runc pause ct1 [ "$status" -eq 0 ] runc delete --force ct1 [ "$status" -eq 0 ] } @test "runc delete --force in cgroupv1 with subcgroups" { requires cgroups_v1 root cgroupns set_cgroups_path set_cgroup_mount_writable # enable cgroupns update_config '.linux.namespaces += [{"type": "cgroup"}]' local subsystems="memory freezer" runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox [ "$status" -eq 0 ] testcontainer test_busybox running __runc exec -d test_busybox sleep 1d # find the pid of sleep pid=$(__runc exec test_busybox ps -a | grep 1d | awk '{print $1}') [[ ${pid} =~ [0-9]+ ]] # create a sub-cgroup cat < tasks cat tasks done EOF [ "$status" -eq 0 ] [[ "$output" =~ [0-9]+ ]] for s in ${subsystems}; do name=CGROUP_${s^^}_BASE_PATH eval path=\$"${name}${REL_CGROUPS_PATH}/foo" # shellcheck disable=SC2154 [ -d "${path}" ] || fail "test failed to create memory sub-cgroup ($path not found)" done runc delete --force test_busybox runc state test_busybox [ "$status" -ne 0 ] output=$(find /sys/fs/cgroup -wholename '*testbusyboxdelete*' -type d 2>/dev/null || true) [ "$output" = "" ] || fail "cgroup not cleaned up correctly: $output" } @test "runc delete --force in cgroupv2 with subcgroups" { requires cgroups_v2 root set_cgroups_path set_cgroup_mount_writable runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox [ "$status" -eq 0 ] testcontainer test_busybox running # create a sub process __runc exec -d test_busybox sleep 1d # find the pid of sleep pid=$(__runc exec test_busybox ps -a | grep 1d | awk '{print $1}') [[ ${pid} =~ [0-9]+ ]] # create subcgroups cat <nest.sh set -e -u -x cd /sys/fs/cgroup echo +pids > cgroup.subtree_control mkdir foo cd foo echo threaded > cgroup.type echo ${pid} > cgroup.threads cat cgroup.threads EOF runc exec test_busybox sh