Merge pull request #14501 from cdoern/podUTS

podman pod create --uts support
This commit is contained in:
openshift-ci[bot]
2022-07-06 14:51:22 +00:00
committed by GitHub
15 changed files with 165 additions and 37 deletions

View File

@ -544,13 +544,6 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
) )
_ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag) _ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag)
utsFlagName := "uts"
createFlags.String(
utsFlagName, "",
"UTS namespace to use",
)
_ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace)
mountFlagName := "mount" mountFlagName := "mount"
createFlags.StringArrayVar( createFlags.StringArrayVar(
&cf.Mount, &cf.Mount,
@ -684,6 +677,14 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
) )
_ = cmd.RegisterFlagCompletionFunc(usernsFlagName, AutocompleteUserNamespace) _ = cmd.RegisterFlagCompletionFunc(usernsFlagName, AutocompleteUserNamespace)
utsFlagName := "uts"
createFlags.StringVar(
&cf.UTS,
utsFlagName, "",
"UTS namespace to use",
)
_ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace)
cgroupParentFlagName := "cgroup-parent" cgroupParentFlagName := "cgroup-parent"
createFlags.StringVar( createFlags.StringVar(
&cf.CgroupParent, &cf.CgroupParent,

View File

@ -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") return vals, errors.New("--cpu-quota and --cpus cannot be set together")
} }
vals.IPC = c.Flag("ipc").Value.String() vals.IPC = c.Flag("ipc").Value.String()
vals.UTS = c.Flag("uts").Value.String()
vals.PID = c.Flag("pid").Value.String() vals.PID = c.Flag("pid").Value.String()
vals.CgroupNS = c.Flag("cgroupns").Value.String() vals.CgroupNS = c.Flag("cgroupns").Value.String()

View File

@ -277,6 +277,7 @@ func create(cmd *cobra.Command, args []string) error {
if err != nil { if err != nil {
return err return err
} }
podSpec.Volumes = podSpec.InfraContainerSpec.Volumes podSpec.Volumes = podSpec.InfraContainerSpec.Volumes
podSpec.ImageVolumes = podSpec.InfraContainerSpec.ImageVolumes podSpec.ImageVolumes = podSpec.InfraContainerSpec.ImageVolumes
podSpec.OverlayVolumes = podSpec.InfraContainerSpec.OverlayVolumes podSpec.OverlayVolumes = podSpec.InfraContainerSpec.OverlayVolumes

View File

@ -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. - *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]]*] #### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]
Create a bind mount. If ` -v /HOST-DIR:/CONTAINER-DIR` is specified, Podman Create a bind mount. If ` -v /HOST-DIR:/CONTAINER-DIR` is specified, Podman

View File

@ -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. - *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]]*] #### **--volume**, **-v**[=*[[SOURCE-VOLUME|HOST-DIR:]CONTAINER-DIR[:OPTIONS]]*]
Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, Podman Create a bind mount. If you specify, ` -v /HOST-DIR:/CONTAINER-DIR`, Podman

View File

@ -1335,3 +1335,52 @@ func (c *Container) getNetworkStatus() map[string]types.StatusBlock {
} }
return nil 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"
}

View File

@ -794,28 +794,8 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
hostConfig.PidMode = pidMode hostConfig.PidMode = pidMode
// UTS namespace mode // UTS namespace mode
utsMode := "" utsMode := c.NamespaceMode(spec.UTSNamespace, ctrSpec)
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"
}
}
hostConfig.UTSMode = utsMode hostConfig.UTSMode = utsMode
// User namespace mode // User namespace mode

View File

@ -122,6 +122,8 @@ type InspectPodInfraConfig struct {
PidNS string `json:"pid_ns,omitempty"` PidNS string `json:"pid_ns,omitempty"`
// UserNS is the usernamespace that all the containers in the pod will join. // UserNS is the usernamespace that all the containers in the pod will join.
UserNS string `json:"userns,omitempty"` 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. // InspectPodContainerInfo contains information on a container in a pod.

View File

@ -676,6 +676,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus infraConfig.CPUSetCPUs = p.ResourceLim().CPU.Cpus
infraConfig.PidNS = p.NamespaceMode(specs.PIDNamespace) infraConfig.PidNS = p.NamespaceMode(specs.PIDNamespace)
infraConfig.UserNS = p.NamespaceMode(specs.UserNamespace) infraConfig.UserNS = p.NamespaceMode(specs.UserNamespace)
infraConfig.UtsNS = p.NamespaceMode(specs.UTSNamespace)
namedVolumes, mounts := infra.SortUserVolumes(infra.config.Spec) namedVolumes, mounts := infra.SortUserVolumes(infra.config.Spec)
inspectMounts, err = infra.GetMounts(namedVolumes, infra.config.ImageVolumes, mounts) inspectMounts, err = infra.GetMounts(namedVolumes, infra.config.ImageVolumes, mounts)
infraSecurity = infra.GetSecurityOptions() infraSecurity = infra.GetSecurityOptions()

View File

@ -59,6 +59,7 @@ func (s *SpecGenerator) Validate() error {
if s.ContainerBasicConfig.UtsNS.IsPod() { 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 joining the pod UTS namespace")
} }
return errors.Wrap(ErrInvalidSpecConfig, "cannot set hostname when running in the host UTS namespace") return errors.Wrap(ErrInvalidSpecConfig, "cannot set hostname when running in the host UTS namespace")
} }
// systemd values must be true, false, or always // systemd values must be true, false, or always

View File

@ -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)) 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 { if err := s.Validate(); err != nil {
return nil, nil, nil, errors.Wrap(err, "invalid config provided") return nil, nil, nil, errors.Wrap(err, "invalid config provided")
} }

View File

@ -176,7 +176,14 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.
if pod == nil || infraCtr == nil { if pod == nil || infraCtr == nil {
return nil, errNoInfra 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: case specgen.FromContainer:
utsCtr, err := rt.LookupContainer(s.UtsNS.Value) utsCtr, err := rt.LookupContainer(s.UtsNS.Value)
if err != nil { if err != nil {

View File

@ -60,6 +60,7 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
spec.Pod = pod.ID() spec.Pod = pod.ID()
opts = append(opts, rt.WithPod(pod)) opts = append(opts, rt.WithPod(pod))
spec.CgroupParent = pod.CgroupParent() spec.CgroupParent = pod.CgroupParent()

View File

@ -11,9 +11,10 @@ import (
var _ = Describe("Podman pod clone", func() { var _ = Describe("Podman pod clone", func() {
var ( var (
tempdir string tempdir string
err error err error
podmanTest *PodmanTestIntegration podmanTest *PodmanTestIntegration
hostname, _ = os.Hostname()
) )
BeforeEach(func() { BeforeEach(func() {
@ -155,4 +156,39 @@ var _ = Describe("Podman pod clone", func() {
Expect(strings[0]).Should(ContainSubstring("size=10240k")) 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")
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))
})
}) })

View File

@ -23,9 +23,10 @@ import (
var _ = Describe("Podman pod create", func() { var _ = Describe("Podman pod create", func() {
var ( var (
tempdir string tempdir string
err error err error
podmanTest *PodmanTestIntegration podmanTest *PodmanTestIntegration
hostname, _ = os.Hostname()
) )
BeforeEach(func() { BeforeEach(func() {
@ -1136,4 +1137,30 @@ ENTRYPOINT ["sleep","99999"]
Expect(run).ShouldNot(Exit(0)) 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))
})
}) })