
The rpc only reports one field, i.e. the cgroup driver, to kubelet. Containerd determines the effective cgroup driver by looking at all runtime handlers, starting from the default runtime handler (the rest in alphabetical order), and returning the cgroup driver setting of the first runtime handler that supports one. If no runtime handler supports cgroup driver (i.e. has a config option for it) containerd falls back to auto-detection, returning systemd if systemd is running and cgroupfs otherwise. This patch implements the CRI server side of Kubernetes KEP-4033: https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/4033-group-driver-detection-over-cri Signed-off-by: Markus Lehtonen <markus.lehtonen@intel.com>
187 lines
4.2 KiB
Go
187 lines
4.2 KiB
Go
package fs
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
"github.com/opencontainers/runc/libcontainer/utils"
|
|
)
|
|
|
|
// The absolute path to the root of the cgroup hierarchies.
|
|
var (
|
|
cgroupRootLock sync.Mutex
|
|
cgroupRoot string
|
|
)
|
|
|
|
const defaultCgroupRoot = "/sys/fs/cgroup"
|
|
|
|
func initPaths(cg *configs.Cgroup) (map[string]string, error) {
|
|
root, err := rootPath()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
inner, err := innerPath(cg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
paths := make(map[string]string)
|
|
for _, sys := range subsystems {
|
|
name := sys.Name()
|
|
path, err := subsysPath(root, inner, name)
|
|
if err != nil {
|
|
// The non-presence of the devices subsystem
|
|
// is considered fatal for security reasons.
|
|
if cgroups.IsNotFound(err) && (cg.SkipDevices || name != "devices") {
|
|
continue
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
paths[name] = path
|
|
}
|
|
|
|
return paths, nil
|
|
}
|
|
|
|
func tryDefaultCgroupRoot() string {
|
|
var st, pst unix.Stat_t
|
|
|
|
// (1) it should be a directory...
|
|
err := unix.Lstat(defaultCgroupRoot, &st)
|
|
if err != nil || st.Mode&unix.S_IFDIR == 0 {
|
|
return ""
|
|
}
|
|
|
|
// (2) ... and a mount point ...
|
|
err = unix.Lstat(filepath.Dir(defaultCgroupRoot), &pst)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
if st.Dev == pst.Dev {
|
|
// parent dir has the same dev -- not a mount point
|
|
return ""
|
|
}
|
|
|
|
// (3) ... of 'tmpfs' fs type.
|
|
var fst unix.Statfs_t
|
|
err = unix.Statfs(defaultCgroupRoot, &fst)
|
|
if err != nil || fst.Type != unix.TMPFS_MAGIC {
|
|
return ""
|
|
}
|
|
|
|
// (4) it should have at least 1 entry ...
|
|
dir, err := os.Open(defaultCgroupRoot)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
names, err := dir.Readdirnames(1)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
if len(names) < 1 {
|
|
return ""
|
|
}
|
|
// ... which is a cgroup mount point.
|
|
err = unix.Statfs(filepath.Join(defaultCgroupRoot, names[0]), &fst)
|
|
if err != nil || fst.Type != unix.CGROUP_SUPER_MAGIC {
|
|
return ""
|
|
}
|
|
|
|
return defaultCgroupRoot
|
|
}
|
|
|
|
// rootPath finds and returns path to the root of the cgroup hierarchies.
|
|
func rootPath() (string, error) {
|
|
cgroupRootLock.Lock()
|
|
defer cgroupRootLock.Unlock()
|
|
|
|
if cgroupRoot != "" {
|
|
return cgroupRoot, nil
|
|
}
|
|
|
|
// fast path
|
|
cgroupRoot = tryDefaultCgroupRoot()
|
|
if cgroupRoot != "" {
|
|
return cgroupRoot, nil
|
|
}
|
|
|
|
// slow path: parse mountinfo
|
|
mi, err := cgroups.GetCgroupMounts(false)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(mi) < 1 {
|
|
return "", errors.New("no cgroup mount found in mountinfo")
|
|
}
|
|
|
|
// Get the first cgroup mount (e.g. "/sys/fs/cgroup/memory"),
|
|
// use its parent directory.
|
|
root := filepath.Dir(mi[0].Mountpoint)
|
|
|
|
if _, err := os.Stat(root); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
cgroupRoot = root
|
|
return cgroupRoot, nil
|
|
}
|
|
|
|
func innerPath(c *configs.Cgroup) (string, error) {
|
|
if (c.Name != "" || c.Parent != "") && c.Path != "" {
|
|
return "", errors.New("cgroup: either Path or Name and Parent should be used")
|
|
}
|
|
|
|
// XXX: Do not remove CleanPath. Path safety is important! -- cyphar
|
|
innerPath := utils.CleanPath(c.Path)
|
|
if innerPath == "" {
|
|
cgParent := utils.CleanPath(c.Parent)
|
|
cgName := utils.CleanPath(c.Name)
|
|
innerPath = filepath.Join(cgParent, cgName)
|
|
}
|
|
|
|
return innerPath, nil
|
|
}
|
|
|
|
func subsysPath(root, inner, subsystem string) (string, error) {
|
|
// If the cgroup name/path is absolute do not look relative to the cgroup of the init process.
|
|
if filepath.IsAbs(inner) {
|
|
mnt, err := cgroups.FindCgroupMountpoint(root, subsystem)
|
|
// If we didn't mount the subsystem, there is no point we make the path.
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Sometimes subsystems can be mounted together as 'cpu,cpuacct'.
|
|
return filepath.Join(root, filepath.Base(mnt), inner), nil
|
|
}
|
|
|
|
// Use GetOwnCgroupPath instead of GetInitCgroupPath, because the creating
|
|
// process could in container and shared pid namespace with host, and
|
|
// /proc/1/cgroup could point to whole other world of cgroups.
|
|
parentPath, err := cgroups.GetOwnCgroupPath(subsystem)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return filepath.Join(parentPath, inner), nil
|
|
}
|
|
|
|
func apply(path string, pid int) error {
|
|
if path == "" {
|
|
return nil
|
|
}
|
|
if err := os.MkdirAll(path, 0o755); err != nil {
|
|
return err
|
|
}
|
|
return cgroups.WriteCgroupProc(path, pid)
|
|
}
|