Update libcontainer to include PRs with fixes to systemd cgroup driver

PR opencontainers/runc#1754 works around an issue in manager.Apply(-1) that
makes Kubelet startup hang when using systemd cgroup driver (by adding a
timeout) and further PR opencontainers/runc#1772 fixes that bug by
checking the proper error status before waiting on the channel.

PR opencontainers/runc#1776 checks whether Delegate works in slices,
which keeps libcontainer systemd cgroup driver working on systemd v237+.

PR opencontainers/runc#1781 makes the channel buffered, so if we time
out waiting on the channel, the updater will not block trying to it
since there are no longer any consumers.
This commit is contained in:
Filipe Brandenburger 2018-04-12 15:02:12 -07:00
parent 44b57338d5
commit 54d93820ee
14 changed files with 249 additions and 117 deletions

64
Godeps/Godeps.json generated
View File

@ -2381,83 +2381,83 @@
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/apparmor",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/fs",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/systemd",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/configs",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/configs/validate",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/criurpc",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/intelrdt",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/keys",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/mount",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/seccomp",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/stacktrace",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/system",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/user",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runc/libcontainer/utils",
"Comment": "v1.0.0-rc4-221-g595bea02",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43"
"Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
},
{
"ImportPath": "github.com/opencontainers/runtime-spec/specs-go",

View File

@ -161,7 +161,7 @@ func (m *Manager) Apply(pid int) (err error) {
}
func (m *Manager) Destroy() error {
if m.Cgroups.Paths != nil {
if m.Cgroups == nil || m.Cgroups.Paths != nil {
return nil
}
m.mu.Lock()

View File

@ -64,6 +64,7 @@ go_library(
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/configs:go_default_library",
"//vendor/github.com/sirupsen/logrus:go_default_library",
],
"@io_bazel_rules_go//go/platform:nacl": [
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library",

View File

@ -17,6 +17,7 @@ import (
"github.com/opencontainers/runc/libcontainer/cgroups"
"github.com/opencontainers/runc/libcontainer/cgroups/fs"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/sirupsen/logrus"
)
type Manager struct {
@ -74,7 +75,8 @@ var (
hasStartTransientUnit bool
hasStartTransientSliceUnit bool
hasTransientDefaultDependencies bool
hasDelegate bool
hasDelegateScope bool
hasDelegateSlice bool
)
func newProp(name string, units interface{}) systemdDbus.Property {
@ -149,12 +151,12 @@ func UseSystemd() bool {
theConn.StopUnit(scope, "replace", nil)
// Assume StartTransientUnit on a scope allows Delegate
hasDelegate = true
dl := newProp("Delegate", true)
if _, err := theConn.StartTransientUnit(scope, "replace", []systemdDbus.Property{dl}, nil); err != nil {
hasDelegateScope = true
dlScope := newProp("Delegate", true)
if _, err := theConn.StartTransientUnit(scope, "replace", []systemdDbus.Property{dlScope}, nil); err != nil {
if dbusError, ok := err.(dbus.Error); ok {
if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") {
hasDelegate = false
hasDelegateScope = false
}
}
}
@ -186,6 +188,22 @@ func UseSystemd() bool {
time.Sleep(time.Millisecond)
}
// Not critical because of the stop unit logic above.
theConn.StopUnit(slice, "replace", nil)
// Assume StartTransientUnit on a slice allows Delegate
hasDelegateSlice = true
dlSlice := newProp("Delegate", true)
if _, err := theConn.StartTransientUnit(slice, "replace", []systemdDbus.Property{dlSlice}, nil); err != nil {
if dbusError, ok := err.(dbus.Error); ok {
// Starting with systemd v237, Delegate is not even a property of slices anymore,
// so the D-Bus call fails with "InvalidArgs" error.
if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") || strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.InvalidArgs") {
hasDelegateSlice = false
}
}
}
// Not critical because of the stop unit logic above.
theConn.StopUnit(scope, "replace", nil)
theConn.StopUnit(slice, "replace", nil)
@ -241,9 +259,16 @@ func (m *Manager) Apply(pid int) error {
properties = append(properties, newProp("PIDs", []uint32{uint32(pid)}))
}
if hasDelegate {
// This is only supported on systemd versions 218 and above.
properties = append(properties, newProp("Delegate", true))
// Check if we can delegate. This is only supported on systemd versions 218 and above.
if strings.HasSuffix(unitName, ".slice") {
if hasDelegateSlice {
// systemd 237 and above no longer allows delegation on a slice
properties = append(properties, newProp("Delegate", true))
}
} else {
if hasDelegateScope {
properties = append(properties, newProp("Delegate", true))
}
}
// Always enable accounting, this gets us the same behaviour as the fs implementation,
@ -295,13 +320,17 @@ func (m *Manager) Apply(pid int) error {
}
}
statusChan := make(chan string)
if _, err := theConn.StartTransientUnit(unitName, "replace", properties, statusChan); err != nil && !isUnitExists(err) {
statusChan := make(chan string, 1)
if _, err := theConn.StartTransientUnit(unitName, "replace", properties, statusChan); err == nil {
select {
case <-statusChan:
case <-time.After(time.Second):
logrus.Warnf("Timed out while waiting for StartTransientUnit(%s) completion signal from dbus. Continuing...", unitName)
}
} else if !isUnitExists(err) {
return err
}
<-statusChan
if err := joinCgroups(c, pid); err != nil {
return err
}

View File

@ -49,7 +49,7 @@ func rootlessMappings(config *configs.Config) error {
return fmt.Errorf("rootless containers requires at least one UID mapping")
}
if len(config.GidMappings) == 0 {
return fmt.Errorf("rootless containers requires at least one UID mapping")
return fmt.Errorf("rootless containers requires at least one GID mapping")
}
return nil

View File

@ -377,10 +377,6 @@ func (c *linuxContainer) start(process *Process, isInit bool) error {
}
}
}
} else {
c.state = &runningState{
c: c,
}
}
return nil
}
@ -1801,8 +1797,7 @@ func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Na
Value: []byte(c.newgidmapPath),
})
}
// The following only applies if we are root.
if !c.config.Rootless {
if requiresRootOrMappingTool(c.config) {
// check if we have CAP_SETGID to setgroup properly
pid, err := capability.NewPid(0)
if err != nil {
@ -1847,3 +1842,10 @@ func ignoreTerminateErrors(err error) error {
}
return err
}
func requiresRootOrMappingTool(c *configs.Config) bool {
gidMap := []configs.IDMap{
{ContainerID: 0, HostID: os.Getegid(), Size: 1},
}
return !reflect.DeepEqual(c.GidMappings, gidMap)
}

View File

@ -77,13 +77,13 @@ func (msg *Boolmsg) Serialize() []byte {
native.PutUint16(buf[0:2], uint16(msg.Len()))
native.PutUint16(buf[2:4], msg.Type)
if msg.Value {
buf[4] = 1
native.PutUint32(buf[4:8], uint32(1))
} else {
buf[4] = 0
native.PutUint32(buf[4:8], uint32(0))
}
return buf
}
func (msg *Boolmsg) Len() int {
return unix.NLA_HDRLEN + 1
return unix.NLA_HDRLEN + 4 // alignment
}

View File

@ -46,7 +46,6 @@ func prepareRootfs(pipe io.ReadWriter, iConfig *initConfig) (err error) {
return newSystemErrorWithCause(err, "preparing rootfs")
}
setupDev := needsSetupDev(config)
for _, m := range config.Mounts {
for _, precmd := range m.PremountCmds {
if err := mountCmd(precmd); err != nil {
@ -65,6 +64,8 @@ func prepareRootfs(pipe io.ReadWriter, iConfig *initConfig) (err error) {
}
}
setupDev := needsSetupDev(config)
if setupDev {
if err := createDevices(config); err != nil {
return newSystemErrorWithCause(err, "creating device nodes")
@ -778,10 +779,10 @@ func remountReadonly(m *configs.Mount) error {
// mounts ( proc/kcore ).
// For files, maskPath bind mounts /dev/null over the top of the specified path.
// For directories, maskPath mounts read-only tmpfs over the top of the specified path.
func maskPath(path string) error {
func maskPath(path string, mountLabel string) error {
if err := unix.Mount("/dev/null", path, "", unix.MS_BIND, ""); err != nil && !os.IsNotExist(err) {
if err == unix.ENOTDIR {
return unix.Mount("tmpfs", path, "tmpfs", unix.MS_RDONLY, "")
return unix.Mount("tmpfs", path, "tmpfs", unix.MS_RDONLY, label.FormatMountLabel("", mountLabel))
}
return err
}

View File

@ -110,7 +110,7 @@ func (l *linuxStandardInit) Init() error {
}
}
for _, path := range l.config.Config.MaskPaths {
if err := maskPath(path); err != nil {
if err := maskPath(path, l.config.Config.MountLabel); err != nil {
return err
}
}

View File

@ -27,6 +27,9 @@ go_library(
"@io_bazel_rules_go//go/platform:solaris": [
"lookup_unix.go",
],
"@io_bazel_rules_go//go/platform:windows": [
"lookup_windows.go",
],
"//conditions:default": [],
}),
importpath = "github.com/opencontainers/runc/libcontainer/user",

View File

@ -12,84 +12,30 @@ var (
ErrNoGroupEntries = errors.New("no matching entries in group file")
)
func lookupUser(filter func(u User) bool) (User, error) {
// Get operating system-specific passwd reader-closer.
passwd, err := GetPasswd()
if err != nil {
return User{}, err
}
defer passwd.Close()
// Get the users.
users, err := ParsePasswdFilter(passwd, filter)
if err != nil {
return User{}, err
}
// No user entries found.
if len(users) == 0 {
return User{}, ErrNoPasswdEntries
}
// Assume the first entry is the "correct" one.
return users[0], nil
}
// LookupUser looks up a user by their username in /etc/passwd. If the user
// cannot be found (or there is no /etc/passwd file on the filesystem), then
// LookupUser returns an error.
func LookupUser(username string) (User, error) {
return lookupUser(func(u User) bool {
return u.Name == username
})
return lookupUser(username)
}
// LookupUid looks up a user by their user id in /etc/passwd. If the user cannot
// be found (or there is no /etc/passwd file on the filesystem), then LookupId
// returns an error.
func LookupUid(uid int) (User, error) {
return lookupUser(func(u User) bool {
return u.Uid == uid
})
}
func lookupGroup(filter func(g Group) bool) (Group, error) {
// Get operating system-specific group reader-closer.
group, err := GetGroup()
if err != nil {
return Group{}, err
}
defer group.Close()
// Get the users.
groups, err := ParseGroupFilter(group, filter)
if err != nil {
return Group{}, err
}
// No user entries found.
if len(groups) == 0 {
return Group{}, ErrNoGroupEntries
}
// Assume the first entry is the "correct" one.
return groups[0], nil
return lookupUid(uid)
}
// LookupGroup looks up a group by its name in /etc/group. If the group cannot
// be found (or there is no /etc/group file on the filesystem), then LookupGroup
// returns an error.
func LookupGroup(groupname string) (Group, error) {
return lookupGroup(func(g Group) bool {
return g.Name == groupname
})
return lookupGroup(groupname)
}
// LookupGid looks up a group by its group id in /etc/group. If the group cannot
// be found (or there is no /etc/group file on the filesystem), then LookupGid
// returns an error.
func LookupGid(gid int) (Group, error) {
return lookupGroup(func(g Group) bool {
return g.Gid == gid
})
return lookupGid(gid)
}

View File

@ -15,6 +15,76 @@ const (
unixGroupPath = "/etc/group"
)
func lookupUser(username string) (User, error) {
return lookupUserFunc(func(u User) bool {
return u.Name == username
})
}
func lookupUid(uid int) (User, error) {
return lookupUserFunc(func(u User) bool {
return u.Uid == uid
})
}
func lookupUserFunc(filter func(u User) bool) (User, error) {
// Get operating system-specific passwd reader-closer.
passwd, err := GetPasswd()
if err != nil {
return User{}, err
}
defer passwd.Close()
// Get the users.
users, err := ParsePasswdFilter(passwd, filter)
if err != nil {
return User{}, err
}
// No user entries found.
if len(users) == 0 {
return User{}, ErrNoPasswdEntries
}
// Assume the first entry is the "correct" one.
return users[0], nil
}
func lookupGroup(groupname string) (Group, error) {
return lookupGroupFunc(func(g Group) bool {
return g.Name == groupname
})
}
func lookupGid(gid int) (Group, error) {
return lookupGroupFunc(func(g Group) bool {
return g.Gid == gid
})
}
func lookupGroupFunc(filter func(g Group) bool) (Group, error) {
// Get operating system-specific group reader-closer.
group, err := GetGroup()
if err != nil {
return Group{}, err
}
defer group.Close()
// Get the users.
groups, err := ParseGroupFilter(group, filter)
if err != nil {
return Group{}, err
}
// No user entries found.
if len(groups) == 0 {
return Group{}, ErrNoGroupEntries
}
// Assume the first entry is the "correct" one.
return groups[0], nil
}
func GetPasswdPath() (string, error) {
return unixPasswdPath, nil
}

View File

@ -0,0 +1,40 @@
// +build windows
package user
import (
"fmt"
"os/user"
)
func lookupUser(username string) (User, error) {
u, err := user.Lookup(username)
if err != nil {
return User{}, err
}
return userFromOS(u)
}
func lookupUid(uid int) (User, error) {
u, err := user.LookupId(fmt.Sprintf("%d", uid))
if err != nil {
return User{}, err
}
return userFromOS(u)
}
func lookupGroup(groupname string) (Group, error) {
g, err := user.LookupGroup(groupname)
if err != nil {
return Group{}, err
}
return groupFromOS(g)
}
func lookupGid(gid int) (Group, error) {
g, err := user.LookupGroupId(fmt.Sprintf("%d", gid))
if err != nil {
return Group{}, err
}
return groupFromOS(g)
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"os/user"
"strconv"
"strings"
)
@ -28,6 +29,28 @@ type User struct {
Shell string
}
// userFromOS converts an os/user.(*User) to local User
//
// (This does not include Pass, Shell or Gecos)
func userFromOS(u *user.User) (User, error) {
newUser := User{
Name: u.Username,
Home: u.HomeDir,
}
id, err := strconv.Atoi(u.Uid)
if err != nil {
return newUser, err
}
newUser.Uid = id
id, err = strconv.Atoi(u.Gid)
if err != nil {
return newUser, err
}
newUser.Gid = id
return newUser, nil
}
type Group struct {
Name string
Pass string
@ -35,6 +58,23 @@ type Group struct {
List []string
}
// groupFromOS converts an os/user.(*Group) to local Group
//
// (This does not include Pass, Shell or Gecos)
func groupFromOS(g *user.Group) (Group, error) {
newGroup := Group{
Name: g.Name,
}
id, err := strconv.Atoi(g.Gid)
if err != nil {
return newGroup, err
}
newGroup.Gid = id
return newGroup, nil
}
func parseLine(line string, v ...interface{}) {
if line == "" {
return