diff --git a/integration-cli/docker_api_ipcmode_test.go b/integration-cli/docker_api_ipcmode_test.go index 886ff88d20..e37a875e8e 100644 --- a/integration-cli/docker_api_ipcmode_test.go +++ b/integration-cli/docker_api_ipcmode_test.go @@ -46,72 +46,6 @@ func testIpcCheckDevExists(mm string) (bool, error) { return false, s.Err() } -// 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 := testEnv.APIClient() - - 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, MinimumAPIVersion("1.32")) - 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, SameHostDaemon) - 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, SameHostDaemon) - 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{ diff --git a/integration/container/ipcmode_test.go b/integration/container/ipcmode_test.go new file mode 100644 index 0000000000..3011748cd0 --- /dev/null +++ b/integration/container/ipcmode_test.go @@ -0,0 +1,116 @@ +package container // import "github.com/docker/docker/integration/container" + +import ( + "bufio" + "context" + "os" + "regexp" + "strings" + "testing" + + "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/request" + "gotest.tools/assert" + is "gotest.tools/assert/cmp" + "gotest.tools/skip" +) + +// 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() +} + +// testIpcNonePrivateShareable is a helper function to test "none", +// "private" and "shareable" modes. +func testIpcNonePrivateShareable(t *testing.T, mode string, mustBeMounted bool, mustBeShared bool) { + defer setupTest(t)() + + cfg := containertypes.Config{ + Image: "busybox", + Cmd: []string{"top"}, + } + hostCfg := containertypes.HostConfig{ + IpcMode: containertypes.IpcMode(mode), + } + client := request.NewAPIClient(t) + ctx := context.Background() + + resp, err := client.ContainerCreate(ctx, &cfg, &hostCfg, 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() + if !mustBeMounted { + assert.Check(t, is.Equal(mm, "")) + // no more checks to perform + return + } + assert.Check(t, is.Equal(true, regexp.MustCompile("^[0-9]+:[0-9]+$").MatchString(mm))) + + shared, err := testIpcCheckDevExists(mm) + assert.NilError(t, err) + t.Logf("[testIpcPrivateShareable] ipcmode: %v, ipcdev: %v, shared: %v, mustBeShared: %v\n", mode, mm, shared, mustBeShared) + assert.Check(t, is.Equal(shared, mustBeShared)) +} + +// TestIpcModeNone checks the container "none" IPC mode +// (--ipc none) works as expected. It makes sure there is no +// /dev/shm mount inside the container. +func TestIpcModeNone(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon()) + + testIpcNonePrivateShareable(t, "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 TestIpcModePrivate(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon()) + + testIpcNonePrivateShareable(t, "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 TestIpcModeShareable(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon()) + + testIpcNonePrivateShareable(t, "shareable", true, true) +}