1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #39173 from olljanat/25885-capabilities-swarm

Add support for capabilities options in services
This commit is contained in:
Kirill Kolyshkin 2019-06-06 15:03:46 -07:00 committed by GitHub
commit 1d5748d975
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 156 additions and 43 deletions

View file

@ -95,4 +95,11 @@ func adjustForAPIVersion(cliVersion string, service *swarm.ServiceSpec) {
service.TaskTemplate.Placement.MaxReplicas = 0 service.TaskTemplate.Placement.MaxReplicas = 0
} }
} }
if versions.LessThan(cliVersion, "1.41") {
if service.TaskTemplate.ContainerSpec != nil {
// Capabilities for docker swarm services weren't supported before
// API version 1.41
service.TaskTemplate.ContainerSpec.Capabilities = nil
}
}
} }

View file

@ -2882,6 +2882,18 @@ definitions:
type: "object" type: "object"
additionalProperties: additionalProperties:
type: "string" type: "string"
# This option is not used by Windows containers
Capabilities:
type: "array"
description: |
A list of kernel capabilities to be available for container (this overrides the default set).
items:
type: "string"
example:
- "CAP_NET_RAW"
- "CAP_SYS_ADMIN"
- "CAP_SYS_CHROOT"
- "CAP_SYSLOG"
NetworkAttachmentSpec: NetworkAttachmentSpec:
description: | description: |
Read-only spec type for non-swarm containers attached to swarm overlay Read-only spec type for non-swarm containers attached to swarm overlay

View file

@ -73,4 +73,5 @@ type ContainerSpec struct {
Configs []*ConfigReference `json:",omitempty"` Configs []*ConfigReference `json:",omitempty"`
Isolation container.Isolation `json:",omitempty"` Isolation container.Isolation `json:",omitempty"`
Sysctls map[string]string `json:",omitempty"` Sysctls map[string]string `json:",omitempty"`
Capabilities []string `json:",omitempty"`
} }

View file

@ -37,6 +37,7 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
Isolation: IsolationFromGRPC(c.Isolation), Isolation: IsolationFromGRPC(c.Isolation),
Init: initFromGRPC(c.Init), Init: initFromGRPC(c.Init),
Sysctls: c.Sysctls, Sysctls: c.Sysctls,
Capabilities: c.Capabilities,
} }
if c.DNSConfig != nil { if c.DNSConfig != nil {
@ -262,6 +263,7 @@ func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
Isolation: isolationToGRPC(c.Isolation), Isolation: isolationToGRPC(c.Isolation),
Init: initToGRPC(c.Init), Init: initToGRPC(c.Init),
Sysctls: c.Sysctls, Sysctls: c.Sysctls,
Capabilities: c.Capabilities,
} }
if c.DNSConfig != nil { if c.DNSConfig != nil {

View file

@ -362,6 +362,7 @@ func (c *containerConfig) hostConfig() *enginecontainer.HostConfig {
Isolation: c.isolation(), Isolation: c.isolation(),
Init: c.init(), Init: c.init(),
Sysctls: c.spec().Sysctls, Sysctls: c.spec().Sysctls,
Capabilities: c.spec().Capabilities,
} }
if c.spec().DNSConfig != nil { if c.spec().DNSConfig != nil {

View file

@ -17,6 +17,12 @@ keywords: "API, Docker, rcli, REST, documentation"
[Docker Engine API v1.41](https://docs.docker.com/engine/api/v1.41/) documentation [Docker Engine API v1.41](https://docs.docker.com/engine/api/v1.41/) documentation
* `GET /services` now returns `Capabilities` as part of the `ContainerSpec`.
* `GET /services/{id}` now returns `Capabilities` as part of the `ContainerSpec`.
* `POST /services/create` now accepts `Capabilities` as part of the `ContainerSpec`.
* `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`.
* `POST /containers/create` on Linux now accepts the `HostConfig.CgroupnsMode` property. * `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 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 `private` to create the container in its own private cgroup namespace. The per-daemon

View file

@ -180,6 +180,14 @@ func ServiceWithSysctls(sysctls map[string]string) ServiceSpecOpt {
} }
} }
// ServiceWithCapabilities sets the Capabilities option of the service's ContainerSpec.
func ServiceWithCapabilities(Capabilities []string) ServiceSpecOpt {
return func(spec *swarmtypes.ServiceSpec) {
ensureContainerSpec(spec)
spec.TaskTemplate.ContainerSpec.Capabilities = Capabilities
}
}
// GetRunningTasks gets the list of running tasks for a service // GetRunningTasks gets the list of running tasks for a service
func GetRunningTasks(t *testing.T, c client.ServiceAPIClient, serviceID string) []swarmtypes.Task { func GetRunningTasks(t *testing.T, c client.ServiceAPIClient, serviceID string) []swarmtypes.Task {
t.Helper() t.Helper()

View file

@ -440,3 +440,79 @@ func TestCreateServiceSysctls(t *testing.T) {
) )
} }
} }
// TestServiceCreateCapabilities tests that a service created with capabilities options in
// the ContainerSpec correctly applies those options.
//
// To test this, we're going to create a service with the capabilities option
//
// []string{"CAP_NET_RAW", "CAP_SYS_CHROOT"}
//
// We'll get the service's tasks to get the container ID, and then we'll
// inspect the container. If the output of the container inspect contains the
// capabilities option with the correct value, we can assume that the capabilities has been
// plumbed correctly.
func TestCreateServiceCapabilities(t *testing.T) {
skip.If(
t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.41"),
"setting service capabilities is unsupported before api v1.41",
)
defer setupTest(t)()
d := swarm.NewSwarm(t, testEnv)
defer d.Stop(t)
client := d.NewClientT(t)
defer client.Close()
ctx := context.Background()
// store the map we're going to be using everywhere.
expectedCapabilities := []string{"CAP_NET_RAW", "CAP_SYS_CHROOT"}
// Create the service with the capabilities options
var instances uint64 = 1
serviceID := swarm.CreateService(t, d,
swarm.ServiceWithCapabilities(expectedCapabilities),
)
// wait for the service to converge to 1 running task as expected
poll.WaitOn(t, swarm.RunningTasksCount(client, serviceID, instances))
// we're going to check 3 things:
//
// 1. Does the container, when inspected, have the capabilities option set?
// 2. Does the task have the capabilities in the spec?
// 3. Does the service have the capabilities in the spec?
//
// if all 3 of these things are true, we know that the capabilities has been
// plumbed correctly through the engine.
//
// We don't actually have to get inside the container and check its
// logs or anything. If we see the capabilities set on the container inspect,
// we know that the capabilities is plumbed correctly. everything below that
// level has been tested elsewhere.
// get all of the tasks of the service, so we can get the container
filter := filters.NewArgs()
filter.Add("service", serviceID)
tasks, err := client.TaskList(ctx, types.TaskListOptions{
Filters: filter,
})
assert.NilError(t, err)
assert.Check(t, is.Equal(len(tasks), 1))
// verify that the container has the capabilities option set
ctnr, err := client.ContainerInspect(ctx, tasks[0].Status.ContainerStatus.ContainerID)
assert.NilError(t, err)
assert.DeepEqual(t, ctnr.HostConfig.Capabilities, expectedCapabilities)
// verify that the task has the capabilities option set in the task object
assert.DeepEqual(t, tasks[0].Spec.ContainerSpec.Capabilities, expectedCapabilities)
// verify that the service also has the capabilities set in the spec.
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
assert.NilError(t, err)
assert.DeepEqual(t,
service.Spec.TaskTemplate.ContainerSpec.Capabilities, expectedCapabilities,
)
}