|
@@ -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)
|
|
|
+}
|