fix: valid Caddyfile handle_errors + frontend health-gate in deploys (#20)
The learning-platform (Caddy) container crash-looped since this morning's
89d3395: `Content-Type application/json` is not a valid subdirective of
`respond` — a Caddyfile parse error aborts Caddy at startup, taking the
whole app offline for authenticated users while every CI run stayed green.
Verified with caddy v2.10.0: "unrecognized subdirective 'Content-Type',
at Caddyfile:53"; the fixed file validates clean.
CI was blind to this twice over: test.yml swaps the real Caddyfile for
Caddyfile.test in the test image, and the deploy health-gate from #18
only covers PocketBase.
- Caddyfile: set Content-Type via a `header @api` directive before the
`respond`; behaviour of the JSON error response is unchanged
- infra/*/site/deploy-playbook.yml: frontend health-gate (docker exec
wget --spider on the container) with log dump + abort on failure, and
an always-on post-deploy smoke report (compose ps, proxy health,
team_members auth-methods, PocketBase log tail) for visibility behind
the auth perimeter
Closes #20
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,10 +48,13 @@
|
|||||||
handle_errors {
|
handle_errors {
|
||||||
# Don't mask API errors with the SPA shell — return a proper
|
# Don't mask API errors with the SPA shell — return a proper
|
||||||
# JSON error so the frontend can display a meaningful message.
|
# JSON error so the frontend can display a meaningful message.
|
||||||
|
# NOTE: `respond` only accepts `body`/`close` subdirectives; the
|
||||||
|
# Content-Type must be set via a separate `header` directive. An
|
||||||
|
# invalid subdirective is a Caddyfile parse error and crash-loops
|
||||||
|
# the container at startup (issue #20).
|
||||||
@api path /api/*
|
@api path /api/*
|
||||||
respond @api `{"code":{err.status_code},"message":"Backend unavailable"}` {err.status_code} {
|
header @api Content-Type application/json
|
||||||
Content-Type application/json
|
respond @api `{"code":{err.status_code},"message":"Backend unavailable"}` {err.status_code}
|
||||||
}
|
|
||||||
|
|
||||||
rewrite * /index.html
|
rewrite * /index.html
|
||||||
file_server
|
file_server
|
||||||
|
|||||||
@@ -100,3 +100,53 @@
|
|||||||
{{ pb_logs.stdout | default('') }}
|
{{ pb_logs.stdout | default('') }}
|
||||||
{{ pb_logs.stderr | default('') }}
|
{{ pb_logs.stderr | default('') }}
|
||||||
when: pb_health is failed
|
when: pb_health is failed
|
||||||
|
|
||||||
|
# The frontend container carries the SPA + Caddy. An invalid Caddyfile
|
||||||
|
# crash-loops it at startup while the PocketBase gate stays green — that
|
||||||
|
# outage (issue #20) was invisible to CI because the test job swaps in
|
||||||
|
# Caddyfile.test. Gate on the real container actually serving.
|
||||||
|
- name: Wait for the frontend (Caddy) to become healthy
|
||||||
|
ansible.builtin.command: docker exec learning-platform wget -q --spider http://127.0.0.1:80/
|
||||||
|
register: fe_health
|
||||||
|
until: fe_health.rc == 0
|
||||||
|
retries: 18
|
||||||
|
delay: 5
|
||||||
|
changed_when: false
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Collect frontend logs for diagnosis
|
||||||
|
ansible.builtin.command: docker logs --tail 80 learning-platform
|
||||||
|
register: fe_logs
|
||||||
|
when: fe_health is failed
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Abort deploy — frontend is not healthy
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: |
|
||||||
|
The frontend (Caddy) container did not become healthy after the deploy.
|
||||||
|
Recent container logs:
|
||||||
|
{{ fe_logs.stdout | default('') }}
|
||||||
|
{{ fe_logs.stderr | default('') }}
|
||||||
|
when: fe_health is failed
|
||||||
|
|
||||||
|
# Observability behind the auth perimeter: surface the end-to-end state
|
||||||
|
# in the CI log on every deploy.
|
||||||
|
- name: Post-deploy smoke report
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
cd /opt/learning-platform
|
||||||
|
echo '--- docker compose ps ---'
|
||||||
|
docker compose ps
|
||||||
|
echo '--- frontend -> pocketbase proxy health ---'
|
||||||
|
docker exec learning-platform wget -q -O - http://127.0.0.1:80/api/health || echo 'PROXY HEALTH FAILED'
|
||||||
|
echo '--- team_members auth methods (OIDC provider present?) ---'
|
||||||
|
docker exec pocketbase-learning wget -q -O - http://127.0.0.1:8090/api/collections/team_members/auth-methods || echo 'AUTH-METHODS FAILED'
|
||||||
|
echo '--- pocketbase logs (tail 30) ---'
|
||||||
|
docker compose logs --tail=30 pocketbase-learning
|
||||||
|
register: smoke_report
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Show smoke report
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ smoke_report.stdout_lines }}"
|
||||||
|
|||||||
@@ -100,3 +100,53 @@
|
|||||||
{{ pb_logs.stdout | default('') }}
|
{{ pb_logs.stdout | default('') }}
|
||||||
{{ pb_logs.stderr | default('') }}
|
{{ pb_logs.stderr | default('') }}
|
||||||
when: pb_health is failed
|
when: pb_health is failed
|
||||||
|
|
||||||
|
# The frontend container carries the SPA + Caddy. An invalid Caddyfile
|
||||||
|
# crash-loops it at startup while the PocketBase gate stays green — that
|
||||||
|
# outage (issue #20) was invisible to CI because the test job swaps in
|
||||||
|
# Caddyfile.test. Gate on the real container actually serving.
|
||||||
|
- name: Wait for the frontend (Caddy) to become healthy
|
||||||
|
ansible.builtin.command: docker exec learning-platform wget -q --spider http://127.0.0.1:80/
|
||||||
|
register: fe_health
|
||||||
|
until: fe_health.rc == 0
|
||||||
|
retries: 18
|
||||||
|
delay: 5
|
||||||
|
changed_when: false
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: Collect frontend logs for diagnosis
|
||||||
|
ansible.builtin.command: docker logs --tail 80 learning-platform
|
||||||
|
register: fe_logs
|
||||||
|
when: fe_health is failed
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Abort deploy — frontend is not healthy
|
||||||
|
ansible.builtin.fail:
|
||||||
|
msg: |
|
||||||
|
The frontend (Caddy) container did not become healthy after the deploy.
|
||||||
|
Recent container logs:
|
||||||
|
{{ fe_logs.stdout | default('') }}
|
||||||
|
{{ fe_logs.stderr | default('') }}
|
||||||
|
when: fe_health is failed
|
||||||
|
|
||||||
|
# Observability behind the auth perimeter: surface the end-to-end state
|
||||||
|
# in the CI log on every deploy.
|
||||||
|
- name: Post-deploy smoke report
|
||||||
|
ansible.builtin.shell: |
|
||||||
|
cd /opt/learning-platform
|
||||||
|
echo '--- docker compose ps ---'
|
||||||
|
docker compose ps
|
||||||
|
echo '--- frontend -> pocketbase proxy health ---'
|
||||||
|
docker exec learning-platform wget -q -O - http://127.0.0.1:80/api/health || echo 'PROXY HEALTH FAILED'
|
||||||
|
echo '--- team_members auth methods (OIDC provider present?) ---'
|
||||||
|
docker exec pocketbase-learning wget -q -O - http://127.0.0.1:8090/api/collections/team_members/auth-methods || echo 'AUTH-METHODS FAILED'
|
||||||
|
echo '--- pocketbase logs (tail 30) ---'
|
||||||
|
docker compose logs --tail=30 pocketbase-learning
|
||||||
|
register: smoke_report
|
||||||
|
changed_when: false
|
||||||
|
failed_when: false
|
||||||
|
|
||||||
|
- name: Show smoke report
|
||||||
|
ansible.builtin.debug:
|
||||||
|
msg: "{{ smoke_report.stdout_lines }}"
|
||||||
|
|||||||
Reference in New Issue
Block a user