diff --git a/libcontainer/cgroups/fs2/fs2.go b/libcontainer/cgroups/fs2/fs2.go index b7fe44277..556684703 100644 --- a/libcontainer/cgroups/fs2/fs2.go +++ b/libcontainer/cgroups/fs2/fs2.go @@ -156,11 +156,35 @@ func (m *manager) Freeze(state configs.FreezerState) error { return nil } -func (m *manager) Destroy() error { - if err := os.Remove(m.dirPath); err != nil && !os.IsNotExist(err) { +// removeCgroupPath aims to remove cgroup path recursively +// Because there may be subcgroups in it. +func removeCgroupPath(path string) error { + infos, err := ioutil.ReadDir(path) + if err != nil { + if os.IsNotExist(err) { + err = nil + } return err } - return nil + for _, info := range infos { + if info.IsDir() { + // We should remove subcgroups dir first + if err = removeCgroupPath(filepath.Join(path, info.Name())); err != nil { + break + } + } + } + if err == nil { + err = os.Remove(path) + if os.IsNotExist(err) { + err = nil + } + } + return err +} + +func (m *manager) Destroy() error { + return removeCgroupPath(m.dirPath) } func (m *manager) Path(_ string) string { diff --git a/tests/integration/delete.bats b/tests/integration/delete.bats index c5ed215d1..44a76fc96 100644 --- a/tests/integration/delete.bats +++ b/tests/integration/delete.bats @@ -51,3 +51,58 @@ function teardown() { runc delete --force notexists [ "$status" -eq 0 ] } + +@test "runc delete --force in cgroupv2 with subcgroups" { + requires cgroups_v2 root + set_cgroups_path "$BUSYBOX_BUNDLE" + + # grant `rw` priviledge to `/sys/fs/cgroup` + cat "${BUSYBOX_BUNDLE}/config.json"\ + | jq '.mounts |= map((select(.type=="cgroup") | .options -= ["ro"]) // .)'\ + > "${BUSYBOX_BUNDLE}/config.json.tmp" + mv "${BUSYBOX_BUNDLE}/config.json"{.tmp,} + + # run busybox detached + runc run -d --console-socket $CONSOLE_SOCKET test_busybox + [ "$status" -eq 0 ] + + # check state + testcontainer test_busybox running + + # create a sub process + __runc exec -d test_busybox sleep 1d + [ "$status" -eq 0 ] + + # 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 + cd /sys/fs/cgroup + for f in \$(cat cgroup.controllers); do echo +\$f > cgroup.subtree_control; done + mkdir foo + cd foo + echo threaded > cgroup.type + echo ${pid} > cgroup.threads + cat cgroup.threads +EOF + cat nest.sh | runc exec test_busybox sh + [[ ${output} =~ [0-9]+ ]] + + # check create subcgroups success + [ -d $CGROUP_PATH/foo ] + + # check cgroup.threads' value + runc exec test_busybox cat /sys/fs/cgroup/foo/cgroup.threads + [[ ${output} =~ [0-9]+ ]] + + # force delete test_busybox + runc delete --force test_busybox + + runc state test_busybox + [ "$status" -ne 0 ] + + # check delete subcgroups success + [ ! -d $CGROUP_PATH/foo ] +}