summaryrefslogtreecommitdiffhomepage
path: root/runsc/cgroup
diff options
context:
space:
mode:
Diffstat (limited to 'runsc/cgroup')
-rw-r--r--runsc/cgroup/cgroup.go43
-rw-r--r--runsc/cgroup/cgroup_test.go85
2 files changed, 111 insertions, 17 deletions
diff --git a/runsc/cgroup/cgroup.go b/runsc/cgroup/cgroup.go
index 56da21584..e5294de55 100644
--- a/runsc/cgroup/cgroup.go
+++ b/runsc/cgroup/cgroup.go
@@ -21,6 +21,7 @@ import (
"context"
"errors"
"fmt"
+ "io"
"io/ioutil"
"os"
"path/filepath"
@@ -198,8 +199,13 @@ func LoadPaths(pid string) (map[string]string, error) {
}
defer f.Close()
+ return loadPathsHelper(f)
+}
+
+func loadPathsHelper(cgroup io.Reader) (map[string]string, error) {
paths := make(map[string]string)
- scanner := bufio.NewScanner(f)
+
+ scanner := bufio.NewScanner(cgroup)
for scanner.Scan() {
// Format: ID:[name=]controller1,controller2:path
// Example: 2:cpu,cpuacct:/user.slice
@@ -207,6 +213,9 @@ func LoadPaths(pid string) (map[string]string, error) {
if len(tokens) != 3 {
return nil, fmt.Errorf("invalid cgroups file, line: %q", scanner.Text())
}
+ if len(tokens[1]) == 0 {
+ continue
+ }
for _, ctrlr := range strings.Split(tokens[1], ",") {
// Remove prefix for cgroups with no controller, eg. systemd.
ctrlr = strings.TrimPrefix(ctrlr, "name=")
@@ -225,7 +234,7 @@ func LoadPaths(pid string) (map[string]string, error) {
type Cgroup struct {
Name string `json:"name"`
Parents map[string]string `json:"parents"`
- Own bool `json:"own"`
+ Own map[string]bool `json:"own"`
}
// New creates a new Cgroup instance if the spec includes a cgroup path.
@@ -242,9 +251,11 @@ func New(spec *specs.Spec) (*Cgroup, error) {
return nil, fmt.Errorf("finding current cgroups: %w", err)
}
}
+ own := make(map[string]bool)
return &Cgroup{
Name: spec.Linux.CgroupsPath,
Parents: parents,
+ Own: own,
}, nil
}
@@ -252,18 +263,8 @@ func New(spec *specs.Spec) (*Cgroup, error) {
// already exists, it means that the caller has already provided a
// pre-configured cgroups, and 'res' is ignored.
func (c *Cgroup) Install(res *specs.LinuxResources) error {
- if _, err := os.Stat(c.makePath("memory")); err == nil {
- // If cgroup has already been created; it has been setup by caller. Don't
- // make any changes to configuration, just join when sandbox/gofer starts.
- log.Debugf("Using pre-created cgroup %q", c.Name)
- return nil
- }
-
log.Debugf("Creating cgroup %q", c.Name)
- // Mark that cgroup resources are owned by me.
- c.Own = true
-
// The Cleanup object cleans up partially created cgroups when an error occurs.
// Errors occuring during cleanup itself are ignored.
clean := cleanup.Make(func() { _ = c.Uninstall() })
@@ -271,6 +272,16 @@ func (c *Cgroup) Install(res *specs.LinuxResources) error {
for key, cfg := range controllers {
path := c.makePath(key)
+ if _, err := os.Stat(path); err == nil {
+ // If cgroup has already been created; it has been setup by caller. Don't
+ // make any changes to configuration, just join when sandbox/gofer starts.
+ log.Debugf("Using pre-created cgroup %q", path)
+ continue
+ }
+
+ // Mark that cgroup resources are owned by me.
+ c.Own[key] = true
+
if err := os.MkdirAll(path, 0755); err != nil {
if cfg.optional && errors.Is(err, syscall.EROFS) {
log.Infof("Skipping cgroup %q", key)
@@ -289,12 +300,12 @@ func (c *Cgroup) Install(res *specs.LinuxResources) error {
// Uninstall removes the settings done in Install(). If cgroup path already
// existed when Install() was called, Uninstall is a noop.
func (c *Cgroup) Uninstall() error {
- if !c.Own {
- // cgroup is managed by caller, don't touch it.
- return nil
- }
log.Debugf("Deleting cgroup %q", c.Name)
for key := range controllers {
+ if !c.Own[key] {
+ // cgroup is managed by caller, don't touch it.
+ continue
+ }
path := c.makePath(key)
log.Debugf("Removing cgroup controller for key=%q path=%q", key, path)
diff --git a/runsc/cgroup/cgroup_test.go b/runsc/cgroup/cgroup_test.go
index 4db5ee5c3..931144cf9 100644
--- a/runsc/cgroup/cgroup_test.go
+++ b/runsc/cgroup/cgroup_test.go
@@ -29,7 +29,10 @@ func TestUninstallEnoent(t *testing.T) {
c := Cgroup{
// set a non-existent name
Name: "runsc-test-uninstall-656e6f656e740a",
- Own: true,
+ }
+ c.Own = make(map[string]bool)
+ for key := range controllers {
+ c.Own[key] = true
}
if err := c.Uninstall(); err != nil {
t.Errorf("Uninstall() failed: %v", err)
@@ -647,3 +650,83 @@ func TestPids(t *testing.T) {
})
}
}
+
+func TestLoadPaths(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ cgroups string
+ want map[string]string
+ err string
+ }{
+ {
+ name: "abs-path",
+ cgroups: "0:ctr:/path",
+ want: map[string]string{"ctr": "/path"},
+ },
+ {
+ name: "rel-path",
+ cgroups: "0:ctr:rel-path",
+ want: map[string]string{"ctr": "rel-path"},
+ },
+ {
+ name: "non-controller",
+ cgroups: "0:name=systemd:/path",
+ want: map[string]string{"systemd": "/path"},
+ },
+ {
+ name: "empty",
+ },
+ {
+ name: "multiple",
+ cgroups: "0:ctr0:/path0\n" +
+ "1:ctr1:/path1\n" +
+ "2::/empty\n",
+ want: map[string]string{
+ "ctr0": "/path0",
+ "ctr1": "/path1",
+ },
+ },
+ {
+ name: "missing-field",
+ cgroups: "0:nopath\n",
+ err: "invalid cgroups file",
+ },
+ {
+ name: "too-many-fields",
+ cgroups: "0:ctr:/path:extra\n",
+ err: "invalid cgroups file",
+ },
+ {
+ name: "multiple-malformed",
+ cgroups: "0:ctr0:/path0\n" +
+ "1:ctr1:/path1\n" +
+ "2:\n",
+ err: "invalid cgroups file",
+ },
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ r := strings.NewReader(tc.cgroups)
+ got, err := loadPathsHelper(r)
+ if len(tc.err) == 0 {
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+ } else if !strings.Contains(err.Error(), tc.err) {
+ t.Fatalf("Wrong error message, want: *%s*, got: %v", tc.err, err)
+ }
+ for key, vWant := range tc.want {
+ vGot, ok := got[key]
+ if !ok {
+ t.Errorf("Missing controller %q", key)
+ }
+ if vWant != vGot {
+ t.Errorf("Wrong controller %q value, want: %q, got: %q", key, vWant, vGot)
+ }
+ delete(got, key)
+ }
+ for k, v := range got {
+ t.Errorf("Unexpected controller %q: %q", k, v)
+ }
+ })
+ }
+}