From a1d5398afa91b0cb107cbfa384808d128e0a9181 Mon Sep 17 00:00:00 2001 From: Danail Branekov Date: Fri, 17 Aug 2018 16:18:18 +0100 Subject: [PATCH] Respect container's cgroup path Respect the container's cgroup path when finding the container's cgroup mount point, which is useful in multi-tenant environments, where containers have their own unique cgroup mounts Signed-off-by: Danail Branekov Signed-off-by: Oliver Stenbom Signed-off-by: Giuseppe Capizzi --- libcontainer/cgroups/fs/apply_raw.go | 2 +- libcontainer/cgroups/systemd/apply_systemd.go | 2 +- libcontainer/cgroups/utils.go | 28 +++++++++---- libcontainer/cgroups/utils_test.go | 41 ++++++++++++++----- 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/libcontainer/cgroups/fs/apply_raw.go b/libcontainer/cgroups/fs/apply_raw.go index 09b96de5..8b7c2755 100644 --- a/libcontainer/cgroups/fs/apply_raw.go +++ b/libcontainer/cgroups/fs/apply_raw.go @@ -311,7 +311,7 @@ func getCgroupData(c *configs.Cgroup, pid int) (*cgroupData, error) { } func (raw *cgroupData) path(subsystem string) (string, error) { - mnt, err := cgroups.FindCgroupMountpoint(subsystem) + mnt, err := cgroups.FindCgroupMountpoint(raw.root, subsystem) // If we didn't mount the subsystem, there is no point we make the path. if err != nil { return "", err diff --git a/libcontainer/cgroups/systemd/apply_systemd.go b/libcontainer/cgroups/systemd/apply_systemd.go index 0c625ba3..b2207973 100644 --- a/libcontainer/cgroups/systemd/apply_systemd.go +++ b/libcontainer/cgroups/systemd/apply_systemd.go @@ -464,7 +464,7 @@ func ExpandSlice(slice string) (string, error) { } func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) { - mountpoint, err := cgroups.FindCgroupMountpoint(subsystem) + mountpoint, err := cgroups.FindCgroupMountpoint(c.Path, subsystem) if err != nil { return "", err } diff --git a/libcontainer/cgroups/utils.go b/libcontainer/cgroups/utils.go index 7c995efe..f4c57990 100644 --- a/libcontainer/cgroups/utils.go +++ b/libcontainer/cgroups/utils.go @@ -22,31 +22,41 @@ const ( ) // https://www.kernel.org/doc/Documentation/cgroup-v1/cgroups.txt -func FindCgroupMountpoint(subsystem string) (string, error) { - mnt, _, err := FindCgroupMountpointAndRoot(subsystem) +func FindCgroupMountpoint(cgroupPath, subsystem string) (string, error) { + mnt, _, err := FindCgroupMountpointAndRoot(cgroupPath, subsystem) return mnt, err } -func FindCgroupMountpointAndRoot(subsystem string) (string, string, error) { +func FindCgroupMountpointAndRoot(cgroupPath, subsystem string) (string, string, error) { // We are not using mount.GetMounts() because it's super-inefficient, // parsing it directly sped up x10 times because of not using Sscanf. // It was one of two major performance drawbacks in container start. if !isSubsystemAvailable(subsystem) { return "", "", NewNotFoundError(subsystem) } + f, err := os.Open("/proc/self/mountinfo") if err != nil { return "", "", err } defer f.Close() - scanner := bufio.NewScanner(f) + return findCgroupMountpointAndRootFromReader(f, cgroupPath, subsystem) +} + +func findCgroupMountpointAndRootFromReader(reader io.Reader, cgroupPath, subsystem string) (string, string, error) { + scanner := bufio.NewScanner(reader) for scanner.Scan() { txt := scanner.Text() - fields := strings.Split(txt, " ") - for _, opt := range strings.Split(fields[len(fields)-1], ",") { - if opt == subsystem { - return fields[4], fields[3], nil + fields := strings.Fields(txt) + if len(fields) < 5 { + continue + } + if strings.HasPrefix(fields[4], cgroupPath) { + for _, opt := range strings.Split(fields[len(fields)-1], ",") { + if opt == subsystem { + return fields[4], fields[3], nil + } } } } @@ -256,7 +266,7 @@ func GetInitCgroupPath(subsystem string) (string, error) { } func getCgroupPathHelper(subsystem, cgroup string) (string, error) { - mnt, root, err := FindCgroupMountpointAndRoot(subsystem) + mnt, root, err := FindCgroupMountpointAndRoot("", subsystem) if err != nil { return "", err } diff --git a/libcontainer/cgroups/utils_test.go b/libcontainer/cgroups/utils_test.go index 4996aced..b1fbe117 100644 --- a/libcontainer/cgroups/utils_test.go +++ b/libcontainer/cgroups/utils_test.go @@ -301,7 +301,8 @@ func TestIgnoreCgroup2Mount(t *testing.T) { } } -const fakeMountInfo = ` 18 24 0:17 / /sys rw,nosuid,nodev,noexec,relatime - sysfs sysfs rw +func TestGetClosestMountpointAncestor(t *testing.T) { + fakeMountInfo := ` 18 24 0:17 / /sys rw,nosuid,nodev,noexec,relatime - sysfs sysfs rw 100 99 1:31 / /foo/bar rw,relatime - fake fake rw,fake 100 99 1:31 / /foo/bar/baz2 rw,relatime - fake fake rw,fake 100 99 1:31 / /foo/bar/baz rw,relatime - fake fake rw,fake @@ -311,21 +312,39 @@ const fakeMountInfo = ` 18 24 0:17 / /sys rw,nosuid,nodev,noexec,relatime - sysf 100 99 1:31 / /unrelated rw,relatime - fake fake rw,fake 100 99 1:31 / / rw,relatime - fake fake rw,fake ` - -func TestGetClosestMountpointAncestor(t *testing.T) { testCases := []struct { - input string - mountinfos string - output string + input string + output string }{ - {input: "/foo/bar/baz/a/b/c", mountinfos: fakeMountInfo, output: "/foo/bar/baz"}, - {input: "/foo/bar/baz", mountinfos: fakeMountInfo, output: "/foo/bar/baz"}, - {input: "/foo/bar/bazza", mountinfos: fakeMountInfo, output: "/foo/bar/bazza"}, - {input: "/a/b/c/d", mountinfos: fakeMountInfo, output: "/"}, + {input: "/foo/bar/baz/a/b/c", output: "/foo/bar/baz"}, + {input: "/foo/bar/baz", output: "/foo/bar/baz"}, + {input: "/foo/bar/bazza", output: "/foo/bar/bazza"}, + {input: "/a/b/c/d", output: "/"}, } for _, c := range testCases { - mountpoint := GetClosestMountpointAncestor(c.input, c.mountinfos) + mountpoint := GetClosestMountpointAncestor(c.input, fakeMountInfo) + if mountpoint != c.output { + t.Errorf("expected %s, got %s", c.output, mountpoint) + } + } +} + +func TestFindCgroupMountpointAndRoot(t *testing.T) { + fakeMountInfo := ` +35 27 0:29 / /foo rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,devices +35 27 0:29 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,devices +` + testCases := []struct { + cgroupPath string + output string + }{ + {cgroupPath: "/sys/fs", output: "/sys/fs/cgroup/devices"}, + {cgroupPath: "", output: "/foo"}, + } + + for _, c := range testCases { + mountpoint, _, _ := findCgroupMountpointAndRootFromReader(strings.NewReader(fakeMountInfo), c.cgroupPath, "devices") if mountpoint != c.output { t.Errorf("expected %s, got %s", c.output, mountpoint) }