Files
computer-playbook/roles
kevinveenbirkenbachandClaude Opus 4.7 c594ca099c fix(web-app-mailu): wait for redis + admin HTTP before user/token tasks
`web-app-mailu/tasks/03_manage_user.yml` and `03b_create_user_token.yml`
run `compose exec admin flask mailu user <...>` and `compose exec admin
curl http://localhost:8080/api/v1/token` immediately after the existing
wait blocks in `02_wait_for_database.yml` (mariadb domain table + admin
flask CLI ready). Under CI load the Mailu admin container is reachable
on the compose network, but two further runtime preconditions are not
guaranteed when those tasks fire:

  1. The flask CLI imports succeed (`flask routes` returns 0) before
     Mailu's admin code has finished establishing the lazy Redis
     connection that `flask mailu user` needs to commit the user.
     Symptom (run 26480944120 job 77980186049, infinito-nexus/core
     run 26480946718 job 77980074367, web-app-suitecrm):
       redis/connection.py:639 ConnectionRefusedError: [Errno 111]
     during `flask mailu user no-reply infinito.example -- <pw>`.

  2. The admin gunicorn HTTP server on :8080 is not yet listening when
     `03b_create_user_token.yml` curls `http://localhost:8080/api/v1/
     token`. Symptom (run 26480946718 job 77980074254, web-app-fediwall):
       curl: (7) Failed to connect to localhost port 8080 after 0 ms.

Add two further wait blocks after the existing flask-routes wait, both
following the same probe/rescue pattern (300-line container log dump
on timeout) the file already establishes:

  * Redis: `compose exec -T redis redis-cli ping` until stdout
    contains "PONG". Matches Mailu's own healthcheck command verbatim
    (svc-db-redis/templates/service.yml.j2 line 24) and depends on no
    extra auth -- Mailu's env.j2 declares no REDIS_PASSWORD and the
    injected svc-db-redis service runs with no `--requirepass`.
  * Admin HTTP: `compose exec -T admin curl -sS -o /dev/null -w
    '%{http_code}' http://localhost:8080/sso/login` until rc=0 and the
    captured status code matches `^[1-5][0-9][0-9]$`. Any HTTP response
    (200 / 302 / 404 / 5xx) proves gunicorn is bound; the regex form
    makes the check robust against an upstream rename of the SSO
    endpoint.

Restructure for clarity:
  * Split the original `02_wait_for_database.yml` into
    `02_wait_for_mariadb.yml` (mariadb domain table + flask CLI ready;
    flask init touches the DB) and `03_wait_for_redis.yml` (Redis PING
    + admin HTTP :8080; the HTTP API requires Redis-backed sessions).
  * Bump every subsequent task file by one step
    (03_manage_user -> 04_manage_user, 04_generate-... -> 05_..., etc.)
    so the directory order still encodes execution order at a glance.
  * Update the cross-`include_tasks` references in `01_core.yml`,
    `04_manage_user.yml` and `04a_manage_user_token.yml`. Also bump
    the trailing comment in `01_core.yml` that pointed at
    `03_manage_user.yml` to the new `04_manage_user.yml`.
  * Hoist the recurring `lookup('container', application_id,
    'directories.instance')` (12 occurrences plus a 6x indirection via
    a per-include `MAILU_DOCKER_DIR` alias previously defined in
    `01_core.yml`) into a single `MAILU_INSTANCE_DIR` constant in
    `vars/main.yml`, matching the existing `MAILU_VOLUMES_DIR` shape.
    All `chdir:` values now resolve to the same string with no extra
    indirection.

`make autoformat` and `make test` (188 tests, 158s) pass on the
restructured tree.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 10:47:51 +02:00
..
…
…