From 08a32ce4c28b212f224511152fdb946cd4dc27ec Mon Sep 17 00:00:00 2001 From: Eric Ames Date: Tue, 2 Jun 2026 07:12:30 -0700 Subject: [PATCH] docs: add example-output.md with pass and fail run transcripts (#22) Add a customer-facing reference doc showing what a real URL Egress Check run looks like end to end: an all-pass run (32 of 32 probes) and a failure run that emits the deduplicated firewall-rule punch list and exits non-zero. Linked from the README Output section and the repo-layout tree. Transcripts are cleaned of AAP-UI chrome (line numbers, per-task timestamps) and contain no customer hostnames, AAP URLs, or RHDP identifiers. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 3 + README.md | 4 + docs/example-output.md | 164 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 docs/example-output.md diff --git a/CHANGELOG.md b/CHANGELOG.md index bbdea60..65eba6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,9 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). walks the operator through the AAP CaC install interactively. Token is held in shell env only, never written to disk. - `docs/install-with-ai.md` — customer-facing doc for the AI-assisted path. +- `docs/example-output.md` — full end-to-end transcripts of both a passing run + (`32 of 32 URL probes passed.`) and a failing run (deduplicated firewall-rule + punch list + non-zero exit), linked from the README `Output` section. (#22) - README "Load into your AAP" section linking to the new docs. - `community.letsencrypt.org`, `access.redhat.com`, and `acs-mirror.azureedge.net` to the URL list. (`acs-mirror.azureedge.net` expects status `400` on a root diff --git a/README.md b/README.md index f49882c..74c4ed2 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ aap_config/ — config-as-code to load the probe into a customer' docs/ install-manual.md — laptop/desktop install path for the AAP CaC install-with-ai.md — AI-assisted install path + example-output.md — full pass/fail run transcripts ``` ## Run it @@ -104,6 +105,9 @@ Firewall rules to add (N unique): The play then fails with a non-zero exit code so the scheduled CI cron opens a tracking issue automatically. +See [docs/example-output.md](docs/example-output.md) for full end-to-end +transcripts of both a passing run and a failing run. + ## References - [Azure Virtual Appliance Routing with Ansible Automation Platform on Azure](https://access.redhat.com/articles/6972355) — Red Hat KB article documenting the egress URLs required for AAP on Azure. Most of the Azure / Google / Microsoft entries in `playbooks/files/websites.yml` come from this article. diff --git a/docs/example-output.md b/docs/example-output.md new file mode 100644 index 0000000..f98ff12 --- /dev/null +++ b/docs/example-output.md @@ -0,0 +1,164 @@ +# Example output + +What a real `URL Egress Check` run looks like, end to end. Use these as a +reference when you run the probe (from a laptop with `ansible-playbook +playbooks/main.yml`, or as the **URL Egress Check** Job Template inside your +AAP) and want to confirm yours matches. + +The transcripts below are cleaned of AAP-UI chrome (line numbers and per-task +timestamps) so they read like plain `ansible-playbook` stdout. + +## Pass — full egress, every probe reachable + +This is the result you want: all 16 URLs answer on both schemes (16 × 2 = 32 +probes), the firewall/punch-list tasks all skip, and the play exits `0`. + +``` +PLAY [Check reachability of required URLs] ************************************* + +TASK [Probe each URL on its configured scheme(s)] ***************************** +ok: [localhost -> 127.0.0.1] => (item=http://redhat.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://redhat.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://access.redhat.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://access.redhat.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://registry.redhat.io (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://registry.redhat.io (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://quay.io (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://quay.io (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://letsencrypt.org (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://letsencrypt.org (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://community.letsencrypt.org (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://community.letsencrypt.org (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://gcr.io (GET)) +ok: [localhost -> 127.0.0.1] => (item=https://gcr.io (GET)) +ok: [localhost -> 127.0.0.1] => (item=http://googleapis.com (OPTIONS)) +ok: [localhost -> 127.0.0.1] => (item=https://googleapis.com (OPTIONS)) +ok: [localhost -> 127.0.0.1] => (item=http://mcr.microsoft.com (GET)) +ok: [localhost -> 127.0.0.1] => (item=https://mcr.microsoft.com (GET)) +ok: [localhost -> 127.0.0.1] => (item=http://login.microsoftonline.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://login.microsoftonline.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://acs-mirror.azureedge.net (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://acs-mirror.azureedge.net (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://azure.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://azure.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://dynatrace.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://dynatrace.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://segment.io (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://segment.io (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://consul.segment.io (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://consul.segment.io (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=http://github.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://github.com (HEAD)) + +TASK [Build failure list] ***************************************************** +ok: [localhost] + +TASK [Show summary] *********************************************************** +ok: [localhost] => { + "msg": "32 of 32 URL probes passed." +} + +TASK [Show failed URLs with firewall hint] ************************************ +skipping: [localhost] + +TASK [Build deduplicated firewall rule list] ********************************** +skipping: [localhost] + +TASK [Show firewall rules to add] ********************************************* +skipping: [localhost] + +TASK [Fail the play if any URL probe failed] ********************************** +skipping: [localhost] + +PLAY RECAP ******************************************************************** +localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=4 rescued=0 ignored=0 +``` + +Key signals: + +- **`32 of 32 URL probes passed.`** — every host answered on every scheme it lists. +- The four conditional tasks **skip**, because there were no misses. +- `failed=0` **and** `ignored=0`, with no `...ignoring` lines. (The probe task + carries `ignore_errors: true` only so the play reaches the summary and the + final `fail` step instead of dying mid-loop — when nothing fails, nothing is + ignored.) + +## Failure — two hosts blocked, punch list emitted + +Illustrative run where the firewall blocks egress to Google (`gcr.io` and +`googleapis.com`), so all four of their probes (both hosts × both schemes) fail +with `Network is unreachable`. The play prints each miss, emits a deduplicated +punch list of the rules to add, and exits non-zero. + +``` +PLAY [Check reachability of required URLs] ************************************* + +TASK [Probe each URL on its configured scheme(s)] ***************************** +ok: [localhost -> 127.0.0.1] => (item=http://redhat.com (HEAD)) +ok: [localhost -> 127.0.0.1] => (item=https://redhat.com (HEAD)) +... (reachable hosts elided) ... +failed: [localhost -> 127.0.0.1] (item=http://gcr.io (GET)) => {"msg": "Status code was -1 and not [200]: Request failed: ", "status": -1, "url": "http://gcr.io"} +failed: [localhost -> 127.0.0.1] (item=https://gcr.io (GET)) => {"msg": "Status code was -1 and not [200]: Request failed: ", "status": -1, "url": "https://gcr.io"} +failed: [localhost -> 127.0.0.1] (item=http://googleapis.com (OPTIONS)) => {"msg": "Status code was -1 and not [404]: Request failed: ", "status": -1, "url": "http://googleapis.com"} +failed: [localhost -> 127.0.0.1] (item=https://googleapis.com (OPTIONS)) => {"msg": "Status code was -1 and not [404]: Request failed: ", "status": -1, "url": "https://googleapis.com"} +... (remaining reachable hosts elided) ... +[WARNING]: Could not match supplied host pattern, ignoring: + +TASK [Build failure list] ***************************************************** +ok: [localhost] + +TASK [Show summary] *********************************************************** +ok: [localhost] => { + "msg": "28 of 32 URL probes passed." +} + +TASK [Show failed URLs with firewall hint] ************************************ +ok: [localhost] => (item=http://gcr.io (GET)) => { + "msg": "FAIL: http://gcr.io (GET)\n expected 200 got -1 - Status code was -1 and not [200]: Request failed: \n -> Add egress rule: allow tcp/80 to gcr.io" +} +ok: [localhost] => (item=https://gcr.io (GET)) => { + "msg": "FAIL: https://gcr.io (GET)\n expected 200 got -1 - Status code was -1 and not [200]: Request failed: \n -> Add egress rule: allow tcp/443 to gcr.io" +} +ok: [localhost] => (item=http://googleapis.com (OPTIONS)) => { + "msg": "FAIL: http://googleapis.com (OPTIONS)\n expected 404 got -1 - Status code was -1 and not [404]: Request failed: \n -> Add egress rule: allow tcp/80 to googleapis.com" +} +ok: [localhost] => (item=https://googleapis.com (OPTIONS)) => { + "msg": "FAIL: https://googleapis.com (OPTIONS)\n expected 404 got -1 - Status code was -1 and not [404]: Request failed: \n -> Add egress rule: allow tcp/443 to googleapis.com" +} + +TASK [Build deduplicated firewall rule list] ********************************** +ok: [localhost] => (item=http://gcr.io (GET)) +ok: [localhost] => (item=https://gcr.io (GET)) +ok: [localhost] => (item=http://googleapis.com (OPTIONS)) +ok: [localhost] => (item=https://googleapis.com (OPTIONS)) + +TASK [Show firewall rules to add] ********************************************* +ok: [localhost] => { + "msg": "Firewall rules to add (4 unique):\n - allow tcp/443 to gcr.io\n - allow tcp/443 to googleapis.com\n - allow tcp/80 to gcr.io\n - allow tcp/80 to googleapis.com" +} + +TASK [Fail the play if any URL probe failed] ********************************** +fatal: [localhost]: FAILED! => { + "changed": false, + "msg": "4 URL probe(s) failed; see output above." +} + +PLAY RECAP ******************************************************************** +localhost : ok=6 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=1 +``` + +Key signals: + +- **`28 of 32 URL probes passed.`** — four misses, all from the two Google hosts. +- Each miss prints the URL, method, expected vs. actual status, the error, and + the exact `allow tcp/ to ` rule it needs. +- **`Firewall rules to add (4 unique):`** — the deduplicated punch list. Hand + this to whoever manages the Azure firewall; once those rules are in place, + re-run and you should land on the all-pass output above. +- `failed=1` (the final `fail` task) and the play exits non-zero — so the + weekly `url-check.yml` cron opens a tracking issue automatically. `ignored=1` + is the probe loop whose item failures were deliberately deferred to the + summary/`fail` step. + +> The reachable-host lines are elided here for length; a real run prints an +> `ok:` line for every probe, the same as the pass transcript.