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

podman pod create --uts support #14501

Merged
merged 1 commit into from
Jul 6, 2022
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
15 changes: 8 additions & 7 deletions cmd/podman/common/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -544,13 +544,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag)

utsFlagName := "uts"
createFlags.String(
utsFlagName, "",
"UTS namespace to use",
)
_ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace)

mountFlagName := "mount"
createFlags.StringArrayVar(
&cf.Mount,
Expand Down Expand Up @@ -684,6 +677,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(usernsFlagName, AutocompleteUserNamespace)

utsFlagName := "uts"
createFlags.StringVar(
&cf.UTS,
utsFlagName, "",
"UTS namespace to use",
)
_ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace)

cgroupParentFlagName := "cgroup-parent"
createFlags.StringVar(
&cf.CgroupParent,
Expand Down
1 change: 0 additions & 1 deletion cmd/podman/containers/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@ func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra
return vals, errors.New("--cpu-quota and --cpus cannot be set together")
}
vals.IPC = c.Flag("ipc").Value.String()
vals.UTS = c.Flag("uts").Value.String()
vals.PID = c.Flag("pid").Value.String()
vals.CgroupNS = c.Flag("cgroupns").Value.String()

Expand Down
1 change: 1 addition & 0 deletions cmd/podman/pods/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ func create(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}

podSpec.Volumes = podSpec.InfraContainerSpec.Volumes
podSpec.ImageVolumes = podSpec.InfraContainerSpec.ImageVolumes
podSpec.OverlayVolumes = podSpec.InfraContainerSpec.OverlayVolumes
Expand Down
9 changes: 9 additions & 0 deletions docs/source/markdown/podman-pod-clone.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,15 @@ Valid _mode_ values are:

- *nomap*: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is ignored for containers created by the root user.

#### **--uts**=*mode*

Set the UTS namespace mode for the pod. The following values are supported:

- **host**: use the host's UTS namespace inside the pod.
- **private**: create a new namespace for the pod (default).
- **ns:[path]**: run the pod in the given existing UTS namespace.


#### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]

Create a bind mount. If ` -v /HOST-DIR:/CONTAINER-DIR` is specified, Podman
Expand Down
8 changes: 8 additions & 0 deletions docs/source/markdown/podman-pod-create.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,14 @@ Valid _mode_ values are:

- *nomap*: creates a user namespace where the current rootless user's UID:GID are not mapped into the container. This option is not allowed for containers created by the root user.

#### **--uts**=*mode*

Set the UTS namespace mode for the pod. The following values are supported:

- **host**: use the host's UTS namespace inside the pod.
- **private**: create a new namespace for the pod (default).
- **ns:[path]**: run the pod in the given existing UTS namespace.

#### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]

Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, Podman
Expand Down
49 changes: 49 additions & 0 deletions libpod/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -1336,3 +1336,52 @@ func (c *Container) getNetworkStatus() map[string]types.StatusBlock {
}
return nil
}

func (c *Container) NamespaceMode(ns spec.LinuxNamespaceType, ctrSpec *spec.Spec) string {
switch ns {
case spec.UTSNamespace:
if c.config.UTSNsCtr != "" {
return fmt.Sprintf("container:%s", c.config.UTSNsCtr)
}
case spec.CgroupNamespace:
if c.config.CgroupNsCtr != "" {
return fmt.Sprintf("container:%s", c.config.CgroupNsCtr)
}
case spec.IPCNamespace:
if c.config.IPCNsCtr != "" {
return fmt.Sprintf("container:%s", c.config.IPCNsCtr)
}
case spec.PIDNamespace:
if c.config.PIDNsCtr != "" {
return fmt.Sprintf("container:%s", c.config.PIDNsCtr)
}
case spec.UserNamespace:
if c.config.UserNsCtr != "" {
return fmt.Sprintf("container:%s", c.config.UserNsCtr)
}
case spec.NetworkNamespace:
if c.config.NetNsCtr != "" {
return fmt.Sprintf("container:%s", c.config.NetNsCtr)
}
case spec.MountNamespace:
if c.config.MountNsCtr != "" {
return fmt.Sprintf("container:%s", c.config.MountNsCtr)
}
}

if ctrSpec.Linux != nil {
// Locate the spec's given namespace.
// If there is none, it's namespace=host.
// If there is one and it has a path, it's "ns:".
// If there is no path, it's default - the empty string.
for _, availableNS := range ctrSpec.Linux.Namespaces {
if availableNS.Type == ns {
if availableNS.Path != "" {
return fmt.Sprintf("ns:%s", availableNS.Path)
}
return "private"
}
}
}
return "host"
}
24 changes: 2 additions & 22 deletions libpod/container_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -794,28 +794,8 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
hostConfig.PidMode = pidMode

// UTS namespace mode
utsMode := ""
if c.config.UTSNsCtr != "" {
utsMode = fmt.Sprintf("container:%s", c.config.UTSNsCtr)
} else if ctrSpec.Linux != nil {
// Locate the spec's UTS namespace.
// If there is none, it's uts=host.
// If there is one and it has a path, it's "ns:".
// If there is no path, it's default - the empty string.
for _, ns := range ctrSpec.Linux.Namespaces {
if ns.Type == spec.UTSNamespace {
if ns.Path != "" {
utsMode = fmt.Sprintf("ns:%s", ns.Path)
} else {
utsMode = "private"
}
break
}
}
if utsMode == "" {
utsMode = "host"
}
}
utsMode := c.NamespaceMode(spec.UTSNamespace, ctrSpec)

hostConfig.UTSMode = utsMode

// User namespace mode
Expand Down
2 changes: 2 additions & 0 deletions libpod/define/pod_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ type InspectPodInfraConfig struct {
PidNS string `json:"pid_ns,omitempty"`
// UserNS is the usernamespace that all the containers in the pod will join.
UserNS string `json:"userns,omitempty"`
// UtsNS is the uts namespace that all containers in the pod will join
UtsNS string `json:"uts_ns,omitempty"`
}

// InspectPodContainerInfo contains information on a container in a pod.
Expand Down
1 change: 1 addition & 0 deletions libpod/pod_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus
infraConfig.PidNS = p.NamespaceMode(specs.PIDNamespace)
infraConfig.UserNS = p.NamespaceMode(specs.UserNamespace)
infraConfig.UtsNS = p.NamespaceMode(specs.UTSNamespace)
namedVolumes, mounts := infra.SortUserVolumes(infra.config.Spec)
inspectMounts, err = infra.GetMounts(namedVolumes, infra.config.ImageVolumes, mounts)
infraSecurity = infra.GetSecurityOptions()
Expand Down
1 change: 1 addition & 0 deletions pkg/specgen/container_validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func (s *SpecGenerator) Validate() error {
if s.ContainerBasicConfig.UtsNS.IsPod() {
return errors.Wrap(ErrInvalidSpecConfig, "cannot set hostname when joining the pod UTS namespace")
}

return errors.Wrap(ErrInvalidSpecConfig, "cannot set hostname when running in the host UTS namespace")
}
// systemd values must be true, false, or always
Expand Down
6 changes: 6 additions & 0 deletions pkg/specgen/generate/container_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener

options = append(options, libpod.WithRootFSFromImage(newImage.ID(), resolvedImageName, s.RawImageName))
}

_, err = rt.LookupPod(s.Hostname)
if len(s.Hostname) > 0 && !s.UtsNS.IsPrivate() && err == nil {
// ok, we are incorrectly setting the pod as the hostname, lets undo that before validation
s.Hostname = ""
}
if err := s.Validate(); err != nil {
return nil, nil, nil, errors.Wrap(err, "invalid config provided")
}
Expand Down
9 changes: 8 additions & 1 deletion pkg/specgen/generate/namespaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,14 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.
if pod == nil || infraCtr == nil {
return nil, errNoInfra
}
toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr))
if pod.NamespaceMode(spec.UTSNamespace) == host {
// adding infra as a nsCtr is not what we want to do when uts == host
// this leads the new ctr to try to add an ns path which is should not in this mode
logrus.Debug("pod has host uts, not adding infra as a nsCtr")
s.UtsNS = specgen.Namespace{NSMode: specgen.Host}
} else {
toReturn = append(toReturn, libpod.WithUTSNSFrom(infraCtr))
}
case specgen.FromContainer:
utsCtr, err := rt.LookupContainer(s.UtsNS.Value)
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions pkg/specgen/generate/pod_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) {
if err != nil {
return nil, err
}

spec.Pod = pod.ID()
opts = append(opts, rt.WithPod(pod))
spec.CgroupParent = pod.CgroupParent()
Expand Down
42 changes: 39 additions & 3 deletions test/e2e/pod_clone_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import (

var _ = Describe("Podman pod clone", func() {
var (
tempdir string
err error
podmanTest *PodmanTestIntegration
tempdir string
err error
podmanTest *PodmanTestIntegration
hostname, _ = os.Hostname()
)

BeforeEach(func() {
Expand Down Expand Up @@ -155,4 +156,39 @@ var _ = Describe("Podman pod clone", func() {
Expect(strings[0]).Should(ContainSubstring("size=10240k"))
})

It("podman pod create --uts test", func() {
SkipIfRemote("hostname for the custom NS test is not as expected on the remote client")
cdoern marked this conversation as resolved.
Show resolved Hide resolved

session := podmanTest.Podman([]string{"pod", "create"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))

session = podmanTest.Podman([]string{"pod", "clone", "--uts", "host", session.OutputToString()})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))

session = podmanTest.Podman([]string{"run", "-it", "--pod", session.OutputToString(), ALPINE, "printenv", "HOSTNAME"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring(hostname))

podName := "utsPod"
ns := "ns:/proc/self/ns/"

session = podmanTest.Podman([]string{"pod", "create"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))

// just share uts with a custom path
podCreate := podmanTest.Podman([]string{"pod", "clone", "--uts", ns, "--name", podName, session.OutputToString()})
podCreate.WaitWithDefaultTimeout()
Expect(podCreate).Should(Exit(0))

podInspect := podmanTest.Podman([]string{"pod", "inspect", podName})
podInspect.WaitWithDefaultTimeout()
Expect(podInspect).Should(Exit(0))
podJSON := podInspect.InspectPodToJSON()
Expect(podJSON.InfraConfig).To(HaveField("UtsNS", ns))
})

})
33 changes: 30 additions & 3 deletions test/e2e/pod_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ import (

var _ = Describe("Podman pod create", func() {
var (
tempdir string
err error
podmanTest *PodmanTestIntegration
tempdir string
err error
podmanTest *PodmanTestIntegration
hostname, _ = os.Hostname()
)

BeforeEach(func() {
Expand Down Expand Up @@ -1136,4 +1137,30 @@ ENTRYPOINT ["sleep","99999"]
Expect(run).ShouldNot(Exit(0))
})

It("podman pod create --uts test", func() {
session := podmanTest.Podman([]string{"pod", "create", "--uts", "host"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))

session = podmanTest.Podman([]string{"run", "-it", "--pod", session.OutputToString(), ALPINE, "printenv", "HOSTNAME"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
if !IsRemote() { // remote hostname will not match os.Hostname()
Expect(session.OutputToString()).To(ContainSubstring(hostname))
}

podName := "utsPod"
ns := "ns:/proc/self/ns/"

// just share uts with a custom path
podCreate := podmanTest.Podman([]string{"pod", "create", "--uts", ns, "--name", podName, "--share", "uts"})
podCreate.WaitWithDefaultTimeout()
Expect(podCreate).Should(Exit(0))

podInspect := podmanTest.Podman([]string{"pod", "inspect", podName})
podInspect.WaitWithDefaultTimeout()
Expect(podInspect).Should(Exit(0))
podJSON := podInspect.InspectPodToJSON()
Expect(podJSON.InfraConfig).To(HaveField("UtsNS", ns))
})
})