After dicussing with maintainers, it was decided putting the burden of
providing the full cap list on the client is not a good design.
Instead we decided to follow along with the container API and use cap
add/drop.
This brings in the changes already merged into swarmkit.
Signed-off-by: Brian Goff <cpuguy83@gmail.com>
| ... | ... |
@@ -489,9 +489,6 @@ func (s *containerRouter) postContainersCreate(ctx context.Context, w http.Respo |
| 489 | 489 |
// Ignore KernelMemoryTCP because it was added in API 1.40. |
| 490 | 490 |
hostConfig.KernelMemoryTCP = 0 |
| 491 | 491 |
|
| 492 |
- // Ignore Capabilities because it was added in API 1.40. |
|
| 493 |
- hostConfig.Capabilities = nil |
|
| 494 |
- |
|
| 495 | 492 |
// Older clients (API < 1.40) expects the default to be shareable, make them happy |
| 496 | 493 |
if hostConfig.IpcMode.IsEmpty() {
|
| 497 | 494 |
hostConfig.IpcMode = container.IpcMode("shareable")
|
| ... | ... |
@@ -99,7 +99,8 @@ func adjustForAPIVersion(cliVersion string, service *swarm.ServiceSpec) {
|
| 99 | 99 |
if service.TaskTemplate.ContainerSpec != nil {
|
| 100 | 100 |
// Capabilities for docker swarm services weren't |
| 101 | 101 |
// supported before API version 1.41 |
| 102 |
- service.TaskTemplate.ContainerSpec.Capabilities = nil |
|
| 102 |
+ service.TaskTemplate.ContainerSpec.CapabilityAdd = nil |
|
| 103 |
+ service.TaskTemplate.ContainerSpec.CapabilityDrop = nil |
|
| 103 | 104 |
} |
| 104 | 105 |
if service.TaskTemplate.Resources != nil && service.TaskTemplate.Resources.Limits != nil {
|
| 105 | 106 |
// Limits.Pids not supported before API version 1.41 |
| ... | ... |
@@ -906,15 +906,6 @@ definitions: |
| 906 | 906 |
$ref: "#/definitions/Mount" |
| 907 | 907 |
|
| 908 | 908 |
# Applicable to UNIX platforms |
| 909 |
- Capabilities: |
|
| 910 |
- type: "array" |
|
| 911 |
- description: | |
|
| 912 |
- A list of kernel capabilities to be available for container (this |
|
| 913 |
- overrides the default set). |
|
| 914 |
- |
|
| 915 |
- Conflicts with options 'CapAdd' and 'CapDrop'" |
|
| 916 |
- items: |
|
| 917 |
- type: "string" |
|
| 918 | 909 |
CapAdd: |
| 919 | 910 |
type: "array" |
| 920 | 911 |
description: | |
| ... | ... |
@@ -3276,11 +3267,11 @@ definitions: |
| 3276 | 3276 |
additionalProperties: |
| 3277 | 3277 |
type: "string" |
| 3278 | 3278 |
# This option is not used by Windows containers |
| 3279 |
- Capabilities: |
|
| 3279 |
+ CapabilityAdd: |
|
| 3280 | 3280 |
type: "array" |
| 3281 | 3281 |
description: | |
| 3282 |
- A list of kernel capabilities to be available for container (this |
|
| 3283 |
- overrides the default set). |
|
| 3282 |
+ A list of kernel capabilities to add to the default set |
|
| 3283 |
+ for the container. |
|
| 3284 | 3284 |
items: |
| 3285 | 3285 |
type: "string" |
| 3286 | 3286 |
example: |
| ... | ... |
@@ -3288,6 +3279,15 @@ definitions: |
| 3288 | 3288 |
- "CAP_SYS_ADMIN" |
| 3289 | 3289 |
- "CAP_SYS_CHROOT" |
| 3290 | 3290 |
- "CAP_SYSLOG" |
| 3291 |
+ CapabilityDrop: |
|
| 3292 |
+ type: "array" |
|
| 3293 |
+ description: | |
|
| 3294 |
+ A list of kernel capabilities to drop from the default set |
|
| 3295 |
+ for the container. |
|
| 3296 |
+ items: |
|
| 3297 |
+ type: "string" |
|
| 3298 |
+ example: |
|
| 3299 |
+ - "CAP_NET_RAW" |
|
| 3291 | 3300 |
NetworkAttachmentSpec: |
| 3292 | 3301 |
description: | |
| 3293 | 3302 |
Read-only spec type for non-swarm containers attached to swarm overlay |
| ... | ... |
@@ -403,7 +403,6 @@ type HostConfig struct {
|
| 403 | 403 |
// Applicable to UNIX platforms |
| 404 | 404 |
CapAdd strslice.StrSlice // List of kernel capabilities to add to the container |
| 405 | 405 |
CapDrop strslice.StrSlice // List of kernel capabilities to remove from the container |
| 406 |
- Capabilities []string `json:"Capabilities"` // List of kernel capabilities to be available for container (this overrides the default set) |
|
| 407 | 406 |
CgroupnsMode CgroupnsMode // Cgroup namespace mode to use for the container |
| 408 | 407 |
DNS []string `json:"Dns"` // List of DNS server to lookup |
| 409 | 408 |
DNSOptions []string `json:"DnsOptions"` // List of DNSOption to look for |
| ... | ... |
@@ -67,11 +67,12 @@ type ContainerSpec struct {
|
| 67 | 67 |
// The format of extra hosts on swarmkit is specified in: |
| 68 | 68 |
// http://man7.org/linux/man-pages/man5/hosts.5.html |
| 69 | 69 |
// IP_address canonical_hostname [aliases...] |
| 70 |
- Hosts []string `json:",omitempty"` |
|
| 71 |
- DNSConfig *DNSConfig `json:",omitempty"` |
|
| 72 |
- Secrets []*SecretReference `json:",omitempty"` |
|
| 73 |
- Configs []*ConfigReference `json:",omitempty"` |
|
| 74 |
- Isolation container.Isolation `json:",omitempty"` |
|
| 75 |
- Sysctls map[string]string `json:",omitempty"` |
|
| 76 |
- Capabilities []string `json:",omitempty"` |
|
| 70 |
+ Hosts []string `json:",omitempty"` |
|
| 71 |
+ DNSConfig *DNSConfig `json:",omitempty"` |
|
| 72 |
+ Secrets []*SecretReference `json:",omitempty"` |
|
| 73 |
+ Configs []*ConfigReference `json:",omitempty"` |
|
| 74 |
+ Isolation container.Isolation `json:",omitempty"` |
|
| 75 |
+ Sysctls map[string]string `json:",omitempty"` |
|
| 76 |
+ CapabilityAdd []string `json:",omitempty"` |
|
| 77 |
+ CapabilityDrop []string `json:",omitempty"` |
|
| 77 | 78 |
} |
| ... | ... |
@@ -18,26 +18,27 @@ func containerSpecFromGRPC(c *swarmapi.ContainerSpec) *types.ContainerSpec {
|
| 18 | 18 |
return nil |
| 19 | 19 |
} |
| 20 | 20 |
containerSpec := &types.ContainerSpec{
|
| 21 |
- Image: c.Image, |
|
| 22 |
- Labels: c.Labels, |
|
| 23 |
- Command: c.Command, |
|
| 24 |
- Args: c.Args, |
|
| 25 |
- Hostname: c.Hostname, |
|
| 26 |
- Env: c.Env, |
|
| 27 |
- Dir: c.Dir, |
|
| 28 |
- User: c.User, |
|
| 29 |
- Groups: c.Groups, |
|
| 30 |
- StopSignal: c.StopSignal, |
|
| 31 |
- TTY: c.TTY, |
|
| 32 |
- OpenStdin: c.OpenStdin, |
|
| 33 |
- ReadOnly: c.ReadOnly, |
|
| 34 |
- Hosts: c.Hosts, |
|
| 35 |
- Secrets: secretReferencesFromGRPC(c.Secrets), |
|
| 36 |
- Configs: configReferencesFromGRPC(c.Configs), |
|
| 37 |
- Isolation: IsolationFromGRPC(c.Isolation), |
|
| 38 |
- Init: initFromGRPC(c.Init), |
|
| 39 |
- Sysctls: c.Sysctls, |
|
| 40 |
- Capabilities: c.Capabilities, |
|
| 21 |
+ Image: c.Image, |
|
| 22 |
+ Labels: c.Labels, |
|
| 23 |
+ Command: c.Command, |
|
| 24 |
+ Args: c.Args, |
|
| 25 |
+ Hostname: c.Hostname, |
|
| 26 |
+ Env: c.Env, |
|
| 27 |
+ Dir: c.Dir, |
|
| 28 |
+ User: c.User, |
|
| 29 |
+ Groups: c.Groups, |
|
| 30 |
+ StopSignal: c.StopSignal, |
|
| 31 |
+ TTY: c.TTY, |
|
| 32 |
+ OpenStdin: c.OpenStdin, |
|
| 33 |
+ ReadOnly: c.ReadOnly, |
|
| 34 |
+ Hosts: c.Hosts, |
|
| 35 |
+ Secrets: secretReferencesFromGRPC(c.Secrets), |
|
| 36 |
+ Configs: configReferencesFromGRPC(c.Configs), |
|
| 37 |
+ Isolation: IsolationFromGRPC(c.Isolation), |
|
| 38 |
+ Init: initFromGRPC(c.Init), |
|
| 39 |
+ Sysctls: c.Sysctls, |
|
| 40 |
+ CapabilityAdd: c.CapabilityAdd, |
|
| 41 |
+ CapabilityDrop: c.CapabilityDrop, |
|
| 41 | 42 |
} |
| 42 | 43 |
|
| 43 | 44 |
if c.DNSConfig != nil {
|
| ... | ... |
@@ -246,25 +247,26 @@ func configReferencesFromGRPC(sr []*swarmapi.ConfigReference) []*types.ConfigRef |
| 246 | 246 |
|
| 247 | 247 |
func containerToGRPC(c *types.ContainerSpec) (*swarmapi.ContainerSpec, error) {
|
| 248 | 248 |
containerSpec := &swarmapi.ContainerSpec{
|
| 249 |
- Image: c.Image, |
|
| 250 |
- Labels: c.Labels, |
|
| 251 |
- Command: c.Command, |
|
| 252 |
- Args: c.Args, |
|
| 253 |
- Hostname: c.Hostname, |
|
| 254 |
- Env: c.Env, |
|
| 255 |
- Dir: c.Dir, |
|
| 256 |
- User: c.User, |
|
| 257 |
- Groups: c.Groups, |
|
| 258 |
- StopSignal: c.StopSignal, |
|
| 259 |
- TTY: c.TTY, |
|
| 260 |
- OpenStdin: c.OpenStdin, |
|
| 261 |
- ReadOnly: c.ReadOnly, |
|
| 262 |
- Hosts: c.Hosts, |
|
| 263 |
- Secrets: secretReferencesToGRPC(c.Secrets), |
|
| 264 |
- Isolation: isolationToGRPC(c.Isolation), |
|
| 265 |
- Init: initToGRPC(c.Init), |
|
| 266 |
- Sysctls: c.Sysctls, |
|
| 267 |
- Capabilities: c.Capabilities, |
|
| 249 |
+ Image: c.Image, |
|
| 250 |
+ Labels: c.Labels, |
|
| 251 |
+ Command: c.Command, |
|
| 252 |
+ Args: c.Args, |
|
| 253 |
+ Hostname: c.Hostname, |
|
| 254 |
+ Env: c.Env, |
|
| 255 |
+ Dir: c.Dir, |
|
| 256 |
+ User: c.User, |
|
| 257 |
+ Groups: c.Groups, |
|
| 258 |
+ StopSignal: c.StopSignal, |
|
| 259 |
+ TTY: c.TTY, |
|
| 260 |
+ OpenStdin: c.OpenStdin, |
|
| 261 |
+ ReadOnly: c.ReadOnly, |
|
| 262 |
+ Hosts: c.Hosts, |
|
| 263 |
+ Secrets: secretReferencesToGRPC(c.Secrets), |
|
| 264 |
+ Isolation: isolationToGRPC(c.Isolation), |
|
| 265 |
+ Init: initToGRPC(c.Init), |
|
| 266 |
+ Sysctls: c.Sysctls, |
|
| 267 |
+ CapabilityAdd: c.CapabilityAdd, |
|
| 268 |
+ CapabilityDrop: c.CapabilityDrop, |
|
| 268 | 269 |
} |
| 269 | 270 |
|
| 270 | 271 |
if c.DNSConfig != nil {
|
| ... | ... |
@@ -360,7 +360,8 @@ func (c *containerConfig) hostConfig() *enginecontainer.HostConfig {
|
| 360 | 360 |
Isolation: c.isolation(), |
| 361 | 361 |
Init: c.init(), |
| 362 | 362 |
Sysctls: c.spec().Sysctls, |
| 363 |
- Capabilities: c.spec().Capabilities, |
|
| 363 |
+ CapAdd: c.spec().CapabilityAdd, |
|
| 364 |
+ CapDrop: c.spec().CapabilityDrop, |
|
| 364 | 365 |
} |
| 365 | 366 |
|
| 366 | 367 |
if c.spec().DNSConfig != nil {
|
| ... | ... |
@@ -305,21 +305,12 @@ func validateHostConfig(hostConfig *containertypes.HostConfig, platform string) |
| 305 | 305 |
} |
| 306 | 306 |
|
| 307 | 307 |
func validateCapabilities(hostConfig *containertypes.HostConfig) error {
|
| 308 |
- if len(hostConfig.CapAdd) > 0 && hostConfig.Capabilities != nil {
|
|
| 309 |
- return errdefs.InvalidParameter(errors.Errorf("conflicting options: Capabilities and CapAdd"))
|
|
| 310 |
- } |
|
| 311 |
- if len(hostConfig.CapDrop) > 0 && hostConfig.Capabilities != nil {
|
|
| 312 |
- return errdefs.InvalidParameter(errors.Errorf("conflicting options: Capabilities and CapDrop"))
|
|
| 313 |
- } |
|
| 314 | 308 |
if _, err := caps.NormalizeLegacyCapabilities(hostConfig.CapAdd); err != nil {
|
| 315 | 309 |
return errors.Wrap(err, "invalid CapAdd") |
| 316 | 310 |
} |
| 317 | 311 |
if _, err := caps.NormalizeLegacyCapabilities(hostConfig.CapDrop); err != nil {
|
| 318 | 312 |
return errors.Wrap(err, "invalid CapDrop") |
| 319 | 313 |
} |
| 320 |
- if err := caps.ValidateCapabilities(hostConfig.Capabilities); err != nil {
|
|
| 321 |
- return errors.Wrap(err, "invalid Capabilities") |
|
| 322 |
- } |
|
| 323 | 314 |
// TODO consider returning warnings if "Privileged" is combined with Capabilities, CapAdd and/or CapDrop |
| 324 | 315 |
return nil |
| 325 | 316 |
} |
| ... | ... |
@@ -390,7 +390,7 @@ func (daemon *Daemon) createSpecLinuxFields(c *container.Container, s *specs.Spe |
| 390 | 390 |
// Note these are against the UVM. |
| 391 | 391 |
setResourcesInSpec(c, s, true) // LCOW is Hyper-V only |
| 392 | 392 |
|
| 393 |
- capabilities, err := caps.TweakCapabilities(caps.DefaultCapabilities(), c.HostConfig.CapAdd, c.HostConfig.CapDrop, c.HostConfig.Capabilities, c.HostConfig.Privileged) |
|
| 393 |
+ capabilities, err := caps.TweakCapabilities(caps.DefaultCapabilities(), c.HostConfig.CapAdd, c.HostConfig.CapDrop, c.HostConfig.Privileged) |
|
| 394 | 394 |
if err != nil {
|
| 395 | 395 |
return fmt.Errorf("linux spec capabilities: %v", err)
|
| 396 | 396 |
} |
| ... | ... |
@@ -28,12 +28,12 @@ keywords: "API, Docker, rcli, REST, documentation" |
| 28 | 28 |
* The `filter` (singular) query parameter, which was deprecated in favor of the |
| 29 | 29 |
`filters` option in Docker 1.13, has now been removed from the `GET /images/json` |
| 30 | 30 |
endpoint. The parameter remains available when using API version 1.40 or below. |
| 31 |
-* `GET /services` now returns `Capabilities` as part of the `ContainerSpec`. |
|
| 32 |
-* `GET /services/{id}` now returns `Capabilities` as part of the `ContainerSpec`.
|
|
| 33 |
-* `POST /services/create` now accepts `Capabilities` as part of the `ContainerSpec`. |
|
| 34 |
-* `POST /services/{id}/update` now accepts `Capabilities` as part of the `ContainerSpec`.
|
|
| 35 |
-* `GET /tasks` now returns `Capabilities` as part of the `ContainerSpec`. |
|
| 36 |
-* `GET /tasks/{id}` now returns `Capabilities` as part of the `ContainerSpec`.
|
|
| 31 |
+* `GET /services` now returns `CappAdd` and `CapDrop` as part of the `ContainerSpec`. |
|
| 32 |
+* `GET /services/{id}` now returns `CapAdd` and `CapDrop` as part of the `ContainerSpec`.
|
|
| 33 |
+* `POST /services/create` now accepts `CapAdd` and `CapDrop` as part of the `ContainerSpec`. |
|
| 34 |
+* `POST /services/{id}/update` now accepts `CapAdd` and `CapDrop` as part of the `ContainerSpec`.
|
|
| 35 |
+* `GET /tasks` now returns `CapAdd` and `CapDrop` as part of the `ContainerSpec`. |
|
| 36 |
+* `GET /tasks/{id}` now returns `CapAdd` and `CapDrop` as part of the `ContainerSpec`.
|
|
| 37 | 37 |
* `GET /services` now returns `Pids` in `TaskTemplate.Resources.Limits`. |
| 38 | 38 |
* `GET /services/{id}` now returns `Pids` in `TaskTemplate.Resources.Limits`.
|
| 39 | 39 |
* `POST /services/create` now accepts `Pids` in `TaskTemplate.Resources.Limits`. |
| ... | ... |
@@ -135,11 +135,6 @@ keywords: "API, Docker, rcli, REST, documentation" |
| 135 | 135 |
* `GET /service/{id}` now returns `MaxReplicas` as part of the `Placement`.
|
| 136 | 136 |
* `POST /service/create` and `POST /services/(id or name)/update` now take the field `MaxReplicas` |
| 137 | 137 |
as part of the service `Placement`, allowing to specify maximum replicas per node for the service. |
| 138 |
-* `GET /containers` now returns `Capabilities` field as part of the `HostConfig`. |
|
| 139 |
-* `GET /containers/{id}/json` now returns a `Capabilities` field as part of the `HostConfig`.
|
|
| 140 |
-* `POST /containers/create` now takes a `Capabilities` field to set the list of |
|
| 141 |
- kernel capabilities to be available for the container (this overrides the default |
|
| 142 |
- set). |
|
| 143 | 138 |
* `POST /containers/create` on Linux now creates a container with `HostConfig.IpcMode=private` |
| 144 | 139 |
by default, if IpcMode is not explicitly specified. The per-daemon default can be changed |
| 145 | 140 |
back to `shareable` by using `DefaultIpcMode` daemon configuration parameter. |
| ... | ... |
@@ -17,7 +17,6 @@ import ( |
| 17 | 17 |
"github.com/docker/docker/errdefs" |
| 18 | 18 |
ctr "github.com/docker/docker/integration/internal/container" |
| 19 | 19 |
"github.com/docker/docker/oci" |
| 20 |
- "github.com/docker/docker/testutil/request" |
|
| 21 | 20 |
specs "github.com/opencontainers/image-spec/specs-go/v1" |
| 22 | 21 |
"gotest.tools/v3/assert" |
| 23 | 22 |
is "gotest.tools/v3/assert/cmp" |
| ... | ... |
@@ -258,133 +257,6 @@ func TestCreateWithCustomMaskedPaths(t *testing.T) {
|
| 258 | 258 |
} |
| 259 | 259 |
} |
| 260 | 260 |
|
| 261 |
-func TestCreateWithCapabilities(t *testing.T) {
|
|
| 262 |
- skip.If(t, testEnv.DaemonInfo.OSType == "windows", "FIXME: test should be able to run on LCOW") |
|
| 263 |
- skip.If(t, versions.LessThan(testEnv.DaemonAPIVersion(), "1.40"), "Capabilities was added in API v1.40") |
|
| 264 |
- |
|
| 265 |
- defer setupTest(t)() |
|
| 266 |
- ctx := context.Background() |
|
| 267 |
- clientNew := request.NewAPIClient(t) |
|
| 268 |
- clientOld := request.NewAPIClient(t, client.WithVersion("1.39"))
|
|
| 269 |
- |
|
| 270 |
- testCases := []struct {
|
|
| 271 |
- doc string |
|
| 272 |
- hostConfig container.HostConfig |
|
| 273 |
- expected []string |
|
| 274 |
- expectedError string |
|
| 275 |
- oldClient bool |
|
| 276 |
- }{
|
|
| 277 |
- {
|
|
| 278 |
- doc: "no capabilities", |
|
| 279 |
- hostConfig: container.HostConfig{},
|
|
| 280 |
- }, |
|
| 281 |
- {
|
|
| 282 |
- doc: "empty capabilities", |
|
| 283 |
- hostConfig: container.HostConfig{
|
|
| 284 |
- Capabilities: []string{},
|
|
| 285 |
- }, |
|
| 286 |
- expected: []string{},
|
|
| 287 |
- }, |
|
| 288 |
- {
|
|
| 289 |
- doc: "valid capabilities", |
|
| 290 |
- hostConfig: container.HostConfig{
|
|
| 291 |
- Capabilities: []string{"CAP_NET_RAW", "CAP_SYS_CHROOT"},
|
|
| 292 |
- }, |
|
| 293 |
- expected: []string{"CAP_NET_RAW", "CAP_SYS_CHROOT"},
|
|
| 294 |
- }, |
|
| 295 |
- {
|
|
| 296 |
- doc: "invalid capabilities", |
|
| 297 |
- hostConfig: container.HostConfig{
|
|
| 298 |
- Capabilities: []string{"NET_RAW"},
|
|
| 299 |
- }, |
|
| 300 |
- expectedError: `invalid Capabilities: unknown capability: "NET_RAW"`, |
|
| 301 |
- }, |
|
| 302 |
- {
|
|
| 303 |
- doc: "duplicate capabilities", |
|
| 304 |
- hostConfig: container.HostConfig{
|
|
| 305 |
- Capabilities: []string{"CAP_SYS_NICE", "CAP_SYS_NICE"},
|
|
| 306 |
- }, |
|
| 307 |
- expected: []string{"CAP_SYS_NICE", "CAP_SYS_NICE"},
|
|
| 308 |
- }, |
|
| 309 |
- {
|
|
| 310 |
- doc: "capabilities API v1.39", |
|
| 311 |
- hostConfig: container.HostConfig{
|
|
| 312 |
- Capabilities: []string{"CAP_NET_RAW", "CAP_SYS_CHROOT"},
|
|
| 313 |
- }, |
|
| 314 |
- expected: nil, |
|
| 315 |
- oldClient: true, |
|
| 316 |
- }, |
|
| 317 |
- {
|
|
| 318 |
- doc: "empty capadd", |
|
| 319 |
- hostConfig: container.HostConfig{
|
|
| 320 |
- Capabilities: []string{"CAP_NET_ADMIN"},
|
|
| 321 |
- CapAdd: []string{},
|
|
| 322 |
- }, |
|
| 323 |
- expected: []string{"CAP_NET_ADMIN"},
|
|
| 324 |
- }, |
|
| 325 |
- {
|
|
| 326 |
- doc: "empty capdrop", |
|
| 327 |
- hostConfig: container.HostConfig{
|
|
| 328 |
- Capabilities: []string{"CAP_NET_ADMIN"},
|
|
| 329 |
- CapDrop: []string{},
|
|
| 330 |
- }, |
|
| 331 |
- expected: []string{"CAP_NET_ADMIN"},
|
|
| 332 |
- }, |
|
| 333 |
- {
|
|
| 334 |
- doc: "capadd capdrop", |
|
| 335 |
- hostConfig: container.HostConfig{
|
|
| 336 |
- CapAdd: []string{"SYS_NICE", "CAP_SYS_NICE"},
|
|
| 337 |
- CapDrop: []string{"SYS_NICE", "CAP_SYS_NICE"},
|
|
| 338 |
- }, |
|
| 339 |
- }, |
|
| 340 |
- {
|
|
| 341 |
- doc: "conflict with capadd", |
|
| 342 |
- hostConfig: container.HostConfig{
|
|
| 343 |
- Capabilities: []string{"CAP_NET_ADMIN"},
|
|
| 344 |
- CapAdd: []string{"SYS_NICE"},
|
|
| 345 |
- }, |
|
| 346 |
- expectedError: `conflicting options: Capabilities and CapAdd`, |
|
| 347 |
- }, |
|
| 348 |
- {
|
|
| 349 |
- doc: "conflict with capdrop", |
|
| 350 |
- hostConfig: container.HostConfig{
|
|
| 351 |
- Capabilities: []string{"CAP_NET_ADMIN"},
|
|
| 352 |
- CapDrop: []string{"NET_RAW"},
|
|
| 353 |
- }, |
|
| 354 |
- expectedError: `conflicting options: Capabilities and CapDrop`, |
|
| 355 |
- }, |
|
| 356 |
- } |
|
| 357 |
- |
|
| 358 |
- for _, tc := range testCases {
|
|
| 359 |
- tc := tc |
|
| 360 |
- t.Run(tc.doc, func(t *testing.T) {
|
|
| 361 |
- t.Parallel() |
|
| 362 |
- client := clientNew |
|
| 363 |
- if tc.oldClient {
|
|
| 364 |
- client = clientOld |
|
| 365 |
- } |
|
| 366 |
- |
|
| 367 |
- c, err := client.ContainerCreate(context.Background(), |
|
| 368 |
- &container.Config{Image: "busybox"},
|
|
| 369 |
- &tc.hostConfig, |
|
| 370 |
- &network.NetworkingConfig{},
|
|
| 371 |
- nil, |
|
| 372 |
- "", |
|
| 373 |
- ) |
|
| 374 |
- if tc.expectedError == "" {
|
|
| 375 |
- assert.NilError(t, err) |
|
| 376 |
- ci, err := client.ContainerInspect(ctx, c.ID) |
|
| 377 |
- assert.NilError(t, err) |
|
| 378 |
- assert.Check(t, ci.HostConfig != nil) |
|
| 379 |
- assert.DeepEqual(t, tc.expected, ci.HostConfig.Capabilities) |
|
| 380 |
- } else {
|
|
| 381 |
- assert.ErrorContains(t, err, tc.expectedError) |
|
| 382 |
- assert.Check(t, errdefs.IsInvalidParameter(err)) |
|
| 383 |
- } |
|
| 384 |
- }) |
|
| 385 |
- } |
|
| 386 |
-} |
|
| 387 |
- |
|
| 388 | 261 |
func TestCreateWithCustomReadonlyPaths(t *testing.T) {
|
| 389 | 262 |
skip.If(t, testEnv.DaemonInfo.OSType != "linux") |
| 390 | 263 |
|
| ... | ... |
@@ -189,10 +189,11 @@ func ServiceWithSysctls(sysctls map[string]string) ServiceSpecOpt {
|
| 189 | 189 |
} |
| 190 | 190 |
|
| 191 | 191 |
// ServiceWithCapabilities sets the Capabilities option of the service's ContainerSpec. |
| 192 |
-func ServiceWithCapabilities(Capabilities []string) ServiceSpecOpt {
|
|
| 192 |
+func ServiceWithCapabilities(add []string, drop []string) ServiceSpecOpt {
|
|
| 193 | 193 |
return func(spec *swarmtypes.ServiceSpec) {
|
| 194 | 194 |
ensureContainerSpec(spec) |
| 195 |
- spec.TaskTemplate.ContainerSpec.Capabilities = Capabilities |
|
| 195 |
+ spec.TaskTemplate.ContainerSpec.CapabilityAdd = add |
|
| 196 |
+ spec.TaskTemplate.ContainerSpec.CapabilityDrop = drop |
|
| 196 | 197 |
} |
| 197 | 198 |
} |
| 198 | 199 |
|
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
|
| 11 | 11 |
"github.com/docker/docker/api/types" |
| 12 | 12 |
"github.com/docker/docker/api/types/filters" |
| 13 |
+ "github.com/docker/docker/api/types/strslice" |
|
| 13 | 14 |
swarmtypes "github.com/docker/docker/api/types/swarm" |
| 14 | 15 |
"github.com/docker/docker/api/types/versions" |
| 15 | 16 |
"github.com/docker/docker/client" |
| ... | ... |
@@ -492,12 +493,13 @@ func TestCreateServiceCapabilities(t *testing.T) {
|
| 492 | 492 |
ctx := context.Background() |
| 493 | 493 |
|
| 494 | 494 |
// store the map we're going to be using everywhere. |
| 495 |
- expectedCapabilities := []string{"CAP_NET_RAW", "CAP_SYS_CHROOT"}
|
|
| 495 |
+ capAdd := []string{"CAP_SYS_CHROOT"}
|
|
| 496 |
+ capDrop := []string{"CAP_NET_RAW"}
|
|
| 496 | 497 |
|
| 497 | 498 |
// Create the service with the capabilities options |
| 498 | 499 |
var instances uint64 = 1 |
| 499 | 500 |
serviceID := swarm.CreateService(t, d, |
| 500 |
- swarm.ServiceWithCapabilities(expectedCapabilities), |
|
| 501 |
+ swarm.ServiceWithCapabilities(capAdd, capDrop), |
|
| 501 | 502 |
) |
| 502 | 503 |
|
| 503 | 504 |
// wait for the service to converge to 1 running task as expected |
| ... | ... |
@@ -529,15 +531,16 @@ func TestCreateServiceCapabilities(t *testing.T) {
|
| 529 | 529 |
// verify that the container has the capabilities option set |
| 530 | 530 |
ctnr, err := client.ContainerInspect(ctx, tasks[0].Status.ContainerStatus.ContainerID) |
| 531 | 531 |
assert.NilError(t, err) |
| 532 |
- assert.DeepEqual(t, ctnr.HostConfig.Capabilities, expectedCapabilities) |
|
| 532 |
+ assert.DeepEqual(t, ctnr.HostConfig.CapAdd, strslice.StrSlice(capAdd)) |
|
| 533 |
+ assert.DeepEqual(t, ctnr.HostConfig.CapDrop, strslice.StrSlice(capDrop)) |
|
| 533 | 534 |
|
| 534 | 535 |
// verify that the task has the capabilities option set in the task object |
| 535 |
- assert.DeepEqual(t, tasks[0].Spec.ContainerSpec.Capabilities, expectedCapabilities) |
|
| 536 |
+ assert.DeepEqual(t, tasks[0].Spec.ContainerSpec.CapabilityAdd, capAdd) |
|
| 537 |
+ assert.DeepEqual(t, tasks[0].Spec.ContainerSpec.CapabilityDrop, capDrop) |
|
| 536 | 538 |
|
| 537 | 539 |
// verify that the service also has the capabilities set in the spec. |
| 538 | 540 |
service, _, err := client.ServiceInspectWithRaw(ctx, serviceID, types.ServiceInspectOptions{})
|
| 539 | 541 |
assert.NilError(t, err) |
| 540 |
- assert.DeepEqual(t, |
|
| 541 |
- service.Spec.TaskTemplate.ContainerSpec.Capabilities, expectedCapabilities, |
|
| 542 |
- ) |
|
| 542 |
+ assert.DeepEqual(t, service.Spec.TaskTemplate.ContainerSpec.CapabilityAdd, capAdd) |
|
| 543 |
+ assert.DeepEqual(t, service.Spec.TaskTemplate.ContainerSpec.CapabilityDrop, capDrop) |
|
| 543 | 544 |
} |
| ... | ... |
@@ -117,17 +117,11 @@ func ValidateCapabilities(caps []string) error {
|
| 117 | 117 |
|
| 118 | 118 |
// TweakCapabilities tweaks capabilities by adding, dropping, or overriding |
| 119 | 119 |
// capabilities in the basics capabilities list. |
| 120 |
-func TweakCapabilities(basics, adds, drops, capabilities []string, privileged bool) ([]string, error) {
|
|
| 120 |
+func TweakCapabilities(basics, adds, drops []string, privileged bool) ([]string, error) {
|
|
| 121 | 121 |
switch {
|
| 122 | 122 |
case privileged: |
| 123 | 123 |
// Privileged containers get all capabilities |
| 124 | 124 |
return GetAllCapabilities(), nil |
| 125 |
- case capabilities != nil: |
|
| 126 |
- // Use custom set of capabilities |
|
| 127 |
- if err := ValidateCapabilities(capabilities); err != nil {
|
|
| 128 |
- return nil, err |
|
| 129 |
- } |
|
| 130 |
- return capabilities, nil |
|
| 131 | 125 |
case len(adds) == 0 && len(drops) == 0: |
| 132 | 126 |
// Nothing to tweak; we're done |
| 133 | 127 |
return basics, nil |