Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
164 changes: 164 additions & 0 deletions docs/example-output.md
Original file line number Diff line number Diff line change
@@ -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: <urlopen error [Errno 101] Network is unreachable>", "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: <urlopen error [Errno 101] Network is unreachable>", "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: <urlopen error [Errno 101] Network is unreachable>", "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: <urlopen error [Errno 101] Network is unreachable>", "status": -1, "url": "https://googleapis.com"}
... (remaining reachable hosts elided) ...
[WARNING]: Could not match supplied host pattern, ignoring: <none>

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: <urlopen error [Errno 101] Network is unreachable>\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: <urlopen error [Errno 101] Network is unreachable>\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: <urlopen error [Errno 101] Network is unreachable>\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: <urlopen error [Errno 101] Network is unreachable>\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/<port> to <host>` 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.
Loading