Merge pull request #61926 from filbranden/cgroupdriver10

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Update libcontainer to include PRs with fixes to systemd cgroup driver

**What this PR does / why we need it**:

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+.

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #61474

**Special notes for your reviewer**:
/assign @derekwaynecarr
cc @vikaschoudhary16 @sjenning @adelton @mrunalp 

**Release note**:

```release-note
NONE
```
This commit is contained in:
Kubernetes Submit Queue 2018-04-25 10:46:28 -07:00 committed by GitHub
commit 667dd711e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 249 additions and 117 deletions

64
Godeps/Godeps.json generated
View File

@ -2358,83 +2358,83 @@
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer", "ImportPath": "github.com/opencontainers/runc/libcontainer",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/apparmor", "ImportPath": "github.com/opencontainers/runc/libcontainer/apparmor",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups", "ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/fs", "ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/fs",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/systemd", "ImportPath": "github.com/opencontainers/runc/libcontainer/cgroups/systemd",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/configs", "ImportPath": "github.com/opencontainers/runc/libcontainer/configs",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/configs/validate", "ImportPath": "github.com/opencontainers/runc/libcontainer/configs/validate",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/criurpc", "ImportPath": "github.com/opencontainers/runc/libcontainer/criurpc",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/intelrdt", "ImportPath": "github.com/opencontainers/runc/libcontainer/intelrdt",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/keys", "ImportPath": "github.com/opencontainers/runc/libcontainer/keys",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/mount", "ImportPath": "github.com/opencontainers/runc/libcontainer/mount",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/seccomp", "ImportPath": "github.com/opencontainers/runc/libcontainer/seccomp",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/stacktrace", "ImportPath": "github.com/opencontainers/runc/libcontainer/stacktrace",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/system", "ImportPath": "github.com/opencontainers/runc/libcontainer/system",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/user", "ImportPath": "github.com/opencontainers/runc/libcontainer/user",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runc/libcontainer/utils", "ImportPath": "github.com/opencontainers/runc/libcontainer/utils",
"Comment": "v1.0.0-rc4-221-g595bea02", "Comment": "v1.0.0-rc5-46-g871ba2e5",
"Rev": "595bea022f077a9e17d7473b34fbaf1adaed9e43" "Rev": "871ba2e58e24314d1fab4517a80410191ba5ad01"
}, },
{ {
"ImportPath": "github.com/opencontainers/runtime-spec/specs-go", "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 { func (m *Manager) Destroy() error {
if m.Cgroups.Paths != nil { if m.Cgroups == nil || m.Cgroups.Paths != nil {
return nil return nil
} }
m.mu.Lock() 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:go_default_library",
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs: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/opencontainers/runc/libcontainer/configs:go_default_library",
"//vendor/github.com/sirupsen/logrus:go_default_library",
], ],
"@io_bazel_rules_go//go/platform:nacl": [ "@io_bazel_rules_go//go/platform:nacl": [
"//vendor/github.com/opencontainers/runc/libcontainer/cgroups:go_default_library", "//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"
"github.com/opencontainers/runc/libcontainer/cgroups/fs" "github.com/opencontainers/runc/libcontainer/cgroups/fs"
"github.com/opencontainers/runc/libcontainer/configs" "github.com/opencontainers/runc/libcontainer/configs"
"github.com/sirupsen/logrus"
) )
type Manager struct { type Manager struct {
@ -74,7 +75,8 @@ var (
hasStartTransientUnit bool hasStartTransientUnit bool
hasStartTransientSliceUnit bool hasStartTransientSliceUnit bool
hasTransientDefaultDependencies bool hasTransientDefaultDependencies bool
hasDelegate bool hasDelegateScope bool
hasDelegateSlice bool
) )
func newProp(name string, units interface{}) systemdDbus.Property { func newProp(name string, units interface{}) systemdDbus.Property {
@ -149,12 +151,12 @@ func UseSystemd() bool {
theConn.StopUnit(scope, "replace", nil) theConn.StopUnit(scope, "replace", nil)
// Assume StartTransientUnit on a scope allows Delegate // Assume StartTransientUnit on a scope allows Delegate
hasDelegate = true hasDelegateScope = true
dl := newProp("Delegate", true) dlScope := newProp("Delegate", true)
if _, err := theConn.StartTransientUnit(scope, "replace", []systemdDbus.Property{dl}, nil); err != nil { if _, err := theConn.StartTransientUnit(scope, "replace", []systemdDbus.Property{dlScope}, nil); err != nil {
if dbusError, ok := err.(dbus.Error); ok { if dbusError, ok := err.(dbus.Error); ok {
if strings.Contains(dbusError.Name, "org.freedesktop.DBus.Error.PropertyReadOnly") { 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) 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. // Not critical because of the stop unit logic above.
theConn.StopUnit(scope, "replace", nil) theConn.StopUnit(scope, "replace", nil)
theConn.StopUnit(slice, "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)})) properties = append(properties, newProp("PIDs", []uint32{uint32(pid)}))
} }
if hasDelegate { // Check if we can delegate. This is only supported on systemd versions 218 and above.
// This is only supported on systemd versions 218 and above. if strings.HasSuffix(unitName, ".slice") {
properties = append(properties, newProp("Delegate", true)) 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, // 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) statusChan := make(chan string, 1)
if _, err := theConn.StartTransientUnit(unitName, "replace", properties, statusChan); err != nil && !isUnitExists(err) { 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 return err
} }
<-statusChan
if err := joinCgroups(c, pid); err != nil { if err := joinCgroups(c, pid); err != nil {
return err 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") return fmt.Errorf("rootless containers requires at least one UID mapping")
} }
if len(config.GidMappings) == 0 { 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 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 return nil
} }
@ -1801,8 +1797,7 @@ func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.Na
Value: []byte(c.newgidmapPath), Value: []byte(c.newgidmapPath),
}) })
} }
// The following only applies if we are root. if requiresRootOrMappingTool(c.config) {
if !c.config.Rootless {
// check if we have CAP_SETGID to setgroup properly // check if we have CAP_SETGID to setgroup properly
pid, err := capability.NewPid(0) pid, err := capability.NewPid(0)
if err != nil { if err != nil {
@ -1847,3 +1842,10 @@ func ignoreTerminateErrors(err error) error {
} }
return err 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[0:2], uint16(msg.Len()))
native.PutUint16(buf[2:4], msg.Type) native.PutUint16(buf[2:4], msg.Type)
if msg.Value { if msg.Value {
buf[4] = 1 native.PutUint32(buf[4:8], uint32(1))
} else { } else {
buf[4] = 0 native.PutUint32(buf[4:8], uint32(0))
} }
return buf return buf
} }
func (msg *Boolmsg) Len() int { 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") return newSystemErrorWithCause(err, "preparing rootfs")
} }
setupDev := needsSetupDev(config)
for _, m := range config.Mounts { for _, m := range config.Mounts {
for _, precmd := range m.PremountCmds { for _, precmd := range m.PremountCmds {
if err := mountCmd(precmd); err != nil { 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 setupDev {
if err := createDevices(config); err != nil { if err := createDevices(config); err != nil {
return newSystemErrorWithCause(err, "creating device nodes") return newSystemErrorWithCause(err, "creating device nodes")
@ -778,10 +779,10 @@ func remountReadonly(m *configs.Mount) error {
// mounts ( proc/kcore ). // mounts ( proc/kcore ).
// For files, maskPath bind mounts /dev/null over the top of the specified path. // 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. // 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.Mount("/dev/null", path, "", unix.MS_BIND, ""); err != nil && !os.IsNotExist(err) {
if err == unix.ENOTDIR { 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 return err
} }

View File

@ -110,7 +110,7 @@ func (l *linuxStandardInit) Init() error {
} }
} }
for _, path := range l.config.Config.MaskPaths { 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 return err
} }
} }

View File

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

View File

@ -12,84 +12,30 @@ var (
ErrNoGroupEntries = errors.New("no matching entries in group file") 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 // 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 // cannot be found (or there is no /etc/passwd file on the filesystem), then
// LookupUser returns an error. // LookupUser returns an error.
func LookupUser(username string) (User, error) { func LookupUser(username string) (User, error) {
return lookupUser(func(u User) bool { return lookupUser(username)
return u.Name == username
})
} }
// LookupUid looks up a user by their user id in /etc/passwd. If the user cannot // 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 // be found (or there is no /etc/passwd file on the filesystem), then LookupId
// returns an error. // returns an error.
func LookupUid(uid int) (User, error) { func LookupUid(uid int) (User, error) {
return lookupUser(func(u User) bool { return lookupUid(uid)
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
} }
// LookupGroup looks up a group by its name in /etc/group. If the group cannot // 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 // be found (or there is no /etc/group file on the filesystem), then LookupGroup
// returns an error. // returns an error.
func LookupGroup(groupname string) (Group, error) { func LookupGroup(groupname string) (Group, error) {
return lookupGroup(func(g Group) bool { return lookupGroup(groupname)
return g.Name == groupname
})
} }
// LookupGid looks up a group by its group id in /etc/group. If the group cannot // 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 // be found (or there is no /etc/group file on the filesystem), then LookupGid
// returns an error. // returns an error.
func LookupGid(gid int) (Group, error) { func LookupGid(gid int) (Group, error) {
return lookupGroup(func(g Group) bool { return lookupGid(gid)
return g.Gid == gid
})
} }

View File

@ -15,6 +15,76 @@ const (
unixGroupPath = "/etc/group" 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) { func GetPasswdPath() (string, error) {
return unixPasswdPath, nil 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" "fmt"
"io" "io"
"os" "os"
"os/user"
"strconv" "strconv"
"strings" "strings"
) )
@ -28,6 +29,28 @@ type User struct {
Shell string 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 { type Group struct {
Name string Name string
Pass string Pass string
@ -35,6 +58,23 @@ type Group struct {
List []string 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{}) { func parseLine(line string, v ...interface{}) {
if line == "" { if line == "" {
return return