Skip to content

Commit

Permalink
deps: switch to runc/libcontainer/cgroups cgroup manager
Browse files Browse the repository at this point in the history
Add a libcontainer/cgroups based manager that implements the existing
cgroups.Manager API.

Remove the containerd/cgroups based managers.

Signed-off-by: Edita Kizinevic <edita.kizinevic@cern.ch>
  • Loading branch information
dtrudg authored and edytuk committed Mar 8, 2022
1 parent 07c5def commit 48f52dc
Show file tree
Hide file tree
Showing 15 changed files with 421 additions and 500 deletions.
14 changes: 4 additions & 10 deletions LICENSE_DEPENDENCIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ The dependencies and their licenses are as follows:

**License URL:** <https://github.com/Netflix/go-expect/blob/master/LICENSE>

## github.com/containerd/cgroups

**License:** Apache-2.0

**License URL:** <https://github.com/containerd/cgroups/blob/master/LICENSE>

## github.com/containerd/containerd

**License:** Apache-2.0
Expand Down Expand Up @@ -179,11 +173,11 @@ The dependencies and their licenses are as follows:

**License URL:** <https://github.com/opencontainers/image-spec/blob/master/specs-go/LICENSE>

## github.com/opencontainers/runc/libcontainer/user
## github.com/opencontainers/runc/libcontainer

**License:** Apache-2.0

**License URL:** <https://github.com/opencontainers/runc/blob/master/libcontainer/user/LICENSE>
**License URL:** <https://github.com/opencontainers/runc/blob/master/libcontainer/LICENSE>

## github.com/opencontainers/runtime-spec/specs-go

Expand Down Expand Up @@ -371,11 +365,11 @@ The dependencies and their licenses are as follows:

**License URL:** <https://github.com/cyphar/filepath-securejoin/blob/master/LICENSE>

## github.com/gogo/protobuf
## github.com/gogo/protobuf/proto

**License:** BSD-3-Clause

**License URL:** <https://github.com/gogo/protobuf/blob/master/LICENSE>
**License URL:** <https://github.com/gogo/protobuf/blob/master/proto/LICENSE>

## github.com/golang/protobuf

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ require (
github.com/blang/semver/v4 v4.0.0
github.com/buger/jsonparser v1.1.1
github.com/cenkalti/backoff/v4 v4.1.2
github.com/containerd/cgroups v1.0.3
github.com/containerd/containerd v1.6.1
github.com/containernetworking/cni v1.0.1
github.com/containernetworking/plugins v1.1.0
Expand All @@ -27,6 +26,7 @@ require (
github.com/moby/sys/mount v0.3.0 // indirect
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.3-0.20211202193544-a5463b7f9c84
github.com/opencontainers/runc v1.1.0
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/opencontainers/runtime-tools v0.9.1-0.20210326182921-59cdde06764b
github.com/opencontainers/selinux v1.10.0
Expand Down
1 change: 0 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1204,7 +1204,6 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
Expand Down
2 changes: 0 additions & 2 deletions internal/pkg/cgroups/config_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ func Int64ptr(i int) *int64 {
return &t
}

var wildcard = Int64ptr(-1)

// LinuxHugepageLimit structure corresponds to limiting kernel hugepages
type LinuxHugepageLimit struct {
// Pagesize is the hugepage size
Expand Down
278 changes: 278 additions & 0 deletions internal/pkg/cgroups/manager_libcontainer_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
// Copyright (c) Contributors to the Apptainer project, established as
// Apptainer a Series of LF Projects LLC.
// For website terms of use, trademark policy, privacy policy and other
// project policies see https://lfprojects.org/policies
// Copyright (c) 2021-2022, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package cgroups

import (
"fmt"
"path/filepath"
"strings"

lccgroups "github.com/opencontainers/runc/libcontainer/cgroups"
lcmanager "github.com/opencontainers/runc/libcontainer/cgroups/manager"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/specconv"
specs "github.com/opencontainers/runtime-spec/specs-go"
)

const unifiedMountPoint = "/sys/fs/cgroup"

// ManagerLibcontainer manages a cgroup 'Group', using the runc/libcontainer packages
type ManagerLC struct {
group string
pid int
cgroup lccgroups.Manager
}

func (m *ManagerLC) load() (err error) {
if m.group != "" {
return m.loadFromPath()
}
return m.loadFromPid()
}

func (m *ManagerLC) loadFromPid() (err error) {
if m.pid == 0 {
return fmt.Errorf("cannot load from pid - no process ID specified")
}

pidCGFile := fmt.Sprintf("/proc/%d/cgroup", m.pid)
paths, err := lccgroups.ParseCgroupFile(pidCGFile)
if err != nil {
return fmt.Errorf("cannot read %s: %w", pidCGFile, err)
}

// cgroups v2 path is always given by the unified "" subsystem
ok := false
if lccgroups.IsCgroup2UnifiedMode() {
m.group, ok = paths[""]
if !ok {
return fmt.Errorf("could not find cgroups v2 unified path")
}
return m.loadFromPath()
}

// For cgroups v1 we are relying on fetching the 'devices' subsystem path.
// The devices subsystem is needed for our OCI engine and its presence is
// enforced in runc/libcontainer/cgroups/fs initialization without 'skipDevices'.
// This means we never explicitly put a container into a cgroup without a
// set 'devices' path.
m.group, ok = paths["devices"]
if !ok {
return fmt.Errorf("could not find cgroups v1 path (using devices subsystem)")
}
return m.loadFromPath()
}

func (m *ManagerLC) loadFromPath() (err error) {
if m.group == "" {
return fmt.Errorf("cannot load from path - no path specified")
}

lcConfig := &configs.Cgroup{
Path: m.group,
Resources: &configs.Resources{},
}

m.cgroup, err = lcmanager.New(lcConfig)
if err != nil {
return fmt.Errorf("while creating cgroup manager: %w", err)
}

return nil
}

// GetCgroupRootPath returns cgroup root path
// TODO - this returns "" on error which needs to be checked for
// carefully. Should return an actual error instead.
func (m *ManagerLC) GetCgroupRootPath() string {
if m.cgroup == nil {
return ""
}

// v2 - has a single fixed mountpoint for the root cgroup
if lccgroups.IsCgroup2UnifiedMode() {
return unifiedMountPoint
}

// v1 - Get absolute paths to cgroup by subsystem
subPaths := m.cgroup.GetPaths()

// For cgroups v1 we are relying on fetching the 'devices' subsystem path.
// The devices subsystem is needed for our OCI engine and its presence is
// enforced in runc/libcontainer/cgroups/fs initialization without 'skipDevices'.
// This means we never explicitly put a container into a cgroup without a
// set 'devices' path.
devicePath, ok := subPaths["devices"]
if !ok {
return ""
}

// Take the piece before the first occurrence of "devices" as the root.
// I.E. /sys/fs/cgroup/devices/singularity/196219 -> /sys/fs/cgroup
pathParts := strings.Split(devicePath, "devices")
if len(pathParts) != 2 {
return ""
}

return filepath.Clean(pathParts[0])
}

// ApplyFromSpec applies a cgroups configuration from an OCI LinuxResources spec
// struct, creating a new group if necessary, and places the process with
// Manager.Pid into the cgroup. The `Unified` key for native v2 cgroup
// specifications is not yet supported.
func (m *ManagerLC) ApplyFromSpec(resources *specs.LinuxResources) (err error) {
if m.group == "" {
return fmt.Errorf("path must be specified when creating a cgroup")
}
if m.pid == 0 {
return fmt.Errorf("pid must be specified when creating a cgroup")
}

spec := &specs.Spec{
Linux: &specs.Linux{
CgroupsPath: m.group,
Resources: resources,
},
}

opts := &specconv.CreateOpts{
CgroupName: m.group,
UseSystemdCgroup: false,
RootlessCgroups: false,
Spec: spec,
}

lcConfig, err := specconv.CreateCgroupConfig(opts, nil)
if err != nil {
return fmt.Errorf("could not create cgroup config: %w", err)
}

m.cgroup, err = lcmanager.New(lcConfig)
if err != nil {
return fmt.Errorf("while creating cgroup manager: %w", err)
}

err = m.cgroup.Apply(m.pid)
if err != nil {
return fmt.Errorf("while creating cgroup: %w", err)
}

err = m.cgroup.Set(lcConfig.Resources)
if err != nil {
return fmt.Errorf("while setting cgroup limits: %w", err)
}

return nil
}

// ApplyFromFile applies a cgroup configuration from a toml file, creating a new
// group if necessary, and places the process with Manager.Pid into the cgroup.
// The `Unified` key for native v2 cgroup specifications is not yet supported.
func (m *ManagerLC) ApplyFromFile(path string) error {
spec, err := readSpecFromFile(path)
if err != nil {
return err
}
return m.ApplyFromSpec(&spec)
}

// UpdateFromSpec updates the existing managed cgroup using configuration from
// an OCI LinuxResources spec struct. The `Unified` key for native v2 cgroup
// specifications is not yet supported.
func (m *ManagerLC) UpdateFromSpec(resources *specs.LinuxResources) (err error) {
if m.cgroup == nil {
err = m.load()
if err != nil {
return fmt.Errorf("while creating cgroup manager: %w", err)
}
}
if m.group == "" {
return fmt.Errorf("cgroup path not set on manager, cannot update")
}

spec := &specs.Spec{
Linux: &specs.Linux{
CgroupsPath: m.group,
Resources: resources,
},
}

opts := &specconv.CreateOpts{
CgroupName: m.group,
UseSystemdCgroup: false,
RootlessCgroups: false,
Spec: spec,
}

lcConfig, err := specconv.CreateCgroupConfig(opts, nil)
if err != nil {
return fmt.Errorf("could not create cgroup config: %w", err)
}

err = m.cgroup.Set(lcConfig.Resources)
if err != nil {
return fmt.Errorf("while setting cgroup limits: %w", err)
}

return nil
}

// UpdateFromFile updates the existing managed cgroup using configuration
// from a toml file.
func (m *ManagerLC) UpdateFromFile(path string) error {
spec, err := readSpecFromFile(path)
if err != nil {
return err
}
return m.UpdateFromSpec(&spec)
}

// Remove deletes the managed cgroup.
func (m *ManagerLC) Remove() (err error) {
if m.cgroup == nil {
if err := m.load(); err != nil {
return err
}
}
return m.cgroup.Destroy()
}

func (m *ManagerLC) AddProc(pid int) (err error) {
if pid == 0 {
return fmt.Errorf("cannot add a zero pid to cgroup")
}
if m.cgroup == nil {
if err := m.load(); err != nil {
return err
}
}
return m.cgroup.Apply(pid)
}

// Pause freezes processes in the managed cgroup.
func (m *ManagerLC) Pause() (err error) {
if m.cgroup == nil {
if err := m.load(); err != nil {
return err
}
}
return m.cgroup.Freeze(configs.Frozen)
}

// Resume unfreezes process in the managed cgroup.
func (m *ManagerLC) Resume() (err error) {
if m.cgroup == nil {
if err := m.load(); err != nil {
return err
}
}
return m.cgroup.Freeze(configs.Thawed)
}
Loading

0 comments on commit 48f52dc

Please sign in to comment.