From 1923c80298ded950d15e34f9f407b5974c10bcd1 Mon Sep 17 00:00:00 2001 From: Amritanshu Date: Tue, 22 Sep 2026 23:44:54 +0530 Subject: [PATCH] Stop the health wait from failing a healthy deploy, and clear a stale address The first real deploy left the site on 502 and this is why. podman builds an OCI image by default and drops the Docker HEALTHCHECK instruction, so the app container has no State.Health at all; the role waited on that field, exhausted its retries and failed the play, so the caddy role never ran and Caddy kept proxying menu.hopsngrains.com to the old nginx container that is now gone. The wait now asks the app itself: docker exec node -e fetch /healthz, which needs no healthcheck and no DNS. And because the address may still be claimed by the snippet the retired nginx-menu playbook wrote, the caddy role drops that marker before writing its own - two blocks for one address make Caddy refuse the config, and a refused reload quietly leaves the old proxy in place. check-site also asked for the font the old design used, so it would have failed a healthy site; it checks one we ship. --- ansible/roles/caddy/tasks/main.yaml | 11 ++++++++ ansible/roles/menu-app/tasks/main.yaml | 35 +++++++++++++++----------- scripts/check-site.ts | 2 +- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/ansible/roles/caddy/tasks/main.yaml b/ansible/roles/caddy/tasks/main.yaml index e539d3e..b36ee25 100644 --- a/ansible/roles/caddy/tasks/main.yaml +++ b/ansible/roles/caddy/tasks/main.yaml @@ -11,6 +11,17 @@ Caddy container and its config, before this one. when: not menu_caddyfile.stat.exists +# The Site's web address may still be claimed by an older snippet: the retired +# nginx-menu playbook wrote one per hostname. Two blocks for one address make +# Caddy refuse the whole config, and a refused reload leaves the old proxy in +# place, which is a 502 once the old container is gone. +- name: Drop any older snippet claiming this web address + blockinfile: + path: '{{ caddyfile_path }}' + marker: '# {mark} Ansible managed Caddy snippet for {{ host }}' + state: absent + notify: Reload Caddy + # blockinfile replaces whatever sits between its markers, so redeploying updates # the snippet in place rather than appending a second one. - name: Point the Site's web address at the app diff --git a/ansible/roles/menu-app/tasks/main.yaml b/ansible/roles/menu-app/tasks/main.yaml index 039f295..3399fcf 100644 --- a/ansible/roles/menu-app/tasks/main.yaml +++ b/ansible/roles/menu-app/tasks/main.yaml @@ -61,27 +61,32 @@ mode: '0600' when: menu_site_env.stat.exists -- name: Wait for the app to report healthy - # The image's own HEALTHCHECK fetches /healthz inside the container. Polling - # that is deliberate: a container name resolves only on the Docker network, so - # the host cannot fetch http://{{ docker_container }}:3000/healthz. - docker_container_info: - name: '{{ docker_container }}' +# Deliberately not the image's HEALTHCHECK: podman builds an OCI image by +# default and drops the Docker HEALTHCHECK instruction, so State.Health is absent +# and a wait on it fails on a perfectly healthy container — which stops the play +# before Caddy is ever pointed at the app. +- name: Wait for the app to answer inside its own container + ansible.builtin.command: + argv: + - docker + - exec + - '{{ docker_container }}' + - node + - -e + - "fetch('http://127.0.0.1:3000/healthz').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))" register: menu_health changed_when: false retries: 20 - delay: 5 - until: >- - menu_health.container is defined and - menu_health.container.State.Health is defined and - menu_health.container.State.Health.Status == 'healthy' + delay: 3 + until: menu_health.rc == 0 + failed_when: false -- name: Fail when the app never becomes healthy +- name: Fail when the app never answers fail: msg: >- - {{ docker_container }} did not report healthy within 100s. Check - `docker logs {{ docker_container }}`. - when: menu_health.container.State.Health.Status | default('unknown') != 'healthy' + {{ docker_container }} did not answer /healthz within a minute. Its logs + say why: docker logs {{ docker_container }} + when: menu_health.rc != 0 - name: Check the data directory for integrity problems # Reads the same directory the container writes, as the same user, so a diff --git a/scripts/check-site.ts b/scripts/check-site.ts index b0ea6d3..5ee7fe9 100644 --- a/scripts/check-site.ts +++ b/scripts/check-site.ts @@ -55,7 +55,7 @@ await checkPage('/robots.txt', ['Disallow: /admin']); await checkPage('/admin/login', ['type="password"']); await checkPage('/assets/styles.css', ['--copper']); await checkPage('/assets/app.js', ['data-zoom']); -await checkPage('/assets/fonts/archivo-latin.woff2', []); +await checkPage('/assets/fonts/playfair-display-latin.woff2', []); const missingPage = await request('/definitely-not-here'); record('GET /definitely-not-here', missingPage.status === 404, `status ${missingPage.status}`);