Merge pull request #3741 from haircommander/uts-fix

Properly share UTS namespaces in a pod
This commit is contained in:
OpenShift Merge Robot
2019-08-07 23:38:23 +02:00
committed by GitHub
3 changed files with 95 additions and 49 deletions

View File

@ -4,17 +4,30 @@ import (
"strings" "strings"
) )
const (
bridgeType = "bridge"
containerType = "container"
defaultType = "default"
hostType = "host"
noneType = "none"
nsType = "ns"
podType = "pod"
privateType = "private"
shareableType = "shareable"
slirpType = "slirp4netns"
)
// CgroupMode represents cgroup mode in the container. // CgroupMode represents cgroup mode in the container.
type CgroupMode string type CgroupMode string
// IsHost indicates whether the container uses the host's cgroup. // IsHost indicates whether the container uses the host's cgroup.
func (n CgroupMode) IsHost() bool { func (n CgroupMode) IsHost() bool {
return n == "host" return n == hostType
} }
// IsNS indicates a cgroup namespace passed in by path (ns:<path>) // IsNS indicates a cgroup namespace passed in by path (ns:<path>)
func (n CgroupMode) IsNS() bool { func (n CgroupMode) IsNS() bool {
return strings.HasPrefix(string(n), "ns:") return strings.HasPrefix(string(n), nsType)
} }
// NS gets the path associated with a ns:<path> cgroup ns // NS gets the path associated with a ns:<path> cgroup ns
@ -29,13 +42,13 @@ func (n CgroupMode) NS() string {
// IsContainer indicates whether the container uses a new cgroup namespace. // IsContainer indicates whether the container uses a new cgroup namespace.
func (n CgroupMode) IsContainer() bool { func (n CgroupMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container" return len(parts) > 1 && parts[0] == containerType
} }
// Container returns the name of the container whose cgroup namespace is going to be used. // Container returns the name of the container whose cgroup namespace is going to be used.
func (n CgroupMode) Container() string { func (n CgroupMode) Container() string {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 { if len(parts) > 1 && parts[0] == containerType {
return parts[1] return parts[1]
} }
return "" return ""
@ -43,15 +56,15 @@ func (n CgroupMode) Container() string {
// IsPrivate indicates whether the container uses the a private cgroup. // IsPrivate indicates whether the container uses the a private cgroup.
func (n CgroupMode) IsPrivate() bool { func (n CgroupMode) IsPrivate() bool {
return n == "private" return n == privateType
} }
// Valid indicates whether the Cgroup namespace is valid. // Valid indicates whether the Cgroup namespace is valid.
func (n CgroupMode) Valid() bool { func (n CgroupMode) Valid() bool {
parts := strings.Split(string(n), ":") parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode { switch mode := parts[0]; mode {
case "", "host", "private", "ns": case "", hostType, privateType, nsType:
case "container": case containerType:
if len(parts) != 2 || parts[1] == "" { if len(parts) != 2 || parts[1] == "" {
return false return false
} }
@ -66,7 +79,7 @@ type UsernsMode string
// IsHost indicates whether the container uses the host's userns. // IsHost indicates whether the container uses the host's userns.
func (n UsernsMode) IsHost() bool { func (n UsernsMode) IsHost() bool {
return n == "host" return n == hostType
} }
// IsKeepID indicates whether container uses a mapping where the (uid, gid) on the host is lept inside of the namespace. // IsKeepID indicates whether container uses a mapping where the (uid, gid) on the host is lept inside of the namespace.
@ -83,8 +96,8 @@ func (n UsernsMode) IsPrivate() bool {
func (n UsernsMode) Valid() bool { func (n UsernsMode) Valid() bool {
parts := strings.Split(string(n), ":") parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode { switch mode := parts[0]; mode {
case "", "host", "keep-id", "ns": case "", hostType, "keep-id", nsType:
case "container": case containerType:
if len(parts) != 2 || parts[1] == "" { if len(parts) != 2 || parts[1] == "" {
return false return false
} }
@ -111,13 +124,13 @@ func (n UsernsMode) NS() string {
// IsContainer indicates whether container uses a container userns. // IsContainer indicates whether container uses a container userns.
func (n UsernsMode) IsContainer() bool { func (n UsernsMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container" return len(parts) > 1 && parts[0] == containerType
} }
// Container is the id of the container which network this container is connected to. // Container is the id of the container which network this container is connected to.
func (n UsernsMode) Container() string { func (n UsernsMode) Container() string {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 { if len(parts) > 1 && parts[0] == containerType {
return parts[1] return parts[1]
} }
return "" return ""
@ -133,19 +146,19 @@ func (n UTSMode) IsPrivate() bool {
// IsHost indicates whether the container uses the host's UTS namespace. // IsHost indicates whether the container uses the host's UTS namespace.
func (n UTSMode) IsHost() bool { func (n UTSMode) IsHost() bool {
return n == "host" return n == hostType
} }
// IsContainer indicates whether the container uses a container's UTS namespace. // IsContainer indicates whether the container uses a container's UTS namespace.
func (n UTSMode) IsContainer() bool { func (n UTSMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container" return len(parts) > 1 && parts[0] == containerType
} }
// Container returns the name of the container whose uts namespace is going to be used. // Container returns the name of the container whose uts namespace is going to be used.
func (n UTSMode) Container() string { func (n UTSMode) Container() string {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 { if len(parts) > 1 && parts[0] == containerType {
return parts[1] return parts[1]
} }
return "" return ""
@ -155,8 +168,8 @@ func (n UTSMode) Container() string {
func (n UTSMode) Valid() bool { func (n UTSMode) Valid() bool {
parts := strings.Split(string(n), ":") parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode { switch mode := parts[0]; mode {
case "", "host": case "", hostType:
case "container": case containerType:
if len(parts) != 2 || parts[1] == "" { if len(parts) != 2 || parts[1] == "" {
return false return false
} }
@ -171,28 +184,28 @@ type IpcMode string
// IsPrivate indicates whether the container uses its own private ipc namespace which cannot be shared. // IsPrivate indicates whether the container uses its own private ipc namespace which cannot be shared.
func (n IpcMode) IsPrivate() bool { func (n IpcMode) IsPrivate() bool {
return n == "private" return n == privateType
} }
// IsHost indicates whether the container shares the host's ipc namespace. // IsHost indicates whether the container shares the host's ipc namespace.
func (n IpcMode) IsHost() bool { func (n IpcMode) IsHost() bool {
return n == "host" return n == hostType
} }
// IsShareable indicates whether the container's ipc namespace can be shared with another container. // IsShareable indicates whether the container's ipc namespace can be shared with another container.
func (n IpcMode) IsShareable() bool { func (n IpcMode) IsShareable() bool {
return n == "shareable" return n == shareableType
} }
// IsContainer indicates whether the container uses another container's ipc namespace. // IsContainer indicates whether the container uses another container's ipc namespace.
func (n IpcMode) IsContainer() bool { func (n IpcMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container" return len(parts) > 1 && parts[0] == containerType
} }
// IsNone indicates whether container IpcMode is set to "none". // IsNone indicates whether container IpcMode is set to "none".
func (n IpcMode) IsNone() bool { func (n IpcMode) IsNone() bool {
return n == "none" return n == noneType
} }
// IsEmpty indicates whether container IpcMode is empty // IsEmpty indicates whether container IpcMode is empty
@ -208,7 +221,7 @@ func (n IpcMode) Valid() bool {
// Container returns the name of the container ipc stack is going to be used. // Container returns the name of the container ipc stack is going to be used.
func (n IpcMode) Container() string { func (n IpcMode) Container() string {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == "container" { if len(parts) > 1 && parts[0] == containerType {
return parts[1] return parts[1]
} }
return "" return ""
@ -224,21 +237,21 @@ func (n PidMode) IsPrivate() bool {
// IsHost indicates whether the container uses the host's pid namespace. // IsHost indicates whether the container uses the host's pid namespace.
func (n PidMode) IsHost() bool { func (n PidMode) IsHost() bool {
return n == "host" return n == hostType
} }
// IsContainer indicates whether the container uses a container's pid namespace. // IsContainer indicates whether the container uses a container's pid namespace.
func (n PidMode) IsContainer() bool { func (n PidMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container" return len(parts) > 1 && parts[0] == containerType
} }
// Valid indicates whether the pid namespace is valid. // Valid indicates whether the pid namespace is valid.
func (n PidMode) Valid() bool { func (n PidMode) Valid() bool {
parts := strings.Split(string(n), ":") parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode { switch mode := parts[0]; mode {
case "", "host": case "", hostType:
case "container": case containerType:
if len(parts) != 2 || parts[1] == "" { if len(parts) != 2 || parts[1] == "" {
return false return false
} }
@ -251,7 +264,7 @@ func (n PidMode) Valid() bool {
// Container returns the name of the container whose pid namespace is going to be used. // Container returns the name of the container whose pid namespace is going to be used.
func (n PidMode) Container() string { func (n PidMode) Container() string {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 { if len(parts) > 1 && parts[0] == containerType {
return parts[1] return parts[1]
} }
return "" return ""
@ -262,17 +275,17 @@ type NetworkMode string
// IsNone indicates whether container isn't using a network stack. // IsNone indicates whether container isn't using a network stack.
func (n NetworkMode) IsNone() bool { func (n NetworkMode) IsNone() bool {
return n == "none" return n == noneType
} }
// IsHost indicates whether the container uses the host's network stack. // IsHost indicates whether the container uses the host's network stack.
func (n NetworkMode) IsHost() bool { func (n NetworkMode) IsHost() bool {
return n == "host" return n == hostType
} }
// IsDefault indicates whether container uses the default network stack. // IsDefault indicates whether container uses the default network stack.
func (n NetworkMode) IsDefault() bool { func (n NetworkMode) IsDefault() bool {
return n == "default" return n == defaultType
} }
// IsPrivate indicates whether container uses its private network stack. // IsPrivate indicates whether container uses its private network stack.
@ -283,13 +296,13 @@ func (n NetworkMode) IsPrivate() bool {
// IsContainer indicates whether container uses a container network stack. // IsContainer indicates whether container uses a container network stack.
func (n NetworkMode) IsContainer() bool { func (n NetworkMode) IsContainer() bool {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
return len(parts) > 1 && parts[0] == "container" return len(parts) > 1 && parts[0] == containerType
} }
// Container is the id of the container which network this container is connected to. // Container is the id of the container which network this container is connected to.
func (n NetworkMode) Container() string { func (n NetworkMode) Container() string {
parts := strings.SplitN(string(n), ":", 2) parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 { if len(parts) > 1 && parts[0] == containerType {
return parts[1] return parts[1]
} }
return "" return ""
@ -305,17 +318,17 @@ func (n NetworkMode) UserDefined() string {
// IsBridge indicates whether container uses the bridge network stack // IsBridge indicates whether container uses the bridge network stack
func (n NetworkMode) IsBridge() bool { func (n NetworkMode) IsBridge() bool {
return n == "bridge" return n == bridgeType
} }
// IsSlirp4netns indicates if we are running a rootless network stack // IsSlirp4netns indicates if we are running a rootless network stack
func (n NetworkMode) IsSlirp4netns() bool { func (n NetworkMode) IsSlirp4netns() bool {
return n == "slirp4netns" return n == slirpType
} }
// IsNS indicates a network namespace passed in by path (ns:<path>) // IsNS indicates a network namespace passed in by path (ns:<path>)
func (n NetworkMode) IsNS() bool { func (n NetworkMode) IsNS() bool {
return strings.HasPrefix(string(n), "ns:") return strings.HasPrefix(string(n), nsType)
} }
// NS gets the path associated with a ns:<path> network ns // NS gets the path associated with a ns:<path> network ns
@ -329,7 +342,7 @@ func (n NetworkMode) NS() string {
// IsPod returns whether the network refers to pod networking // IsPod returns whether the network refers to pod networking
func (n NetworkMode) IsPod() bool { func (n NetworkMode) IsPod() bool {
return n == "pod" return n == podType
} }
// IsUserDefined indicates user-created network // IsUserDefined indicates user-created network

View File

@ -174,10 +174,20 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
} }
hostname := config.Hostname hostname := config.Hostname
if hostname == "" && (config.NetMode.IsHost() || config.UtsMode.IsHost()) { if hostname == "" {
if utsCtrID := config.UtsMode.Container(); utsCtrID != "" {
utsCtr, err := runtime.GetContainer(utsCtrID)
if err != nil {
return nil, errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID)
}
hostname = utsCtr.Hostname()
} else if config.NetMode.IsHost() || config.UtsMode.IsHost() {
hostname, err = os.Hostname() hostname, err = os.Hostname()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "unable to retrieve hostname") return nil, errors.Wrap(err, "unable to retrieve hostname of the host")
}
} else {
logrus.Debug("No hostname set; container's hostname will default to runtime default")
} }
} }
g.RemoveHostname() g.RemoveHostname()
@ -541,8 +551,8 @@ func addPidNS(config *CreateConfig, g *generate.Generator) error {
if pidMode.IsHost() { if pidMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.PIDNamespace)) return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
} }
if pidMode.IsContainer() { if pidCtr := pidMode.Container(); pidCtr != "" {
logrus.Debug("using container pidmode") logrus.Debugf("using container %s pidmode", pidCtr)
} }
if IsPod(string(pidMode)) { if IsPod(string(pidMode)) {
logrus.Debug("using pod pidmode") logrus.Debug("using pod pidmode")
@ -579,8 +589,8 @@ func addNetNS(config *CreateConfig, g *generate.Generator) error {
} else if netMode.IsBridge() { } else if netMode.IsBridge() {
logrus.Debug("Using bridge netmode") logrus.Debug("Using bridge netmode")
return nil return nil
} else if netMode.IsContainer() { } else if netCtr := netMode.Container(); netCtr != "" {
logrus.Debug("Using container netmode") logrus.Debugf("using container %s netmode", netCtr)
return nil return nil
} else if IsNS(string(netMode)) { } else if IsNS(string(netMode)) {
logrus.Debug("Using ns netmode") logrus.Debug("Using ns netmode")
@ -606,6 +616,9 @@ func addUTSNS(config *CreateConfig, g *generate.Generator) error {
if utsMode.IsHost() { if utsMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.UTSNamespace)) return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
} }
if utsCtr := utsMode.Container(); utsCtr != "" {
logrus.Debugf("using container %s utsmode", utsCtr)
}
return nil return nil
} }
@ -617,8 +630,8 @@ func addIpcNS(config *CreateConfig, g *generate.Generator) error {
if ipcMode.IsHost() { if ipcMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.IPCNamespace)) return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
} }
if ipcMode.IsContainer() { if ipcCtr := ipcMode.Container(); ipcCtr != "" {
logrus.Debug("Using container ipcmode") logrus.Debugf("Using container %s ipcmode", ipcCtr)
} }
return nil return nil
@ -635,8 +648,8 @@ func addCgroupNS(config *CreateConfig, g *generate.Generator) error {
if cgroupMode.IsPrivate() { if cgroupMode.IsPrivate() {
return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "") return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
} }
if cgroupMode.IsContainer() { if cgCtr := cgroupMode.Container(); cgCtr != "" {
logrus.Debug("Using container cgroup mode") logrus.Debugf("Using container %s cgroup mode", cgCtr)
} }
return nil return nil
} }

View File

@ -383,4 +383,24 @@ var _ = Describe("Podman pod create", func() {
session.WaitWithDefaultTimeout() session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0)) Expect(session.ExitCode()).To(Equal(0))
}) })
It("podman run hostname is shared", func() {
session := podmanTest.Podman([]string{"pod", "create"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
podID := session.OutputToString()
// verify we can add a host to the infra's /etc/hosts
session = podmanTest.Podman([]string{"run", "--pod", podID, ALPINE, "hostname"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
hostname := session.OutputToString()
infraName := podID[:12] + "-infra"
// verify we can see the other hosts of infra's /etc/hosts
session = podmanTest.Podman([]string{"inspect", infraName})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(hostname))
})
}) })