diff --git a/integration-cli/docker_api_ipcmode_test.go b/integration-cli/docker_api_ipcmode_test.go deleted file mode 100644 index 9eb483d8b2..0000000000 --- a/integration-cli/docker_api_ipcmode_test.go +++ /dev/null @@ -1,81 +0,0 @@ -// build +linux -package main - -import ( - "bufio" - "context" - "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/go-check/check" -) - -/* 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 - } - } - - return false, s.Err() -} - -/* 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, SameHostDaemon, NotUserNamespace) - - cfg := container.Config{ - Image: "busybox", - Cmd: []string{"top"}, - } - hostCfg := container.HostConfig{ - IpcMode: container.IpcMode("host"), - } - ctx := context.Background() - - client := testEnv.APIClient() - 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) -} diff --git a/integration-cli/docker_cli_daemon_test.go b/integration-cli/docker_cli_daemon_test.go index dae4c9dc0d..8c66248c41 100644 --- a/integration-cli/docker_cli_daemon_test.go +++ b/integration-cli/docker_cli_daemon_test.go @@ -2901,67 +2901,6 @@ 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, SameHostDaemon) - - 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, SameHostDaemon) - - 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) { - testRequires(c, DaemonIsLinux, SameHostDaemon) - testDaemonIpcFromConfig(s, c, "private", false) -} - -// TestDaemonIpcModeShareableFromConfig checks that "default-ipc-mode: shareable" config works as intended. -func (s *DockerDaemonSuite) TestDaemonIpcModeShareableFromConfig(c *check.C) { - testRequires(c, DaemonIsLinux, SameHostDaemon) - testDaemonIpcFromConfig(s, c, "shareable", true) -} - func testDaemonStartIpcMode(c *check.C, from, mode string, valid bool) { d := daemon.New(c, dockerBinary, dockerdBinary, testdaemon.WithEnvironment(testEnv.Execution)) c.Logf("Checking IpcMode %s set from %s\n", mode, from) diff --git a/integration/container/ipcmode_test.go b/integration/container/ipcmode_test.go index b0ed84293f..7f4f818cfd 100644 --- a/integration/container/ipcmode_test.go +++ b/integration/container/ipcmode_test.go @@ -3,6 +3,7 @@ package container // import "github.com/docker/docker/integration/container" import ( "bufio" "context" + "io/ioutil" "os" "regexp" "strings" @@ -11,9 +12,11 @@ import ( "github.com/docker/docker/api/types" containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/integration/internal/container" + "github.com/docker/docker/internal/test/daemon" "github.com/docker/docker/internal/test/request" "gotest.tools/assert" is "gotest.tools/assert/cmp" + "gotest.tools/fs" "gotest.tools/skip" ) @@ -179,3 +182,114 @@ func TestAPIIpcModeShareableAndContainer(t *testing.T) { testIpcContainer(t, "private", false) } + +/* TestAPIIpcModeHost checks that a container created with --ipc host + * can use IPC of the host system. + */ +func TestAPIIpcModeHost(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon() || testEnv.IsUserNamespace()) + + cfg := containertypes.Config{ + Image: "busybox", + Cmd: []string{"top"}, + } + hostCfg := containertypes.HostConfig{ + IpcMode: containertypes.IpcMode("host"), + } + ctx := context.Background() + + client := testEnv.APIClient() + resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, nil, "") + assert.NilError(t, err) + assert.Check(t, is.Equal(len(resp.Warnings), 0)) + name := resp.ID + + err = client.ContainerStart(ctx, name, types.ContainerStartOptions{}) + assert.NilError(t, err) + + // check that IPC is shared + // 1. create a file inside container + _, err = container.Exec(ctx, client, name, []string{"sh", "-c", "printf covfefe > /dev/shm/." + name}) + assert.NilError(t, err) + // 2. check it's the same on the host + bytes, err := ioutil.ReadFile("/dev/shm/." + name) + assert.NilError(t, err) + assert.Check(t, is.Equal("covfefe", string(bytes))) + // 3. clean up + _, err = container.Exec(ctx, client, name, []string{"rm", "-f", "/dev/shm/." + name}) + assert.NilError(t, err) +} + +// testDaemonIpcPrivateShareable is a helper function to test "private" and "shareable" daemon default ipc modes. +func testDaemonIpcPrivateShareable(t *testing.T, mustBeShared bool, arg ...string) { + defer setupTest(t)() + + d := daemon.New(t) + d.StartWithBusybox(t, arg...) + defer d.Stop(t) + + client, err := d.NewClient() + assert.Check(t, err, "error creating client") + + cfg := containertypes.Config{ + Image: "busybox", + Cmd: []string{"top"}, + } + ctx := context.Background() + + resp, err := client.ContainerCreate(ctx, &cfg, &containertypes.HostConfig{}, nil, "") + assert.NilError(t, err) + assert.Check(t, is.Equal(len(resp.Warnings), 0)) + + err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}) + assert.NilError(t, err) + + // get major:minor pair for /dev/shm from container's /proc/self/mountinfo + cmd := "awk '($5 == \"/dev/shm\") {printf $3}' /proc/self/mountinfo" + result, err := container.Exec(ctx, client, resp.ID, []string{"sh", "-c", cmd}) + assert.NilError(t, err) + mm := result.Combined() + assert.Check(t, is.Equal(true, regexp.MustCompile("^[0-9]+:[0-9]+$").MatchString(mm))) + + shared, err := testIpcCheckDevExists(mm) + assert.NilError(t, err) + t.Logf("[testDaemonIpcPrivateShareable] ipcdev: %v, shared: %v, mustBeShared: %v\n", mm, shared, mustBeShared) + assert.Check(t, is.Equal(shared, mustBeShared)) +} + +// TestDaemonIpcModeShareable checks that --default-ipc-mode shareable works as intended. +func TestDaemonIpcModeShareable(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon()) + + testDaemonIpcPrivateShareable(t, true, "--default-ipc-mode", "shareable") +} + +// TestDaemonIpcModePrivate checks that --default-ipc-mode private works as intended. +func TestDaemonIpcModePrivate(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon()) + + testDaemonIpcPrivateShareable(t, false, "--default-ipc-mode", "private") +} + +// used to check if an IpcMode given in config works as intended +func testDaemonIpcFromConfig(t *testing.T, mode string, mustExist bool) { + config := `{"default-ipc-mode": "` + mode + `"}` + file := fs.NewFile(t, "test-daemon-ipc-config", fs.WithContent(config)) + defer file.Remove() + + testDaemonIpcPrivateShareable(t, mustExist, "--config-file", file.Path()) +} + +// TestDaemonIpcModePrivateFromConfig checks that "default-ipc-mode: private" config works as intended. +func TestDaemonIpcModePrivateFromConfig(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon()) + + testDaemonIpcFromConfig(t, "private", false) +} + +// TestDaemonIpcModeShareableFromConfig checks that "default-ipc-mode: shareable" config works as intended. +func TestDaemonIpcModeShareableFromConfig(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon()) + + testDaemonIpcFromConfig(t, "shareable", true) +} diff --git a/internal/test/environment/environment.go b/internal/test/environment/environment.go index 74c8e2ce0a..5538d2097e 100644 --- a/internal/test/environment/environment.go +++ b/internal/test/environment/environment.go @@ -145,6 +145,12 @@ func (e *Execution) APIClient() client.APIClient { return e.client } +// IsUserNamespace returns whether the user namespace remapping is enabled +func (e *Execution) IsUserNamespace() bool { + root := os.Getenv("DOCKER_REMAP_ROOT") + return root != "" +} + // EnsureFrozenImagesLinux loads frozen test images into the daemon // if they aren't already loaded func EnsureFrozenImagesLinux(testEnv *Execution) error {