diff --git a/api/server/router/swarm/helpers.go b/api/server/router/swarm/helpers.go index 60b8f40233..969724a1a2 100644 --- a/api/server/router/swarm/helpers.go +++ b/api/server/router/swarm/helpers.go @@ -97,10 +97,11 @@ func adjustForAPIVersion(cliVersion string, service *swarm.ServiceSpec) { } if versions.LessThan(cliVersion, "1.41") { if service.TaskTemplate.ContainerSpec != nil { - // Capabilities for docker swarm services weren't + // Capabilities and Ulimits for docker swarm services weren't // supported before API version 1.41 service.TaskTemplate.ContainerSpec.CapabilityAdd = nil service.TaskTemplate.ContainerSpec.CapabilityDrop = nil + service.TaskTemplate.ContainerSpec.Ulimits = nil } if service.TaskTemplate.Resources != nil && service.TaskTemplate.Resources.Limits != nil { // Limits.Pids not supported before API version 1.41 diff --git a/api/server/router/swarm/helpers_test.go b/api/server/router/swarm/helpers_test.go index cfbb6ff40d..87fa220125 100644 --- a/api/server/router/swarm/helpers_test.go +++ b/api/server/router/swarm/helpers_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/docker/docker/api/types/swarm" + "github.com/docker/go-units" ) func TestAdjustForAPIVersion(t *testing.T) { @@ -39,6 +40,13 @@ func TestAdjustForAPIVersion(t *testing.T) { ConfigName: "configRuntime", }, }, + Ulimits: []*units.Ulimit{ + { + Name: "nofile", + Soft: 100, + Hard: 200, + }, + }, }, Placement: &swarm.Placement{ MaxReplicas: 222, @@ -78,6 +86,10 @@ func TestAdjustForAPIVersion(t *testing.T) { t.Error("MaxReplicas was stripped from spec") } + if len(spec.TaskTemplate.ContainerSpec.Ulimits) == 0 { + t.Error("Ulimits were stripped from spec") + } + // next, does calling this with an earlier version correctly strip fields? adjustForAPIVersion("1.29", spec) if spec.TaskTemplate.ContainerSpec.Sysctls != nil { @@ -100,4 +112,8 @@ func TestAdjustForAPIVersion(t *testing.T) { t.Error("MaxReplicas was not stripped from spec") } + if len(spec.TaskTemplate.ContainerSpec.Ulimits) != 0 { + t.Error("Ulimits were not stripped from spec") + } + } diff --git a/api/swagger.yaml b/api/swagger.yaml index 6ebdd04946..0713c042e2 100644 --- a/api/swagger.yaml +++ b/api/swagger.yaml @@ -3306,6 +3306,22 @@ definitions: type: "string" example: - "CAP_NET_RAW" + Ulimits: + description: | + A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`" + type: "array" + items: + type: "object" + properties: + Name: + description: "Name of ulimit" + type: "string" + Soft: + description: "Soft limit" + type: "integer" + Hard: + description: "Hard limit" + type: "integer" NetworkAttachmentSpec: description: | Read-only spec type for non-swarm containers attached to swarm overlay diff --git a/api/types/swarm/container.go b/api/types/swarm/container.go index 32202ecc6c..af5e1c0bc2 100644 --- a/api/types/swarm/container.go +++ b/api/types/swarm/container.go @@ -5,6 +5,7 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/mount" + "github.com/docker/go-units" ) // DNSConfig specifies DNS related configurations in resolver configuration file (resolv.conf) @@ -75,4 +76,5 @@ type ContainerSpec struct { Sysctls map[string]string `json:",omitempty"` CapabilityAdd []string `json:",omitempty"` CapabilityDrop []string `json:",omitempty"` + Ulimits []*units.Ulimit `json:",omitempty"` } diff --git a/daemon/cluster/convert/container.go b/daemon/cluster/convert/container.go index 635c2ddc0f..a743442331 100644 --- a/daemon/cluster/convert/container.go +++ b/daemon/cluster/convert/container.go @@ -7,6 +7,7 @@ import ( "github.com/docker/docker/api/types/container" mounttypes "github.com/docker/docker/api/types/mount" types "github.com/docker/docker/api/types/swarm" + "github.com/docker/go-units" swarmapi "github.com/docker/swarmkit/api" gogotypes "github.com/gogo/protobuf/types" "github.com/pkg/errors" @@ -39,6 +40,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec { Sysctls: c.Sysctls, CapabilityAdd: c.CapabilityAdd, CapabilityDrop: c.CapabilityDrop, + Ulimits: ulimitsFromGRPC(c.Ulimits), } if c.DNSConfig != nil { @@ -267,6 +269,7 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) { Sysctls: c.Sysctls, CapabilityAdd: c.CapabilityAdd, CapabilityDrop: c.CapabilityDrop, + Ulimits: ulimitsToGRPC(c.Ulimits), } if c.DNSConfig != nil { @@ -471,3 +474,31 @@ func isolationToGRPC(i container.Isolation) swarmapi.ContainerSpec_Isolation { } return swarmapi.ContainerIsolationDefault } + +func ulimitsFromGRPC(u []*swarmapi.ContainerSpec_Ulimit) []*units.Ulimit { + ulimits := make([]*units.Ulimit, len(u)) + + for i, ulimit := range u { + ulimits[i] = &units.Ulimit{ + Name: ulimit.Name, + Soft: ulimit.Soft, + Hard: ulimit.Hard, + } + } + + return ulimits +} + +func ulimitsToGRPC(u []*units.Ulimit) []*swarmapi.ContainerSpec_Ulimit { + ulimits := make([]*swarmapi.ContainerSpec_Ulimit, len(u)) + + for i, ulimit := range u { + ulimits[i] = &swarmapi.ContainerSpec_Ulimit{ + Name: ulimit.Name, + Soft: ulimit.Soft, + Hard: ulimit.Hard, + } + } + + return ulimits +} diff --git a/daemon/cluster/executor/container/container.go b/daemon/cluster/executor/container/container.go index c69a4c71ad..6868bb11e2 100644 --- a/daemon/cluster/executor/container/container.go +++ b/daemon/cluster/executor/container/container.go @@ -21,6 +21,7 @@ import ( executorpkg "github.com/docker/docker/daemon/cluster/executor" clustertypes "github.com/docker/docker/daemon/cluster/provider" "github.com/docker/go-connections/nat" + "github.com/docker/go-units" netconst "github.com/docker/libnetwork/datastore" "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" @@ -438,6 +439,15 @@ func (c *containerConfig) resources() enginecontainer.Resources { resources.PidsLimit = &pidsLimit } + resources.Ulimits = make([]*units.Ulimit, len(c.spec().Ulimits)) + for i, ulimit := range c.spec().Ulimits { + resources.Ulimits[i] = &units.Ulimit{ + Name: ulimit.Name, + Soft: ulimit.Soft, + Hard: ulimit.Hard, + } + } + // If no limits are specified let the engine use its defaults. // // TODO(aluzzardi): We might want to set some limits anyway otherwise diff --git a/docs/api/version-history.md b/docs/api/version-history.md index d1bcadf0db..1c9f9519be 100644 --- a/docs/api/version-history.md +++ b/docs/api/version-history.md @@ -76,6 +76,10 @@ keywords: "API, Docker, rcli, REST, documentation" single set of stats instead of waiting for two collection cycles to have 2 CPU stats over a 1 second period. * The `KernelMemory` field in `HostConfig.Resources` is now deprecated. * The `KernelMemory` field in `Info` is now deprecated. +* `GET /services` now returns `Ulimits` as part of `ContainerSpec`. +* `GET /services/{id}` now returns `Ulimits` as part of `ContainerSpec`. +* `POST /services/create` now accepts `Ulimits` as part of `ContainerSpec`. +* `POST /services/{id}/update` now accepts `Ulimits` as part of `ContainerSpec`. ## v1.40 API changes