vendor: cadvisor v0.39.0
Main upgrades: - github.com/opencontainers/runc v1.0.0-rc93 - github.com/containerd/containerd v1.4.4 - github.com/docker/docker v20.10.2 - github.com/mrunalp/fileutils v0.5.0 - github.com/opencontainers/selinux v1.8.0 - github.com/cilium/ebpf v0.2.0
This commit is contained in:
118
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/common.go
generated
vendored
118
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/common.go
generated
vendored
@@ -13,12 +13,20 @@ import (
|
||||
|
||||
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
|
||||
dbus "github.com/godbus/dbus/v5"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/devices"
|
||||
cgroupdevices "github.com/opencontainers/runc/libcontainer/cgroups/devices"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/opencontainers/runc/libcontainer/devices"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
// Default kernel value for cpu quota period is 100000 us (100 ms), same for v1 and v2.
|
||||
// v1: https://www.kernel.org/doc/html/latest/scheduler/sched-bwc.html and
|
||||
// v2: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
|
||||
defCPUQuotaPeriod = uint64(100000)
|
||||
)
|
||||
|
||||
var (
|
||||
connOnce sync.Once
|
||||
connDbus *systemdDbus.Conn
|
||||
@@ -26,7 +34,6 @@ var (
|
||||
|
||||
versionOnce sync.Once
|
||||
version int
|
||||
versionErr error
|
||||
|
||||
isRunningSystemdOnce sync.Once
|
||||
isRunningSystemd bool
|
||||
@@ -81,11 +88,11 @@ func ExpandSlice(slice string) (string, error) {
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func groupPrefix(ruleType configs.DeviceType) (string, error) {
|
||||
func groupPrefix(ruleType devices.Type) (string, error) {
|
||||
switch ruleType {
|
||||
case configs.BlockDevice:
|
||||
case devices.BlockDevice:
|
||||
return "block-", nil
|
||||
case configs.CharDevice:
|
||||
case devices.CharDevice:
|
||||
return "char-", nil
|
||||
default:
|
||||
return "", errors.Errorf("device type %v has no group prefix", ruleType)
|
||||
@@ -93,10 +100,10 @@ func groupPrefix(ruleType configs.DeviceType) (string, error) {
|
||||
}
|
||||
|
||||
// findDeviceGroup tries to find the device group name (as listed in
|
||||
// /proc/devices) with the type prefixed as requried for DeviceAllow, for a
|
||||
// /proc/devices) with the type prefixed as required for DeviceAllow, for a
|
||||
// given (type, major) combination. If more than one device group exists, an
|
||||
// arbitrary one is chosen.
|
||||
func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, error) {
|
||||
func findDeviceGroup(ruleType devices.Type, ruleMajor int64) (string, error) {
|
||||
fh, err := os.Open("/proc/devices")
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -109,7 +116,7 @@ func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, erro
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(fh)
|
||||
var currentType configs.DeviceType
|
||||
var currentType devices.Type
|
||||
for scanner.Scan() {
|
||||
// We need to strip spaces because the first number is column-aligned.
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
@@ -117,10 +124,10 @@ func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, erro
|
||||
// Handle the "header" lines.
|
||||
switch line {
|
||||
case "Block devices:":
|
||||
currentType = configs.BlockDevice
|
||||
currentType = devices.BlockDevice
|
||||
continue
|
||||
case "Character devices:":
|
||||
currentType = configs.CharDevice
|
||||
currentType = devices.CharDevice
|
||||
continue
|
||||
case "":
|
||||
continue
|
||||
@@ -156,7 +163,7 @@ func findDeviceGroup(ruleType configs.DeviceType, ruleMajor int64) (string, erro
|
||||
|
||||
// generateDeviceProperties takes the configured device rules and generates a
|
||||
// corresponding set of systemd properties to configure the devices correctly.
|
||||
func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Property, error) {
|
||||
func generateDeviceProperties(rules []*devices.Rule) ([]systemdDbus.Property, error) {
|
||||
// DeviceAllow is the type "a(ss)" which means we need a temporary struct
|
||||
// to represent it in Go.
|
||||
type deviceAllowEntry struct {
|
||||
@@ -172,7 +179,7 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
|
||||
}
|
||||
|
||||
// Figure out the set of rules.
|
||||
configEmu := &devices.Emulator{}
|
||||
configEmu := &cgroupdevices.Emulator{}
|
||||
for _, rule := range rules {
|
||||
if err := configEmu.Apply(*rule); err != nil {
|
||||
return nil, errors.Wrap(err, "apply rule for systemd")
|
||||
@@ -199,7 +206,7 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
|
||||
// Now generate the set of rules we actually need to apply. Unlike the
|
||||
// normal devices cgroup, in "strict" mode systemd defaults to a deny-all
|
||||
// whitelist which is the default for devices.Emulator.
|
||||
baseEmu := &devices.Emulator{}
|
||||
baseEmu := &cgroupdevices.Emulator{}
|
||||
finalRules, err := baseEmu.Transition(configEmu)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get simplified rules for systemd")
|
||||
@@ -211,7 +218,7 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
|
||||
return nil, errors.Errorf("[internal error] cannot add deny rule to systemd DeviceAllow list: %v", *rule)
|
||||
}
|
||||
switch rule.Type {
|
||||
case configs.BlockDevice, configs.CharDevice:
|
||||
case devices.BlockDevice, devices.CharDevice:
|
||||
default:
|
||||
// Should never happen.
|
||||
return nil, errors.Errorf("invalid device type for DeviceAllow: %v", rule.Type)
|
||||
@@ -243,9 +250,9 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
|
||||
// so we'll give a warning in that case (note that the fallback code
|
||||
// will insert any rules systemd couldn't handle). What amazing fun.
|
||||
|
||||
if rule.Major == configs.Wildcard {
|
||||
if rule.Major == devices.Wildcard {
|
||||
// "_ *:n _" rules aren't supported by systemd.
|
||||
if rule.Minor != configs.Wildcard {
|
||||
if rule.Minor != devices.Wildcard {
|
||||
logrus.Warnf("systemd doesn't support '*:n' device rules -- temporarily ignoring rule: %v", *rule)
|
||||
continue
|
||||
}
|
||||
@@ -256,7 +263,7 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
|
||||
return nil, err
|
||||
}
|
||||
entry.Path = prefix + "*"
|
||||
} else if rule.Minor == configs.Wildcard {
|
||||
} else if rule.Minor == devices.Wildcard {
|
||||
// "_ n:* _" rules require a device group from /proc/devices.
|
||||
group, err := findDeviceGroup(rule.Type, rule.Major)
|
||||
if err != nil {
|
||||
@@ -271,9 +278,9 @@ func generateDeviceProperties(rules []*configs.DeviceRule) ([]systemdDbus.Proper
|
||||
} else {
|
||||
// "_ n:m _" rules are just a path in /dev/{block,char}/.
|
||||
switch rule.Type {
|
||||
case configs.BlockDevice:
|
||||
case devices.BlockDevice:
|
||||
entry.Path = fmt.Sprintf("/dev/block/%d:%d", rule.Major, rule.Minor)
|
||||
case configs.CharDevice:
|
||||
case devices.CharDevice:
|
||||
entry.Path = fmt.Sprintf("/dev/char/%d:%d", rule.Major, rule.Minor)
|
||||
}
|
||||
}
|
||||
@@ -307,7 +314,7 @@ func newProp(name string, units interface{}) systemdDbus.Property {
|
||||
func getUnitName(c *configs.Cgroup) string {
|
||||
// by default, we create a scope unless the user explicitly asks for a slice.
|
||||
if !strings.HasSuffix(c.Name, ".slice") {
|
||||
return fmt.Sprintf("%s-%s.scope", c.ScopePrefix, c.Name)
|
||||
return c.ScopePrefix + "-" + c.Name + ".scope"
|
||||
}
|
||||
return c.Name
|
||||
}
|
||||
@@ -325,6 +332,9 @@ func isUnitExists(err error) bool {
|
||||
func startUnit(dbusConnection *systemdDbus.Conn, unitName string, properties []systemdDbus.Property) error {
|
||||
statusChan := make(chan string, 1)
|
||||
if _, err := dbusConnection.StartTransientUnit(unitName, "replace", properties, statusChan); err == nil {
|
||||
timeout := time.NewTimer(30 * time.Second)
|
||||
defer timeout.Stop()
|
||||
|
||||
select {
|
||||
case s := <-statusChan:
|
||||
close(statusChan)
|
||||
@@ -333,8 +343,9 @@ func startUnit(dbusConnection *systemdDbus.Conn, unitName string, properties []s
|
||||
dbusConnection.ResetFailedUnit(unitName)
|
||||
return errors.Errorf("error creating systemd unit `%s`: got `%s`", unitName, s)
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
logrus.Warnf("Timed out while waiting for StartTransientUnit(%s) completion signal from dbus. Continuing...", unitName)
|
||||
case <-timeout.C:
|
||||
dbusConnection.ResetFailedUnit(unitName)
|
||||
return errors.New("Timeout waiting for systemd to create " + unitName)
|
||||
}
|
||||
} else if !isUnitExists(err) {
|
||||
return err
|
||||
@@ -360,20 +371,20 @@ func stopUnit(dbusConnection *systemdDbus.Conn, unitName string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func systemdVersion(conn *systemdDbus.Conn) (int, error) {
|
||||
func systemdVersion(conn *systemdDbus.Conn) int {
|
||||
versionOnce.Do(func() {
|
||||
version = -1
|
||||
verStr, err := conn.GetManagerProperty("Version")
|
||||
if err != nil {
|
||||
versionErr = err
|
||||
return
|
||||
if err == nil {
|
||||
version, err = systemdVersionAtoi(verStr)
|
||||
}
|
||||
|
||||
version, versionErr = systemdVersionAtoi(verStr)
|
||||
return
|
||||
if err != nil {
|
||||
logrus.WithError(err).Error("unable to get systemd version")
|
||||
}
|
||||
})
|
||||
|
||||
return version, versionErr
|
||||
return version
|
||||
}
|
||||
|
||||
func systemdVersionAtoi(verStr string) (int, error) {
|
||||
@@ -394,12 +405,13 @@ func systemdVersionAtoi(verStr string) (int, error) {
|
||||
func addCpuQuota(conn *systemdDbus.Conn, properties *[]systemdDbus.Property, quota int64, period uint64) {
|
||||
if period != 0 {
|
||||
// systemd only supports CPUQuotaPeriodUSec since v242
|
||||
sdVer, err := systemdVersion(conn)
|
||||
if err != nil {
|
||||
logrus.Warnf("systemdVersion: %s", err)
|
||||
} else if sdVer >= 242 {
|
||||
sdVer := systemdVersion(conn)
|
||||
if sdVer >= 242 {
|
||||
*properties = append(*properties,
|
||||
newProp("CPUQuotaPeriodUSec", period))
|
||||
} else {
|
||||
logrus.Debugf("systemd v%d is too old to support CPUQuotaPeriodSec "+
|
||||
" (setting will still be applied to cgroupfs)", sdVer)
|
||||
}
|
||||
}
|
||||
if quota != 0 || period != 0 {
|
||||
@@ -407,10 +419,8 @@ func addCpuQuota(conn *systemdDbus.Conn, properties *[]systemdDbus.Property, quo
|
||||
cpuQuotaPerSecUSec := uint64(math.MaxUint64)
|
||||
if quota > 0 {
|
||||
if period == 0 {
|
||||
// assume the default kernel value of 100000 us (100 ms), same for v1 and v2.
|
||||
// v1: https://www.kernel.org/doc/html/latest/scheduler/sched-bwc.html and
|
||||
// v2: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
|
||||
period = 100000
|
||||
// assume the default
|
||||
period = defCPUQuotaPeriod
|
||||
}
|
||||
// systemd converts CPUQuotaPerSecUSec (microseconds per CPU second) to CPUQuota
|
||||
// (integer percentage of CPU) internally. This means that if a fractional percent of
|
||||
@@ -425,3 +435,37 @@ func addCpuQuota(conn *systemdDbus.Conn, properties *[]systemdDbus.Property, quo
|
||||
newProp("CPUQuotaPerSecUSec", cpuQuotaPerSecUSec))
|
||||
}
|
||||
}
|
||||
|
||||
func addCpuset(conn *systemdDbus.Conn, props *[]systemdDbus.Property, cpus, mems string) error {
|
||||
if cpus == "" && mems == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// systemd only supports AllowedCPUs/AllowedMemoryNodes since v244
|
||||
sdVer := systemdVersion(conn)
|
||||
if sdVer < 244 {
|
||||
logrus.Debugf("systemd v%d is too old to support AllowedCPUs/AllowedMemoryNodes"+
|
||||
" (settings will still be applied to cgroupfs)", sdVer)
|
||||
return nil
|
||||
}
|
||||
|
||||
if cpus != "" {
|
||||
bits, err := rangeToBits(cpus)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resources.CPU.Cpus=%q conversion error: %w",
|
||||
cpus, err)
|
||||
}
|
||||
*props = append(*props,
|
||||
newProp("AllowedCPUs", bits))
|
||||
}
|
||||
if mems != "" {
|
||||
bits, err := rangeToBits(mems)
|
||||
if err != nil {
|
||||
return fmt.Errorf("resources.CPU.Mems=%q conversion error: %w",
|
||||
mems, err)
|
||||
}
|
||||
*props = append(*props,
|
||||
newProp("AllowedMemoryNodes", bits))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
67
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/cpuset.go
generated
vendored
Normal file
67
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/cpuset.go
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
package systemd
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/willf/bitset"
|
||||
)
|
||||
|
||||
// rangeToBits converts a text representation of a CPU mask (as written to
|
||||
// or read from cgroups' cpuset.* files, e.g. "1,3-5") to a slice of bytes
|
||||
// with the corresponding bits set (as consumed by systemd over dbus as
|
||||
// AllowedCPUs/AllowedMemoryNodes unit property value).
|
||||
func rangeToBits(str string) ([]byte, error) {
|
||||
bits := &bitset.BitSet{}
|
||||
|
||||
for _, r := range strings.Split(str, ",") {
|
||||
// allow extra spaces around
|
||||
r = strings.TrimSpace(r)
|
||||
// allow empty elements (extra commas)
|
||||
if r == "" {
|
||||
continue
|
||||
}
|
||||
ranges := strings.SplitN(r, "-", 2)
|
||||
if len(ranges) > 1 {
|
||||
start, err := strconv.ParseUint(ranges[0], 10, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
end, err := strconv.ParseUint(ranges[1], 10, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if start > end {
|
||||
return nil, errors.New("invalid range: " + r)
|
||||
}
|
||||
for i := uint(start); i <= uint(end); i++ {
|
||||
bits.Set(i)
|
||||
}
|
||||
} else {
|
||||
val, err := strconv.ParseUint(ranges[0], 10, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bits.Set(uint(val))
|
||||
}
|
||||
}
|
||||
|
||||
val := bits.Bytes()
|
||||
if len(val) == 0 {
|
||||
// do not allow empty values
|
||||
return nil, errors.New("empty value")
|
||||
}
|
||||
ret := make([]byte, len(val)*8)
|
||||
for i := range val {
|
||||
// bitset uses BigEndian internally
|
||||
binary.BigEndian.PutUint64(ret[i*8:], val[len(val)-1-i])
|
||||
}
|
||||
// remove upper all-zero bytes
|
||||
for ret[0] == 0 {
|
||||
ret = ret[1:]
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
4
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/user.go
generated
vendored
4
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/user.go
generated
vendored
@@ -57,7 +57,7 @@ func DetectUID() (int, error) {
|
||||
}
|
||||
b, err := exec.Command("busctl", "--user", "--no-pager", "status").CombinedOutput()
|
||||
if err != nil {
|
||||
return -1, errors.Wrap(err, "could not execute `busctl --user --no-pager status`")
|
||||
return -1, errors.Wrapf(err, "could not execute `busctl --user --no-pager status`: %q", string(b))
|
||||
}
|
||||
scanner := bufio.NewScanner(bytes.NewReader(b))
|
||||
for scanner.Scan() {
|
||||
@@ -102,5 +102,5 @@ func DetectUserDbusSessionBusAddress() (string, error) {
|
||||
return strings.TrimPrefix(s, "DBUS_SESSION_BUS_ADDRESS="), nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("could not detect DBUS_SESSION_BUS_ADDRESS from `systemctl --user --no-pager show-environment`")
|
||||
return "", errors.New("could not detect DBUS_SESSION_BUS_ADDRESS from `systemctl --user --no-pager show-environment`. Make sure you have installed the dbus-user-session or dbus-daemon package and then run: `systemctl --user start dbus`")
|
||||
}
|
||||
|
140
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/v1.go
generated
vendored
140
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/v1.go
generated
vendored
@@ -4,7 +4,6 @@ package systemd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -13,6 +12,7 @@ import (
|
||||
systemdDbus "github.com/coreos/go-systemd/v22/dbus"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -90,6 +90,11 @@ func genV1ResourcesProperties(c *configs.Cgroup, conn *systemdDbus.Conn) ([]syst
|
||||
newProp("TasksMax", uint64(r.PidsLimit)))
|
||||
}
|
||||
|
||||
err = addCpuset(conn, &properties, r.CpusetCpus, r.CpusetMems)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return properties, nil
|
||||
}
|
||||
|
||||
@@ -101,20 +106,23 @@ func (m *legacyManager) Apply(pid int) error {
|
||||
properties []systemdDbus.Property
|
||||
)
|
||||
|
||||
if c.Resources.Unified != nil {
|
||||
return cgroups.ErrV1NoUnified
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if c.Paths != nil {
|
||||
paths := make(map[string]string)
|
||||
cgMap, err := cgroups.ParseCgroupFile("/proc/self/cgroup")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// XXX(kolyshkin@): why this check is needed?
|
||||
for name, path := range c.Paths {
|
||||
_, err := getSubsystemPath(m.cgroups, name)
|
||||
if err != nil {
|
||||
// Don't fail if a cgroup hierarchy was not found, just skip this subsystem
|
||||
if cgroups.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
return err
|
||||
if _, ok := cgMap[name]; ok {
|
||||
paths[name] = path
|
||||
}
|
||||
paths[name] = path
|
||||
}
|
||||
m.paths = paths
|
||||
return cgroups.EnterPid(m.paths, pid)
|
||||
@@ -179,14 +187,16 @@ func (m *legacyManager) Apply(pid int) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := joinCgroups(c, pid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
paths := make(map[string]string)
|
||||
for _, s := range legacySubsystems {
|
||||
subsystemPath, err := getSubsystemPath(m.cgroups, s.Name())
|
||||
if err != nil {
|
||||
// Even if it's `not found` error, we'll return err
|
||||
// because devices cgroup is hard requirement for
|
||||
// container security.
|
||||
if s.Name() == "devices" {
|
||||
return err
|
||||
}
|
||||
// Don't fail if a cgroup hierarchy was not found, just skip this subsystem
|
||||
if cgroups.IsNotFound(err) {
|
||||
continue
|
||||
@@ -196,6 +206,11 @@ func (m *legacyManager) Apply(pid int) error {
|
||||
paths[s.Name()] = subsystemPath
|
||||
}
|
||||
m.paths = paths
|
||||
|
||||
if err := m.joinCgroups(pid); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -212,17 +227,14 @@ func (m *legacyManager) Destroy() error {
|
||||
}
|
||||
unitName := getUnitName(m.cgroups)
|
||||
|
||||
err = stopUnit(dbusConnection, unitName)
|
||||
stopErr := stopUnit(dbusConnection, unitName)
|
||||
// Both on success and on error, cleanup all the cgroups we are aware of.
|
||||
// Some of them were created directly by Apply() and are not managed by systemd.
|
||||
if err := cgroups.RemovePaths(m.paths); err != nil {
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.paths = make(map[string]string)
|
||||
return nil
|
||||
|
||||
return stopErr
|
||||
}
|
||||
|
||||
func (m *legacyManager) Path(subsys string) string {
|
||||
@@ -231,48 +243,25 @@ func (m *legacyManager) Path(subsys string) string {
|
||||
return m.paths[subsys]
|
||||
}
|
||||
|
||||
func join(c *configs.Cgroup, subsystem string, pid int) (string, error) {
|
||||
path, err := getSubsystemPath(c, subsystem)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(path, 0755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := cgroups.WriteCgroupProc(path, pid); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func joinCgroups(c *configs.Cgroup, pid int) error {
|
||||
func (m *legacyManager) joinCgroups(pid int) error {
|
||||
for _, sys := range legacySubsystems {
|
||||
name := sys.Name()
|
||||
switch name {
|
||||
case "name=systemd":
|
||||
// let systemd handle this
|
||||
case "cpuset":
|
||||
path, err := getSubsystemPath(c, name)
|
||||
if err != nil && !cgroups.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
s := &fs.CpusetGroup{}
|
||||
if err := s.ApplyDir(path, c, pid); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
_, err := join(c, name, pid)
|
||||
if err != nil {
|
||||
// Even if it's `not found` error, we'll return err
|
||||
// because devices cgroup is hard requirement for
|
||||
// container security.
|
||||
if name == "devices" {
|
||||
if path, ok := m.paths[name]; ok {
|
||||
s := &fs.CpusetGroup{}
|
||||
if err := s.ApplyDir(path, m.cgroups, pid); err != nil {
|
||||
return err
|
||||
}
|
||||
// For other subsystems, omit the `not found` error
|
||||
// because they are optional.
|
||||
if !cgroups.IsNotFound(err) {
|
||||
}
|
||||
default:
|
||||
if path, ok := m.paths[name]; ok {
|
||||
if err := os.MkdirAll(path, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := cgroups.WriteCgroupProc(path, pid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -283,7 +272,7 @@ func joinCgroups(c *configs.Cgroup, pid int) error {
|
||||
}
|
||||
|
||||
func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) {
|
||||
mountpoint, err := cgroups.FindCgroupMountpoint(c.Path, subsystem)
|
||||
mountpoint, err := cgroups.FindCgroupMountpoint("", subsystem)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -309,15 +298,14 @@ func getSubsystemPath(c *configs.Cgroup, subsystem string) (string, error) {
|
||||
}
|
||||
|
||||
func (m *legacyManager) Freeze(state configs.FreezerState) error {
|
||||
path, err := getSubsystemPath(m.cgroups, "freezer")
|
||||
if err != nil {
|
||||
return err
|
||||
path, ok := m.paths["freezer"]
|
||||
if !ok {
|
||||
return errSubsystemDoesNotExist
|
||||
}
|
||||
prevState := m.cgroups.Resources.Freezer
|
||||
m.cgroups.Resources.Freezer = state
|
||||
freezer := &fs.FreezerGroup{}
|
||||
err = freezer.Set(path, m.cgroups)
|
||||
if err != nil {
|
||||
if err := freezer.Set(path, m.cgroups); err != nil {
|
||||
m.cgroups.Resources.Freezer = prevState
|
||||
return err
|
||||
}
|
||||
@@ -325,17 +313,17 @@ func (m *legacyManager) Freeze(state configs.FreezerState) error {
|
||||
}
|
||||
|
||||
func (m *legacyManager) GetPids() ([]int, error) {
|
||||
path, err := getSubsystemPath(m.cgroups, "devices")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
path, ok := m.paths["devices"]
|
||||
if !ok {
|
||||
return nil, errSubsystemDoesNotExist
|
||||
}
|
||||
return cgroups.GetPids(path)
|
||||
}
|
||||
|
||||
func (m *legacyManager) GetAllPids() ([]int, error) {
|
||||
path, err := getSubsystemPath(m.cgroups, "devices")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
path, ok := m.paths["devices"]
|
||||
if !ok {
|
||||
return nil, errSubsystemDoesNotExist
|
||||
}
|
||||
return cgroups.GetAllPids(path)
|
||||
}
|
||||
@@ -363,6 +351,9 @@ func (m *legacyManager) Set(container *configs.Config) error {
|
||||
if m.cgroups.Paths != nil {
|
||||
return nil
|
||||
}
|
||||
if container.Cgroups.Resources.Unified != nil {
|
||||
return cgroups.ErrV1NoUnified
|
||||
}
|
||||
dbusConnection, err := getDbusConnection(false)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -406,9 +397,9 @@ func (m *legacyManager) Set(container *configs.Config) error {
|
||||
|
||||
for _, sys := range legacySubsystems {
|
||||
// Get the subsystem path, but don't error out for not found cgroups.
|
||||
path, err := getSubsystemPath(container.Cgroups, sys.Name())
|
||||
if err != nil && !cgroups.IsNotFound(err) {
|
||||
return err
|
||||
path, ok := m.paths[sys.Name()]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if err := sys.Set(path, container.Cgroups); err != nil {
|
||||
return err
|
||||
@@ -420,7 +411,10 @@ func (m *legacyManager) Set(container *configs.Config) error {
|
||||
|
||||
func enableKmem(c *configs.Cgroup) error {
|
||||
path, err := getSubsystemPath(c, "memory")
|
||||
if err != nil && !cgroups.IsNotFound(err) {
|
||||
if err != nil {
|
||||
if cgroups.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -429,7 +423,7 @@ func enableKmem(c *configs.Cgroup) error {
|
||||
}
|
||||
// do not try to enable the kernel memory if we already have
|
||||
// tasks in the cgroup.
|
||||
content, err := ioutil.ReadFile(filepath.Join(path, "tasks"))
|
||||
content, err := fscommon.ReadFile(path, "tasks")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -450,9 +444,9 @@ func (m *legacyManager) GetCgroups() (*configs.Cgroup, error) {
|
||||
}
|
||||
|
||||
func (m *legacyManager) GetFreezerState() (configs.FreezerState, error) {
|
||||
path, err := getSubsystemPath(m.cgroups, "freezer")
|
||||
if err != nil && !cgroups.IsNotFound(err) {
|
||||
return configs.Undefined, err
|
||||
path, ok := m.paths["freezer"]
|
||||
if !ok {
|
||||
return configs.Undefined, nil
|
||||
}
|
||||
freezer := &fs.FreezerGroup{}
|
||||
return freezer.GetState(path)
|
||||
|
143
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/v2.go
generated
vendored
143
vendor/github.com/opencontainers/runc/libcontainer/cgroups/systemd/v2.go
generated
vendored
@@ -3,6 +3,8 @@
|
||||
package systemd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -34,6 +36,133 @@ func NewUnifiedManager(config *configs.Cgroup, path string, rootless bool) cgrou
|
||||
}
|
||||
}
|
||||
|
||||
// unifiedResToSystemdProps tries to convert from Cgroup.Resources.Unified
|
||||
// key/value map (where key is cgroupfs file name) to systemd unit properties.
|
||||
// This is on a best-effort basis, so the properties that are not known
|
||||
// (to this function and/or systemd) are ignored (but logged with "debug"
|
||||
// log level).
|
||||
//
|
||||
// For the list of keys, see https://www.kernel.org/doc/Documentation/cgroup-v2.txt
|
||||
//
|
||||
// For the list of systemd unit properties, see systemd.resource-control(5).
|
||||
func unifiedResToSystemdProps(conn *systemdDbus.Conn, res map[string]string) (props []systemdDbus.Property, _ error) {
|
||||
var err error
|
||||
|
||||
for k, v := range res {
|
||||
if strings.Contains(k, "/") {
|
||||
return nil, fmt.Errorf("unified resource %q must be a file name (no slashes)", k)
|
||||
}
|
||||
sk := strings.SplitN(k, ".", 2)
|
||||
if len(sk) != 2 {
|
||||
return nil, fmt.Errorf("unified resource %q must be in the form CONTROLLER.PARAMETER", k)
|
||||
}
|
||||
// Kernel is quite forgiving to extra whitespace
|
||||
// around the value, and so should we.
|
||||
v = strings.TrimSpace(v)
|
||||
// Please keep cases in alphabetical order.
|
||||
switch k {
|
||||
case "cpu.max":
|
||||
// value: quota [period]
|
||||
quota := int64(0) // 0 means "unlimited" for addCpuQuota, if period is set
|
||||
period := defCPUQuotaPeriod
|
||||
sv := strings.Fields(v)
|
||||
if len(sv) < 1 || len(sv) > 2 {
|
||||
return nil, fmt.Errorf("unified resource %q value invalid: %q", k, v)
|
||||
}
|
||||
// quota
|
||||
if sv[0] != "max" {
|
||||
quota, err = strconv.ParseInt(sv[0], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unified resource %q period value conversion error: %w", k, err)
|
||||
}
|
||||
}
|
||||
// period
|
||||
if len(sv) == 2 {
|
||||
period, err = strconv.ParseUint(sv[1], 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unified resource %q quota value conversion error: %w", k, err)
|
||||
}
|
||||
}
|
||||
addCpuQuota(conn, &props, quota, period)
|
||||
|
||||
case "cpu.weight":
|
||||
num, err := strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unified resource %q value conversion error: %w", k, err)
|
||||
}
|
||||
props = append(props,
|
||||
newProp("CPUWeight", num))
|
||||
|
||||
case "cpuset.cpus", "cpuset.mems":
|
||||
bits, err := rangeToBits(v)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unified resource %q=%q conversion error: %w", k, v, err)
|
||||
}
|
||||
m := map[string]string{
|
||||
"cpuset.cpus": "AllowedCPUs",
|
||||
"cpuset.mems": "AllowedMemoryNodes",
|
||||
}
|
||||
// systemd only supports these properties since v244
|
||||
sdVer := systemdVersion(conn)
|
||||
if sdVer >= 244 {
|
||||
props = append(props,
|
||||
newProp(m[k], bits))
|
||||
} else {
|
||||
logrus.Debugf("systemd v%d is too old to support %s"+
|
||||
" (setting will still be applied to cgroupfs)",
|
||||
sdVer, m[k])
|
||||
}
|
||||
|
||||
case "memory.high", "memory.low", "memory.min", "memory.max", "memory.swap.max":
|
||||
num := uint64(math.MaxUint64)
|
||||
if v != "max" {
|
||||
num, err = strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unified resource %q value conversion error: %w", k, err)
|
||||
}
|
||||
}
|
||||
m := map[string]string{
|
||||
"memory.high": "MemoryHigh",
|
||||
"memory.low": "MemoryLow",
|
||||
"memory.min": "MemoryMin",
|
||||
"memory.max": "MemoryMax",
|
||||
"memory.swap.max": "MemorySwapMax",
|
||||
}
|
||||
props = append(props,
|
||||
newProp(m[k], num))
|
||||
|
||||
case "pids.max":
|
||||
num := uint64(math.MaxUint64)
|
||||
if v != "max" {
|
||||
var err error
|
||||
num, err = strconv.ParseUint(v, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unified resource %q value conversion error: %w", k, err)
|
||||
}
|
||||
}
|
||||
props = append(props,
|
||||
newProp("TasksAccounting", true),
|
||||
newProp("TasksMax", num))
|
||||
|
||||
case "memory.oom.group":
|
||||
// Setting this to 1 is roughly equivalent to OOMPolicy=kill
|
||||
// (as per systemd.service(5) and
|
||||
// https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html),
|
||||
// but it's not clear what to do if it is unset or set
|
||||
// to 0 in runc update, as there are two other possible
|
||||
// values for OOMPolicy (continue/stop).
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
// Ignore the unknown resource here -- will still be
|
||||
// applied in Set which calls fs2.Set.
|
||||
logrus.Debugf("don't know how to convert unified resource %q=%q to systemd unit property; skipping (will still be applied to cgroupfs)", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
return props, nil
|
||||
}
|
||||
|
||||
func genV2ResourcesProperties(c *configs.Cgroup, conn *systemdDbus.Conn) ([]systemdDbus.Property, error) {
|
||||
var properties []systemdDbus.Property
|
||||
r := c.Resources
|
||||
@@ -80,8 +209,22 @@ func genV2ResourcesProperties(c *configs.Cgroup, conn *systemdDbus.Conn) ([]syst
|
||||
newProp("TasksMax", uint64(r.PidsLimit)))
|
||||
}
|
||||
|
||||
err = addCpuset(conn, &properties, r.CpusetCpus, r.CpusetMems)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ignore r.KernelMemory
|
||||
|
||||
// convert Resources.Unified map to systemd properties
|
||||
if r.Unified != nil {
|
||||
unifiedProps, err := unifiedResToSystemdProps(conn, r.Unified)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
properties = append(properties, unifiedProps...)
|
||||
}
|
||||
|
||||
return properties, nil
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user