- name: Gather join tokens from the primary manager hosts: manager become: true tasks: - name: Get manager join-token command: docker swarm join-token manager -q register: manager_join_token - name: Get worker join-token command: docker swarm join-token worker -q register: worker_join_token - name: Set fact with join details set_fact: swarm_leader_ip: "{{ ansible_default_ipv4.address }}" swarm_manager_token: "{{ manager_join_token.stdout }}" swarm_worker_token: "{{ worker_join_token.stdout }}" - name: Propagate join info globally add_host: name: "swarm_info_host" swarm_leader_ip: "{{ swarm_leader_ip }}" swarm_manager_token: "{{ swarm_manager_token }}" swarm_worker_token: "{{ swarm_worker_token }}" changed_when: false - name: Add new managers to the Swarm hosts: swarm_managers become: true vars: swarm_info: "{{ hostvars['swarm_info_host'] }}" tasks: - name: Check if already in a swarm command: docker info --format '{{ "{{ .Swarm.LocalNodeState }}" }}' register: manager_swarm_status ignore_errors: true changed_when: false - name: Join as manager (if not already part of a swarm) command: > docker swarm join --token {{ swarm_info['swarm_manager_token'] }} {{ swarm_info['swarm_leader_ip'] }}:2377 when: manager_swarm_status.stdout != "active" - name: Add new workers to the Swarm hosts: swarm_workers become: true vars: swarm_info: "{{ hostvars['swarm_info_host'] }}" tasks: - name: Check if node is already part of a swarm command: docker info --format '{{ "{{ .Swarm.LocalNodeState }}" }}' register: worker_swarm_status ignore_errors: true changed_when: false - name: Join as worker (if not already part of a swarm) command: > docker swarm join --token {{ swarm_info['swarm_worker_token'] }} {{ swarm_info['swarm_leader_ip'] }}:2377 when: worker_swarm_status.stdout != "active" - name: Verify all nodes are present in the swarm hosts: manager[0] become: true tasks: - name: Get list of all nodes and their roles in the swarm command: docker node ls --format '{{ "{{ .Hostname }}|{{ .ManagerStatus }}" }}' register: swarm_nodes - name: Parse manager and worker counts set_fact: manager_count: "{{ swarm_nodes.stdout_lines | select('search', 'Leader|Reachable') | list | length }}" worker_count: "{{ swarm_nodes.stdout_lines | select('search', '^((?!Leader|Reachable).)*$') | list | length }}" - name: Show parsed node roles debug: msg: - "Managers found: {{ manager_count }} (expected: {{ groups['swarm_managers'] | length }})" - "Workers found: {{ worker_count }} (expected: {{ groups['swarm_workers'] | length }})" - name: Fail if manager or worker counts do not match inventory fail: msg: | Swarm node count mismatch! Managers found: {{ manager_count }} (expected: {{ groups['swarm_managers'] | length }}) Workers found: {{ worker_count }} (expected: {{ groups['swarm_workers'] | length }}) All nodes: {{ swarm_nodes.stdout_lines }} when: (manager_count | int) != (groups['swarm_managers'] | length) or (worker_count | int) != (groups['swarm_workers'] | length)