diff options
author | Andrei Vagin <avagin@gmail.com> | 2021-10-29 16:57:11 +0100 |
---|---|---|
committer | Daniel Dao <dqminh89@gmail.com> | 2021-11-01 11:06:16 +0000 |
commit | 588a28be53e191107569056b5352df60872bc45b (patch) | |
tree | c390d00d0b439abf23ae6d48637f18c65669381e /runsc/cgroup | |
parent | 1953d2ad28d405a3ab028feba7b6fca18339e9be (diff) |
Add common Cgroup interface
This is part of cgroupv2 patch set. Here we add a Cgroup interface that
both v1 and v2 need to conform to, and port cgroupv1 to use that first.
Signed-off-by: Daniel Dao <dqminh89@gmail.com>
Diffstat (limited to 'runsc/cgroup')
-rw-r--r-- | runsc/cgroup/cgroup.go | 76 | ||||
-rw-r--r-- | runsc/cgroup/cgroup_test.go | 2 |
2 files changed, 60 insertions, 18 deletions
diff --git a/runsc/cgroup/cgroup.go b/runsc/cgroup/cgroup.go index fdcaed4ea..0eb5821a9 100644 --- a/runsc/cgroup/cgroup.go +++ b/runsc/cgroup/cgroup.go @@ -19,6 +19,7 @@ package cgroup import ( "bufio" "context" + "encoding/json" "fmt" "io" "io/ioutil" @@ -286,7 +287,19 @@ func loadPathsHelper(cgroup, mountinfo io.Reader) (map[string]string, error) { return paths, nil } -// Cgroup represents a group inside all controllers. For example: +// Cgroup represents a cgroup configuration. +type Cgroup interface { + Install(res *specs.LinuxResources) error + Uninstall() error + Join() (func(), error) + CPUQuota() (float64, error) + CPUUsage() (uint64, error) + NumCPU() (int, error) + MemoryLimit() (uint64, error) + MakePath(controllerName string) string +} + +// cgroupV1 represents a group inside all controllers. For example: // Name='/foo/bar' maps to /sys/fs/cgroup/<controller>/foo/bar on // all controllers. // @@ -294,7 +307,7 @@ func loadPathsHelper(cgroup, mountinfo io.Reader) (map[string]string, error) { // location. For example: // Name='foo/bar' and Parent[ctrl]="/user.slice", then it will map to // /sys/fs/cgroup/<ctrl>/user.slice/foo/bar -type Cgroup struct { +type cgroupV1 struct { Name string `json:"name"` Parents map[string]string `json:"parents"` Own map[string]bool `json:"own"` @@ -302,7 +315,7 @@ type Cgroup struct { // NewFromSpec creates a new Cgroup instance if the spec includes a cgroup path. // Returns nil otherwise. Cgroup paths are loaded based on the current process. -func NewFromSpec(spec *specs.Spec) (*Cgroup, error) { +func NewFromSpec(spec *specs.Spec) (Cgroup, error) { if spec.Linux == nil || spec.Linux.CgroupsPath == "" { return nil, nil } @@ -311,16 +324,16 @@ func NewFromSpec(spec *specs.Spec) (*Cgroup, error) { // NewFromPath creates a new Cgroup instance from the specified relative path. // Cgroup paths are loaded based on the current process. -func NewFromPath(cgroupsPath string) (*Cgroup, error) { +func NewFromPath(cgroupsPath string) (Cgroup, error) { return new("self", cgroupsPath) } // NewFromPid loads cgroup for the given process. -func NewFromPid(pid int) (*Cgroup, error) { +func NewFromPid(pid int) (Cgroup, error) { return new(strconv.Itoa(pid), "") } -func new(pid, cgroupsPath string) (*Cgroup, error) { +func new(pid, cgroupsPath string) (Cgroup, error) { var parents map[string]string // If path is relative, load cgroup paths for the process to build the @@ -332,7 +345,7 @@ func new(pid, cgroupsPath string) (*Cgroup, error) { return nil, fmt.Errorf("finding current cgroups: %w", err) } } - cg := &Cgroup{ + cg := &cgroupV1{ Name: cgroupsPath, Parents: parents, Own: make(map[string]bool), @@ -341,10 +354,39 @@ func new(pid, cgroupsPath string) (*Cgroup, error) { return cg, nil } +// CgroupJSON is a wrapper for Cgroup that can be encoded to JSON. +type CgroupJSON struct { + Cgroup Cgroup `json:"cgroup"` +} + +type cgroupJSONv1 struct { + Cgroup *cgroupV1 `json:"cgroup"` +} + +// UnmarshalJSON implements json.Unmarshaler.UnmarshalJSON +func (c *CgroupJSON) UnmarshalJSON(data []byte) error { + v1 := cgroupJSONv1{} + err := json.Unmarshal(data, &v1) + if v1.Cgroup != nil { + c.Cgroup = v1.Cgroup + } + return err +} + +// MarshalJSON implements json.Marshaler.MarshalJSON +func (c *CgroupJSON) MarshalJSON() ([]byte, error) { + if c.Cgroup == nil { + v1 := cgroupJSONv1{} + return json.Marshal(&v1) + } + v1 := cgroupJSONv1{Cgroup: c.Cgroup.(*cgroupV1)} + return json.Marshal(&v1) +} + // Install creates and configures cgroups according to 'res'. If cgroup path // 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 { +func (c *cgroupV1) Install(res *specs.LinuxResources) error { log.Debugf("Installing cgroup path %q", c.Name) // Clean up partially created cgroups on error. Errors during cleanup itself @@ -369,7 +411,7 @@ func (c *Cgroup) Install(res *specs.LinuxResources) error { for _, key := range missing { ctrlr := controllers[key] - if skip, err := c.createController(key); skip && ctrlr.optional() { + if skip, err := createController(c, key); skip && ctrlr.optional() { if err := ctrlr.skip(res); err != nil { return err } @@ -394,7 +436,7 @@ func (c *Cgroup) Install(res *specs.LinuxResources) error { // controller is enabled in the system. It returns a boolean indicating whether // the controller should be skipped (e.g. controller is disabled). In case it // should be skipped, it also returns the error it got. -func (c *Cgroup) createController(name string) (bool, error) { +func createController(c Cgroup, name string) (bool, error) { ctrlrPath := filepath.Join(cgroupRoot, name) if _, err := os.Stat(ctrlrPath); err != nil { return os.IsNotExist(err), err @@ -410,7 +452,7 @@ func (c *Cgroup) createController(name string) (bool, 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 { +func (c *cgroupV1) Uninstall() error { log.Debugf("Deleting cgroup %q", c.Name) g, ctx := errgroup.WithContext(context.Background()) for key := range controllers { @@ -447,7 +489,7 @@ func (c *Cgroup) Uninstall() error { // Join adds the current process to the all controllers. Returns function that // restores cgroup to the original state. -func (c *Cgroup) Join() (func(), error) { +func (c *cgroupV1) Join() (func(), error) { // First save the current state so it can be restored. paths, err := loadPaths("self") if err != nil { @@ -492,7 +534,7 @@ func (c *Cgroup) Join() (func(), error) { } // CPUQuota returns the CFS CPU quota. -func (c *Cgroup) CPUQuota() (float64, error) { +func (c *cgroupV1) CPUQuota() (float64, error) { path := c.MakePath("cpu") quota, err := getInt(path, "cpu.cfs_quota_us") if err != nil { @@ -509,7 +551,7 @@ func (c *Cgroup) CPUQuota() (float64, error) { } // CPUUsage returns the total CPU usage of the cgroup. -func (c *Cgroup) CPUUsage() (uint64, error) { +func (c *cgroupV1) CPUUsage() (uint64, error) { path := c.MakePath("cpuacct") usage, err := getValue(path, "cpuacct.usage") if err != nil { @@ -519,7 +561,7 @@ func (c *Cgroup) CPUUsage() (uint64, error) { } // NumCPU returns the number of CPUs configured in 'cpuset/cpuset.cpus'. -func (c *Cgroup) NumCPU() (int, error) { +func (c *cgroupV1) NumCPU() (int, error) { path := c.MakePath("cpuset") cpuset, err := getValue(path, "cpuset.cpus") if err != nil { @@ -529,7 +571,7 @@ func (c *Cgroup) NumCPU() (int, error) { } // MemoryLimit returns the memory limit. -func (c *Cgroup) MemoryLimit() (uint64, error) { +func (c *cgroupV1) MemoryLimit() (uint64, error) { path := c.MakePath("memory") limStr, err := getValue(path, "memory.limit_in_bytes") if err != nil { @@ -539,7 +581,7 @@ func (c *Cgroup) MemoryLimit() (uint64, error) { } // MakePath builds a path to the given controller. -func (c *Cgroup) MakePath(controllerName string) string { +func (c *cgroupV1) MakePath(controllerName string) string { path := c.Name if parent, ok := c.Parents[controllerName]; ok { path = filepath.Join(parent, c.Name) diff --git a/runsc/cgroup/cgroup_test.go b/runsc/cgroup/cgroup_test.go index 0b6a5431b..1447078a0 100644 --- a/runsc/cgroup/cgroup_test.go +++ b/runsc/cgroup/cgroup_test.go @@ -59,7 +59,7 @@ var dindMountinfo = ` ` func TestUninstallEnoent(t *testing.T) { - c := Cgroup{ + c := cgroupV1{ // Use a non-existent name. Name: "runsc-test-uninstall-656e6f656e740a", Own: make(map[string]bool), |