mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #39882 from thaJeztah/swarm_pids_limit
Add API support for PidsLimit on services
This commit is contained in:
commit
c8e31dc2f2
9 changed files with 134 additions and 4 deletions
|
@ -97,9 +97,10 @@ 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 supported before
|
||||
// API version 1.41
|
||||
// Capabilities and PidsLimit for docker swarm services weren't
|
||||
// supported before API version 1.41
|
||||
service.TaskTemplate.ContainerSpec.Capabilities = nil
|
||||
service.TaskTemplate.ContainerSpec.PidsLimit = 0
|
||||
}
|
||||
|
||||
// jobs were only introduced in API version 1.41. Nil out both Job
|
||||
|
|
|
@ -17,7 +17,8 @@ func TestAdjustForAPIVersion(t *testing.T) {
|
|||
spec := &swarm.ServiceSpec{
|
||||
TaskTemplate: swarm.TaskSpec{
|
||||
ContainerSpec: &swarm.ContainerSpec{
|
||||
Sysctls: expectedSysctls,
|
||||
Sysctls: expectedSysctls,
|
||||
PidsLimit: 300,
|
||||
Privileges: &swarm.Privileges{
|
||||
CredentialSpec: &swarm.CredentialSpec{
|
||||
Config: "someconfig",
|
||||
|
@ -49,11 +50,18 @@ func TestAdjustForAPIVersion(t *testing.T) {
|
|||
// first, does calling this with a later version correctly NOT strip
|
||||
// fields? do the later version first, so we can reuse this spec in the
|
||||
// next test.
|
||||
adjustForAPIVersion("1.40", spec)
|
||||
adjustForAPIVersion("1.41", spec)
|
||||
if !reflect.DeepEqual(spec.TaskTemplate.ContainerSpec.Sysctls, expectedSysctls) {
|
||||
t.Error("Sysctls was stripped from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.ContainerSpec.PidsLimit == 0 {
|
||||
t.Error("PidsLimit was stripped from spec")
|
||||
}
|
||||
if spec.TaskTemplate.ContainerSpec.PidsLimit != 300 {
|
||||
t.Error("PidsLimit did not preserve the value from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config != "someconfig" {
|
||||
t.Error("CredentialSpec.Config field was stripped from spec")
|
||||
}
|
||||
|
@ -72,6 +80,10 @@ func TestAdjustForAPIVersion(t *testing.T) {
|
|||
t.Error("Sysctls was not stripped from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.ContainerSpec.PidsLimit != 0 {
|
||||
t.Error("PidsLimit was not stripped from spec")
|
||||
}
|
||||
|
||||
if spec.TaskTemplate.ContainerSpec.Privileges.CredentialSpec.Config != "" {
|
||||
t.Error("CredentialSpec.Config field was not stripped from spec")
|
||||
}
|
||||
|
|
|
@ -2967,6 +2967,13 @@ definitions:
|
|||
description: "Run an init inside the container that forwards signals and reaps processes. This field is omitted if empty, and the default (as configured on the daemon) is used."
|
||||
type: "boolean"
|
||||
x-nullable: true
|
||||
PidsLimit:
|
||||
description: |
|
||||
Tune a container's PIDs limit. Set `0` for unlimited.
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
default: 0
|
||||
example: 100
|
||||
Sysctls:
|
||||
description: |
|
||||
Set kernel namedspaced parameters (sysctls) in the container.
|
||||
|
|
|
@ -72,6 +72,7 @@ type ContainerSpec struct {
|
|||
Secrets []*SecretReference `json:",omitempty"`
|
||||
Configs []*ConfigReference `json:",omitempty"`
|
||||
Isolation container.Isolation `json:",omitempty"`
|
||||
PidsLimit int64 `json:",omitempty"`
|
||||
Sysctls map[string]string `json:",omitempty"`
|
||||
Capabilities []string `json:",omitempty"`
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
|
|||
Configs: configReferencesFromGRPC(c.Configs),
|
||||
Isolation: IsolationFromGRPC(c.Isolation),
|
||||
Init: initFromGRPC(c.Init),
|
||||
PidsLimit: c.PidsLimit,
|
||||
Sysctls: c.Sysctls,
|
||||
Capabilities: c.Capabilities,
|
||||
}
|
||||
|
@ -263,6 +264,7 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
|
|||
Secrets: secretReferencesToGRPC(c.Secrets),
|
||||
Isolation: isolationToGRPC(c.Isolation),
|
||||
Init: initToGRPC(c.Init),
|
||||
PidsLimit: c.PidsLimit,
|
||||
Sysctls: c.Sysctls,
|
||||
Capabilities: c.Capabilities,
|
||||
}
|
||||
|
|
|
@ -431,6 +431,12 @@ func (c *containerConfig) volumeCreateRequest(mount *api.Mount) *volumetypes.Vol
|
|||
func (c *containerConfig) resources() enginecontainer.Resources {
|
||||
resources := enginecontainer.Resources{}
|
||||
|
||||
// set pids limit
|
||||
pidsLimit := c.spec().PidsLimit
|
||||
if pidsLimit > 0 {
|
||||
resources.PidsLimit = &pidsLimit
|
||||
}
|
||||
|
||||
// If no limits are specified let the engine use its defaults.
|
||||
//
|
||||
// TODO(aluzzardi): We might want to set some limits anyway otherwise
|
||||
|
|
|
@ -30,6 +30,12 @@ keywords: "API, Docker, rcli, REST, documentation"
|
|||
* `POST /services/{id}/update` now accepts `Capabilities` as part of the `ContainerSpec`.
|
||||
* `GET /tasks` now returns `Capabilities` as part of the `ContainerSpec`.
|
||||
* `GET /tasks/{id}` now returns `Capabilities` as part of the `ContainerSpec`.
|
||||
* `GET /services` now returns `PidsLimit` as part of the `ContainerSpec`.
|
||||
* `GET /services/{id}` now returns `PidsLimit` as part of the `ContainerSpec`.
|
||||
* `POST /services/create` now accepts `PidsLimit` as part of the `ContainerSpec`.
|
||||
* `POST /services/{id}/update` now accepts `PidsLimit` as part of the `ContainerSpec`.
|
||||
* `GET /tasks` now returns `PidsLimit` as part of the `ContainerSpec`.
|
||||
* `GET /tasks/{id}` now returns `PidsLimit` as part of the `ContainerSpec`.
|
||||
* `POST /containers/create` on Linux now accepts the `HostConfig.CgroupnsMode` property.
|
||||
Set the property to `host` to create the container in the daemon's cgroup namespace, or
|
||||
`private` to create the container in its own private cgroup namespace. The per-daemon
|
||||
|
|
|
@ -196,6 +196,14 @@ func ServiceWithCapabilities(Capabilities []string) ServiceSpecOpt {
|
|||
}
|
||||
}
|
||||
|
||||
// ServiceWithPidsLimit sets the PidsLimit option of the service's ContainerSpec.
|
||||
func ServiceWithPidsLimit(limit int64) ServiceSpecOpt {
|
||||
return func(spec *swarmtypes.ServiceSpec) {
|
||||
ensureContainerSpec(spec)
|
||||
spec.TaskTemplate.ContainerSpec.PidsLimit = limit
|
||||
}
|
||||
}
|
||||
|
||||
// GetRunningTasks gets the list of running tasks for a service
|
||||
func GetRunningTasks(t *testing.T, c client.ServiceAPIClient, serviceID string) []swarmtypes.Task {
|
||||
t.Helper()
|
||||
|
|
|
@ -5,7 +5,9 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
swarmtypes "github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/docker/docker/integration/internal/network"
|
||||
"github.com/docker/docker/integration/internal/swarm"
|
||||
|
@ -248,6 +250,91 @@ func TestServiceUpdateNetwork(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
// TestServiceUpdatePidsLimit tests creating and updating a service with PidsLimit
|
||||
func TestServiceUpdatePidsLimit(t *testing.T) {
|
||||
skip.If(
|
||||
t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.41"),
|
||||
"setting pidslimit for services is not supported before api v1.41",
|
||||
)
|
||||
skip.If(t, testEnv.DaemonInfo.OSType != "linux")
|
||||
tests := []struct {
|
||||
name string
|
||||
pidsLimit int64
|
||||
expected int64
|
||||
}{
|
||||
{
|
||||
name: "create service with PidsLimit 300",
|
||||
pidsLimit: 300,
|
||||
expected: 300,
|
||||
},
|
||||
{
|
||||
name: "unset PidsLimit to 0",
|
||||
pidsLimit: 0,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "update PidsLimit to 100",
|
||||
pidsLimit: 100,
|
||||
expected: 100,
|
||||
},
|
||||
}
|
||||
|
||||
defer setupTest(t)()
|
||||
d := swarm.NewSwarm(t, testEnv)
|
||||
defer d.Stop(t)
|
||||
cli := d.NewClientT(t)
|
||||
defer func() { _ = cli.Close() }()
|
||||
ctx := context.Background()
|
||||
var (
|
||||
serviceID string
|
||||
service swarmtypes.Service
|
||||
)
|
||||
for i, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if i == 0 {
|
||||
serviceID = swarm.CreateService(t, d, swarm.ServiceWithPidsLimit(tc.pidsLimit))
|
||||
} else {
|
||||
service = getService(t, cli, serviceID)
|
||||
service.Spec.TaskTemplate.ContainerSpec.PidsLimit = tc.pidsLimit
|
||||
_, err := cli.ServiceUpdate(ctx, serviceID, service.Version, service.Spec, types.ServiceUpdateOptions{})
|
||||
assert.NilError(t, err)
|
||||
poll.WaitOn(t, serviceIsUpdated(cli, serviceID), swarm.ServicePoll)
|
||||
}
|
||||
|
||||
poll.WaitOn(t, swarm.RunningTasksCount(cli, serviceID, 1), swarm.ServicePoll)
|
||||
service = getService(t, cli, serviceID)
|
||||
container := getServiceTaskContainer(ctx, t, cli, serviceID)
|
||||
assert.Equal(t, service.Spec.TaskTemplate.ContainerSpec.PidsLimit, tc.expected)
|
||||
if tc.expected == 0 {
|
||||
if container.HostConfig.Resources.PidsLimit != nil {
|
||||
t.Fatalf("Expected container.HostConfig.Resources.PidsLimit to be nil")
|
||||
}
|
||||
} else {
|
||||
assert.Assert(t, container.HostConfig.Resources.PidsLimit != nil)
|
||||
assert.Equal(t, *container.HostConfig.Resources.PidsLimit, tc.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
err := cli.ServiceRemove(ctx, serviceID)
|
||||
assert.NilError(t, err)
|
||||
}
|
||||
|
||||
func getServiceTaskContainer(ctx context.Context, t *testing.T, cli client.APIClient, serviceID string) types.ContainerJSON {
|
||||
t.Helper()
|
||||
filter := filters.NewArgs()
|
||||
filter.Add("service", serviceID)
|
||||
filter.Add("desired-state", "running")
|
||||
tasks, err := cli.TaskList(ctx, types.TaskListOptions{Filters: filter})
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, len(tasks) > 0)
|
||||
|
||||
ctr, err := cli.ContainerInspect(ctx, tasks[0].Status.ContainerStatus.ContainerID)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, ctr.State.Running, true)
|
||||
return ctr
|
||||
}
|
||||
|
||||
func getService(t *testing.T, cli client.ServiceAPIClient, serviceID string) swarmtypes.Service {
|
||||
t.Helper()
|
||||
service, _, err := cli.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{})
|
||||
|
|
Loading…
Add table
Reference in a new issue