Skip to content

Commit 7ac8b56

Browse files
committed
Add CapabilityAdd and CapabilityDrop to
ContainerSpec Docker Engine v1.41 added `CapAdd` and `CapDrop` as part of the ContainerSpec, and `docker-py` should do the same. ``` GET /services now returns CapAdd and CapDrop as part of the ContainerSpec. GET /services/{id} now returns CapAdd and CapDrop as part of the ContainerSpec. POST /services/create now accepts CapAdd and CapDrop as part of the ContainerSpec. POST /services/{id}/update now accepts CapAdd and CapDrop as part of the ContainerSpec. GET /tasks now returns CapAdd and CapDrop as part of the ContainerSpec. GET /tasks/{id} now returns CapAdd and CapDrop as part of the ContainerSpec. ``` I added capabilities on docstrings, `service.create` init method and create tests for that. That change was mention in issue #2802. Signed-off-by: Felipe Ruhland <[email protected]>
1 parent f53e615 commit 7ac8b56

File tree

4 files changed

+92
-1
lines changed

4 files changed

+92
-1
lines changed

docker/models/services.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ def create(self, image, command=None, **kwargs):
213213
to the service.
214214
privileges (Privileges): Security options for the service's
215215
containers.
216+
cap_add (:py:class:`list`): A list of kernel capabilities to add to
217+
the default set for the container.
218+
cap_drop (:py:class:`list`): A list of kernel capabilities to drop
219+
from the default set for the container.
216220
217221
Returns:
218222
:py:class:`Service`: The created service.
@@ -277,6 +281,8 @@ def list(self, **kwargs):
277281
# kwargs to copy straight over to ContainerSpec
278282
CONTAINER_SPEC_KWARGS = [
279283
'args',
284+
'cap_add',
285+
'cap_drop',
280286
'command',
281287
'configs',
282288
'dns_config',

docker/types/services.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,18 @@ class ContainerSpec(dict):
112112
containers. Only used for Windows containers.
113113
init (boolean): Run an init inside the container that forwards signals
114114
and reaps processes.
115+
cap_add (:py:class:`list`): A list of kernel capabilities to add to the
116+
default set for the container.
117+
cap_drop (:py:class:`list`): A list of kernel capabilities to drop from
118+
the default set for the container.
115119
"""
116120
def __init__(self, image, command=None, args=None, hostname=None, env=None,
117121
workdir=None, user=None, labels=None, mounts=None,
118122
stop_grace_period=None, secrets=None, tty=None, groups=None,
119123
open_stdin=None, read_only=None, stop_signal=None,
120124
healthcheck=None, hosts=None, dns_config=None, configs=None,
121-
privileges=None, isolation=None, init=None):
125+
privileges=None, isolation=None, init=None, cap_add=None,
126+
cap_drop=None):
122127
self['Image'] = image
123128

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

196+
if cap_add is not None:
197+
if not isinstance(cap_add, list):
198+
raise TypeError('cap_add must be a list')
199+
200+
self['CapabilityAdd'] = cap_add
201+
202+
if cap_drop is not None:
203+
if not isinstance(cap_drop, list):
204+
raise TypeError('cap_drop must be a list')
205+
206+
self['CapabilityDrop'] = cap_drop
207+
191208

192209
class Mount(dict):
193210
"""

tests/integration/api_service_test.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,3 +1358,33 @@ def _update_service(self, svc_id, *args, **kwargs):
13581358
self.client.update_service(*args, **kwargs)
13591359
else:
13601360
raise
1361+
1362+
@requires_api_version('1.41')
1363+
def test_create_service_cap_add(self):
1364+
name = self.get_service_name()
1365+
container_spec = docker.types.ContainerSpec(
1366+
TEST_IMG, ['echo', 'hello'], cap_add=['CAP_SYSLOG']
1367+
)
1368+
task_tmpl = docker.types.TaskTemplate(container_spec)
1369+
svc_id = self.client.create_service(task_tmpl, name=name)
1370+
assert self.client.inspect_service(svc_id)
1371+
services = self.client.services(filters={'name': name})
1372+
assert len(services) == 1
1373+
assert services[0]['ID'] == svc_id['ID']
1374+
spec = services[0]['Spec']['TaskTemplate']['ContainerSpec']
1375+
assert 'CAP_SYSLOG' in spec['CapabilityAdd']
1376+
1377+
@requires_api_version('1.41')
1378+
def test_create_service_cap_drop(self):
1379+
name = self.get_service_name()
1380+
container_spec = docker.types.ContainerSpec(
1381+
TEST_IMG, ['echo', 'hello'], cap_drop=['CAP_SYSLOG']
1382+
)
1383+
task_tmpl = docker.types.TaskTemplate(container_spec)
1384+
svc_id = self.client.create_service(task_tmpl, name=name)
1385+
assert self.client.inspect_service(svc_id)
1386+
services = self.client.services(filters={'name': name})
1387+
assert len(services) == 1
1388+
assert services[0]['ID'] == svc_id['ID']
1389+
spec = services[0]['Spec']['TaskTemplate']['ContainerSpec']
1390+
assert 'CAP_SYSLOG' in spec['CapabilityDrop']

tests/integration/models_services_test.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,3 +333,41 @@ def test_force_update_service_using_shorthand_method(self):
333333
assert service.force_update()
334334
service.reload()
335335
assert service.version > initial_version
336+
337+
@helpers.requires_api_version('1.41')
338+
def test_create_cap_add(self):
339+
client = docker.from_env(version=TEST_API_VERSION)
340+
name = helpers.random_name()
341+
service = client.services.create(
342+
name=name,
343+
labels={'foo': 'bar'},
344+
image="alpine",
345+
command="sleep 300",
346+
container_labels={'container': 'label'},
347+
cap_add=["CAP_SYSLOG"]
348+
)
349+
assert service.name == name
350+
assert service.attrs['Spec']['Labels']['foo'] == 'bar'
351+
container_spec = service.attrs['Spec']['TaskTemplate']['ContainerSpec']
352+
assert "alpine" in container_spec['Image']
353+
assert container_spec['Labels'] == {'container': 'label'}
354+
assert "CAP_SYSLOG" in container_spec["CapabilityAdd"]
355+
356+
@helpers.requires_api_version('1.41')
357+
def test_create_cap_drop(self):
358+
client = docker.from_env(version=TEST_API_VERSION)
359+
name = helpers.random_name()
360+
service = client.services.create(
361+
name=name,
362+
labels={'foo': 'bar'},
363+
image="alpine",
364+
command="sleep 300",
365+
container_labels={'container': 'label'},
366+
cap_drop=["CAP_SYSLOG"]
367+
)
368+
assert service.name == name
369+
assert service.attrs['Spec']['Labels']['foo'] == 'bar'
370+
container_spec = service.attrs['Spec']['TaskTemplate']['ContainerSpec']
371+
assert "alpine" in container_spec['Image']
372+
assert container_spec['Labels'] == {'container': 'label'}
373+
assert "CAP_SYSLOG" in container_spec["CapabilityDrop"]

0 commit comments

Comments
 (0)