Browse code

Add ulimits support to services

Add Ulimits field to the ContainerSpec API type and wire it to Swarmkit.

This is related to #40639.

Signed-off-by: Albin Kerouanton <albin@akerouanton.name>

Albin Kerouanton authored on 2020/07/26 20:42:33
Showing 7 changed files
... ...
@@ -97,10 +97,11 @@ func adjustForAPIVersion(cliVersion string, service *swarm.ServiceSpec) {
97 97
 	}
98 98
 	if versions.LessThan(cliVersion, "1.41") {
99 99
 		if service.TaskTemplate.ContainerSpec != nil {
100
-			// Capabilities for docker swarm services weren't
100
+			// Capabilities and Ulimits for docker swarm services weren't
101 101
 			// supported before API version 1.41
102 102
 			service.TaskTemplate.ContainerSpec.CapabilityAdd = nil
103 103
 			service.TaskTemplate.ContainerSpec.CapabilityDrop = nil
104
+			service.TaskTemplate.ContainerSpec.Ulimits = nil
104 105
 		}
105 106
 		if service.TaskTemplate.Resources != nil && service.TaskTemplate.Resources.Limits != nil {
106 107
 			// Limits.Pids  not supported before API version 1.41
... ...
@@ -5,6 +5,7 @@ import (
5 5
 	"testing"
6 6
 
7 7
 	"github.com/docker/docker/api/types/swarm"
8
+	"github.com/docker/go-units"
8 9
 )
9 10
 
10 11
 func TestAdjustForAPIVersion(t *testing.T) {
... ...
@@ -39,6 +40,13 @@ func TestAdjustForAPIVersion(t *testing.T) {
39 39
 						ConfigName: "configRuntime",
40 40
 					},
41 41
 				},
42
+				Ulimits: []*units.Ulimit{
43
+					{
44
+						Name: "nofile",
45
+						Soft: 100,
46
+						Hard: 200,
47
+					},
48
+				},
42 49
 			},
43 50
 			Placement: &swarm.Placement{
44 51
 				MaxReplicas: 222,
... ...
@@ -78,6 +86,10 @@ func TestAdjustForAPIVersion(t *testing.T) {
78 78
 		t.Error("MaxReplicas was stripped from spec")
79 79
 	}
80 80
 
81
+	if len(spec.TaskTemplate.ContainerSpec.Ulimits) == 0 {
82
+		t.Error("Ulimits were stripped from spec")
83
+	}
84
+
81 85
 	// next, does calling this with an earlier version correctly strip fields?
82 86
 	adjustForAPIVersion("1.29", spec)
83 87
 	if spec.TaskTemplate.ContainerSpec.Sysctls != nil {
... ...
@@ -100,4 +112,8 @@ func TestAdjustForAPIVersion(t *testing.T) {
100 100
 		t.Error("MaxReplicas was not stripped from spec")
101 101
 	}
102 102
 
103
+	if len(spec.TaskTemplate.ContainerSpec.Ulimits) != 0 {
104
+		t.Error("Ulimits were not stripped from spec")
105
+	}
106
+
103 107
 }
... ...
@@ -3306,6 +3306,22 @@ definitions:
3306 3306
               type: "string"
3307 3307
             example:
3308 3308
               - "CAP_NET_RAW"
3309
+          Ulimits:
3310
+            description: |
3311
+              A list of resource limits to set in the container. For example: `{"Name": "nofile", "Soft": 1024, "Hard": 2048}`"
3312
+            type: "array"
3313
+            items:
3314
+              type: "object"
3315
+              properties:
3316
+                Name:
3317
+                  description: "Name of ulimit"
3318
+                  type: "string"
3319
+                Soft:
3320
+                  description: "Soft limit"
3321
+                  type: "integer"
3322
+                Hard:
3323
+                  description: "Hard limit"
3324
+                  type: "integer"
3309 3325
       NetworkAttachmentSpec:
3310 3326
         description: |
3311 3327
           Read-only spec type for non-swarm containers attached to swarm overlay
... ...
@@ -5,6 +5,7 @@ import (
5 5
 
6 6
 	"github.com/docker/docker/api/types/container"
7 7
 	"github.com/docker/docker/api/types/mount"
8
+	"github.com/docker/go-units"
8 9
 )
9 10
 
10 11
 // DNSConfig specifies DNS related configurations in resolver configuration file (resolv.conf)
... ...
@@ -75,4 +76,5 @@ type ContainerSpec struct {
75 75
 	Sysctls        map[string]string   `json:",omitempty"`
76 76
 	CapabilityAdd  []string            `json:",omitempty"`
77 77
 	CapabilityDrop []string            `json:",omitempty"`
78
+	Ulimits        []*units.Ulimit     `json:",omitempty"`
78 79
 }
... ...
@@ -7,6 +7,7 @@ import (
7 7
 	"github.com/docker/docker/api/types/container"
8 8
 	mounttypes "github.com/docker/docker/api/types/mount"
9 9
 	types "github.com/docker/docker/api/types/swarm"
10
+	"github.com/docker/go-units"
10 11
 	swarmapi "github.com/docker/swarmkit/api"
11 12
 	gogotypes "github.com/gogo/protobuf/types"
12 13
 	"github.com/pkg/errors"
... ...
@@ -39,6 +40,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
39 39
 		Sysctls:        c.Sysctls,
40 40
 		CapabilityAdd:  c.CapabilityAdd,
41 41
 		CapabilityDrop: c.CapabilityDrop,
42
+		Ulimits:        ulimitsFromGRPC(c.Ulimits),
42 43
 	}
43 44
 
44 45
 	if c.DNSConfig != nil {
... ...
@@ -267,6 +269,7 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
267 267
 		Sysctls:        c.Sysctls,
268 268
 		CapabilityAdd:  c.CapabilityAdd,
269 269
 		CapabilityDrop: c.CapabilityDrop,
270
+		Ulimits:        ulimitsToGRPC(c.Ulimits),
270 271
 	}
271 272
 
272 273
 	if c.DNSConfig != nil {
... ...
@@ -471,3 +474,31 @@ func isolationToGRPC(i container.Isolation) swarmapi.ContainerSpec_Isolation {
471 471
 	}
472 472
 	return swarmapi.ContainerIsolationDefault
473 473
 }
474
+
475
+func ulimitsFromGRPC(u []*swarmapi.ContainerSpec_Ulimit) []*units.Ulimit {
476
+	ulimits := make([]*units.Ulimit, len(u))
477
+
478
+	for i, ulimit := range u {
479
+		ulimits[i] = &units.Ulimit{
480
+			Name: ulimit.Name,
481
+			Soft: ulimit.Soft,
482
+			Hard: ulimit.Hard,
483
+		}
484
+	}
485
+
486
+	return ulimits
487
+}
488
+
489
+func ulimitsToGRPC(u []*units.Ulimit) []*swarmapi.ContainerSpec_Ulimit {
490
+	ulimits := make([]*swarmapi.ContainerSpec_Ulimit, len(u))
491
+
492
+	for i, ulimit := range u {
493
+		ulimits[i] = &swarmapi.ContainerSpec_Ulimit{
494
+			Name: ulimit.Name,
495
+			Soft: ulimit.Soft,
496
+			Hard: ulimit.Hard,
497
+		}
498
+	}
499
+
500
+	return ulimits
501
+}
... ...
@@ -21,6 +21,7 @@ import (
21 21
 	executorpkg "github.com/docker/docker/daemon/cluster/executor"
22 22
 	clustertypes "github.com/docker/docker/daemon/cluster/provider"
23 23
 	"github.com/docker/go-connections/nat"
24
+	"github.com/docker/go-units"
24 25
 	netconst "github.com/docker/libnetwork/datastore"
25 26
 	"github.com/docker/swarmkit/agent/exec"
26 27
 	"github.com/docker/swarmkit/api"
... ...
@@ -438,6 +439,15 @@ func (c *containerConfig) resources() enginecontainer.Resources {
438 438
 		resources.PidsLimit = &pidsLimit
439 439
 	}
440 440
 
441
+	resources.Ulimits = make([]*units.Ulimit, len(c.spec().Ulimits))
442
+	for i, ulimit := range c.spec().Ulimits {
443
+		resources.Ulimits[i] = &units.Ulimit{
444
+			Name: ulimit.Name,
445
+			Soft: ulimit.Soft,
446
+			Hard: ulimit.Hard,
447
+		}
448
+	}
449
+
441 450
 	// If no limits are specified let the engine use its defaults.
442 451
 	//
443 452
 	// TODO(aluzzardi): We might want to set some limits anyway otherwise
... ...
@@ -76,6 +76,10 @@ keywords: "API, Docker, rcli, REST, documentation"
76 76
   single set of stats instead of waiting for two collection cycles to have 2 CPU stats over a 1 second period.
77 77
 * The `KernelMemory` field in `HostConfig.Resources` is now deprecated.
78 78
 * The `KernelMemory` field in `Info` is now deprecated.
79
+* `GET /services` now returns `Ulimits` as part of `ContainerSpec`.
80
+* `GET /services/{id}` now returns `Ulimits` as part of `ContainerSpec`.
81
+* `POST /services/create` now accepts `Ulimits` as part of `ContainerSpec`.
82
+* `POST /services/{id}/update` now accepts `Ulimits` as part of `ContainerSpec`.
79 83
 
80 84
 ## v1.40 API changes
81 85