diff --git a/daemon/inspect.go b/daemon/inspect.go index 3577beaaea..b6879b0585 100644 --- a/daemon/inspect.go +++ b/daemon/inspect.go @@ -110,6 +110,9 @@ func (daemon *Daemon) getInspectData(container *container.Container, size bool) hostConfig.Links = append(hostConfig.Links, fmt.Sprintf("%s:%s", child.Name, linkAlias)) } + // We merge the Ulimits from hostConfig with daemon default + daemon.mergeUlimits(&hostConfig) + var containerHealth *types.Health if container.State.Health != nil { containerHealth = &types.Health{ diff --git a/daemon/oci_linux.go b/daemon/oci_linux.go index 49d9e1c8aa..62db5da4f3 100644 --- a/daemon/oci_linux.go +++ b/daemon/oci_linux.go @@ -115,19 +115,11 @@ func setDevices(s *specs.Spec, c *container.Container) error { func setRlimits(daemon *Daemon, s *specs.Spec, c *container.Container) error { var rlimits []specs.Rlimit - ulimits := c.HostConfig.Ulimits - // Merge ulimits with daemon defaults - ulIdx := make(map[string]struct{}) - for _, ul := range ulimits { - ulIdx[ul.Name] = struct{}{} - } - for name, ul := range daemon.configStore.Ulimits { - if _, exists := ulIdx[name]; !exists { - ulimits = append(ulimits, ul) - } - } - - for _, ul := range ulimits { + // We want to leave the original HostConfig alone so make a copy here + hostConfig := *c.HostConfig + // Merge with the daemon defaults + daemon.mergeUlimits(&hostConfig) + for _, ul := range hostConfig.Ulimits { rlimits = append(rlimits, specs.Rlimit{ Type: "RLIMIT_" + strings.ToUpper(ul.Name), Soft: uint64(ul.Soft), @@ -709,3 +701,19 @@ func clearReadOnly(m *specs.Mount) { } m.Options = opt } + +// mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig +func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig) { + ulimits := c.Ulimits + // Merge ulimits with daemon defaults + ulIdx := make(map[string]struct{}) + for _, ul := range ulimits { + ulIdx[ul.Name] = struct{}{} + } + for name, ul := range daemon.configStore.Ulimits { + if _, exists := ulIdx[name]; !exists { + ulimits = append(ulimits, ul) + } + } + c.Ulimits = ulimits +} diff --git a/daemon/oci_solaris.go b/daemon/oci_solaris.go index 05eca21169..771baac683 100644 --- a/daemon/oci_solaris.go +++ b/daemon/oci_solaris.go @@ -1,6 +1,7 @@ package daemon import ( + containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/libcontainerd" "github.com/docker/docker/oci" @@ -10,3 +11,9 @@ func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, e s := oci.DefaultSpec() return (*libcontainerd.Spec)(&s), nil } + +// mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig +// It will do nothing on non-Linux platform +func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig) { + return +} diff --git a/daemon/oci_windows.go b/daemon/oci_windows.go index e030889a76..c93afcf3a4 100644 --- a/daemon/oci_windows.go +++ b/daemon/oci_windows.go @@ -7,6 +7,7 @@ import ( "path/filepath" "syscall" + containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/container" "github.com/docker/docker/image" "github.com/docker/docker/layer" @@ -192,3 +193,9 @@ func escapeArgs(args []string) []string { } return escapedArgs } + +// mergeUlimits merge the Ulimits from HostConfig with daemon defaults, and update HostConfig +// It will do nothing on non-Linux platform +func (daemon *Daemon) mergeUlimits(c *containertypes.HostConfig) { + return +} diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 0624d605c7..717388c4e7 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -4518,3 +4518,25 @@ exec "$@"`, c.Assert(err, check.NotNil) c.Assert(err.Error(), checker.Contains, "No command specified") } + +func (s *DockerDaemonSuite) TestRunWithUlimitAndDaemonDefault(c *check.C) { + c.Assert(s.d.StartWithBusybox("--debug", "--default-ulimit=nofile=65535"), checker.IsNil) + + name := "test-A" + _, err := s.d.Cmd("run", "--name", name, "-d", "busybox", "top") + c.Assert(err, checker.IsNil) + c.Assert(s.d.waitRun(name), check.IsNil) + + out, err := s.d.Cmd("inspect", "--format", "{{.HostConfig.Ulimits}}", name) + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Contains, "[nofile=65535:65535]") + + name = "test-B" + _, err = s.d.Cmd("run", "--name", name, "--ulimit=nofile=42", "-d", "busybox", "top") + c.Assert(err, checker.IsNil) + c.Assert(s.d.waitRun(name), check.IsNil) + + out, err = s.d.Cmd("inspect", "--format", "{{.HostConfig.Ulimits}}", name) + c.Assert(err, checker.IsNil) + c.Assert(out, checker.Contains, "[nofile=42:42]") +}