From 0ef767ed37cc1d3ce28e31597d61d2dab642e430 Mon Sep 17 00:00:00 2001 From: Eric Ames Date: Thu, 21 May 2026 09:56:42 -0700 Subject: [PATCH] feat: failure output prints firewall-rule punch list (#12) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For each failed probe, print the corresponding `allow tcp/ to ` rule beneath the FAIL line. After the per-failure list, print a deduplicated summary of every rule the customer needs to add. Host is extracted with `.split('/') | first` so URLs with paths (e.g. `googleapis.com/foo`) still produce a clean firewall rule. Failure exit code is unchanged — the play still fails non-zero on any miss so the weekly CI cron still opens issues as before. Tested: - Success path: 16 of 16 live URLs pass, all firewall-rule tasks skip, exit 0. - Failure path (synthetic): 3 failures across http+https and a path- bearing URL — per-line rules render correctly, dedup summary lists 3 unique rules with correct port/host derivation, exit 2. Refs #12. Co-Authored-By: Claude Opus 4.7 --- CHANGELOG.md | 5 +++++ playbooks/main.yml | 34 +++++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2057225..de28e94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,6 +44,11 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). required, instead of the aspirational "defaults to [https] if omitted" (no default was ever implemented — `subelements('schemes')` hard-fails on entries missing the field). +- Failure output is now a firewall-rule punch list. Each failed probe + prints the corresponding `allow tcp/ to ` rule beneath it, + and the end of the play prints a deduplicated list of every rule the + customer needs to add. Failure exit code is unchanged (non-zero on + any miss). - Replaced the dual port-80 / port-443 loop in `playbooks/main.yml` with a single per-scheme loop driven by each entry's `schemes` field. - `playbooks/files/websites.yml` reorganized around the new schema. diff --git a/playbooks/main.yml b/playbooks/main.yml index 94a22bf..66eef57 100644 --- a/playbooks/main.yml +++ b/playbooks/main.yml @@ -31,6 +31,9 @@ {{ probe_results.results | selectattr('failed', 'equalto', true) | list }} + scheme_to_port: + http: 80 + https: 443 - name: Show summary ansible.builtin.debug: @@ -38,19 +41,36 @@ {{ (probe_results.results | length) - (url_failures | length) }} of {{ probe_results.results | length }} URL probes passed. - - name: Show failed URLs + - name: Show failed URLs with firewall hint when: url_failures | length > 0 ansible.builtin.debug: - msg: >- - FAIL: {{ item.item.1 }}://{{ item.item.0.url }} - ({{ item.item.0.method }}) - expected {{ item.item.0.status }} - got {{ item.status | default('no response') }} - {{ '- ' ~ item.msg if item.msg is defined else '' }} + msg: |- + FAIL: {{ item.item.1 }}://{{ item.item.0.url }} ({{ item.item.0.method }}) + expected {{ item.item.0.status }} got {{ item.status | default('no response') }}{{ ' - ' ~ item.msg if item.msg is defined else '' }} + -> Add egress rule: allow tcp/{{ scheme_to_port[item.item.1] }} to {{ item.item.0.url.split('/') | first }} + loop: "{{ url_failures }}" + loop_control: + label: "{{ item.item.1 }}://{{ item.item.0.url }}" + + - name: Build deduplicated firewall rule list + when: url_failures | length > 0 + ansible.builtin.set_fact: + firewall_rules: "{{ (firewall_rules | default([])) + [rule] }}" + vars: + rule: "allow tcp/{{ scheme_to_port[item.item.1] }} to {{ item.item.0.url.split('/') | first }}" loop: "{{ url_failures }}" loop_control: label: "{{ item.item.1 }}://{{ item.item.0.url }}" + - name: Show firewall rules to add + when: url_failures | length > 0 + ansible.builtin.debug: + msg: |- + Firewall rules to add ({{ firewall_rules | unique | length }} unique): + {% for r in firewall_rules | unique | sort %} + - {{ r }} + {% endfor %} + - name: Fail the play if any URL probe failed when: url_failures | length > 0 ansible.builtin.fail: