Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add service capability #2809

Merged
merged 2 commits into from
Oct 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
TEST_API_VERSION ?= 1.39
TEST_ENGINE_VERSION ?= 19.03.13
TEST_API_VERSION ?= 1.41
TEST_ENGINE_VERSION ?= 20.10.05

.PHONY: all
all: test
Expand Down
2 changes: 1 addition & 1 deletion docker/constants.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys
from .version import version

DEFAULT_DOCKER_API_VERSION = '1.39'
DEFAULT_DOCKER_API_VERSION = '1.41'
MINIMUM_DOCKER_API_VERSION = '1.21'
DEFAULT_TIMEOUT_SECONDS = 60
STREAM_HEADER_SIZE_BYTES = 8
Expand Down
6 changes: 6 additions & 0 deletions docker/models/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ def create(self, image, command=None, **kwargs):
to the service.
privileges (Privileges): Security options for the service's
containers.
cap_add (:py:class:`list`): A list of kernel capabilities to add to
the default set for the container.
cap_drop (:py:class:`list`): A list of kernel capabilities to drop
from the default set for the container.

Returns:
:py:class:`Service`: The created service.
Expand Down Expand Up @@ -277,6 +281,8 @@ def list(self, **kwargs):
# kwargs to copy straight over to ContainerSpec
CONTAINER_SPEC_KWARGS = [
'args',
'cap_add',
'cap_drop',
'command',
'configs',
'dns_config',
Expand Down
19 changes: 18 additions & 1 deletion docker/types/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,18 @@ class ContainerSpec(dict):
containers. Only used for Windows containers.
init (boolean): Run an init inside the container that forwards signals
and reaps processes.
cap_add (:py:class:`list`): A list of kernel capabilities to add to the
default set for the container.
cap_drop (:py:class:`list`): A list of kernel capabilities to drop from
the default set for the container.
"""
def __init__(self, image, command=None, args=None, hostname=None, env=None,
workdir=None, user=None, labels=None, mounts=None,
stop_grace_period=None, secrets=None, tty=None, groups=None,
open_stdin=None, read_only=None, stop_signal=None,
healthcheck=None, hosts=None, dns_config=None, configs=None,
privileges=None, isolation=None, init=None):
privileges=None, isolation=None, init=None, cap_add=None,
cap_drop=None):
self['Image'] = image

if isinstance(command, six.string_types):
Expand Down Expand Up @@ -188,6 +193,18 @@ def __init__(self, image, command=None, args=None, hostname=None, env=None,
if init is not None:
self['Init'] = init

if cap_add is not None:
if not isinstance(cap_add, list):
raise TypeError('cap_add must be a list')

self['CapabilityAdd'] = cap_add

if cap_drop is not None:
if not isinstance(cap_drop, list):
raise TypeError('cap_drop must be a list')

self['CapabilityDrop'] = cap_drop


class Mount(dict):
"""
Expand Down
30 changes: 30 additions & 0 deletions tests/integration/api_service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1358,3 +1358,33 @@ def _update_service(self, svc_id, *args, **kwargs):
self.client.update_service(*args, **kwargs)
else:
raise

@requires_api_version('1.41')
def test_create_service_cap_add(self):
name = self.get_service_name()
container_spec = docker.types.ContainerSpec(
TEST_IMG, ['echo', 'hello'], cap_add=['CAP_SYSLOG']
)
task_tmpl = docker.types.TaskTemplate(container_spec)
svc_id = self.client.create_service(task_tmpl, name=name)
assert self.client.inspect_service(svc_id)
services = self.client.services(filters={'name': name})
assert len(services) == 1
assert services[0]['ID'] == svc_id['ID']
spec = services[0]['Spec']['TaskTemplate']['ContainerSpec']
assert 'CAP_SYSLOG' in spec['CapabilityAdd']

@requires_api_version('1.41')
def test_create_service_cap_drop(self):
name = self.get_service_name()
container_spec = docker.types.ContainerSpec(
TEST_IMG, ['echo', 'hello'], cap_drop=['CAP_SYSLOG']
)
task_tmpl = docker.types.TaskTemplate(container_spec)
svc_id = self.client.create_service(task_tmpl, name=name)
assert self.client.inspect_service(svc_id)
services = self.client.services(filters={'name': name})
assert len(services) == 1
assert services[0]['ID'] == svc_id['ID']
spec = services[0]['Spec']['TaskTemplate']['ContainerSpec']
assert 'CAP_SYSLOG' in spec['CapabilityDrop']
38 changes: 38 additions & 0 deletions tests/integration/models_services_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,41 @@ def test_force_update_service_using_shorthand_method(self):
assert service.force_update()
service.reload()
assert service.version > initial_version

@helpers.requires_api_version('1.41')
def test_create_cap_add(self):
client = docker.from_env(version=TEST_API_VERSION)
name = helpers.random_name()
service = client.services.create(
name=name,
labels={'foo': 'bar'},
image="alpine",
command="sleep 300",
container_labels={'container': 'label'},
cap_add=["CAP_SYSLOG"]
)
assert service.name == name
assert service.attrs['Spec']['Labels']['foo'] == 'bar'
container_spec = service.attrs['Spec']['TaskTemplate']['ContainerSpec']
assert "alpine" in container_spec['Image']
assert container_spec['Labels'] == {'container': 'label'}
assert "CAP_SYSLOG" in container_spec["CapabilityAdd"]

@helpers.requires_api_version('1.41')
def test_create_cap_drop(self):
client = docker.from_env(version=TEST_API_VERSION)
name = helpers.random_name()
service = client.services.create(
name=name,
labels={'foo': 'bar'},
image="alpine",
command="sleep 300",
container_labels={'container': 'label'},
cap_drop=["CAP_SYSLOG"]
)
assert service.name == name
assert service.attrs['Spec']['Labels']['foo'] == 'bar'
container_spec = service.attrs['Spec']['TaskTemplate']['ContainerSpec']
assert "alpine" in container_spec['Image']
assert container_spec['Labels'] == {'container': 'label'}
assert "CAP_SYSLOG" in container_spec["CapabilityDrop"]