diff --git a/docker/models/services.py b/docker/models/services.py index 925506811..06438748f 100644 --- a/docker/models/services.py +++ b/docker/models/services.py @@ -217,6 +217,8 @@ def create(self, image, command=None, **kwargs): 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. + sysctls (:py:class:`dict`): A dict of sysctl values to add to the + container Returns: :py:class:`Service`: The created service. @@ -305,6 +307,7 @@ def list(self, **kwargs): 'tty', 'user', 'workdir', + 'sysctls', ] # kwargs to copy straight over to TaskTemplate diff --git a/docker/types/services.py b/docker/types/services.py index 268684e0a..a3383ef75 100644 --- a/docker/types/services.py +++ b/docker/types/services.py @@ -115,6 +115,8 @@ class ContainerSpec(dict): default set for the container. cap_drop (:py:class:`list`): A list of kernel capabilities to drop from the default set for the container. + sysctls (:py:class:`dict`): A dict of sysctl values to add to + the container """ def __init__(self, image, command=None, args=None, hostname=None, env=None, @@ -123,7 +125,7 @@ def __init__(self, image, command=None, args=None, hostname=None, env=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, cap_add=None, - cap_drop=None): + cap_drop=None, sysctls=None): self['Image'] = image if isinstance(command, str): @@ -205,6 +207,12 @@ def __init__(self, image, command=None, args=None, hostname=None, env=None, self['CapabilityDrop'] = cap_drop + if sysctls is not None: + if not isinstance(sysctls, dict): + raise TypeError('sysctls must be a dict') + + self['Sysctls'] = sysctls + class Mount(dict): """ diff --git a/tests/integration/api_service_test.py b/tests/integration/api_service_test.py index 03770a03e..8ce7c9d57 100644 --- a/tests/integration/api_service_test.py +++ b/tests/integration/api_service_test.py @@ -1419,3 +1419,23 @@ def test_create_service_cap_drop(self): assert services[0]['ID'] == svc_id['ID'] spec = services[0]['Spec']['TaskTemplate']['ContainerSpec'] assert 'CAP_SYSLOG' in spec['CapabilityDrop'] + + @requires_api_version('1.40') + def test_create_service_with_sysctl(self): + name = self.get_service_name() + sysctls = { + 'net.core.somaxconn': '1024', + 'net.ipv4.tcp_syncookies': '0', + } + container_spec = docker.types.ContainerSpec( + TEST_IMG, ['echo', 'hello'], sysctls=sysctls + ) + 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 spec['Sysctls']['net.core.somaxconn'] == '1024' + assert spec['Sysctls']['net.ipv4.tcp_syncookies'] == '0' diff --git a/tests/unit/models_services_test.py b/tests/unit/models_services_test.py index 94a27f0e5..45c63ac9e 100644 --- a/tests/unit/models_services_test.py +++ b/tests/unit/models_services_test.py @@ -29,7 +29,8 @@ def test_get_create_service_kwargs(self): 'constraints': ['foo=bar'], 'preferences': ['bar=baz'], 'platforms': [('x86_64', 'linux')], - 'maxreplicas': 1 + 'maxreplicas': 1, + 'sysctls': {'foo': 'bar'} }) task_template = kwargs.pop('task_template') @@ -59,5 +60,5 @@ def test_get_create_service_kwargs(self): assert task_template['Networks'] == [{'Target': 'somenet'}] assert set(task_template['ContainerSpec'].keys()) == { 'Image', 'Command', 'Args', 'Hostname', 'Env', 'Dir', 'User', - 'Labels', 'Mounts', 'StopGracePeriod' + 'Labels', 'Mounts', 'StopGracePeriod', 'Sysctls' }