From 3ddd75ac68619a0932d97bba4280b9283483145b Mon Sep 17 00:00:00 2001 From: Piotr Wojciechowski <23406016+WojciechowskiPiotr@users.noreply.github.com> Date: Wed, 24 Feb 2021 10:14:06 +0100 Subject: [PATCH] Replicas max per node (#92) * Adding 'replicas_max_per_node' placement parameter * Adding 'replicas_max_per_node' placement parameter * Adding 'replicas_max_per_node' integration tests * Adding 'replicas_max_per_node' feature * 'replicas_max_per_node' documentation update * 'replicas_max_per_node' unit test fix * Documentation and changelog updates * Update changelogs/fragments/92-replicas-max-per-node.yml Co-authored-by: Felix Fontein Co-authored-by: WojciechowskiPiotr Co-authored-by: Felix Fontein --- .../fragments/92-replicas-max-per-node.yml | 2 + plugins/modules/docker_swarm_service.py | 32 +++++++++++ .../tasks/tests/placement.yml | 56 +++++++++++++++++++ .../docker_swarm_service/vars/main.yml | 1 + 4 files changed, 91 insertions(+) create mode 100644 changelogs/fragments/92-replicas-max-per-node.yml diff --git a/changelogs/fragments/92-replicas-max-per-node.yml b/changelogs/fragments/92-replicas-max-per-node.yml new file mode 100644 index 000000000..99d4473d5 --- /dev/null +++ b/changelogs/fragments/92-replicas-max-per-node.yml @@ -0,0 +1,2 @@ +minor_changes: + - "docker_swarm_service - adding support for maximum number of tasks per node (``replicas_max_per_node``) when running swarm service in replicated mode. Introduced in API 1.40 (https://github.com/ansible-collections/community.docker/issues/7, https://github.com/ansible-collections/community.docker/pull/92)." diff --git a/plugins/modules/docker_swarm_service.py b/plugins/modules/docker_swarm_service.py index f8a4bd00e..4d42a1e18 100644 --- a/plugins/modules/docker_swarm_service.py +++ b/plugins/modules/docker_swarm_service.py @@ -13,6 +13,7 @@ - "Dario Zanzico (@dariko)" - "Jason Witkowski (@jwitko)" - "Hannes Ljungberg (@hannseman)" + - "Piotr Wojciechowski (@wojciechowskipiotr)" short_description: docker swarm service description: - Manages docker services via a swarm manager node. @@ -344,6 +345,13 @@ - Requires API version >= 1.27. type: list elements: dict + replicas_max_per_node: + description: + - Maximum number of tasks per node. + - Corresponds to the C(--replicas_max_per_node) option of C(docker service create). + - Requires API version >= 1.40 + type: int + version_added: 1.3.0 type: dict publish: description: @@ -718,6 +726,7 @@ "publish": null, "read_only": null, "replicas": 1, + "replicas_max_per_node": 1, "reserve_cpu": 0.25, "reserve_memory": 20971520, "restart_policy": "on-failure", @@ -840,6 +849,7 @@ constraints: - node.role == manager - engine.labels.operatingsystem == ubuntu 14.04 + replicas_max_per_node: 2 - name: Set configs community.docker.docker_swarm_service: @@ -1208,6 +1218,7 @@ def __init__(self, docker_api_version, docker_py_version): self.configs = None self.secrets = None self.constraints = None + self.replicas_max_per_node = None self.networks = None self.stop_grace_period = None self.stop_signal = None @@ -1257,6 +1268,7 @@ def get_facts(self): 'log_driver_options': self.log_driver_options, 'publish': self.publish, 'constraints': self.constraints, + 'replicas_max_per_node': self.replicas_max_per_node, 'placement_preferences': self.placement_preferences, 'labels': self.labels, 'container_labels': self.container_labels, @@ -1472,9 +1484,15 @@ def get_placement_from_ansible_params(params): ) preferences = placement.get('preferences') + replicas_max_per_node = get_value( + 'replicas_max_per_node', + placement + ) + return { 'constraints': constraints, 'placement_preferences': preferences, + 'replicas_max_per_node': replicas_max_per_node, } @classmethod @@ -1683,6 +1701,8 @@ def compare(self, os): differences.add('args', parameter=self.args, active=os.args) if has_list_changed(self.constraints, os.constraints): differences.add('constraints', parameter=self.constraints, active=os.constraints) + if self.replicas_max_per_node is not None and self.replicas_max_per_node != os.replicas_max_per_node: + differences.add('replicas_max_per_node', parameter=self.replicas_max_per_node, active=os.replicas_max_per_node) if has_list_changed(self.placement_preferences, os.placement_preferences, sort_lists=False): differences.add('placement_preferences', parameter=self.placement_preferences, active=os.placement_preferences) if has_list_changed(self.groups, os.groups): @@ -1923,6 +1943,8 @@ def build_placement(self): placement_args = {} if self.constraints is not None: placement_args['constraints'] = self.constraints + if self.replicas_max_per_node is not None: + placement_args['maxreplicas'] = self.replicas_max_per_node if self.placement_preferences is not None: placement_args['preferences'] = [ {key.title(): {'SpreadDescriptor': value}} @@ -2164,6 +2186,7 @@ def get_service(self, name): placement = task_template_data.get('Placement') if placement: ds.constraints = placement.get('Constraints') + ds.replicas_max_per_node = placement.get('MaxReplicas') placement_preferences = [] for preference in placement.get('Preferences', []): placement_preferences.append( @@ -2603,6 +2626,7 @@ def main(): placement=dict(type='dict', options=dict( constraints=dict(type='list', elements='str'), preferences=dict(type='list', elements='dict'), + replicas_max_per_node=dict(type='int'), )), tty=dict(type='bool'), dns=dict(type='list', elements='str'), @@ -2748,6 +2772,14 @@ def main(): ) is not None, usage_msg='set placement.constraints' ), + placement_config_replicas_max_per_node=dict( + docker_py_version='4.4.3', + docker_api_version='1.40', + detect_usage=lambda c: (c.module.params['placement'] or {}).get( + 'replicas_max_per_node' + ) is not None, + usage_msg='set placement.replicas_max_per_node' + ), mounts_tmpfs=dict( docker_py_version='2.6.0', detect_usage=_detect_mount_tmpfs_usage, diff --git a/tests/integration/targets/docker_swarm_service/tasks/tests/placement.yml b/tests/integration/targets/docker_swarm_service/tasks/tests/placement.yml index 71873b206..cc4290dee 100644 --- a/tests/integration/targets/docker_swarm_service/tasks/tests/placement.yml +++ b/tests/integration/targets/docker_swarm_service/tasks/tests/placement.yml @@ -200,3 +200,59 @@ - constraints_1 is failed - "'Minimum version required' in constraints_1.msg" when: docker_api_version is version('1.27', '<') or docker_py_version is version('2.4.0', '<') + +#################################################################### +## placement.replicas_max_per_node ##################################################### +#################################################################### + +- name: placement.replicas_max_per_node + docker_swarm_service: + name: "{{ service_name }}" + image: "{{ docker_test_image_alpine }}" + resolve_image: no + command: '/bin/sh -v -c "sleep 10m"' + placement: + replicas_max_per_node: 1 + register: replicas_max_per_node_1 + ignore_errors: yes + +- name: placement.replicas_max_per_node (idempotency) + docker_swarm_service: + name: "{{ service_name }}" + image: "{{ docker_test_image_alpine }}" + resolve_image: no + command: '/bin/sh -v -c "sleep 10m"' + placement: + replicas_max_per_node: 1 + register: replicas_max_per_node_2 + ignore_errors: yes + +- name: placement.replicas_max_per_node (change) + docker_swarm_service: + name: "{{ service_name }}" + image: "{{ docker_test_image_alpine }}" + resolve_image: no + command: '/bin/sh -v -c "sleep 10m"' + placement: + replicas_max_per_node: 2 + register: replicas_max_per_node_3 + ignore_errors: yes + + +- name: cleanup + docker_swarm_service: + name: "{{ service_name }}" + state: absent + diff: no + +- assert: + that: + - replicas_max_per_node_1 is changed + - replicas_max_per_node_2 is not changed + - replicas_max_per_node_3 is changed + when: docker_api_version is version('1.40', '>=') and docker_py_version is version('4.4.3', '>=') +- assert: + that: + - replicas_max_per_node_1 is failed + - "'Minimum version required' in replicas_max_per_node_1.msg" + when: docker_api_version is version('1.40', '<') or docker_py_version is version('4.4.3', '<') diff --git a/tests/integration/targets/docker_swarm_service/vars/main.yml b/tests/integration/targets/docker_swarm_service/vars/main.yml index 8ec7ffebd..0f21e6456 100644 --- a/tests/integration/targets/docker_swarm_service/vars/main.yml +++ b/tests/integration/targets/docker_swarm_service/vars/main.yml @@ -35,6 +35,7 @@ service_expected_output: - {mode: null, protocol: udp, published_port: 60001, target_port: 60001} read_only: null replicas: null + replicas_max_per_node: null reserve_cpu: null reserve_memory: null restart_policy: null