diff --git a/integration-cli/docker_api_containers_test.go b/integration-cli/docker_api_containers_test.go index 0dd5a12b7f..e70bfd7e52 100644 --- a/integration-cli/docker_api_containers_test.go +++ b/integration-cli/docker_api_containers_test.go @@ -11,14 +11,12 @@ import ( "net/http" "os" "path/filepath" - "regexp" "runtime" "strings" "testing" "time" cerrdefs "github.com/containerd/errdefs" - dconfig "github.com/docker/docker/daemon/config" "github.com/docker/docker/daemon/volume" "github.com/docker/docker/integration-cli/cli" "github.com/docker/docker/integration-cli/cli/build" @@ -1142,110 +1140,6 @@ func (s *DockerAPISuite) TestPostContainersCreateWithWrongCpusetValues(c *testin assert.ErrorContains(c, err, expected) } -func (s *DockerAPISuite) TestPostContainersCreateShmSizeNegative(c *testing.T) { - // ShmSize is not supported on Windows - testRequires(c, DaemonIsLinux) - config := container.Config{ - Image: "busybox", - } - hostConfig := container.HostConfig{ - ShmSize: -1, - } - - apiClient, err := client.NewClientWithOpts(client.FromEnv) - assert.NilError(c, err) - defer apiClient.Close() - - _, err = apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, "") - assert.ErrorContains(c, err, "SHM size can not be less than 0") -} - -func (s *DockerAPISuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *testing.T) { - // ShmSize is not supported on Windows - testRequires(c, DaemonIsLinux) - - config := container.Config{ - Image: "busybox", - Cmd: []string{"mount"}, - } - - apiClient, err := client.NewClientWithOpts(client.FromEnv) - assert.NilError(c, err) - defer apiClient.Close() - - ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "") - assert.NilError(c, err) - - containerJSON, err := apiClient.ContainerInspect(testutil.GetContext(c), ctr.ID) - assert.NilError(c, err) - - assert.Equal(c, containerJSON.HostConfig.ShmSize, dconfig.DefaultShmSize) - - out := cli.DockerCmd(c, "start", "-i", containerJSON.ID).Combined() - shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) - if !shmRegexp.MatchString(out) { - c.Fatalf("Expected shm of 64MB in mount command, got %v", out) - } -} - -func (s *DockerAPISuite) TestPostContainersCreateShmSizeOmitted(c *testing.T) { - // ShmSize is not supported on Windows - testRequires(c, DaemonIsLinux) - config := container.Config{ - Image: "busybox", - Cmd: []string{"mount"}, - } - - apiClient, err := client.NewClientWithOpts(client.FromEnv) - assert.NilError(c, err) - defer apiClient.Close() - - ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), &config, &container.HostConfig{}, &network.NetworkingConfig{}, nil, "") - assert.NilError(c, err) - - containerJSON, err := apiClient.ContainerInspect(testutil.GetContext(c), ctr.ID) - assert.NilError(c, err) - - assert.Equal(c, containerJSON.HostConfig.ShmSize, int64(67108864)) - - out := cli.DockerCmd(c, "start", "-i", containerJSON.ID).Combined() - shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`) - if !shmRegexp.MatchString(out) { - c.Fatalf("Expected shm of 64MB in mount command, got %v", out) - } -} - -func (s *DockerAPISuite) TestPostContainersCreateWithShmSize(c *testing.T) { - // ShmSize is not supported on Windows - testRequires(c, DaemonIsLinux) - config := container.Config{ - Image: "busybox", - Cmd: []string{"mount"}, - } - - hostConfig := container.HostConfig{ - ShmSize: 1073741824, - } - - apiClient, err := client.NewClientWithOpts(client.FromEnv) - assert.NilError(c, err) - defer apiClient.Close() - - ctr, err := apiClient.ContainerCreate(testutil.GetContext(c), &config, &hostConfig, &network.NetworkingConfig{}, nil, "") - assert.NilError(c, err) - - containerJSON, err := apiClient.ContainerInspect(testutil.GetContext(c), ctr.ID) - assert.NilError(c, err) - - assert.Equal(c, containerJSON.HostConfig.ShmSize, int64(1073741824)) - - out := cli.DockerCmd(c, "start", "-i", containerJSON.ID).Combined() - shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`) - if !shmRegex.MatchString(out) { - c.Fatalf("Expected shm of 1GB in mount command, got %v", out) - } -} - func (s *DockerAPISuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *testing.T) { // Swappiness is not supported on Windows testRequires(c, DaemonIsLinux) diff --git a/integration/container/run_linux_test.go b/integration/container/run_linux_test.go index 9a583e131f..fe10f29fbc 100644 --- a/integration/container/run_linux_test.go +++ b/integration/container/run_linux_test.go @@ -9,10 +9,12 @@ import ( "strings" "testing" + cerrdefs "github.com/containerd/errdefs" "github.com/docker/docker/integration/internal/container" net "github.com/docker/docker/integration/internal/network" "github.com/docker/docker/testutil" "github.com/docker/docker/testutil/daemon" + "github.com/docker/go-units" "github.com/moby/moby/api/stdcopy" containertypes "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/versions" @@ -457,8 +459,8 @@ func TestCgroupRW(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - config := container.NewTestConfig(tc.ops...) - resp, err := container.CreateFromConfig(ctx, apiClient, config) + cfg := container.NewTestConfig(tc.ops...) + resp, err := container.CreateFromConfig(ctx, apiClient, cfg) if err != nil { assert.Equal(t, tc.expectedErrMsg, err.Error()) return @@ -488,3 +490,79 @@ func TestCgroupRW(t *testing.T) { }) } } + +func TestContainerShmSize(t *testing.T) { + ctx := setupTest(t) + + const defaultSize = "1000k" + defaultSizeBytes, err := units.RAMInBytes(defaultSize) + assert.NilError(t, err) + + d := daemon.New(t) + d.StartWithBusybox(ctx, t, "--default-shm-size="+defaultSize) + defer d.Stop(t) + + apiClient := d.NewClientT(t) + + tests := []struct { + doc string + opt container.ConfigOpt + expSize string + expErr string + }{ + { + doc: "nil hostConfig", + opt: container.WithHostConfig(nil), + expSize: defaultSize, + }, + { + doc: "empty hostConfig", + opt: container.WithHostConfig(&containertypes.HostConfig{}), + expSize: defaultSize, + }, + { + doc: "custom shmSize", + opt: container.WithHostConfig(&containertypes.HostConfig{ShmSize: defaultSizeBytes * 2}), + expSize: "2000k", + }, + { + doc: "negative shmSize", + opt: container.WithHostConfig(&containertypes.HostConfig{ShmSize: -1}), + expErr: "Error response from daemon: SHM size can not be less than 0", + }, + } + + for _, tc := range tests { + t.Run(tc.doc, func(t *testing.T) { + if tc.expErr != "" { + cfg := container.NewTestConfig(container.WithCmd("sh", "-c", "grep /dev/shm /proc/self/mountinfo"), tc.opt) + _, err := container.CreateFromConfig(ctx, apiClient, cfg) + assert.Check(t, is.ErrorContains(err, tc.expErr)) + assert.Check(t, is.ErrorType(err, cerrdefs.IsInvalidArgument)) + return + } + + cID := container.Run(ctx, t, apiClient, + container.WithCmd("sh", "-c", "grep /dev/shm /proc/self/mountinfo"), + tc.opt, + ) + + t.Cleanup(func() { + container.Remove(ctx, t, apiClient, cID, containertypes.RemoveOptions{}) + }) + + expectedSize, err := units.RAMInBytes(tc.expSize) + assert.NilError(t, err) + + ctr := container.Inspect(ctx, t, apiClient, cID) + assert.Check(t, is.Equal(ctr.HostConfig.ShmSize, expectedSize)) + + out, err := container.Output(ctx, apiClient, cID) + assert.NilError(t, err) + + // e.g., "218 213 0:87 / /dev/shm rw,nosuid,nodev,noexec,relatime - tmpfs shm rw,size=1000k" + assert.Assert(t, is.Contains(out.Stdout, "/dev/shm "), "shm mount not found in output: \n%v", out.Stdout) + assert.Check(t, is.Contains(out.Stdout, "size="+tc.expSize)) + }) + } +} diff --git a/integration/internal/container/ops.go b/integration/internal/container/ops.go index ff490da8a9..86d49aa84f 100644 --- a/integration/internal/container/ops.go +++ b/integration/internal/container/ops.go @@ -12,6 +12,9 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) +// ConfigOpt is an option to apply to a container. +type ConfigOpt func(*TestContainerConfig) + // WithName sets the name of the container func WithName(name string) func(*TestContainerConfig) { return func(c *TestContainerConfig) { @@ -361,3 +364,10 @@ func WithContainerWideMacAddress(address string) func(c *TestContainerConfig) { c.Config.MacAddress = address //nolint:staticcheck // ignore SA1019: field is deprecated, but still used on API < v1.44. } } + +// WithHostConfig sets a custom [container.HostConfig] for the container. +func WithHostConfig(hc *container.HostConfig) func(c *TestContainerConfig) { + return func(c *TestContainerConfig) { + c.HostConfig = hc + } +}