Files
computer-playbook/roles/sys-stk-front-proxy/tasks/main.yml
kevinveenbirkenbachandClaude Opus 5 1d54eb7367 fix(front-proxy): stop redeploying the proxy when a backend answers 5xx
The drift check that runs on an unchanged vhost escalated on any status the
app does not accept. That escalation notifies restart-openresty, whose handler
runs container service update --force at a measured 40.32s mean over 186 fires,
and openresty sets service_update_order stop-first, so the update stops the
serving task before the replacement starts.

A 5xx cannot justify that. OpenResty emits one only after a server block
matched and proxied, so the vhost is loaded and the proxy is not at fault.
Restarting it takes the front end away from every other vhost and leaves the
backend exactly where it was.

Require the status to be a stale-config signature as well, the same [-1, 404]
the sibling escalation has used since 2c192ab7e6. Ansible's uri returns -1 when
the TLS handshake is rejected, which is what default_server.conf.j2 does when no
server block matches, and 404 is what that same default server returns on port
80. Both mean the vhost is not loaded, which is the only case a task redeploy
repairs.

The sibling's reload arm is deliberately not copied. It reads openresty_reload,
which reload.yml registers only when a reload ran. This branch notifies nothing,
so the variable would carry the previous domain's result.

default(-1) keeps the test total. The probe has no when of its own and shares
the block guard with the escalation, so site_check is always registered and a
missing status can only mean a probe that got no response.

Across 98 job logs the block executed 6 times, answering 200 four times and 405
twice, and all six keep their outcome. The other 488 of 494 evaluations skipped
the block outright, because a fresh CI host renders every vhost as changed. A
repeat deploy of the same inventory is what reaches this branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 19:03:07 +02:00

155 lines
5.2 KiB
YAML

---
# nocheck: run-once
- name: 🧊 Freeze front proxy context before nested role loads
ansible.builtin.set_fact:
front_proxy_application_id: "{{ application_id }}"
front_proxy_domain: "{{ domain | default(lookup('domain', application_id)) }}"
front_proxy_service_key: >-
{{
webserver_proxy_service_key
| default(
lookup('config', application_id,
'services.' ~ (application_id | get_entity_name) ~ '.proxy.service_key',
application_id | get_entity_name
)
)
}}
front_proxy_http_port: >-
{{
http_port
| default(
lookup('config', application_id,
'services.' ~ (application_id | get_entity_name) ~ '.ports.local.http',
''
)
)
}}
front_proxy_internal_port: >-
{{
proxy_internal_port
| default(
lookup('config', application_id,
'services.' ~ (application_id | get_entity_name) ~ '.proxy.internal_port',
''
)
)
}}
front_proxy_domain_conf_dst_resolved: >-
{{
lookup(
'nginx',
'files.domain',
(domain | default(lookup('domain', application_id)))
)
}}
changed_when: false
- name: 🧊 Freeze the probe target for this vhost
ansible.builtin.set_fact:
front_proxy_probe_url: "{{ lookup('tls', front_proxy_domain, 'url.base') }}"
front_proxy_status_codes: "{{ lookup('applications') | accepted_status_codes(front_proxy_application_id, lookup('tls', front_proxy_domain, 'domain')) }}"
changed_when: false
- name: 📂 Include dependencies
include_role:
name: "sys-svc-nss"
when: not (run_once_sys_svc_nss | default(false) | bool)
- name: 🥾 Bootstrap Frontend Base
ansible.builtin.include_role:
name: sys-stk-front-base
- name: "📌 TLS | Apply tls layer"
include_role:
name: sys-front-tls
when: lookup('tls', front_proxy_application_id, 'enabled') | bool
vars:
application_id: "{{ front_proxy_application_id }}"
domain: "{{ front_proxy_domain }}"
- name: "📌 CSP/INJ | Apply global injections for {{ front_proxy_domain }}"
ansible.builtin.include_role:
name: sys-front-inj-all
vars:
application_id: "{{ front_proxy_application_id }}"
domain: "{{ front_proxy_domain }}"
- name: 🔍 Resolve application-specific front proxy template path
ansible.builtin.set_fact:
front_proxy_application_template: "{{ front_proxy_application_id | abs_role_path_by_application_id }}/templates/proxy.conf.j2"
- name: 🔍 Resolve front proxy template source
ansible.builtin.set_fact:
front_proxy_template_src: >-
{{
front_proxy_application_template
if (
lookup('fileglob', front_proxy_application_template, wantlist=True) | length
) > 0
else "roles/sys-svc-proxy/templates/vhost/%s.conf.j2" | format(webserver_vhost_flavour)
}}
vars:
webserver_vhost_flavour: basic
- name: "🚀 Deploy NGINX config for '{{ front_proxy_domain }}'"
stack_host_template:
src: "{{ front_proxy_template_src }}"
dest: "{{ front_proxy_domain_conf_dst_resolved }}"
vars:
application_id: "{{ front_proxy_application_id }}"
domain: "{{ front_proxy_domain }}"
http_port: "{{ front_proxy_http_port }}"
proxy_service_key: "{{ front_proxy_service_key }}"
proxy_internal_port: "{{ front_proxy_internal_port }}"
webserver_extra_configuration: ""
webserver_websocket_location: ""
webserver_client_max_body_size: "{{ lookup('config', front_proxy_application_id, 'server.client_max_body_size', '100m') }}"
register: nginx_conf
notify: reload-openresty
- block:
- name: "🔍 Check if '{{ front_proxy_domain }}' is reachable"
uri_probe:
url: "{{ front_proxy_probe_url }}"
status_code: "{{ front_proxy_status_codes }}"
register: site_check
failed_when: false
changed_when: false
- name: "🔄 Redeploy: site answered {{ site_check.status | default('none') }}"
ansible.builtin.command:
cmd: "true"
notify: restart-openresty
when:
- site_check.status | default(-1) not in front_proxy_status_codes
- site_check.status | default(-1) in front_proxy_stale_config_status_codes
when: not nginx_conf.changed
- name: "🔄 Restart Webserver for '{{ front_proxy_domain_conf_dst_resolved }}'"
ansible.builtin.meta: flush_handlers
- name: "🔍 Verify '{{ front_proxy_domain }}' is served after the reload"
uri_probe:
url: "{{ front_proxy_probe_url }}"
status_code: "{{ front_proxy_status_codes }}"
register: front_proxy_reload_check
failed_when: false
changed_when: false
when: nginx_conf.changed
- name: "🔄 Redeploy: reload left {{ front_proxy_reload_check.status | default('none') }}"
ansible.builtin.command:
cmd: "true"
notify: restart-openresty
when:
- nginx_conf.changed
- front_proxy_reload_check.status | default(-1) not in front_proxy_status_codes
- >-
not openresty_reload.changed
or front_proxy_reload_check.status | default(-1) in front_proxy_stale_config_status_codes
- name: "🔄 Flush the fallback redeploy for '{{ front_proxy_domain }}'"
ansible.builtin.meta: flush_handlers
when: nginx_conf.changed