Merge pull request #34087 from kolyshkin/ipcmode
Implement private and shareable ipc modes
This commit is contained in:
commit
bb6fc72cd2
22 changed files with 654 additions and 118 deletions
|
@ -665,7 +665,17 @@ definitions:
|
|||
type: "string"
|
||||
IpcMode:
|
||||
type: "string"
|
||||
description: "IPC namespace to use for the container."
|
||||
description: |
|
||||
IPC sharing mode for the container. Possible values are:
|
||||
|
||||
- `"none"`: own private IPC namespace, with /dev/shm not mounted
|
||||
- `"private"`: own private IPC namespace
|
||||
- `"shareable"`: own private IPC namespace, with a possibility to share it with other containers
|
||||
- `"container:<name|id>"`: join another (shareable) container's IPC namespace
|
||||
- `"host"`: use the host system's IPC namespace
|
||||
|
||||
If not specified, daemon default is used, which can either be `"private"`
|
||||
or `"shareable"`, depending on daemon version and configuration.
|
||||
Cgroup:
|
||||
type: "string"
|
||||
description: "Cgroup to use for the container."
|
||||
|
|
|
@ -23,41 +23,46 @@ func (i Isolation) IsDefault() bool {
|
|||
// IpcMode represents the container ipc stack.
|
||||
type IpcMode string
|
||||
|
||||
// IsPrivate indicates whether the container uses its private ipc stack.
|
||||
// IsPrivate indicates whether the container uses its own private ipc namespace which can not be shared.
|
||||
func (n IpcMode) IsPrivate() bool {
|
||||
return !(n.IsHost() || n.IsContainer())
|
||||
return n == "private"
|
||||
}
|
||||
|
||||
// IsHost indicates whether the container uses the host's ipc stack.
|
||||
// IsHost indicates whether the container shares the host's ipc namespace.
|
||||
func (n IpcMode) IsHost() bool {
|
||||
return n == "host"
|
||||
}
|
||||
|
||||
// IsContainer indicates whether the container uses a container's ipc stack.
|
||||
// IsShareable indicates whether the container's ipc namespace can be shared with another container.
|
||||
func (n IpcMode) IsShareable() bool {
|
||||
return n == "shareable"
|
||||
}
|
||||
|
||||
// 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"
|
||||
}
|
||||
|
||||
// Valid indicates whether the ipc stack is valid.
|
||||
// IsNone indicates whether container IpcMode is set to "none".
|
||||
func (n IpcMode) IsNone() bool {
|
||||
return n == "none"
|
||||
}
|
||||
|
||||
// IsEmpty indicates whether container IpcMode is empty
|
||||
func (n IpcMode) IsEmpty() bool {
|
||||
return n == ""
|
||||
}
|
||||
|
||||
// Valid indicates whether the ipc mode is valid.
|
||||
func (n IpcMode) Valid() bool {
|
||||
parts := strings.Split(string(n), ":")
|
||||
switch mode := parts[0]; mode {
|
||||
case "", "host":
|
||||
case "container":
|
||||
if len(parts) != 2 || parts[1] == "" {
|
||||
return false
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return n.IsEmpty() || n.IsNone() || n.IsPrivate() || n.IsHost() || n.IsShareable() || n.IsContainer()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if len(parts) > 1 && parts[0] == "container" {
|
||||
return parts[1]
|
||||
}
|
||||
return ""
|
||||
|
|
|
@ -47,6 +47,7 @@ func installConfigFlags(conf *config.Config, flags *pflag.FlagSet) {
|
|||
flags.StringVar(&conf.SeccompProfile, "seccomp-profile", "", "Path to seccomp profile")
|
||||
flags.Var(&conf.ShmSize, "default-shm-size", "Default shm size for containers")
|
||||
flags.BoolVar(&conf.NoNewPrivileges, "no-new-privileges", false, "Set no-new-privileges by default for new containers")
|
||||
flags.StringVar(&conf.IpcMode, "default-ipc-mode", config.DefaultIpcMode, `Default mode for containers ipc ("shareable" | "private")`)
|
||||
|
||||
attachExperimentalFlags(conf, flags)
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
|
@ -19,6 +18,7 @@ import (
|
|||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/volume"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
@ -171,47 +171,47 @@ func (container *Container) HasMountFor(path string) bool {
|
|||
return exists
|
||||
}
|
||||
|
||||
// UnmountIpcMounts uses the provided unmount function to unmount shm and mqueue if they were mounted
|
||||
func (container *Container) UnmountIpcMounts(unmount func(pth string) error) {
|
||||
if container.HostConfig.IpcMode.IsContainer() || container.HostConfig.IpcMode.IsHost() {
|
||||
return
|
||||
// UnmountIpcMount uses the provided unmount function to unmount shm if it was mounted
|
||||
func (container *Container) UnmountIpcMount(unmount func(pth string) error) error {
|
||||
if container.HasMountFor("/dev/shm") {
|
||||
return nil
|
||||
}
|
||||
|
||||
var warnings []string
|
||||
|
||||
if !container.HasMountFor("/dev/shm") {
|
||||
shmPath, err := container.ShmResourcePath()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
warnings = append(warnings, err.Error())
|
||||
} else if shmPath != "" {
|
||||
if err := unmount(shmPath); err != nil && !os.IsNotExist(err) {
|
||||
if mounted, mErr := mount.Mounted(shmPath); mounted || mErr != nil {
|
||||
warnings = append(warnings, fmt.Sprintf("failed to umount %s: %v", shmPath, err))
|
||||
}
|
||||
}
|
||||
|
||||
// container.ShmPath should not be used here as it may point
|
||||
// to the host's or other container's /dev/shm
|
||||
shmPath, err := container.ShmResourcePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if shmPath == "" {
|
||||
return nil
|
||||
}
|
||||
if err = unmount(shmPath); err != nil && !os.IsNotExist(err) {
|
||||
if mounted, mErr := mount.Mounted(shmPath); mounted || mErr != nil {
|
||||
return errors.Wrapf(err, "umount %s", shmPath)
|
||||
}
|
||||
}
|
||||
|
||||
if len(warnings) > 0 {
|
||||
logrus.Warnf("failed to cleanup ipc mounts:\n%v", strings.Join(warnings, "\n"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IpcMounts returns the list of IPC mounts
|
||||
func (container *Container) IpcMounts() []Mount {
|
||||
var mounts []Mount
|
||||
|
||||
if !container.HasMountFor("/dev/shm") {
|
||||
label.SetFileLabel(container.ShmPath, container.MountLabel)
|
||||
mounts = append(mounts, Mount{
|
||||
Source: container.ShmPath,
|
||||
Destination: "/dev/shm",
|
||||
Writable: true,
|
||||
Propagation: string(volume.DefaultPropagationMode),
|
||||
})
|
||||
if container.HasMountFor("/dev/shm") {
|
||||
return mounts
|
||||
}
|
||||
if container.ShmPath == "" {
|
||||
return mounts
|
||||
}
|
||||
|
||||
label.SetFileLabel(container.ShmPath, container.MountLabel)
|
||||
mounts = append(mounts, Mount{
|
||||
Source: container.ShmPath,
|
||||
Destination: "/dev/shm",
|
||||
Writable: true,
|
||||
Propagation: string(volume.DefaultPropagationMode),
|
||||
})
|
||||
|
||||
return mounts
|
||||
}
|
||||
|
|
|
@ -24,9 +24,10 @@ type ExitStatus struct {
|
|||
ExitCode int
|
||||
}
|
||||
|
||||
// UnmountIpcMounts unmounts Ipc related mounts.
|
||||
// UnmountIpcMount unmounts Ipc related mounts.
|
||||
// This is a NOOP on windows.
|
||||
func (container *Container) UnmountIpcMounts(unmount func(pth string) error) {
|
||||
func (container *Container) UnmountIpcMount(unmount func(pth string) error) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IpcMounts returns the list of Ipc related mounts.
|
||||
|
|
|
@ -513,6 +513,11 @@ func Validate(config *Config) error {
|
|||
}
|
||||
}
|
||||
|
||||
// validate platform-specific settings
|
||||
if err := config.ValidatePlatformConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -27,3 +27,8 @@ type BridgeConfig struct {
|
|||
func (conf *Config) IsSwarmCompatible() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidatePlatformConfig checks if any platform-specific configuration settings are invalid.
|
||||
func (conf *Config) ValidatePlatformConfig() error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -5,10 +5,16 @@ package config
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
containertypes "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/opts"
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultIpcMode is default for container's IpcMode, if not set otherwise
|
||||
DefaultIpcMode = "shareable" // TODO: change to private
|
||||
)
|
||||
|
||||
// Config defines the configuration of a docker daemon.
|
||||
// It includes json tags to deserialize configuration from a file
|
||||
// using the same names that the flags in the command line uses.
|
||||
|
@ -31,6 +37,7 @@ type Config struct {
|
|||
SeccompProfile string `json:"seccomp-profile,omitempty"`
|
||||
ShmSize opts.MemBytes `json:"default-shm-size,omitempty"`
|
||||
NoNewPrivileges bool `json:"no-new-privileges,omitempty"`
|
||||
IpcMode string `json:"default-ipc-mode,omitempty"`
|
||||
}
|
||||
|
||||
// BridgeConfig stores all the bridge driver specific
|
||||
|
@ -61,3 +68,21 @@ func (conf *Config) IsSwarmCompatible() error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyDefaultIpcMode(mode string) error {
|
||||
const hint = "Use \"shareable\" or \"private\"."
|
||||
|
||||
dm := containertypes.IpcMode(mode)
|
||||
if !dm.Valid() {
|
||||
return fmt.Errorf("Default IPC mode setting (%v) is invalid. "+hint, dm)
|
||||
}
|
||||
if dm != "" && !dm.IsPrivate() && !dm.IsShareable() {
|
||||
return fmt.Errorf("IPC mode \"%v\" is not supported as default value. "+hint, dm)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidatePlatformConfig checks if any platform-specific configuration settings are invalid.
|
||||
func (conf *Config) ValidatePlatformConfig() error {
|
||||
return verifyDefaultIpcMode(conf.IpcMode)
|
||||
}
|
||||
|
|
|
@ -50,3 +50,8 @@ func (conf *Config) GetExecRoot() string {
|
|||
func (conf *Config) IsSwarmCompatible() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidatePlatformConfig checks if any platform-specific configuration settings are invalid.
|
||||
func (conf *Config) ValidatePlatformConfig() error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -57,13 +57,27 @@ func (daemon *Daemon) setupLinkedContainers(container *container.Container) ([]s
|
|||
return env, nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) getIpcContainer(container *container.Container) (*container.Container, error) {
|
||||
containerID := container.HostConfig.IpcMode.Container()
|
||||
container, err := daemon.GetContainer(containerID)
|
||||
func (daemon *Daemon) getIpcContainer(id string) (*container.Container, error) {
|
||||
errMsg := "can't join IPC of container " + id
|
||||
// Check the container exists
|
||||
container, err := daemon.GetContainer(id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "cannot join IPC of a non running container: %s", container.ID)
|
||||
return nil, errors.Wrap(err, errMsg)
|
||||
}
|
||||
return container, daemon.checkContainer(container, containerIsRunning, containerIsNotRestarting)
|
||||
// Check the container is running and not restarting
|
||||
if err := daemon.checkContainer(container, containerIsRunning, containerIsNotRestarting); err != nil {
|
||||
return nil, errors.Wrap(err, errMsg)
|
||||
}
|
||||
// Check the container ipc is shareable
|
||||
if st, err := os.Stat(container.ShmPath); err != nil || !st.IsDir() {
|
||||
if err == nil || os.IsNotExist(err) {
|
||||
return nil, errors.New(errMsg + ": non-shareable IPC")
|
||||
}
|
||||
// stat() failed?
|
||||
return nil, errors.Wrap(err, errMsg+": unexpected error from stat "+container.ShmPath)
|
||||
}
|
||||
|
||||
return container, nil
|
||||
}
|
||||
|
||||
func (daemon *Daemon) getPidContainer(container *container.Container) (*container.Container, error) {
|
||||
|
@ -90,25 +104,33 @@ func containerIsNotRestarting(c *container.Container) error {
|
|||
}
|
||||
|
||||
func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
|
||||
var err error
|
||||
ipcMode := c.HostConfig.IpcMode
|
||||
|
||||
c.ShmPath, err = c.ShmResourcePath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.HostConfig.IpcMode.IsContainer() {
|
||||
ic, err := daemon.getIpcContainer(c)
|
||||
switch {
|
||||
case ipcMode.IsContainer():
|
||||
ic, err := daemon.getIpcContainer(ipcMode.Container())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.ShmPath = ic.ShmPath
|
||||
} else if c.HostConfig.IpcMode.IsHost() {
|
||||
|
||||
case ipcMode.IsHost():
|
||||
if _, err := os.Stat("/dev/shm"); err != nil {
|
||||
return fmt.Errorf("/dev/shm is not mounted, but must be for --ipc=host")
|
||||
}
|
||||
c.ShmPath = "/dev/shm"
|
||||
} else {
|
||||
|
||||
case ipcMode.IsPrivate(), ipcMode.IsNone():
|
||||
// c.ShmPath will/should not be used, so make it empty.
|
||||
// Container's /dev/shm mount comes from OCI spec.
|
||||
c.ShmPath = ""
|
||||
|
||||
case ipcMode.IsEmpty():
|
||||
// A container was created by an older version of the daemon.
|
||||
// The default behavior used to be what is now called "shareable".
|
||||
fallthrough
|
||||
|
||||
case ipcMode.IsShareable():
|
||||
rootIDs := daemon.idMappings.RootPair()
|
||||
if !c.HasMountFor("/dev/shm") {
|
||||
shmPath, err := c.ShmResourcePath()
|
||||
|
@ -120,19 +142,18 @@ func (daemon *Daemon) setupIpcDirs(c *container.Container) error {
|
|||
return err
|
||||
}
|
||||
|
||||
shmSize := int64(daemon.configStore.ShmSize)
|
||||
if c.HostConfig.ShmSize != 0 {
|
||||
shmSize = c.HostConfig.ShmSize
|
||||
}
|
||||
shmproperty := "mode=1777,size=" + strconv.FormatInt(shmSize, 10)
|
||||
shmproperty := "mode=1777,size=" + strconv.FormatInt(c.HostConfig.ShmSize, 10)
|
||||
if err := unix.Mount("shm", shmPath, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), label.FormatMountLabel(shmproperty, c.GetMountLabel())); err != nil {
|
||||
return fmt.Errorf("mounting shm tmpfs: %s", err)
|
||||
}
|
||||
if err := os.Chown(shmPath, rootIDs.UID, rootIDs.GID); err != nil {
|
||||
return err
|
||||
}
|
||||
c.ShmPath = shmPath
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid IPC mode: %v", ipcMode)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -308,7 +308,8 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
|
|||
|
||||
// reloadPlatform updates configuration with platform specific options
|
||||
// and updates the passed attributes
|
||||
func (daemon *Daemon) reloadPlatform(conf *config.Config, attributes map[string]string) {
|
||||
func (daemon *Daemon) reloadPlatform(conf *config.Config, attributes map[string]string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyDaemonSettings performs validation of daemon config struct
|
||||
|
|
|
@ -276,6 +276,15 @@ func (daemon *Daemon) adaptContainerSettings(hostConfig *containertypes.HostConf
|
|||
hostConfig.ShmSize = int64(daemon.configStore.ShmSize)
|
||||
}
|
||||
}
|
||||
// Set default IPC mode, if unset for container
|
||||
if hostConfig.IpcMode.IsEmpty() {
|
||||
m := config.DefaultIpcMode
|
||||
if daemon.configStore != nil {
|
||||
m = daemon.configStore.IpcMode
|
||||
}
|
||||
hostConfig.IpcMode = containertypes.IpcMode(m)
|
||||
}
|
||||
|
||||
var err error
|
||||
opts, err := daemon.generateSecurityOpt(hostConfig)
|
||||
if err != nil {
|
||||
|
@ -581,7 +590,11 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
|
|||
|
||||
// reloadPlatform updates configuration with platform specific options
|
||||
// and updates the passed attributes
|
||||
func (daemon *Daemon) reloadPlatform(conf *config.Config, attributes map[string]string) {
|
||||
func (daemon *Daemon) reloadPlatform(conf *config.Config, attributes map[string]string) error {
|
||||
if err := conf.ValidatePlatformConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if conf.IsValueSet("runtimes") {
|
||||
daemon.configStore.Runtimes = conf.Runtimes
|
||||
// Always set the default one
|
||||
|
@ -596,6 +609,10 @@ func (daemon *Daemon) reloadPlatform(conf *config.Config, attributes map[string]
|
|||
daemon.configStore.ShmSize = conf.ShmSize
|
||||
}
|
||||
|
||||
if conf.IpcMode != "" {
|
||||
daemon.configStore.IpcMode = conf.IpcMode
|
||||
}
|
||||
|
||||
// Update attributes
|
||||
var runtimeList bytes.Buffer
|
||||
for name, rt := range daemon.configStore.Runtimes {
|
||||
|
@ -608,6 +625,9 @@ func (daemon *Daemon) reloadPlatform(conf *config.Config, attributes map[string]
|
|||
attributes["runtimes"] = runtimeList.String()
|
||||
attributes["default-runtime"] = daemon.configStore.DefaultRuntime
|
||||
attributes["default-shm-size"] = fmt.Sprintf("%d", daemon.configStore.ShmSize)
|
||||
attributes["default-ipc-mode"] = daemon.configStore.IpcMode
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyDaemonSettings performs validation of daemon config struct
|
||||
|
|
|
@ -231,7 +231,8 @@ func verifyPlatformContainerSettings(daemon *Daemon, hostConfig *containertypes.
|
|||
|
||||
// reloadPlatform updates configuration with platform specific options
|
||||
// and updates the passed attributes
|
||||
func (daemon *Daemon) reloadPlatform(config *config.Config, attributes map[string]string) {
|
||||
func (daemon *Daemon) reloadPlatform(config *config.Config, attributes map[string]string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyDaemonSettings performs validation of daemon config struct
|
||||
|
|
|
@ -301,10 +301,13 @@ func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error
|
|||
}
|
||||
setNamespace(s, ns)
|
||||
}
|
||||
|
||||
// ipc
|
||||
if c.HostConfig.IpcMode.IsContainer() {
|
||||
ipcMode := c.HostConfig.IpcMode
|
||||
switch {
|
||||
case ipcMode.IsContainer():
|
||||
ns := specs.LinuxNamespace{Type: "ipc"}
|
||||
ic, err := daemon.getIpcContainer(c)
|
||||
ic, err := daemon.getIpcContainer(ipcMode.Container())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -316,12 +319,19 @@ func setNamespaces(daemon *Daemon, s *specs.Spec, c *container.Container) error
|
|||
nsUser.Path = fmt.Sprintf("/proc/%d/ns/user", ic.State.GetPID())
|
||||
setNamespace(s, nsUser)
|
||||
}
|
||||
} else if c.HostConfig.IpcMode.IsHost() {
|
||||
case ipcMode.IsHost():
|
||||
oci.RemoveNamespace(s, specs.LinuxNamespaceType("ipc"))
|
||||
} else {
|
||||
case ipcMode.IsEmpty():
|
||||
// A container was created by an older version of the daemon.
|
||||
// The default behavior used to be what is now called "shareable".
|
||||
fallthrough
|
||||
case ipcMode.IsPrivate(), ipcMode.IsShareable(), ipcMode.IsNone():
|
||||
ns := specs.LinuxNamespace{Type: "ipc"}
|
||||
setNamespace(s, ns)
|
||||
default:
|
||||
return fmt.Errorf("Invalid IPC mode: %v", ipcMode)
|
||||
}
|
||||
|
||||
// pid
|
||||
if c.HostConfig.PidMode.IsContainer() {
|
||||
ns := specs.LinuxNamespace{Type: "pid"}
|
||||
|
@ -486,10 +496,16 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c
|
|||
userMounts[m.Destination] = struct{}{}
|
||||
}
|
||||
|
||||
// Filter out mounts that are overridden by user supplied mounts
|
||||
// Filter out mounts from spec
|
||||
noIpc := c.HostConfig.IpcMode.IsNone()
|
||||
var defaultMounts []specs.Mount
|
||||
_, mountDev := userMounts["/dev"]
|
||||
for _, m := range s.Mounts {
|
||||
// filter out /dev/shm mount if case IpcMode is none
|
||||
if noIpc && m.Destination == "/dev/shm" {
|
||||
continue
|
||||
}
|
||||
// filter out mount overridden by a user supplied mount
|
||||
if _, ok := userMounts[m.Destination]; !ok {
|
||||
if mountDev && strings.HasPrefix(m.Destination, "/dev/") {
|
||||
continue
|
||||
|
@ -589,6 +605,14 @@ func setMounts(daemon *Daemon, s *specs.Spec, c *container.Container, mounts []c
|
|||
s.Linux.MaskedPaths = nil
|
||||
}
|
||||
|
||||
// Set size for /dev/shm mount that comes from spec (IpcMode: private only)
|
||||
for i, m := range s.Mounts {
|
||||
if m.Destination == "/dev/shm" {
|
||||
sizeOpt := "size=" + strconv.FormatInt(c.HostConfig.ShmSize, 10)
|
||||
s.Mounts[i].Options = append(s.Mounts[i].Options, sizeOpt)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: until a kernel/mount solution exists for handling remount in a user namespace,
|
||||
// we must clear the readonly flag for the cgroups mount (@mrunalp concurs)
|
||||
if uidMap := daemon.idMappings.UIDs(); uidMap != nil || c.HostConfig.Privileged {
|
||||
|
@ -745,7 +769,9 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ms = append(ms, c.IpcMounts()...)
|
||||
if !c.HostConfig.IpcMode.IsPrivate() && !c.HostConfig.IpcMode.IsEmpty() {
|
||||
ms = append(ms, c.IpcMounts()...)
|
||||
}
|
||||
|
||||
tmpfsMounts, err := c.TmpfsMounts()
|
||||
if err != nil {
|
||||
|
|
|
@ -37,7 +37,9 @@ func (daemon *Daemon) Reload(conf *config.Config) (err error) {
|
|||
}
|
||||
}()
|
||||
|
||||
daemon.reloadPlatform(conf, attributes)
|
||||
if err := daemon.reloadPlatform(conf, attributes); err != nil {
|
||||
return err
|
||||
}
|
||||
daemon.reloadDebug(conf, attributes)
|
||||
daemon.reloadMaxConcurrentDownloadsAndUploads(conf, attributes)
|
||||
daemon.reloadShutdownTimeout(conf, attributes)
|
||||
|
|
|
@ -198,7 +198,9 @@ func (daemon *Daemon) containerStart(container *container.Container, checkpoint
|
|||
func (daemon *Daemon) Cleanup(container *container.Container) {
|
||||
daemon.releaseNetwork(container)
|
||||
|
||||
container.UnmountIpcMounts(detachMounted)
|
||||
if err := container.UnmountIpcMount(detachMounted); err != nil {
|
||||
logrus.Warnf("%s cleanup: failed to unmount IPC: %s", container.ID, err)
|
||||
}
|
||||
|
||||
if err := daemon.conditionalUnmountOnCleanup(container); err != nil {
|
||||
// FIXME: remove once reference counting for graphdrivers has been refactored
|
||||
|
|
|
@ -17,6 +17,9 @@ keywords: "API, Docker, rcli, REST, documentation"
|
|||
|
||||
[Docker Engine API v1.32](https://docs.docker.com/engine/api/v1.32/) documentation
|
||||
|
||||
* `POST /containers/create` now accepts additional values for the
|
||||
`HostConfig.IpcMode` property. New values are `private`, `shareable`,
|
||||
and `none`.
|
||||
|
||||
## v1.31 API changes
|
||||
|
||||
|
|
222
integration-cli/docker_api_ipcmode_test.go
Normal file
222
integration-cli/docker_api_ipcmode_test.go
Normal file
|
@ -0,0 +1,222 @@
|
|||
// build +linux
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/integration-cli/checker"
|
||||
"github.com/docker/docker/integration-cli/cli"
|
||||
"github.com/docker/docker/integration-cli/request"
|
||||
"github.com/go-check/check"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
/* testIpcCheckDevExists checks whether a given mount (identified by its
|
||||
* major:minor pair from /proc/self/mountinfo) exists on the host system.
|
||||
*
|
||||
* The format of /proc/self/mountinfo is like:
|
||||
*
|
||||
* 29 23 0:24 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw
|
||||
* ^^^^\
|
||||
* - this is the minor:major we look for
|
||||
*/
|
||||
func testIpcCheckDevExists(mm string) (bool, error) {
|
||||
f, err := os.Open("/proc/self/mountinfo")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
fields := strings.Fields(s.Text())
|
||||
if len(fields) < 7 {
|
||||
continue
|
||||
}
|
||||
if fields[2] == mm {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Err(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// testIpcNonePrivateShareable is a helper function to test "none",
|
||||
// "private" and "shareable" modes.
|
||||
func testIpcNonePrivateShareable(c *check.C, mode string, mustBeMounted bool, mustBeShared bool) {
|
||||
cfg := container.Config{
|
||||
Image: "busybox",
|
||||
Cmd: []string{"top"},
|
||||
}
|
||||
hostCfg := container.HostConfig{
|
||||
IpcMode: container.IpcMode(mode),
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
client, err := request.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(len(resp.Warnings), checker.Equals, 0)
|
||||
|
||||
err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// get major:minor pair for /dev/shm from container's /proc/self/mountinfo
|
||||
cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo"
|
||||
mm := cli.DockerCmd(c, "exec", "-i", resp.ID, "sh", "-c", cmd).Combined()
|
||||
if !mustBeMounted {
|
||||
c.Assert(mm, checker.Equals, "")
|
||||
// no more checks to perform
|
||||
return
|
||||
}
|
||||
c.Assert(mm, checker.Matches, "^[0-9]+:[0-9]+$")
|
||||
|
||||
shared, err := testIpcCheckDevExists(mm)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Logf("[testIpcPrivateShareable] ipcmode: %v, ipcdev: %v, shared: %v, mustBeShared: %v\n", mode, mm, shared, mustBeShared)
|
||||
c.Assert(shared, checker.Equals, mustBeShared)
|
||||
}
|
||||
|
||||
/* TestAPIIpcModeNone checks the container "none" IPC mode
|
||||
* (--ipc none) works as expected. It makes sure there is no
|
||||
* /dev/shm mount inside the container.
|
||||
*/
|
||||
func (s *DockerSuite) TestAPIIpcModeNone(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testIpcNonePrivateShareable(c, "none", false, false)
|
||||
}
|
||||
|
||||
/* TestAPIIpcModePrivate checks the container private IPC mode
|
||||
* (--ipc private) works as expected. It gets the minor:major pair
|
||||
* of /dev/shm mount from the container, and makes sure there is no
|
||||
* such pair on the host.
|
||||
*/
|
||||
func (s *DockerSuite) TestAPIIpcModePrivate(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testIpcNonePrivateShareable(c, "private", true, false)
|
||||
}
|
||||
|
||||
/* TestAPIIpcModeShareable checks the container shareable IPC mode
|
||||
* (--ipc shareable) works as expected. It gets the minor:major pair
|
||||
* of /dev/shm mount from the container, and makes sure such pair
|
||||
* also exists on the host.
|
||||
*/
|
||||
func (s *DockerSuite) TestAPIIpcModeShareable(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testIpcNonePrivateShareable(c, "shareable", true, true)
|
||||
}
|
||||
|
||||
// testIpcContainer is a helper function to test --ipc container:NNN mode in various scenarios
|
||||
func testIpcContainer(s *DockerSuite, c *check.C, donorMode string, mustWork bool) {
|
||||
cfg := container.Config{
|
||||
Image: "busybox",
|
||||
Cmd: []string{"top"},
|
||||
}
|
||||
hostCfg := container.HostConfig{
|
||||
IpcMode: container.IpcMode(donorMode),
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
client, err := request.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// create and start the "donor" container
|
||||
resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(len(resp.Warnings), checker.Equals, 0)
|
||||
name1 := resp.ID
|
||||
|
||||
err = client.ContainerStart(ctx, name1, types.ContainerStartOptions{})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// create and start the second container
|
||||
hostCfg.IpcMode = container.IpcMode("container:" + name1)
|
||||
resp, err = client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(len(resp.Warnings), checker.Equals, 0)
|
||||
name2 := resp.ID
|
||||
|
||||
err = client.ContainerStart(ctx, name2, types.ContainerStartOptions{})
|
||||
if !mustWork {
|
||||
// start should fail with a specific error
|
||||
c.Assert(err, checker.NotNil)
|
||||
c.Assert(fmt.Sprintf("%v", err), checker.Contains, "non-shareable IPC")
|
||||
// no more checks to perform here
|
||||
return
|
||||
}
|
||||
|
||||
// start should succeed
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// check that IPC is shared
|
||||
// 1. create a file in the first container
|
||||
cli.DockerCmd(c, "exec", name1, "sh", "-c", "printf covfefe > /dev/shm/bar")
|
||||
// 2. check it's the same file in the second one
|
||||
out := cli.DockerCmd(c, "exec", "-i", name2, "cat", "/dev/shm/bar").Combined()
|
||||
c.Assert(out, checker.Matches, "^covfefe$")
|
||||
}
|
||||
|
||||
/* TestAPIIpcModeShareableAndContainer checks that a container created with
|
||||
* --ipc container:ID can use IPC of another shareable container.
|
||||
*/
|
||||
func (s *DockerSuite) TestAPIIpcModeShareableAndContainer(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testIpcContainer(s, c, "shareable", true)
|
||||
}
|
||||
|
||||
/* TestAPIIpcModePrivateAndContainer checks that a container created with
|
||||
* --ipc container:ID can NOT use IPC of another private container.
|
||||
*/
|
||||
func (s *DockerSuite) TestAPIIpcModePrivateAndContainer(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testIpcContainer(s, c, "private", false)
|
||||
}
|
||||
|
||||
/* TestAPIIpcModeHost checks that a container created with --ipc host
|
||||
* can use IPC of the host system.
|
||||
*/
|
||||
func (s *DockerSuite) TestAPIIpcModeHost(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
|
||||
cfg := container.Config{
|
||||
Image: "busybox",
|
||||
Cmd: []string{"top"},
|
||||
}
|
||||
hostCfg := container.HostConfig{
|
||||
IpcMode: container.IpcMode("host"),
|
||||
}
|
||||
ctx := context.Background()
|
||||
|
||||
client, err := request.NewClient()
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "")
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(len(resp.Warnings), checker.Equals, 0)
|
||||
name := resp.ID
|
||||
|
||||
err = client.ContainerStart(ctx, name, types.ContainerStartOptions{})
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// check that IPC is shared
|
||||
// 1. create a file inside container
|
||||
cli.DockerCmd(c, "exec", name, "sh", "-c", "printf covfefe > /dev/shm/."+name)
|
||||
// 2. check it's the same on the host
|
||||
bytes, err := ioutil.ReadFile("/dev/shm/." + name)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(string(bytes), checker.Matches, "^covfefe$")
|
||||
// 3. clean up
|
||||
cli.DockerCmd(c, "exec", name, "rm", "-f", "/dev/shm/."+name)
|
||||
}
|
|
@ -2985,6 +2985,165 @@ func (s *DockerDaemonSuite) TestShmSizeReload(c *check.C) {
|
|||
c.Assert(strings.TrimSpace(out), check.Equals, fmt.Sprintf("%v", size))
|
||||
}
|
||||
|
||||
// this is used to test both "private" and "shareable" daemon default ipc modes
|
||||
func testDaemonIpcPrivateShareable(d *daemon.Daemon, c *check.C, mustExist bool) {
|
||||
name := "test-ipcmode"
|
||||
_, err := d.Cmd("run", "-d", "--name", name, "busybox", "top")
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
// get major:minor pair for /dev/shm from container's /proc/self/mountinfo
|
||||
cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo"
|
||||
mm, err := d.Cmd("exec", "-i", name, "sh", "-c", cmd)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Assert(mm, checker.Matches, "^[0-9]+:[0-9]+$")
|
||||
|
||||
exists, err := testIpcCheckDevExists(mm)
|
||||
c.Assert(err, checker.IsNil)
|
||||
c.Logf("[testDaemonIpcPrivateShareable] ipcdev: %v, exists: %v, mustExist: %v\n", mm, exists, mustExist)
|
||||
c.Assert(exists, checker.Equals, mustExist)
|
||||
}
|
||||
|
||||
// TestDaemonIpcModeShareable checks that --default-ipc-mode shareable works as intended.
|
||||
func (s *DockerDaemonSuite) TestDaemonIpcModeShareable(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
|
||||
s.d.StartWithBusybox(c, "--default-ipc-mode", "shareable")
|
||||
testDaemonIpcPrivateShareable(s.d, c, true)
|
||||
}
|
||||
|
||||
// TestDaemonIpcModePrivate checks that --default-ipc-mode private works as intended.
|
||||
func (s *DockerDaemonSuite) TestDaemonIpcModePrivate(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
|
||||
s.d.StartWithBusybox(c, "--default-ipc-mode", "private")
|
||||
testDaemonIpcPrivateShareable(s.d, c, false)
|
||||
}
|
||||
|
||||
// used to check if an IpcMode given in config works as intended
|
||||
func testDaemonIpcFromConfig(s *DockerDaemonSuite, c *check.C, mode string, mustExist bool) {
|
||||
f, err := ioutil.TempFile("", "test-daemon-ipc-config")
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
config := `{"default-ipc-mode": "` + mode + `"}`
|
||||
_, err = f.WriteString(config)
|
||||
c.Assert(f.Close(), checker.IsNil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
s.d.StartWithBusybox(c, "--config-file", f.Name())
|
||||
testDaemonIpcPrivateShareable(s.d, c, mustExist)
|
||||
}
|
||||
|
||||
// TestDaemonIpcModePrivateFromConfig checks that "default-ipc-mode: private" config works as intended.
|
||||
func (s *DockerDaemonSuite) TestDaemonIpcModePrivateFromConfig(c *check.C) {
|
||||
testDaemonIpcFromConfig(s, c, "private", false)
|
||||
}
|
||||
|
||||
// TestDaemonIpcModeShareableFromConfig checks that "default-ipc-mode: shareable" config works as intended.
|
||||
func (s *DockerDaemonSuite) TestDaemonIpcModeShareableFromConfig(c *check.C) {
|
||||
testDaemonIpcFromConfig(s, c, "shareable", true)
|
||||
}
|
||||
|
||||
func testDaemonStartIpcMode(c *check.C, from, mode string, valid bool) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
|
||||
d := daemon.New(c, dockerBinary, dockerdBinary, daemon.Config{
|
||||
Experimental: testEnv.ExperimentalDaemon(),
|
||||
})
|
||||
c.Logf("Checking IpcMode %s set from %s\n", mode, from)
|
||||
var serr error
|
||||
switch from {
|
||||
case "config":
|
||||
f, err := ioutil.TempFile("", "test-daemon-ipc-config")
|
||||
c.Assert(err, checker.IsNil)
|
||||
defer os.Remove(f.Name())
|
||||
config := `{"default-ipc-mode": "` + mode + `"}`
|
||||
_, err = f.WriteString(config)
|
||||
c.Assert(f.Close(), checker.IsNil)
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
serr = d.StartWithError("--config-file", f.Name())
|
||||
case "cli":
|
||||
serr = d.StartWithError("--default-ipc-mode", mode)
|
||||
default:
|
||||
c.Fatalf("testDaemonStartIpcMode: invalid 'from' argument")
|
||||
}
|
||||
if serr == nil {
|
||||
d.Stop(c)
|
||||
}
|
||||
|
||||
if valid {
|
||||
c.Assert(serr, check.IsNil)
|
||||
} else {
|
||||
c.Assert(serr, check.NotNil)
|
||||
icmd.RunCommand("grep", "-E", "IPC .* is (invalid|not supported)", d.LogFileName()).Assert(c, icmd.Success)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDaemonStartWithIpcModes checks that daemon starts fine given correct
|
||||
// arguments for default IPC mode, and bails out with incorrect ones.
|
||||
// Both CLI option (--default-ipc-mode) and config parameter are tested.
|
||||
func (s *DockerDaemonSuite) TestDaemonStartWithIpcModes(c *check.C) {
|
||||
ipcModes := []struct {
|
||||
mode string
|
||||
valid bool
|
||||
}{
|
||||
{"private", true},
|
||||
{"shareable", true},
|
||||
|
||||
{"host", false},
|
||||
{"container:123", false},
|
||||
{"nosuchmode", false},
|
||||
}
|
||||
|
||||
for _, from := range []string{"config", "cli"} {
|
||||
for _, m := range ipcModes {
|
||||
testDaemonStartIpcMode(c, from, m.mode, m.valid)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDaemonRestartIpcMode makes sure a container keeps its ipc mode
|
||||
// (derived from daemon default) even after the daemon is restarted
|
||||
// with a different default ipc mode.
|
||||
func (s *DockerDaemonSuite) TestDaemonRestartIpcMode(c *check.C) {
|
||||
f, err := ioutil.TempFile("", "test-daemon-ipc-config-restart")
|
||||
c.Assert(err, checker.IsNil)
|
||||
file := f.Name()
|
||||
defer os.Remove(file)
|
||||
c.Assert(f.Close(), checker.IsNil)
|
||||
|
||||
config := []byte(`{"default-ipc-mode": "private"}`)
|
||||
c.Assert(ioutil.WriteFile(file, config, 0644), checker.IsNil)
|
||||
s.d.StartWithBusybox(c, "--config-file", file)
|
||||
|
||||
// check the container is created with private ipc mode as per daemon default
|
||||
name := "ipc1"
|
||||
_, err = s.d.Cmd("run", "-d", "--name", name, "--restart=always", "busybox", "top")
|
||||
c.Assert(err, checker.IsNil)
|
||||
m, err := s.d.InspectField(name, ".HostConfig.IpcMode")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(m, checker.Equals, "private")
|
||||
|
||||
// restart the daemon with shareable default ipc mode
|
||||
config = []byte(`{"default-ipc-mode": "shareable"}`)
|
||||
c.Assert(ioutil.WriteFile(file, config, 0644), checker.IsNil)
|
||||
s.d.Restart(c, "--config-file", file)
|
||||
|
||||
// check the container is still having private ipc mode
|
||||
m, err = s.d.InspectField(name, ".HostConfig.IpcMode")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(m, checker.Equals, "private")
|
||||
|
||||
// check a new container is created with shareable ipc mode as per new daemon default
|
||||
name = "ipc2"
|
||||
_, err = s.d.Cmd("run", "-d", "--name", name, "busybox", "top")
|
||||
c.Assert(err, checker.IsNil)
|
||||
m, err = s.d.InspectField(name, ".HostConfig.IpcMode")
|
||||
c.Assert(err, check.IsNil)
|
||||
c.Assert(m, checker.Equals, "shareable")
|
||||
}
|
||||
|
||||
// TestFailedPluginRemove makes sure that a failed plugin remove does not block
|
||||
// the daemon from starting
|
||||
func (s *DockerDaemonSuite) TestFailedPluginRemove(c *check.C) {
|
||||
|
|
|
@ -428,7 +428,32 @@ func (s *DockerDaemonSuite) TestDaemonEvents(c *check.C) {
|
|||
out, err = s.d.Cmd("events", "--since=0", "--until", daemonUnixTime(c))
|
||||
c.Assert(err, checker.IsNil)
|
||||
|
||||
c.Assert(out, checker.Contains, fmt.Sprintf("daemon reload %s (allow-nondistributable-artifacts=[], cluster-advertise=, cluster-store=, cluster-store-opts={}, debug=true, default-runtime=runc, default-shm-size=67108864, insecure-registries=[], labels=[\"bar=foo\"], live-restore=false, max-concurrent-downloads=1, max-concurrent-uploads=5, name=%s, registry-mirrors=[], runtimes=runc:{docker-runc []}, shutdown-timeout=10)", daemonID, daemonName))
|
||||
// only check for values known (daemon ID/name) or explicitly set above,
|
||||
// otherwise just check for names being present.
|
||||
expectedSubstrings := []string{
|
||||
" daemon reload " + daemonID + " ",
|
||||
"(allow-nondistributable-artifacts=[",
|
||||
" cluster-advertise=, ",
|
||||
" cluster-store=, ",
|
||||
" cluster-store-opts={",
|
||||
" debug=true, ",
|
||||
" default-ipc-mode=",
|
||||
" default-runtime=",
|
||||
" default-shm-size=",
|
||||
" insecure-registries=[",
|
||||
" labels=[\"bar=foo\"], ",
|
||||
" live-restore=",
|
||||
" max-concurrent-downloads=1, ",
|
||||
" max-concurrent-uploads=5, ",
|
||||
" name=" + daemonName,
|
||||
" registry-mirrors=[",
|
||||
" runtimes=",
|
||||
" shutdown-timeout=10)",
|
||||
}
|
||||
|
||||
for _, s := range expectedSubstrings {
|
||||
c.Assert(out, checker.Contains, s)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DockerDaemonSuite) TestDaemonEventsWithFilters(c *check.C) {
|
||||
|
|
|
@ -117,6 +117,12 @@ func DefaultLinuxSpec() specs.Spec {
|
|||
Source: "mqueue",
|
||||
Options: []string{"nosuid", "noexec", "nodev"},
|
||||
},
|
||||
{
|
||||
Destination: "/dev/shm",
|
||||
Type: "tmpfs",
|
||||
Source: "shm",
|
||||
Options: []string{"nosuid", "noexec", "nodev", "mode=1777"},
|
||||
},
|
||||
}
|
||||
s.Process.Capabilities = &specs.LinuxCapabilities{
|
||||
Bounding: defaultCapabilities(),
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/sysinfo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TODO Windows: This will need addressing for a Windows daemon.
|
||||
|
@ -61,43 +62,33 @@ func TestNetworkModeTest(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestIpcModeTest(t *testing.T) {
|
||||
ipcModes := map[container.IpcMode][]bool{
|
||||
// private, host, container, valid
|
||||
"": {true, false, false, true},
|
||||
"something:weird": {true, false, false, false},
|
||||
":weird": {true, false, false, true},
|
||||
"host": {false, true, false, true},
|
||||
"container:name": {false, false, true, true},
|
||||
"container:name:something": {false, false, true, false},
|
||||
"container:": {false, false, true, false},
|
||||
ipcModes := map[container.IpcMode]struct {
|
||||
private bool
|
||||
host bool
|
||||
container bool
|
||||
shareable bool
|
||||
valid bool
|
||||
ctrName string
|
||||
}{
|
||||
"": {valid: true},
|
||||
"private": {private: true, valid: true},
|
||||
"something:weird": {},
|
||||
":weird": {},
|
||||
"host": {host: true, valid: true},
|
||||
"container": {},
|
||||
"container:": {container: true, valid: true, ctrName: ""},
|
||||
"container:name": {container: true, valid: true, ctrName: "name"},
|
||||
"container:name1:name2": {container: true, valid: true, ctrName: "name1:name2"},
|
||||
"shareable": {shareable: true, valid: true},
|
||||
}
|
||||
|
||||
for ipcMode, state := range ipcModes {
|
||||
if ipcMode.IsPrivate() != state[0] {
|
||||
t.Fatalf("IpcMode.IsPrivate for %v should have been %v but was %v", ipcMode, state[0], ipcMode.IsPrivate())
|
||||
}
|
||||
if ipcMode.IsHost() != state[1] {
|
||||
t.Fatalf("IpcMode.IsHost for %v should have been %v but was %v", ipcMode, state[1], ipcMode.IsHost())
|
||||
}
|
||||
if ipcMode.IsContainer() != state[2] {
|
||||
t.Fatalf("IpcMode.IsContainer for %v should have been %v but was %v", ipcMode, state[2], ipcMode.IsContainer())
|
||||
}
|
||||
if ipcMode.Valid() != state[3] {
|
||||
t.Fatalf("IpcMode.Valid for %v should have been %v but was %v", ipcMode, state[3], ipcMode.Valid())
|
||||
}
|
||||
}
|
||||
containerIpcModes := map[container.IpcMode]string{
|
||||
"": "",
|
||||
"something": "",
|
||||
"something:weird": "weird",
|
||||
"container": "",
|
||||
"container:": "",
|
||||
"container:name": "name",
|
||||
"container:name1:name2": "name1:name2",
|
||||
}
|
||||
for ipcMode, container := range containerIpcModes {
|
||||
if ipcMode.Container() != container {
|
||||
t.Fatalf("Expected %v for %v but was %v", container, ipcMode, ipcMode.Container())
|
||||
}
|
||||
assert.Equal(t, state.private, ipcMode.IsPrivate(), "IpcMode.IsPrivate() parsing failed for %q", ipcMode)
|
||||
assert.Equal(t, state.host, ipcMode.IsHost(), "IpcMode.IsHost() parsing failed for %q", ipcMode)
|
||||
assert.Equal(t, state.container, ipcMode.IsContainer(), "IpcMode.IsContainer() parsing failed for %q", ipcMode)
|
||||
assert.Equal(t, state.shareable, ipcMode.IsShareable(), "IpcMode.IsShareable() parsing failed for %q", ipcMode)
|
||||
assert.Equal(t, state.valid, ipcMode.Valid(), "IpcMode.Valid() parsing failed for %q", ipcMode)
|
||||
assert.Equal(t, state.ctrName, ipcMode.Container(), "IpcMode.Container() parsing failed for %q", ipcMode)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue