Files
computer-playbook/roles/sys-svc-compose/handlers/compose.yml
kevinveenbirkenbachandClaude Opus 5.5 a0d94cac90 fix(sys-svc-compose): resume a compose up that a failed run never completed
A deploy that renders a changed compose.yml, .env or compose override
notifies the compose-up handler. When the run fails before that handler
completes, for example because the image pull hits Docker Hub's
toomanyrequests, the notification is lost. The next run renders the same
files (changed=false), and the fallback in tasks/utils/up.yml notifies
compose-up only when no container of the stack runs, so the stack keeps
its previous images and configuration while the deploy ends with
failed=0 (reproduced with web-app-akaunting 3.1.8 -> 3.1.21).

04_files.yml now writes `.compose-up-pending` into the instance
directory whenever one of those three files changed in compose mode. The
compose-up handler removes it right after `compose up` succeeds, so a
failed pull or up leaves it in place, and up.yml notifies compose-up
whenever it finds the marker on a later run.

Comparing `compose config --hash` with the containers' config-hash
labels was ruled out: on an up-to-date stack the two differ for every
service, so it would force-recreate on every deploy.

Fixes #1169

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:29:11 +02:00

126 lines
3.8 KiB
YAML

---
- name: "🛡️ Guard instance dir at compose flush"
ansible.builtin.include_tasks: "{{ lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/guard_instance_dir.yml') }}"
listen:
- compose-up
- compose-build
- container-recreate
- name: 📝 Generate CA trust override
ansible.builtin.shell: |
set -euo pipefail
{{ lookup('compose_ca_inject_cmd', application_id) }}
args:
chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
executable: /bin/bash
register: compose_ca_override
timeout: "{{ lookup('timeout', 1200) | int }}"
retries: 7
delay: 20
until: compose_ca_override is succeeded
listen:
- compose-up
when: lookup('ca_injected', application_id)
- name: ✅ Validate Docker Compose configuration
ansible.builtin.shell: |
set -euo pipefail
compose config --quiet
args:
chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
executable: /bin/bash
register: dc_validate
changed_when: false
failed_when: dc_validate.rc != 0
listen:
- compose-up
when: MODE_ASSERT_LOCAL | bool
- name: 📥 compose pull
ansible.builtin.script:
cmd: >-
python/pull.py
--chdir {{ lookup('container', application_id, 'directories.instance') }}
--project {{ application_id | get_entity_name }}
--compose-files {{ lookup('compose_file_args', application_id, include_ca=False) | quote }}
{% if lookup('container', application_id, 'files.env') is file %}
--env-file {{ lookup('container', application_id, 'files.env') }}
{% endif %}
--lock-dir {{ DIR_COMPOSE_PULL_LOCK }}
--lock-key {{ lookup('container', application_id, 'directories.instance') | hash('sha1') }}
--ignore-buildable
register: pull
changed_when: "'pulled' in pull.stdout"
timeout: "{{ lookup('timeout', 3600) | int }}"
retries: 10
delay: 30
until: pull is succeeded
listen:
- compose-up
- name: 🔨 Build compose
ansible.builtin.shell: |
set -euo pipefail
compose build
args:
chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
executable: /bin/bash
register: compose_build
timeout: "{{ lookup('timeout', 3600) | int }}"
retries: 3
delay: 30
until: compose_build is succeeded
ignore_errors: true
listen:
- compose-build
- name: 🔨 Rebuild compose without cache once the retries are exhausted
ansible.builtin.shell: |
set -euo pipefail
compose build --no-cache --pull
args:
chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
executable: /bin/bash
register: compose_rebuild
timeout: "{{ lookup('timeout', 3600) | int }}"
retries: 3
delay: 30
until: compose_rebuild is succeeded
when: compose_build is failed
listen:
- compose-build
- name: "🌐 compose: pre-create external provider networks 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
- name: 🐳 compose up
ansible.builtin.shell: |
set -euo pipefail
compose up -d --force-recreate --remove-orphans
register: dc_up
changed_when: true
timeout: "{{ lookup('timeout', 1800) | int }}"
environment:
COMPOSE_PARALLEL_LIMIT: "{{ '4' if (DOCKER_IN_CONTAINER | bool) else omit }}"
args:
chdir: "{{ lookup('container', application_id, 'directories.instance') }}"
executable: /bin/bash
listen:
- compose-up
- container-recreate
- name: "🧹 Clear the pending compose up marker"
ansible.builtin.file:
path: "{{ COMPOSE_UP_PENDING_MARKER }}"
state: absent
listen:
- compose-up
- container-recreate