swarm.ymlΒΆ

---
- name: "πŸ›‘οΈ Guard instance dir at swarm flush"
  ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/guard_instance_dir.yml') }}"
  run_once: true
  listen:
    - compose-up
    - swarm-deploy

- name: "🐝 swarm: pre-deploy build of local images"
  ansible.builtin.shell: |
    set -euo pipefail
    timeout -k 60 {{ lookup('timeout', 3600) | int }} compose build
  args:
    chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
    executable: /bin/bash
  register: _swarm_pre_deploy_build
  timeout: "{{ lookup('timeout', 3600) | int }}"
  retries: 3
  delay: 30
  until: _swarm_pre_deploy_build is succeeded
  ignore_errors: true
  listen:
    - compose-up
    - swarm-deploy
  run_once: true
  delegate_to: "{{ STACK_HOST }}"

- name: "🐝 swarm: rebuild without cache once the spaced retries are exhausted"
  ansible.builtin.shell: |
    set -euo pipefail
    timeout -k 60 {{ lookup('timeout', 3600) | int }} compose build --no-cache --pull
  args:
    chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
    executable: /bin/bash
  register: _swarm_pre_deploy_rebuild
  timeout: "{{ lookup('timeout', 3600) | int }}"
  retries: 3
  delay: 30
  until: _swarm_pre_deploy_rebuild is succeeded
  when: _swarm_pre_deploy_build is failed
  listen:
    - compose-up
    - swarm-deploy
  run_once: true
  delegate_to: "{{ STACK_HOST }}"

- name: "🐝 swarm: cache externals + retag built images and push to registry"
  ansible.builtin.script:
    cmd: >-
      python/swarm/registry_sync.py
      --chdir {{ lookup('container', application_id, 'directories.instance') }}
      --registry-prefix {{ SWARM_REGISTRY_PREFIX | quote }}
  register: _swarm_registry_sync
  retries: 10
  delay: 6
  until: _swarm_registry_sync is succeeded
  listen:
    - compose-up
    - swarm-deploy
  when: (swarm.registry.host | default('') | length > 0) and (application_id != 'svc-registry-docker')
  run_once: true
  delegate_to: "{{ STACK_HOST }}"

- name: "🐝 swarm: pre-pull images the local registry cannot serve"
  ansible.builtin.shell: |
    set -euo pipefail
    images="$(compose config --images)"
    [ -n "$images" ] || { echo "no images resolved from compose config" >&2; exit 1; }
    printf '%s\n' "$images" | xargs -n1 docker pull
  args:
    chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
    executable: /bin/bash
  register: _swarm_bypass_pull
  timeout: "{{ lookup('timeout', 600) | int }}"
  retries: 5
  delay: 30
  until: _swarm_bypass_pull is succeeded
  listen:
    - compose-up
    - swarm-deploy
  when:
    - swarm.registry.host | default('') | length > 0
    - _swarm_registry_sync is skipped
  run_once: true
  delegate_to: "{{ STACK_HOST }}"

- name: "🐝 swarm: recompute file-backed secret/config hashes on the stack host"
  ansible.builtin.script:
    cmd: >-
      python/swarm/secret_rotate.py
      --chdir {{ lookup('container', application_id, 'directories.instance') }}
  register: _swarm_secret_rotate
  changed_when: "'>>> rotate' in _swarm_secret_rotate.stderr"
  listen:
    - compose-up
    - swarm-deploy
  run_once: true
  delegate_to: "{{ STACK_HOST }}"

- name: "🐝 swarm: generate CA trust override (env + CA mount, no wrapper)"
  ansible.builtin.shell: |
    set -euo pipefail
    {{ lookup('compose_ca_inject_cmd', application_id, wrapper=False) }}
  args:
    chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
    executable: /bin/bash
  register: swarm_compose_ca_override
  retries: 7
  delay: 20
  until: swarm_compose_ca_override is succeeded
  listen:
    - compose-up
    - swarm-deploy
  when: lookup('ca_injected', application_id)
  run_once: true
  delegate_to: "{{ STACK_HOST }}"

- name: "🐝 swarm: pre-create external provider overlays for the stack"
  ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/network/create.yml') }}"
  vars:
    network_role_id: "{{ external_network_role }}"
  loop: "{{ lookup('compose_external_networks') }}"
  loop_control:
    loop_var: external_network_role
  listen:
    - compose-up
    - swarm-deploy
  when: IS_SWARM_MODE | bool

- name: "βœ… swarm: validate stack file via docker stack config"
  ansible.builtin.shell: |
    set -euo pipefail
    docker stack config {{ lookup('compose_file_args', application_id, flag='--compose-file') }} > /dev/null
  args:
    chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
    executable: /bin/bash
  register: swarm_validate
  changed_when: false
  failed_when: swarm_validate.rc != 0
  listen:
    - compose-up
    - swarm-deploy
  when: MODE_ASSERT_LOCAL | bool
  run_once: true
  delegate_to: "{{ STACK_HOST }}"

- name: "πŸš€ swarm: docker stack deploy"
  ansible.builtin.shell: |
    set -euo pipefail
    date -u +%s
    timeout {{ lookup('timeout', swarm_stack_converge_retries | int) | int }} docker stack deploy \
      {{ lookup('compose_file_args', application_id, flag='--compose-file') }} \
      --resolve-image never \
      --prune \
      --with-registry-auth \
      "{{ application_id | entity_name }}"
  args:
    chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
    executable: /bin/bash
  register: swarm_deploy_result
  changed_when: true
  listen:
    - compose-up
    - swarm-deploy
  run_once: true
  delegate_to: "{{ STACK_HOST }}"

- name: "⏳ swarm: wait until stack tasks converge"
  ansible.builtin.script: "{{ lookup('path_absolute', 'roles/sys-svc-compose/files/shell/swarm/stack_ready.sh') }}"
  environment:
    STACK: "{{ application_id | entity_name }}"
    FATAL_GRACE: "{{ swarm_stack_fatal_grace }}"
    DEPLOY_SINCE: "{{ swarm_deploy_result.stdout_lines | default([]) | first | default('') }}"
    DEPLOYED_SERVICES: "{{ swarm_deploy_result | swarm_deployed_services | join(' ') }}"
  register: _swarm_stack_ready_handler
  retries: "{{ swarm_stack_converge_retries }}"
  delay: 2
  changed_when: false
  until: _swarm_stack_ready_handler.rc in [0, 2]
  failed_when: _swarm_stack_ready_handler.rc != 0
  listen:
    - compose-up
    - swarm-deploy
  run_once: true
  delegate_to: "{{ STACK_HOST }}"

- name: "πŸ›‘οΈ swarm: bound the ports the routing mesh now publishes on every node"
  ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/svc-swarm-node/tasks/utils/ingress_guard/refresh.yml') }}"
  listen:
    - compose-up
    - swarm-deploy

- name: "πŸ”„ swarm: force task recreation so in-container state is reloaded"
  ansible.builtin.shell: |
    set -euo pipefail
    docker stack services --format '{{ "{{.Name}}" }}' {{ application_id | entity_name | quote }} \
      | xargs -r -n1 timeout {{ lookup('timeout', 900) | int }} docker service update --detach=false --force
  args:
    executable: /bin/bash
  register: _swarm_force_recreate
  changed_when: true
  timeout: "{{ lookup('timeout', 1800) | int }}"
  retries: 5
  delay: 15
  until: _swarm_force_recreate is succeeded
  listen:
    - container-recreate
  run_once: true
  delegate_to: "{{ STACK_HOST }}"