mirror of
https://github.com/containers/podman.git
synced 2025-06-22 01:48:54 +08:00
Merge pull request #14501 from cdoern/podUTS
podman pod create --uts support
This commit is contained in:
@ -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,
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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"
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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")
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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()
|
||||||
|
@ -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))
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
@ -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))
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user