diff --git a/docker/models/services.py b/docker/models/services.py index a35687b3..a29ff132 100644 --- a/docker/models/services.py +++ b/docker/models/services.py @@ -157,6 +157,8 @@ class ServiceCollection(Collection): constraints. preferences (list of tuple): :py:class:`~docker.types.Placement` preferences. + maxreplicas (int): :py:class:`~docker.types.Placement` maxreplicas + or (int) representing maximum number of replicas per node. platforms (list of tuple): A list of platform constraints expressed as ``(arch, os)`` tuples. container_labels (dict): Labels to apply to the container. @@ -319,6 +321,7 @@ PLACEMENT_KWARGS = [ 'constraints', 'preferences', 'platforms', + 'maxreplicas', ] diff --git a/docker/types/services.py b/docker/types/services.py index 05dda15d..29498e97 100644 --- a/docker/types/services.py +++ b/docker/types/services.py @@ -659,10 +659,12 @@ class Placement(dict): are provided in order from highest to lowest precedence and are expressed as ``(strategy, descriptor)`` tuples. See :py:class:`PlacementPreference` for details. + maxreplicas (int): Maximum number of replicas per node platforms (:py:class:`list` of tuple): A list of platforms expressed as ``(arch, os)`` tuples """ - def __init__(self, constraints=None, preferences=None, platforms=None): + def __init__(self, constraints=None, preferences=None, platforms=None, + maxreplicas=None): if constraints is not None: self['Constraints'] = constraints if preferences is not None: @@ -671,6 +673,8 @@ class Placement(dict): if isinstance(pref, tuple): pref = PlacementPreference(*pref) self['Preferences'].append(pref) + if maxreplicas is not None: + self['MaxReplicas'] = maxreplicas if platforms: self['Platforms'] = [] for plat in platforms: diff --git a/tests/integration/api_service_test.py b/tests/integration/api_service_test.py index b6b7ec53..7e5336e2 100644 --- a/tests/integration/api_service_test.py +++ b/tests/integration/api_service_test.py @@ -471,6 +471,19 @@ class ServiceTest(BaseAPIIntegrationTest): assert 'Placement' in svc_info['Spec']['TaskTemplate'] assert svc_info['Spec']['TaskTemplate']['Placement'] == placemt + @requires_api_version('1.40') + def test_create_service_with_placement_maxreplicas(self): + container_spec = docker.types.ContainerSpec(TEST_IMG, ['true']) + placemt = docker.types.Placement(maxreplicas=1) + task_tmpl = docker.types.TaskTemplate( + container_spec, placement=placemt + ) + name = self.get_service_name() + svc_id = self.client.create_service(task_tmpl, name=name) + svc_info = self.client.inspect_service(svc_id) + assert 'Placement' in svc_info['Spec']['TaskTemplate'] + assert svc_info['Spec']['TaskTemplate']['Placement'] == placemt + def test_create_service_with_endpoint_spec(self): container_spec = docker.types.ContainerSpec(TEST_IMG, ['true']) task_tmpl = docker.types.TaskTemplate(container_spec) diff --git a/tests/unit/models_services_test.py b/tests/unit/models_services_test.py index a4ac50c3..07bb5897 100644 --- a/tests/unit/models_services_test.py +++ b/tests/unit/models_services_test.py @@ -28,6 +28,7 @@ class CreateServiceKwargsTest(unittest.TestCase): 'constraints': ['foo=bar'], 'preferences': ['bar=baz'], 'platforms': [('x86_64', 'linux')], + 'maxreplicas': 1 }) task_template = kwargs.pop('task_template') @@ -47,6 +48,7 @@ class CreateServiceKwargsTest(unittest.TestCase): 'Constraints': ['foo=bar'], 'Preferences': ['bar=baz'], 'Platforms': [{'Architecture': 'x86_64', 'OS': 'linux'}], + 'MaxReplicas': 1, } assert task_template['LogDriver'] == { 'Name': 'logdriver',