Skip to content

Commit

Permalink
Add Ulimits to ContainerSpec
Browse files Browse the repository at this point in the history
Unlike Docker and docker-compose, Swarm never supported ulimits.

This change introduces a new Ulimits field on the ContainerSpec type. It
can be used by API clients to set desired ulimits.

This is related to moby/moby#40639.

Signed-off-by: Albin Kerouanton <[email protected]>
  • Loading branch information
akerouanton committed Jul 26, 2020
1 parent 035d564 commit ee3aa3c
Show file tree
Hide file tree
Showing 7 changed files with 565 additions and 141 deletions.
10 changes: 10 additions & 0 deletions agent/exec/dockerapi/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/api/types/volume"
"github.com/docker/go-connections/nat"
"github.com/docker/go-units"
"github.com/docker/swarmkit/agent/exec"
"github.com/docker/swarmkit/api"
"github.com/docker/swarmkit/api/genericresource"
Expand Down Expand Up @@ -447,6 +448,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
Expand Down
33 changes: 33 additions & 0 deletions agent/exec/dockerapi/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
enginecontainer "github.com/docker/docker/api/types/container"
enginemount "github.com/docker/docker/api/types/mount"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/go-units"
"github.com/docker/swarmkit/api"
gogotypes "github.com/gogo/protobuf/types"
)
Expand Down Expand Up @@ -297,3 +298,35 @@ func TestCapabilityDrop(t *testing.T) {
t.Fatalf("expected %s, got %s", expected, actual)
}
}

func TestUlimits(t *testing.T) {
c := containerConfig{
task: &api.Task{
Spec: api.TaskSpec{
Runtime: &api.TaskSpec_Container{
Container: &api.ContainerSpec{
Ulimits: []*api.ContainerSpec_Ulimit{
{
Name: "nofile",
Soft: 1024,
Hard: 2048,
},
},
},
},
},
},
}

expected := []*units.Ulimit{
{
Name: "nofile",
Soft: 1024,
Hard: 2048,
},
}
actual := c.resources().Ulimits
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("expected %v, got %v", expected, actual)
}
}
32 changes: 32 additions & 0 deletions api/api.pb.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5081,6 +5081,14 @@ file {
type: TYPE_STRING
json_name: "capabilityDrop"
}
field {
name: "ulimits"
number: 29
label: LABEL_REPEATED
type: TYPE_MESSAGE
type_name: ".docker.swarmkit.v1.ContainerSpec.Ulimit"
json_name: "ulimits"
}
nested_type {
name: "LabelsEntry"
field {
Expand Down Expand Up @@ -5155,6 +5163,30 @@ file {
map_entry: true
}
}
nested_type {
name: "Ulimit"
field {
name: "name"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "name"
}
field {
name: "soft"
number: 2
label: LABEL_OPTIONAL
type: TYPE_INT64
json_name: "soft"
}
field {
name: "hard"
number: 3
label: LABEL_OPTIONAL
type: TYPE_INT64
json_name: "hard"
}
}
enum_type {
name: "Isolation"
value {
Expand Down
593 changes: 452 additions & 141 deletions api/specs.pb.go

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions api/specs.proto
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,16 @@ message ContainerSpec {
repeated string capability_add = 27;
// CapabilityAdd sets the list of capabilities to drop from the default capability list
repeated string capability_drop = 28;

message Ulimit {
string name = 1;
int64 soft = 2;
int64 hard = 3;
}

// Ulimits defines the list of ulimits to set in the container. This option
// is equivalent to passing --ulimit to docker run.
repeated Ulimit ulimits = 29;
}

// EndpointSpec defines the properties that can be configured to
Expand Down
1 change: 1 addition & 0 deletions cmd/swarmctl/service/flagparser/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func AddServiceFlags(flags *pflag.FlagSet) {
flags.String("cpu-reservation", "", "number of CPU cores reserved (e.g. 0.5)")
flags.String("cpu-limit", "", "CPU cores limit (e.g. 0.5)")
flags.String("generic-resources", "", "user defined resources request (e.g. gpu=3,fpga=1)")
flags.StringSlice("ulimit", []string{}, "ulimit options")

flags.Uint64("update-parallelism", 0, "task update parallelism (0 = all at once)")
flags.String("update-delay", "0s", "delay between task updates (0s = none)")
Expand Down
27 changes: 27 additions & 0 deletions cmd/swarmctl/service/flagparser/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,32 @@ func parseResource(flags *pflag.FlagSet, spec *api.ServiceSpec) error {
}
}

if flags.Changed("ulimit") {
container := spec.Task.GetContainer()
if container == nil {
return nil
}

ulimits, err := flags.GetStringSlice("ulimit")
if err != nil {
return err
}

container.Ulimits = make([]*api.ContainerSpec_Ulimit, len(ulimits))

for i, ulimit := range ulimits {
parsed, err := units.ParseUlimit(ulimit)
if err != nil {
return err
}

container.Ulimits[i] = &api.ContainerSpec_Ulimit{
Name: parsed.Name,
Soft: parsed.Soft,
Hard: parsed.Hard,
}
}
}

return nil
}

0 comments on commit ee3aa3c

Please sign in to comment.