129 lines
3.6 KiB
Go
129 lines
3.6 KiB
Go
// +build linux
|
|
|
|
package rootless
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
|
"github.com/opencontainers/runc/libcontainer/configs/validate"
|
|
)
|
|
|
|
// TODO: This is copied from libcontainer/cgroups/fs, which duplicates this code
|
|
// needlessly. We should probably export this list.
|
|
|
|
var subsystems = []subsystem{
|
|
&fs.CpusetGroup{},
|
|
&fs.DevicesGroup{},
|
|
&fs.MemoryGroup{},
|
|
&fs.CpuGroup{},
|
|
&fs.CpuacctGroup{},
|
|
&fs.PidsGroup{},
|
|
&fs.BlkioGroup{},
|
|
&fs.HugetlbGroup{},
|
|
&fs.NetClsGroup{},
|
|
&fs.NetPrioGroup{},
|
|
&fs.PerfEventGroup{},
|
|
&fs.FreezerGroup{},
|
|
&fs.NameGroup{GroupName: "name=systemd"},
|
|
}
|
|
|
|
type subsystem interface {
|
|
// Name returns the name of the subsystem.
|
|
Name() string
|
|
|
|
// Returns the stats, as 'stats', corresponding to the cgroup under 'path'.
|
|
GetStats(path string, stats *cgroups.Stats) error
|
|
}
|
|
|
|
// The noop cgroup manager is used for rootless containers, because we currently
|
|
// cannot manage cgroups if we are in a rootless setup. This manager is chosen
|
|
// by factory if we are in rootless mode. We error out if any cgroup options are
|
|
// set in the config -- this may change in the future with upcoming kernel features
|
|
// like the cgroup namespace.
|
|
|
|
type Manager struct {
|
|
Cgroups *configs.Cgroup
|
|
Paths map[string]string
|
|
}
|
|
|
|
func (m *Manager) Apply(pid int) error {
|
|
// If there are no cgroup settings, there's nothing to do.
|
|
if m.Cgroups == nil {
|
|
return nil
|
|
}
|
|
|
|
// We can't set paths.
|
|
// TODO(cyphar): Implement the case where the runner of a rootless container
|
|
// owns their own cgroup, which would allow us to set up a
|
|
// cgroup for each path.
|
|
if m.Cgroups.Paths != nil {
|
|
return fmt.Errorf("cannot change cgroup path in rootless container")
|
|
}
|
|
|
|
// We load the paths into the manager.
|
|
paths := make(map[string]string)
|
|
for _, sys := range subsystems {
|
|
name := sys.Name()
|
|
|
|
path, err := cgroups.GetOwnCgroupPath(name)
|
|
if err != nil {
|
|
// Ignore paths we couldn't resolve.
|
|
continue
|
|
}
|
|
|
|
paths[name] = path
|
|
}
|
|
|
|
m.Paths = paths
|
|
return nil
|
|
}
|
|
|
|
func (m *Manager) GetPaths() map[string]string {
|
|
return m.Paths
|
|
}
|
|
|
|
func (m *Manager) Set(container *configs.Config) error {
|
|
// We have to re-do the validation here, since someone might decide to
|
|
// update a rootless container.
|
|
return validate.New().Validate(container)
|
|
}
|
|
|
|
func (m *Manager) GetPids() ([]int, error) {
|
|
dir, err := cgroups.GetOwnCgroupPath("devices")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return cgroups.GetPids(dir)
|
|
}
|
|
|
|
func (m *Manager) GetAllPids() ([]int, error) {
|
|
dir, err := cgroups.GetOwnCgroupPath("devices")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return cgroups.GetAllPids(dir)
|
|
}
|
|
|
|
func (m *Manager) GetStats() (*cgroups.Stats, error) {
|
|
// TODO(cyphar): We can make this work if we figure out a way to allow usage
|
|
// of cgroups with a rootless container. While this doesn't
|
|
// actually require write access to a cgroup directory, the
|
|
// statistics are not useful if they can be affected by
|
|
// non-container processes.
|
|
return nil, fmt.Errorf("cannot get cgroup stats in rootless container")
|
|
}
|
|
|
|
func (m *Manager) Freeze(state configs.FreezerState) error {
|
|
// TODO(cyphar): We can make this work if we figure out a way to allow usage
|
|
// of cgroups with a rootless container.
|
|
return fmt.Errorf("cannot use freezer cgroup in rootless container")
|
|
}
|
|
|
|
func (m *Manager) Destroy() error {
|
|
// We don't have to do anything here because we didn't do any setup.
|
|
return nil
|
|
}
|