mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Test cases for new ipc modes
These test cases cover various arguments for docker create/run --ipc option, as well as daemon's --default-ipc-mode cli option and configuration file parameter. For the description of container IPC modes, see previous commit. To run these: TESTFLAGS='-check.f IpcMode' make test-integration-cli [v2: simplify TestDaemonEvents(), add default-ipc-mode presense check] [v3: add TestDaemonIpcModeVSRestart] [v4: ipcmode test now uses client lib instead of CLI (except for exec)] [v5: nitpicks in comments] [v6: add test case for "none"; fix a typo; simplify TestDaemonEvents() more] Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
parent
7120976d74
commit
9a60e1cc87
3 changed files with 407 additions and 1 deletions
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) {
|
||||
|
|
Loading…
Add table
Reference in a new issue