Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly share UTS namespaces in a pod #3741

Merged
merged 4 commits into from
Aug 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 50 additions & 37 deletions pkg/namespaces/namespaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,30 @@ import (
"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.
type CgroupMode string

// IsHost indicates whether the container uses the host's cgroup.
func (n CgroupMode) IsHost() bool {
return n == "host"
return n == hostType
}

// IsNS indicates a cgroup namespace passed in by path (ns:<path>)
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
Expand All @@ -29,29 +42,29 @@ func (n CgroupMode) NS() string {
// IsContainer indicates whether the container uses a new cgroup namespace.
func (n CgroupMode) IsContainer() bool {
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.
func (n CgroupMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
}

// IsPrivate indicates whether the container uses the a private cgroup.
func (n CgroupMode) IsPrivate() bool {
return n == "private"
return n == privateType
}

// Valid indicates whether the Cgroup namespace is valid.
func (n CgroupMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", "host", "private", "ns":
case "container":
case "", hostType, privateType, nsType:
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
}
Expand All @@ -66,7 +79,7 @@ type UsernsMode string

// IsHost indicates whether the container uses the host's userns.
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.
Expand All @@ -83,8 +96,8 @@ func (n UsernsMode) IsPrivate() bool {
func (n UsernsMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", "host", "keep-id", "ns":
case "container":
case "", hostType, "keep-id", nsType:
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
}
Expand All @@ -111,13 +124,13 @@ func (n UsernsMode) NS() string {
// IsContainer indicates whether container uses a container userns.
func (n UsernsMode) IsContainer() bool {
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.
func (n UsernsMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
Expand All @@ -133,19 +146,19 @@ func (n UTSMode) IsPrivate() bool {

// IsHost indicates whether the container uses the host's UTS namespace.
func (n UTSMode) IsHost() bool {
return n == "host"
return n == hostType
}

// IsContainer indicates whether the container uses a container's UTS namespace.
func (n UTSMode) IsContainer() bool {
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.
func (n UTSMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
Expand All @@ -155,8 +168,8 @@ func (n UTSMode) Container() string {
func (n UTSMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", "host":
case "container":
case "", hostType:
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
}
Expand All @@ -171,28 +184,28 @@ type IpcMode string

// IsPrivate indicates whether the container uses its own private ipc namespace which cannot be shared.
func (n IpcMode) IsPrivate() bool {
return n == "private"
return n == privateType
}

// IsHost indicates whether the container shares the host's ipc namespace.
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.
func (n IpcMode) IsShareable() bool {
return n == "shareable"
return n == shareableType
}

// IsContainer indicates whether the container uses another container's ipc namespace.
func (n IpcMode) IsContainer() bool {
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".
func (n IpcMode) IsNone() bool {
return n == "none"
return n == noneType
}

// IsEmpty indicates whether container IpcMode is empty
Expand All @@ -208,7 +221,7 @@ func (n IpcMode) Valid() bool {
// Container returns the name of the container ipc stack is going to be used.
func (n IpcMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 && parts[0] == "container" {
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
Expand All @@ -224,21 +237,21 @@ func (n PidMode) IsPrivate() bool {

// IsHost indicates whether the container uses the host's pid namespace.
func (n PidMode) IsHost() bool {
return n == "host"
return n == hostType
}

// IsContainer indicates whether the container uses a container's pid namespace.
func (n PidMode) IsContainer() bool {
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.
func (n PidMode) Valid() bool {
parts := strings.Split(string(n), ":")
switch mode := parts[0]; mode {
case "", "host":
case "container":
case "", hostType:
case containerType:
if len(parts) != 2 || parts[1] == "" {
return false
}
Expand All @@ -251,7 +264,7 @@ func (n PidMode) Valid() bool {
// Container returns the name of the container whose pid namespace is going to be used.
func (n PidMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
Expand All @@ -262,17 +275,17 @@ type NetworkMode string

// IsNone indicates whether container isn't using a network stack.
func (n NetworkMode) IsNone() bool {
return n == "none"
return n == noneType
}

// IsHost indicates whether the container uses the host's network stack.
func (n NetworkMode) IsHost() bool {
return n == "host"
return n == hostType
}

// IsDefault indicates whether container uses the default network stack.
func (n NetworkMode) IsDefault() bool {
return n == "default"
return n == defaultType
}

// IsPrivate indicates whether container uses its private network stack.
Expand All @@ -283,13 +296,13 @@ func (n NetworkMode) IsPrivate() bool {
// IsContainer indicates whether container uses a container network stack.
func (n NetworkMode) IsContainer() bool {
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.
func (n NetworkMode) Container() string {
parts := strings.SplitN(string(n), ":", 2)
if len(parts) > 1 {
if len(parts) > 1 && parts[0] == containerType {
return parts[1]
}
return ""
Expand All @@ -305,17 +318,17 @@ func (n NetworkMode) UserDefined() string {

// IsBridge indicates whether container uses the bridge network stack
func (n NetworkMode) IsBridge() bool {
return n == "bridge"
return n == bridgeType
}

// IsSlirp4netns indicates if we are running a rootless network stack
func (n NetworkMode) IsSlirp4netns() bool {
return n == "slirp4netns"
return n == slirpType
}

// IsNS indicates a network namespace passed in by path (ns:<path>)
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
Expand All @@ -329,7 +342,7 @@ func (n NetworkMode) NS() string {

// IsPod returns whether the network refers to pod networking
func (n NetworkMode) IsPod() bool {
return n == "pod"
return n == podType
}

// IsUserDefined indicates user-created network
Expand Down
37 changes: 25 additions & 12 deletions pkg/spec/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,20 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
}

hostname := config.Hostname
if hostname == "" && (config.NetMode.IsHost() || config.UtsMode.IsHost()) {
hostname, err = os.Hostname()
if err != nil {
return nil, errors.Wrap(err, "unable to retrieve hostname")
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()
if err != nil {
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")
jwhonce marked this conversation as resolved.
Show resolved Hide resolved
}
}
haircommander marked this conversation as resolved.
Show resolved Hide resolved
g.RemoveHostname()
Expand Down Expand Up @@ -541,8 +551,8 @@ func addPidNS(config *CreateConfig, g *generate.Generator) error {
if pidMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
}
if pidMode.IsContainer() {
logrus.Debug("using container pidmode")
if pidCtr := pidMode.Container(); pidCtr != "" {
logrus.Debugf("using container %s pidmode", pidCtr)
}
if IsPod(string(pidMode)) {
logrus.Debug("using pod pidmode")
Expand Down Expand Up @@ -579,8 +589,8 @@ func addNetNS(config *CreateConfig, g *generate.Generator) error {
} else if netMode.IsBridge() {
logrus.Debug("Using bridge netmode")
return nil
} else if netMode.IsContainer() {
logrus.Debug("Using container netmode")
} else if netCtr := netMode.Container(); netCtr != "" {
logrus.Debugf("using container %s netmode", netCtr)
return nil
} else if IsNS(string(netMode)) {
logrus.Debug("Using ns netmode")
Expand All @@ -606,6 +616,9 @@ func addUTSNS(config *CreateConfig, g *generate.Generator) error {
if utsMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
}
if utsCtr := utsMode.Container(); utsCtr != "" {
logrus.Debugf("using container %s utsmode", utsCtr)
}
return nil
}

Expand All @@ -617,8 +630,8 @@ func addIpcNS(config *CreateConfig, g *generate.Generator) error {
if ipcMode.IsHost() {
return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
}
if ipcMode.IsContainer() {
logrus.Debug("Using container ipcmode")
if ipcCtr := ipcMode.Container(); ipcCtr != "" {
logrus.Debugf("Using container %s ipcmode", ipcCtr)
}

return nil
Expand All @@ -635,8 +648,8 @@ func addCgroupNS(config *CreateConfig, g *generate.Generator) error {
if cgroupMode.IsPrivate() {
return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
}
if cgroupMode.IsContainer() {
logrus.Debug("Using container cgroup mode")
if cgCtr := cgroupMode.Container(); cgCtr != "" {
logrus.Debugf("Using container %s cgroup mode", cgCtr)
}
return nil
}
Expand Down
20 changes: 20 additions & 0 deletions test/e2e/pod_infra_container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,4 +383,24 @@ var _ = Describe("Podman pod create", func() {
session.WaitWithDefaultTimeout()
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))
})
})