From 517d013df37500bc9c87f6ad931f5ef2e674fffa Mon Sep 17 00:00:00 2001 From: Kevin Velarde Date: Sat, 8 Aug 2026 10:53:07 -0600 Subject: [PATCH 1/5] Initial version of repo connectivity verification --- CLAUDE.md | 3 +- README.md | 16 +++- playbooks/verify.yml | 3 + playbooks/verify_gateway.yml | 13 +++ roles/common/CLAUDE.md | 32 ++++++- roles/common/tasks/verify-connectivity.yml | 82 ++++++++++++++++++ roles/common/tasks/verify-results.yml | 7 +- roles/common/vars/main.yml | 94 +++++++++++++++++++++ roles/gateway/CLAUDE.md | 4 + roles/gateway/tasks/update-release-file.yml | 5 ++ roles/gateway/tasks/verify-gateway.yml | 30 +++++++ roles/mongodb/tasks/verify-mongodb.yml | 7 ++ roles/platform/tasks/verify-platform.yml | 17 ++++ roles/redis/tasks/verify-redis.yml | 7 ++ 14 files changed, 313 insertions(+), 7 deletions(-) create mode 100644 playbooks/verify_gateway.yml create mode 100644 roles/common/tasks/verify-connectivity.yml create mode 100644 roles/common/vars/main.yml create mode 100644 roles/gateway/tasks/verify-gateway.yml diff --git a/CLAUDE.md b/CLAUDE.md index cc948fdc..c0ab8da3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -45,10 +45,11 @@ Also requires the `jmespath` Python module on the control node. | `certify_redis.yml` | `itential.deployer.certify_redis` | Generate Redis/Sentinel installation certification reports | | `certify_mongodb.yml` | `itential.deployer.certify_mongodb` | Generate MongoDB installation certification reports | | `certify_platform.yml` | `itential.deployer.certify_platform` | Generate Platform installation certification reports | -| `verify.yml` | `itential.deployer.verify` | Pre-install environment verification (OS, HW specs, proxy) for all components | +| `verify.yml` | `itential.deployer.verify` | Pre-install environment verification (OS, HW specs, proxy, required repository connectivity) for all components | | `verify_redis.yml` | `itential.deployer.verify_redis` | Pre-install verification for Redis hosts | | `verify_mongodb.yml` | `itential.deployer.verify_mongodb` | Pre-install verification for MongoDB hosts | | `verify_platform.yml` | `itential.deployer.verify_platform` | Pre-install verification for Platform hosts | +| `verify_gateway.yml` | `itential.deployer.verify_gateway` | Pre-install verification (required repository connectivity only) for Gateway hosts | | `download_packages_site.yml` | `itential.deployer.download_packages_site` | Download all packages for offline install (Platform stack + Gateway) | | `download_packages_platform_site.yml` | `itential.deployer.download_packages_platform_site` | Download all packages for Platform stack offline install | | `download_packages_platform.yml` | `itential.deployer.download_packages_platform` | Download Platform packages for offline install | diff --git a/README.md b/README.md index c3ace9d8..c7b69c5a 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,17 @@ If internal YUM repositories are used, refer to the > proxy or other such method to restrict access, this list may not represent the final URLs that are > required. +To verify connectivity to these repositories from the target hosts (excluding the Ansible +Control Node rows, which are the control node's own responsibility), run the `verify` playbook: + +```bash +ansible-playbook itential.deployer.verify -i +``` + +This checks each component's target hosts (`gateway`, `platform*`, `mongodb*`, `redis_master`/ +`redis_replica`) against the rows in the table above. See +[Confirm Requirements](#confirm-requirements) for details. + ### Ports and Networking In a clustered environment where components are installed on more than one host, the following @@ -390,7 +401,7 @@ applicable, IAG). For more information, refer to the [Itential Dependencies] pag nodes. - **SSH Access**: The control node must have SSH connectivity to all managed nodes. -The deployer includes a playbook that can be used to confirm the environment is suitable and ready for installation. +The deployer includes a playbook that can be used to confirm the environment is suitable and ready for installation. This includes checking outbound connectivity from each component's target hosts to the URLs listed in the [Required Public Repositories](#required-public-repositories) table above. ```bash # Verify everything @@ -404,6 +415,9 @@ ansible-playbook -i itential.deployer.verify_mongodb # Verify Platform ansible-playbook -i itential.deployer.verify_platform + +# Verify Gateway +ansible-playbook -i itential.deployer.verify_gateway ``` **ⓘ Note:** diff --git a/playbooks/verify.yml b/playbooks/verify.yml index ab15cb1f..3732a81c 100644 --- a/playbooks/verify.yml +++ b/playbooks/verify.yml @@ -10,3 +10,6 @@ - name: Verify Platform Installation import_playbook: itential.deployer.verify_platform + +- name: Verify Gateway Installation + import_playbook: itential.deployer.verify_gateway diff --git a/playbooks/verify_gateway.yml b/playbooks/verify_gateway.yml new file mode 100644 index 00000000..66ffc4fe --- /dev/null +++ b/playbooks/verify_gateway.yml @@ -0,0 +1,13 @@ +# Copyright (c) 2026, Itential, Inc +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) +--- + +- name: Run Gateway Verification Tasks + hosts: gateway + gather_facts: true + become: true + tasks: + - name: Verify Gateway Installation # noqa run-once + ansible.builtin.import_role: + name: itential.deployer.gateway + tasks_from: verify-gateway diff --git a/roles/common/CLAUDE.md b/roles/common/CLAUDE.md index 524d3479..1d3aa98e 100644 --- a/roles/common/CLAUDE.md +++ b/roles/common/CLAUDE.md @@ -2,13 +2,17 @@ ## Purpose -Provides shared default variables consumed by all other roles. Has no `main.yml` task entry point — it is imported by playbooks solely to inject its defaults into the variable scope. Also contains the shared `verify-host.yml` task file used by the `verify_*` playbooks. +Provides shared default variables consumed by all other roles. Has no `main.yml` task entry point — it is imported by playbooks solely to inject its defaults into the variable scope. Also contains the shared `verify-host.yml`, `verify-connectivity.yml`, and `verify-results.yml` task files used by the `verify_*` playbooks. ## Entry Point Tasks -There is no `tasks/main.yml`. The only task file is: +There is no `tasks/main.yml`. The task files are: -- `tasks/verify-host.yml` — imported by `verify_redis`, `verify_mongodb`, and `verify_platform` task files via `tasks_from:`. It is not called by any role's `main.yml`. +- `tasks/verify-host.yml` — imported by `verify-mongodb`, `verify-redis`, and `verify-platform` task files via `tasks_from:`. Checks OS/arch/HW specs/proxy and initializes `validation_errors`. Not called by Gateway's `verify-gateway.yml` (Gateway has no `gateway_hw_specs`/HW requirements defined yet). +- `tasks/verify-connectivity.yml` — imported by `verify-mongodb`, `verify-redis`, `verify-platform`, and `verify-gateway` (all four components) via `tasks_from:`. Checks outbound connectivity to the URLs in `common_required_repositories` (`vars/main.yml`) that match the caller's `component_name`. See "verify-connectivity.yml Logic" below. +- `tasks/verify-results.yml` — imported by `verify-mongodb`, `verify-redis`, and `verify-platform` (not Gateway, which does its own minimal assert) to print `validation_errors` and assert `cpu_validation`/`memory_validation`/`disk_validation`/`proxy_validation`/`connectivity_validation` all passed. + +None of these task files are called by any role's `main.yml`. ## verify-host.yml Logic @@ -30,6 +34,24 @@ Execution order: The hardware specs dicts (e.g., `redis_hw_specs`, `platform_hw_specs`, `mongodb_hw_specs`) are defined in `roles//vars/platform-release-.yml` and keyed by `env` value. +## verify-connectivity.yml Logic + +Expects one variable from the caller: + +| Variable | Example | Purpose | +|----------|---------|---------| +| `component_name` | `"MongoDB"` | Selects the matching rows from `common_required_repositories` | + +`common_required_repositories` (`vars/main.yml`, auto-loaded whenever the `common` role is used) mirrors the "Required Public Repositories" table in `README.md`, excluding the Ansible Control Node rows. Each entry has a `component`, `url`, `type` (`bare` or `path`), optional `check_target`, and `notes`. + +Execution order: +1. Filter `common_required_repositories` down to entries matching `component_name` +2. Check baseline reachability of every entry's bare `url` via `ansible.builtin.uri` — any real HTTP status (100-599) counts as reachable; only a connection-level failure (DNS/TCP/TLS/timeout, which `fetch_url` reports as `status: -1`) counts as unreachable. Append failures to `validation_errors`. +3. For entries with `type: path` that passed step 2, request the specific `check_target` resource and expect an actual `[200, 301, 302]` response. Append failures to `validation_errors`. +4. Assert both checks passed (`ignore_errors: true`, registers `connectivity_validation`), following the same pattern as `cpu_validation`/`memory_validation`/etc. in `verify-host.yml`. + +Called from `verify-mongodb.yml`, `verify-redis.yml`, and `verify-platform.yml` (which also calls it a second time with `component_name: "Vault"`, gated on `platform_configure_vault | bool`, since this collection doesn't have its own Vault role/host group but Platform is what integrates with it) and `verify-gateway.yml`. + ## Key Variables | Variable | Default | Source File | Purpose | @@ -40,6 +62,7 @@ The hardware specs dicts (e.g., `redis_hw_specs`, `platform_hw_specs`, `mongodb_ | `offline_target_node_root` | `/var/tmp` | `defaults/main/offline.yml` | Root on target nodes for offline package staging. | | `offline_control_node_root` | `{{ playbook_dir }}/files` | `defaults/main/offline.yml` | Root on control node where offline packages are staged. | | `offline_itential_packages_path` | `itential_packages/{{ ansible_distribution \| lower }}_{{ ansible_distribution_major_version }}` | `defaults/main/offline.yml` | OS-specific subdirectory under the offline roots. | +| `common_required_repositories` | See `vars/main.yml` | `vars/main.yml` | List of required public repositories per component, consumed by `verify-connectivity.yml`. Keep in sync with the README table. | ## Dependencies / Assumptions @@ -52,3 +75,6 @@ The hardware specs dicts (e.g., `redis_hw_specs`, `platform_hw_specs`, `mongodb_ - `offline_install_enabled` defaults to `false` here but the `download_packages_*` playbooks override it to `false` explicitly at the play level — the download playbooks always run online even when deploying to offline targets. - The `common_itential_release_file` check in the `os` role skips OS package installation if the file already exists, making the `os` role effectively idempotent for re-runs. - `verify-host.yml` uses `ignore_errors: true` on individual assertions and collects them, then does a final combined assert. This means a failing host will show all failures rather than stopping at the first. +- `verify-connectivity.yml`'s baseline check deliberately does NOT use `ansible.builtin.uri`'s `status_code: -1` — that is not a wildcard; `status_code` is a literal list of acceptable HTTP codes (default `[200]`). Instead it passes `status_code: "{{ range(100, 600) | list }}"` so any real HTTP response passes, and relies on `fetch_url` setting `status: -1` (which is never in that range) to detect genuine connection failures. +- `verify-connectivity.yml` deliberately avoids requiring the `git` binary (e.g. via `git ls-remote`) for the `gitlab.com`/`github.com` rows, since verification runs pre-install and `git` may not be installed on the target yet. It uses plain HTTPS `check_target` requests instead (a real gitlab.com group page, and a `codeload.github.com` tarball URL that also exercises the `github.com` → `codeload.github.com` redirect). +- `verify-results.yml` previously asserted on `platform_validation is not failed`, but nothing ever registered a variable by that name (Platform's own TLS checks register `platform_tls_dir_validation`/`platform_tls_contents_validation` instead) — this made `verify_redis`/`verify_mongodb`/`verify_platform` fail with "the 'failed' test expects a dictionary" on an undefined variable. It was replaced with `connectivity_validation is not failed`. diff --git a/roles/common/tasks/verify-connectivity.yml b/roles/common/tasks/verify-connectivity.yml new file mode 100644 index 00000000..72801954 --- /dev/null +++ b/roles/common/tasks/verify-connectivity.yml @@ -0,0 +1,82 @@ +# Copyright (c) 2026, Itential, Inc +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +# Verifies outbound connectivity to the public repositories a component needs, as listed in +# the "Required Public Repositories" table in README.md (excluding the Ansible Control Node +# rows, which are the control node's responsibility, not a target host's). +# +# Expects one variable from the caller: +# - component_name: must match a "component" value in common_required_repositories +# (roles/common/vars/main.yml), e.g. "Gateway", "Platform", "MongoDB", "Redis", "Vault" +# +# Appends a message to validation_errors for every failure, then sets connectivity_validation +# via an ignore_errors assert, following the same pattern as verify-host.yml. + +- name: Select required repositories for this component + ansible.builtin.set_fact: + component_required_repositories: "{{ common_required_repositories + | selectattr('component', 'equalto', component_name) | list }}" + +- name: Check baseline reachability of required repositories + ansible.builtin.uri: + url: "{{ item.url }}" + method: GET + # Accept any real HTTP status code (100-599) as "reachable" -- fetch_url only returns + # status -1 when the request never got a response at all (DNS/TCP/TLS/timeout failure). + status_code: "{{ range(100, 600) | list }}" + validate_certs: true + timeout: 10 + register: repository_reachability + loop: "{{ component_required_repositories }}" + loop_control: + label: "{{ item.url }}" + ignore_errors: true + +- name: Add unreachable repository errors to list + ansible.builtin.set_fact: + validation_errors: "{{ validation_errors + ['Connectivity: ' ~ item.item.url ~ ' (' ~ + item.item.notes ~ ') is unreachable: ' ~ (item.msg | default('unknown error'))] }}" + loop: "{{ repository_reachability.results }}" + loop_control: + label: "{{ item.item.url }}" + when: item is failed + +- name: Identify repositories needing a resource-specific check + ansible.builtin.set_fact: + repositories_needing_path_check: "{{ repository_reachability.results + | rejectattr('failed') + | map(attribute='item') + | selectattr('type', 'equalto', 'path') + | list }}" + +- name: Check resource-specific access for path-type repositories + ansible.builtin.uri: + url: "{{ item.check_target }}" + method: GET + status_code: [200, 301, 302] + validate_certs: true + timeout: 10 + register: repository_path_check + loop: "{{ repositories_needing_path_check }}" + loop_control: + label: "{{ item.check_target }}" + ignore_errors: true + +- name: Add resource-specific check failures to list + ansible.builtin.set_fact: + validation_errors: "{{ validation_errors + ['Connectivity: ' ~ item.item.check_target ~ ' (' ~ + item.item.notes ~ ') did not return a successful response'] }}" + loop: "{{ repository_path_check.results | default([]) }}" + loop_control: + label: "{{ item.item.check_target }}" + when: item is failed + +- name: Verify that repository connectivity checks passed + ansible.builtin.assert: + that: + - repository_reachability.results | selectattr('failed') | list | length == 0 + - repository_path_check.results | default([]) | selectattr('failed') | list | length == 0 + fail_msg: "One or more required repository connectivity checks failed! See above." + quiet: true + ignore_errors: true + register: connectivity_validation diff --git a/roles/common/tasks/verify-results.yml b/roles/common/tasks/verify-results.yml index 4f5d25ee..01aed4d2 100644 --- a/roles/common/tasks/verify-results.yml +++ b/roles/common/tasks/verify-results.yml @@ -6,7 +6,10 @@ - name: Display failed validation results ansible.builtin.debug: msg: "{{ validation_errors }}" - when: cpu_validation is failed or memory_validation is failed or disk_validation is failed or platform_validation is failed + when: cpu_validation is failed or + memory_validation is failed or + disk_validation is failed or + connectivity_validation is failed # Assert that none of the tests failed - name: Verify that all tests passed @@ -16,6 +19,6 @@ - "memory_validation is not failed" - "disk_validation is not failed" - "proxy_validation is not failed" - - "platform_validation is not failed" + - "connectivity_validation is not failed" fail_msg: "See above, assertions not passed! ✗" success_msg: "All assertions passed! ✓" diff --git a/roles/common/vars/main.yml b/roles/common/vars/main.yml new file mode 100644 index 00000000..15e201b7 --- /dev/null +++ b/roles/common/vars/main.yml @@ -0,0 +1,94 @@ +# Copyright (c) 2026, Itential, Inc +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +# Mirrors the "Required Public Repositories" table in README.md, excluding the +# "Ansible Control Node" rows (those are the control node's responsibility, not a target +# host's, and are not checked by verify-connectivity.yml). Keep this list in sync with +# README.md if that table changes. +# +# component: matches the component_name passed in to verify-connectivity.yml +# url: the bare location from the README table +# type: "bare" checks only that the URL is reachable (any HTTP response counts). +# "path" additionally checks a real, specific resource (check_target) that the +# component actually needs, expecting a genuine successful response. +# check_target: required when type is "path" +# notes: carried over from the README table's Notes column +common_required_repositories: + - component: "Gateway" + url: "https://registry.aws.itential.com" + type: bare + notes: "Itential packages" + - component: "Gateway" + url: "https://galaxy.ansible.com" + type: bare + notes: "Community Ansible collections" + - component: "Gateway" + url: "https://pypi.org" + type: bare + notes: "Python modules" + + - component: "Platform" + url: "https://registry.aws.itential.com" + type: bare + notes: "Itential packages" + - component: "Platform" + url: "https://registry.npmjs.org" + type: bare + notes: "Core NPM packages" + - component: "Platform" + url: "https://pypi.org" + type: bare + notes: "Python modules" + - component: "Platform" + url: "https://gitlab.com" + type: path + check_target: "https://gitlab.com/itentialopensource/adapters" + notes: "Platform Opensource Adapters NodeJS source repository" + + - component: "MongoDB" + url: "https://repo.mongodb.org" + type: path + check_target: "https://repo.mongodb.org/yum/redhat/" + notes: "MongoDB YUM RPMs" + - component: "MongoDB" + url: "https://www.mongodb.org" + type: bare + notes: "MongoDB YUM RPMs" + - component: "MongoDB" + url: "https://pgp.mongodb.org" + type: bare + notes: "MongoDB YUM repository GPG Key" + - component: "MongoDB" + url: "https://pgp.mongodb.com" + type: bare + notes: "MongoDB YUM repository GPG Key" + - component: "MongoDB" + url: "https://pypi.org" + type: bare + notes: "Python modules" + + - component: "Redis" + url: "http://rpms.remirepo.net" + type: path + check_target: "http://rpms.remirepo.net/enterprise/" + notes: "Redis YUM RPMs" + - component: "Redis" + url: "https://dl.fedoraproject.org" + type: path + check_target: "https://dl.fedoraproject.org/pub/epel/" + notes: "EPEL YUM RPMs" + - component: "Redis" + url: "https://github.com" + type: path + check_target: "https://codeload.github.com/redis/redis/tar.gz/refs/heads/unstable" + notes: "Redis source packages" + - component: "Redis" + url: "https://codeload.github.com" + type: bare + notes: "Redis source packages" + + - component: "Vault" + url: "https://rpm.releases.hashicorp.com" + type: path + check_target: "https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo" + notes: "Vault YUM RPMs" diff --git a/roles/gateway/CLAUDE.md b/roles/gateway/CLAUDE.md index 77d5a282..9b0ec9f7 100644 --- a/roles/gateway/CLAUDE.md +++ b/roles/gateway/CLAUDE.md @@ -33,6 +33,10 @@ Installs and configures Itential Automation Gateway (IAG). Handles Python virtua 25. Remove temp working directory 26. `always` block: remove build packages that were installed; assert service is active +## Other Entry Points + +- `tasks/verify-gateway.yml` (invoked by `playbooks/verify_gateway.yml`, tags_from `verify-gateway`) — checks connectivity to Gateway's required public repositories (`common_required_repositories` filtered to `component: "Gateway"`) via `common:verify-connectivity`. Unlike the other components' verify flows, it does not call `common:verify-host` (no `gateway_hw_specs` exists yet), so it skips OS/CPU/RAM/disk/proxy validation entirely. + ## Key Variables ### gateway.yml defaults diff --git a/roles/gateway/tasks/update-release-file.yml b/roles/gateway/tasks/update-release-file.yml index c387bb6b..26ae1907 100644 --- a/roles/gateway/tasks/update-release-file.yml +++ b/roles/gateway/tasks/update-release-file.yml @@ -4,6 +4,7 @@ - name: Update release file with IAG version ansible.builtin.lineinfile: path: "{{ common_itential_release_file }}" + regexp: "^GATEWAY=" line: "GATEWAY={{ gateway_release }}" mode: "0666" create: true @@ -18,6 +19,7 @@ - name: Update release file with Python version ansible.builtin.lineinfile: path: "{{ common_itential_release_file }}" + regexp: "^PYTHON=" line: "PYTHON={{ python_installed_version.stdout }}" mode: "0666" create: true @@ -32,6 +34,7 @@ - name: Update release file with Pip version ansible.builtin.lineinfile: path: "{{ common_itential_release_file }}" + regexp: "^PIP=" line: "PIP={{ pip_installed_version.stdout }}" mode: "0666" create: true @@ -53,6 +56,7 @@ - name: Update release file with Ansible version ansible.builtin.lineinfile: path: "{{ common_itential_release_file }}" + regexp: "^ANSIBLE=" line: "ANSIBLE={{ gateway_ansible_version }}" mode: "0666" create: true @@ -61,6 +65,7 @@ - name: Update release file with Nornir version ansible.builtin.lineinfile: path: "{{ common_itential_release_file }}" + regexp: "^NORNIR=" line: "NORNIR=true" mode: "0666" create: true diff --git a/roles/gateway/tasks/verify-gateway.yml b/roles/gateway/tasks/verify-gateway.yml new file mode 100644 index 00000000..e88157e5 --- /dev/null +++ b/roles/gateway/tasks/verify-gateway.yml @@ -0,0 +1,30 @@ +# Copyright (c) 2026, Itential, Inc +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +# Unlike verify-mongodb.yml/verify-redis.yml/verify-platform.yml, this does not call +# common:verify-host — Gateway has no documented hardware-spec requirements (gateway_hw_specs) +# in this collection yet. This task file only verifies connectivity to Gateway's required +# public repositories. + +- name: Initialize validation errors list + ansible.builtin.set_fact: + validation_errors: [] + +- name: Verify connectivity to required public repositories + ansible.builtin.include_role: + name: common + tasks_from: verify-connectivity + vars: + component_name: "Gateway" + +- name: Display failed validation results + ansible.builtin.debug: + msg: "{{ validation_errors }}" + when: connectivity_validation is failed + +- name: Verify that all tests passed + ansible.builtin.assert: + that: + - "connectivity_validation is not failed" + fail_msg: "See above, assertions not passed! ✗" + success_msg: "All assertions passed! ✓" diff --git a/roles/mongodb/tasks/verify-mongodb.yml b/roles/mongodb/tasks/verify-mongodb.yml index aebf00a5..561eb155 100644 --- a/roles/mongodb/tasks/verify-mongodb.yml +++ b/roles/mongodb/tasks/verify-mongodb.yml @@ -20,6 +20,13 @@ # mongodb specific verification tasks go here... +- name: Verify connectivity to required public repositories + ansible.builtin.include_role: + name: common + tasks_from: verify-connectivity + vars: + component_name: "MongoDB" + - name: Verify Platform host requirements ansible.builtin.include_role: name: common diff --git a/roles/platform/tasks/verify-platform.yml b/roles/platform/tasks/verify-platform.yml index e4204aaa..f6b7b867 100644 --- a/roles/platform/tasks/verify-platform.yml +++ b/roles/platform/tasks/verify-platform.yml @@ -19,6 +19,23 @@ hw_specs_var_name: "platform_hw_specs" # platform specific verification tasks go here... +- name: Verify connectivity to required public repositories + ansible.builtin.include_role: + name: common + tasks_from: verify-connectivity + vars: + component_name: "Platform" + +# Vault itself isn't installed by this collection, but Platform is the component that +# integrates with it, so its repository is checked here when Vault integration is enabled. +- name: Verify connectivity to Vault's required public repositories + ansible.builtin.include_role: + name: common + tasks_from: verify-connectivity + vars: + component_name: "Vault" + when: platform_configure_vault | bool + - name: Verify TLS certs for platform webserver are defined ansible.builtin.assert: that: platform_https_pki_src_dir is defined and platform_https_pki_src_dir | length > 0 diff --git a/roles/redis/tasks/verify-redis.yml b/roles/redis/tasks/verify-redis.yml index 09391d2e..bf32b08b 100644 --- a/roles/redis/tasks/verify-redis.yml +++ b/roles/redis/tasks/verify-redis.yml @@ -20,6 +20,13 @@ # redis specific verification tasks go here... +- name: Verify connectivity to required public repositories + ansible.builtin.include_role: + name: common + tasks_from: verify-connectivity + vars: + component_name: "Redis" + - name: Verify Platform host requirements ansible.builtin.include_role: name: common From d33c38d3d995c3b204a67d3e51d4eb325a0c8c48 Mon Sep 17 00:00:00 2001 From: Kevin Velarde Date: Tue, 11 Aug 2026 11:54:12 -0600 Subject: [PATCH 2/5] Refactor main verify playbook to run for all components --- playbooks/verify.yml | 15 ++++++++++ roles/common/CLAUDE.md | 16 +++++++---- roles/common/tasks/verify-host.yml | 18 ++++++++++-- roles/common/tasks/verify-results.yml | 38 ++++++++++++++++++++------ roles/gateway/CLAUDE.md | 2 +- roles/gateway/tasks/verify-gateway.yml | 12 ++++++++ 6 files changed, 84 insertions(+), 17 deletions(-) diff --git a/playbooks/verify.yml b/playbooks/verify.yml index 3732a81c..05957c60 100644 --- a/playbooks/verify.yml +++ b/playbooks/verify.yml @@ -13,3 +13,18 @@ - name: Verify Gateway Installation import_playbook: itential.deployer.verify_gateway + +# Each component's own final assert is non-fatal (ignore_errors) so that a failure in one +# component (e.g. verify_redis) doesn't abort the ansible-playbook run before the remaining +# import_playbooks above get a chance to execute -- Ansible stops the whole run, not just the +# current play, when a play ends with 100% of its hosts failed. This final play is the one +# place that actually fails the run, and since nothing runs after it, that's safe to do. +- name: Report Overall Verification Result + hosts: all + gather_facts: false + tasks: + - name: Fail if any verification check failed for this host + ansible.builtin.assert: + that: verification_passed | default(true) + fail_msg: "One or more Itential Deployer verification checks failed for this host. See output above for details." + success_msg: "All Itential Deployer verification checks passed for this host." diff --git a/roles/common/CLAUDE.md b/roles/common/CLAUDE.md index 1d3aa98e..7bad2108 100644 --- a/roles/common/CLAUDE.md +++ b/roles/common/CLAUDE.md @@ -10,7 +10,7 @@ There is no `tasks/main.yml`. The task files are: - `tasks/verify-host.yml` — imported by `verify-mongodb`, `verify-redis`, and `verify-platform` task files via `tasks_from:`. Checks OS/arch/HW specs/proxy and initializes `validation_errors`. Not called by Gateway's `verify-gateway.yml` (Gateway has no `gateway_hw_specs`/HW requirements defined yet). - `tasks/verify-connectivity.yml` — imported by `verify-mongodb`, `verify-redis`, `verify-platform`, and `verify-gateway` (all four components) via `tasks_from:`. Checks outbound connectivity to the URLs in `common_required_repositories` (`vars/main.yml`) that match the caller's `component_name`. See "verify-connectivity.yml Logic" below. -- `tasks/verify-results.yml` — imported by `verify-mongodb`, `verify-redis`, and `verify-platform` (not Gateway, which does its own minimal assert) to print `validation_errors` and assert `cpu_validation`/`memory_validation`/`disk_validation`/`proxy_validation`/`connectivity_validation` all passed. +- `tasks/verify-results.yml` — imported by `verify-mongodb`, `verify-redis`, and `verify-platform` (not Gateway, which does its own minimal assert) to print `validation_errors` and assert `cpu_validation`/`memory_validation`/`disk_validation`/`os_validation`/`arch_validation`/`proxy_validation`/`connectivity_validation` all passed. Each lookup is wrapped in `| default({'failed': false})` before the `is failed`/`is not failed` test, since not every caller registers every one of these (e.g. `verify-sentinel.yml` never calls `verify-connectivity.yml`, so `connectivity_validation` is undefined there). The final assert itself uses `ignore_errors: true` and sets a per-host `verification_passed` fact — see "Non-Fatal Verify Design" below. None of these task files are called by any role's `main.yml`. @@ -24,13 +24,13 @@ Expects two variables from the caller: | `hw_specs_var_name` | `"redis_hw_specs"` | Name of the hardware specs dict variable to validate against | Execution order: -1. Assert `platform_release` and `env` are defined, `env` in `['dev','test','prod']` +1. Assert `platform_release` and `env` are defined, `env` in `['dev','test','prod']` — these two are the only remaining hard (non-`ignore_errors`) asserts in the file, since they represent inventory misconfiguration rather than a per-host result. See "Non-Fatal Verify Design" below for why that distinction matters. 2. Call `itential.deployer.gather_host_information` module to collect OS/arch/hardware facts -3. Validate OS: RedHat/Rocky/OracleLinux 8 or 9, or Amazon Linux 2023 -4. Validate architecture: `x86_64` or `aarch64` +3. Validate OS: RedHat/Rocky/OracleLinux 8 or 9, or Amazon Linux 2023 (`ignore_errors: true`, registers `os_validation`, collects failures into `validation_errors`) +4. Validate architecture: `x86_64` or `aarch64` (`ignore_errors: true`, registers `arch_validation`, collects failures into `validation_errors`) 5. Validate CPU, RAM, and disk against the `hw_specs_var_name[env]` dict (uses `ignore_errors: true` and collects failures into `validation_errors`) 6. Check for proxy settings in env vars, `/etc/environment`, `/etc/profile.d/` -7. Assert all validations passed +7. Assert all validations passed (this is `verify-results.yml`'s final assert, not part of this file) The hardware specs dicts (e.g., `redis_hw_specs`, `platform_hw_specs`, `mongodb_hw_specs`) are defined in `roles//vars/platform-release-.yml` and keyed by `env` value. @@ -64,6 +64,12 @@ Called from `verify-mongodb.yml`, `verify-redis.yml`, and `verify-platform.yml` | `offline_itential_packages_path` | `itential_packages/{{ ansible_distribution \| lower }}_{{ ansible_distribution_major_version }}` | `defaults/main/offline.yml` | OS-specific subdirectory under the offline roots. | | `common_required_repositories` | See `vars/main.yml` | `vars/main.yml` | List of required public repositories per component, consumed by `verify-connectivity.yml`. Keep in sync with the README table. | +## Non-Fatal Verify Design + +`playbooks/verify.yml` chains `verify_redis`/`verify_mongodb`/`verify_platform`/`verify_gateway` together via `import_playbook`. Ansible aborts the *entire* `ansible-playbook` run (not just the current play) the moment a play ends with 100% of its hosts failed — this happens even for later plays targeting completely unrelated hosts. So every task in the verify chain that can fail on a genuine per-host/per-component result (OS/arch checks, the final combined assert in `verify-results.yml`, Gateway's own final assert) uses `ignore_errors: true` + `register:` instead of failing hard, and the final assert in each also sets a per-host `verification_passed` fact. `playbooks/verify.yml` ends with one extra `hosts: all` play ("Report Overall Verification Result") that asserts `verification_passed | default(true)` for real (no `ignore_errors`) — since nothing runs after it, failing there is safe and is what actually gives the overall run a non-zero exit code, while still letting every component's checks run against every host regardless of earlier failures. + +The two asserts in `verify-host.yml` that check `platform_release`/`env` are deliberately left hard-failing — they indicate the inventory itself is misconfigured (not a specific host failing a real check), typically affects every host identically, and there's little value in continuing past it. + ## Dependencies / Assumptions - The `common` role has no task dependencies. diff --git a/roles/common/tasks/verify-host.yml b/roles/common/tasks/verify-host.yml index 1dfce956..a5fddba2 100644 --- a/roles/common/tasks/verify-host.yml +++ b/roles/common/tasks/verify-host.yml @@ -30,6 +30,10 @@ ansible.builtin.set_fact: os: "{{ host_info.os }}" +- name: Initialize validation errors list + ansible.builtin.set_fact: + validation_errors: [] + # OS and Architecture validation - name: Check OS compatibility ansible.builtin.set_fact: @@ -47,6 +51,13 @@ fail_msg: "{{ os.distribution }} {{ os.distribution_version }} is not a supported OS!" success_msg: "OS validation passed!" quiet: true + ignore_errors: true + register: os_validation + +- name: Add OS error to list + ansible.builtin.set_fact: + validation_errors: "{{ validation_errors + ['OS: ' ~ os.distribution ~ ' ' ~ os.distribution_version ~ ' is not a supported OS'] }}" + when: os_validation is failed - name: Check architecture compatibility ansible.builtin.set_fact: @@ -58,10 +69,13 @@ fail_msg: "{{ os.architecture }} is not a supported architecture!" success_msg: "Architecture validation passed!" quiet: true + ignore_errors: true + register: arch_validation -- name: Initialize validation errors list +- name: Add Architecture error to list ansible.builtin.set_fact: - validation_errors: [] + validation_errors: "{{ validation_errors + ['Architecture: ' ~ os.architecture ~ ' is not a supported architecture'] }}" + when: arch_validation is failed - name: Get root partition size ansible.builtin.set_fact: diff --git a/roles/common/tasks/verify-results.yml b/roles/common/tasks/verify-results.yml index 01aed4d2..3bc135bc 100644 --- a/roles/common/tasks/verify-results.yml +++ b/roles/common/tasks/verify-results.yml @@ -2,23 +2,43 @@ # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) --- +# Not every caller registers every one of these (e.g. verify-sentinel.yml skips +# verify-connectivity.yml entirely), so each lookup is defaulted to a non-failed result +# before the "failed" test runs -- that test raises an error on a plain undefined value. # Display results - name: Display failed validation results ansible.builtin.debug: msg: "{{ validation_errors }}" - when: cpu_validation is failed or - memory_validation is failed or - disk_validation is failed or - connectivity_validation is failed + when: >- + (cpu_validation | default({'failed': false})) is failed or + (memory_validation | default({'failed': false})) is failed or + (disk_validation | default({'failed': false})) is failed or + (os_validation | default({'failed': false})) is failed or + (arch_validation | default({'failed': false})) is failed or + (connectivity_validation | default({'failed': false})) is failed # Assert that none of the tests failed - name: Verify that all tests passed ansible.builtin.assert: that: - - "cpu_validation is not failed" - - "memory_validation is not failed" - - "disk_validation is not failed" - - "proxy_validation is not failed" - - "connectivity_validation is not failed" + - "(cpu_validation | default({'failed': false})) is not failed" + - "(memory_validation | default({'failed': false})) is not failed" + - "(disk_validation | default({'failed': false})) is not failed" + - "(os_validation | default({'failed': false})) is not failed" + - "(arch_validation | default({'failed': false})) is not failed" + - "(proxy_validation | default({'failed': false})) is not failed" + - "(connectivity_validation | default({'failed': false})) is not failed" fail_msg: "See above, assertions not passed! ✗" success_msg: "All assertions passed! ✓" + ignore_errors: true + register: component_verification + +# Recorded per-host so playbooks/verify.yml can do one final, non-fatal-until-the-end +# assert after every component has been checked, instead of aborting the whole +# ansible-playbook run the moment one component's checks fail on a host. ANDed with any +# prior value (default true) rather than overwritten -- a host can belong to more than one +# component group (e.g. an all-in-one host), and a later component passing must not erase +# an earlier component's failure on the same host. +- name: Record overall verification result for this host + ansible.builtin.set_fact: + verification_passed: "{{ (verification_passed | default(true)) and (component_verification is not failed) }}" diff --git a/roles/gateway/CLAUDE.md b/roles/gateway/CLAUDE.md index 0979872a..b6444a8c 100644 --- a/roles/gateway/CLAUDE.md +++ b/roles/gateway/CLAUDE.md @@ -35,7 +35,7 @@ Installs and configures Itential Automation Gateway (IAG). Handles Python virtua ## Other Entry Points -- `tasks/verify-gateway.yml` (invoked by `playbooks/verify_gateway.yml`, tags_from `verify-gateway`) — checks connectivity to Gateway's required public repositories (`common_required_repositories` filtered to `component: "Gateway"`) via `common:verify-connectivity`. Unlike the other components' verify flows, it does not call `common:verify-host` (no `gateway_hw_specs` exists yet), so it skips OS/CPU/RAM/disk/proxy validation entirely. +- `tasks/verify-gateway.yml` (invoked by `playbooks/verify_gateway.yml`, tags_from `verify-gateway`) — checks connectivity to Gateway's required public repositories (`common_required_repositories` filtered to `component: "Gateway"`) via `common:verify-connectivity`. Unlike the other components' verify flows, it does not call `common:verify-host` (no `gateway_hw_specs` exists yet), so it skips OS/CPU/RAM/disk/proxy validation entirely. Its final assert uses `ignore_errors: true` and sets a per-host `verification_passed` fact instead of failing hard, for the same reason described in `roles/common/CLAUDE.md`'s "Non-Fatal Verify Design" section — so that a Gateway failure doesn't abort `playbooks/verify.yml` before the other imported verify playbooks run. ## Key Variables diff --git a/roles/gateway/tasks/verify-gateway.yml b/roles/gateway/tasks/verify-gateway.yml index e88157e5..32ac61c5 100644 --- a/roles/gateway/tasks/verify-gateway.yml +++ b/roles/gateway/tasks/verify-gateway.yml @@ -28,3 +28,15 @@ - "connectivity_validation is not failed" fail_msg: "See above, assertions not passed! ✗" success_msg: "All assertions passed! ✓" + ignore_errors: true + register: component_verification + +# Recorded per-host so playbooks/verify.yml can do one final, non-fatal-until-the-end +# assert after every component has been checked, instead of aborting the whole +# ansible-playbook run the moment one component's checks fail on a host. ANDed with any +# prior value (default true) rather than overwritten -- a host can belong to more than one +# component group (e.g. an all-in-one host), and a later component passing must not erase +# an earlier component's failure on the same host. +- name: Record overall verification result for this host + ansible.builtin.set_fact: + verification_passed: "{{ (verification_passed | default(true)) and (component_verification is not failed) }}" From 11123cd2ddd648d0d12587a229cb54c16545f8de Mon Sep 17 00:00:00 2001 From: Kevin Velarde Date: Wed, 12 Aug 2026 09:48:00 -0600 Subject: [PATCH 3/5] Remove Vault connectivity verification --- README.md | 1 - roles/common/CLAUDE.md | 2 +- roles/common/vars/main.yml | 6 ------ roles/mongodb/tasks/verify-mongodb.yml | 2 +- roles/platform/tasks/verify-platform.yml | 12 +----------- roles/redis/tasks/verify-redis.yml | 2 +- roles/redis/tasks/verify-sentinel.yml | 9 ++++++++- 7 files changed, 12 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c7b69c5a..15392b35 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,6 @@ the Deployer will either install the required repository or download the package | Redis | | TCP | EPEL YUM RPMs
When installing Redis from the Remi repository | | Redis | | TCP | Redis source packages
When installing Redis from source | | Redis | | TCP | Redis source packages
When installing Redis from source | -| Vault | | TCP | Vault YUM RPMs | If internal YUM repositories are used, refer to the [Using Internal YUM Repositories](#using-internal-yum-repositories) section. diff --git a/roles/common/CLAUDE.md b/roles/common/CLAUDE.md index 7bad2108..5093b58f 100644 --- a/roles/common/CLAUDE.md +++ b/roles/common/CLAUDE.md @@ -50,7 +50,7 @@ Execution order: 3. For entries with `type: path` that passed step 2, request the specific `check_target` resource and expect an actual `[200, 301, 302]` response. Append failures to `validation_errors`. 4. Assert both checks passed (`ignore_errors: true`, registers `connectivity_validation`), following the same pattern as `cpu_validation`/`memory_validation`/etc. in `verify-host.yml`. -Called from `verify-mongodb.yml`, `verify-redis.yml`, and `verify-platform.yml` (which also calls it a second time with `component_name: "Vault"`, gated on `platform_configure_vault | bool`, since this collection doesn't have its own Vault role/host group but Platform is what integrates with it) and `verify-gateway.yml`. +Called from `verify-mongodb.yml`, `verify-redis.yml`, `verify-platform.yml` and `verify-gateway.yml`. ## Key Variables diff --git a/roles/common/vars/main.yml b/roles/common/vars/main.yml index 15e201b7..0108e778 100644 --- a/roles/common/vars/main.yml +++ b/roles/common/vars/main.yml @@ -86,9 +86,3 @@ common_required_repositories: url: "https://codeload.github.com" type: bare notes: "Redis source packages" - - - component: "Vault" - url: "https://rpm.releases.hashicorp.com" - type: path - check_target: "https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo" - notes: "Vault YUM RPMs" diff --git a/roles/mongodb/tasks/verify-mongodb.yml b/roles/mongodb/tasks/verify-mongodb.yml index 561eb155..0751c294 100644 --- a/roles/mongodb/tasks/verify-mongodb.yml +++ b/roles/mongodb/tasks/verify-mongodb.yml @@ -27,7 +27,7 @@ vars: component_name: "MongoDB" -- name: Verify Platform host requirements +- name: Report MongoDB results ansible.builtin.include_role: name: common tasks_from: verify-results diff --git a/roles/platform/tasks/verify-platform.yml b/roles/platform/tasks/verify-platform.yml index f6b7b867..306f6273 100644 --- a/roles/platform/tasks/verify-platform.yml +++ b/roles/platform/tasks/verify-platform.yml @@ -26,16 +26,6 @@ vars: component_name: "Platform" -# Vault itself isn't installed by this collection, but Platform is the component that -# integrates with it, so its repository is checked here when Vault integration is enabled. -- name: Verify connectivity to Vault's required public repositories - ansible.builtin.include_role: - name: common - tasks_from: verify-connectivity - vars: - component_name: "Vault" - when: platform_configure_vault | bool - - name: Verify TLS certs for platform webserver are defined ansible.builtin.assert: that: platform_https_pki_src_dir is defined and platform_https_pki_src_dir | length > 0 @@ -79,7 +69,7 @@ - platform_webserver_https_enabled | bool - platform_tls_dir_validation is failed or platform_tls_contents_validation is failed -- name: Verify Platform host requirements +- name: Report Platform results ansible.builtin.include_role: name: common tasks_from: verify-results diff --git a/roles/redis/tasks/verify-redis.yml b/roles/redis/tasks/verify-redis.yml index bf32b08b..c6de23e0 100644 --- a/roles/redis/tasks/verify-redis.yml +++ b/roles/redis/tasks/verify-redis.yml @@ -27,7 +27,7 @@ vars: component_name: "Redis" -- name: Verify Platform host requirements +- name: Report Redis results ansible.builtin.include_role: name: common tasks_from: verify-results diff --git a/roles/redis/tasks/verify-sentinel.yml b/roles/redis/tasks/verify-sentinel.yml index 0baf93c3..87d82fda 100644 --- a/roles/redis/tasks/verify-sentinel.yml +++ b/roles/redis/tasks/verify-sentinel.yml @@ -20,7 +20,14 @@ # redis sentinel specific verification tasks go here... -- name: Verify Platform host requirements +- name: Verify connectivity to required public repositories + ansible.builtin.include_role: + name: common + tasks_from: verify-connectivity + vars: + component_name: "Redis" + +- name: Report Redis Sentinel results ansible.builtin.include_role: name: common tasks_from: verify-results From e41a9810307ad9a522a354e3bf3c051b1079daab Mon Sep 17 00:00:00 2001 From: Kevin Velarde Date: Wed, 12 Aug 2026 10:48:25 -0600 Subject: [PATCH 4/5] Move required repositories list to role vars --- roles/common/CLAUDE.md | 18 ++--- roles/common/tasks/verify-connectivity.yml | 12 +-- roles/common/vars/main.yml | 88 ---------------------- roles/gateway/CLAUDE.md | 8 +- roles/gateway/tasks/verify-gateway.yml | 2 +- roles/gateway/vars/main.yml | 15 ++++ roles/mongodb/CLAUDE.md | 6 ++ roles/mongodb/tasks/verify-mongodb.yml | 2 +- roles/mongodb/vars/main.yml | 20 +++++ roles/platform/CLAUDE.md | 6 ++ roles/platform/tasks/verify-platform.yml | 2 +- roles/platform/vars/main.yml | 17 +++++ roles/redis/CLAUDE.md | 6 ++ roles/redis/tasks/verify-redis.yml | 2 +- roles/redis/tasks/verify-sentinel.yml | 2 +- roles/redis/vars/main.yml | 20 +++++ 16 files changed, 114 insertions(+), 112 deletions(-) delete mode 100644 roles/common/vars/main.yml create mode 100644 roles/gateway/vars/main.yml diff --git a/roles/common/CLAUDE.md b/roles/common/CLAUDE.md index 5093b58f..b62ec3ef 100644 --- a/roles/common/CLAUDE.md +++ b/roles/common/CLAUDE.md @@ -2,14 +2,14 @@ ## Purpose -Provides shared default variables consumed by all other roles. Has no `main.yml` task entry point — it is imported by playbooks solely to inject its defaults into the variable scope. Also contains the shared `verify-host.yml`, `verify-connectivity.yml`, and `verify-results.yml` task files used by the `verify_*` playbooks. +Provides shared default variables consumed by all other roles. Has no `main.yml` task entry point — it is imported by playbooks solely to inject its defaults into the variable scope. Also contains the shared `verify-host.yml`, `verify-connectivity.yml`, and `verify-results.yml` task files used by the `verify_*` playbooks. Has no `vars/main.yml` — each component role owns its own variables (e.g. required-repository lists live in `roles//vars/main.yml`, not here); `common` only ships `defaults/`. ## Entry Point Tasks There is no `tasks/main.yml`. The task files are: - `tasks/verify-host.yml` — imported by `verify-mongodb`, `verify-redis`, and `verify-platform` task files via `tasks_from:`. Checks OS/arch/HW specs/proxy and initializes `validation_errors`. Not called by Gateway's `verify-gateway.yml` (Gateway has no `gateway_hw_specs`/HW requirements defined yet). -- `tasks/verify-connectivity.yml` — imported by `verify-mongodb`, `verify-redis`, `verify-platform`, and `verify-gateway` (all four components) via `tasks_from:`. Checks outbound connectivity to the URLs in `common_required_repositories` (`vars/main.yml`) that match the caller's `component_name`. See "verify-connectivity.yml Logic" below. +- `tasks/verify-connectivity.yml` — imported by `verify-mongodb`, `verify-redis`, `verify-platform`, and `verify-gateway` (all four components) via `tasks_from:`. Checks outbound connectivity to the URLs in the caller's own `_required_repositories` list, passed in as `required_repositories`. See "verify-connectivity.yml Logic" below. - `tasks/verify-results.yml` — imported by `verify-mongodb`, `verify-redis`, and `verify-platform` (not Gateway, which does its own minimal assert) to print `validation_errors` and assert `cpu_validation`/`memory_validation`/`disk_validation`/`os_validation`/`arch_validation`/`proxy_validation`/`connectivity_validation` all passed. Each lookup is wrapped in `| default({'failed': false})` before the `is failed`/`is not failed` test, since not every caller registers every one of these (e.g. `verify-sentinel.yml` never calls `verify-connectivity.yml`, so `connectivity_validation` is undefined there). The final assert itself uses `ignore_errors: true` and sets a per-host `verification_passed` fact — see "Non-Fatal Verify Design" below. None of these task files are called by any role's `main.yml`. @@ -40,17 +40,16 @@ Expects one variable from the caller: | Variable | Example | Purpose | |----------|---------|---------| -| `component_name` | `"MongoDB"` | Selects the matching rows from `common_required_repositories` | +| `required_repositories` | `"{{ redis_required_repositories }}"` | The list of repository dicts to check — owned by the calling component's role, not by `common` | -`common_required_repositories` (`vars/main.yml`, auto-loaded whenever the `common` role is used) mirrors the "Required Public Repositories" table in `README.md`, excluding the Ansible Control Node rows. Each entry has a `component`, `url`, `type` (`bare` or `path`), optional `check_target`, and `notes`. +Each component role defines its own `_required_repositories` list in `roles//vars/main.yml` (`redis_required_repositories`, `mongodb_required_repositories`, `platform_required_repositories`, `gateway_required_repositories`), auto-loaded whenever that role runs. Together they mirror the "Required Public Repositories" table in `README.md`, excluding the Ansible Control Node rows. Each entry has a `url`, `type` (`bare` or `path`), optional `check_target`, and `notes`. Redis Sentinel-only hosts reuse `redis_required_repositories` (`verify-sentinel.yml`) since Sentinel is installed from the same Redis source/package. Execution order: -1. Filter `common_required_repositories` down to entries matching `component_name` -2. Check baseline reachability of every entry's bare `url` via `ansible.builtin.uri` — any real HTTP status (100-599) counts as reachable; only a connection-level failure (DNS/TCP/TLS/timeout, which `fetch_url` reports as `status: -1`) counts as unreachable. Append failures to `validation_errors`. -3. For entries with `type: path` that passed step 2, request the specific `check_target` resource and expect an actual `[200, 301, 302]` response. Append failures to `validation_errors`. -4. Assert both checks passed (`ignore_errors: true`, registers `connectivity_validation`), following the same pattern as `cpu_validation`/`memory_validation`/etc. in `verify-host.yml`. +1. Check baseline reachability of every entry's bare `url` via `ansible.builtin.uri` — any real HTTP status (100-599) counts as reachable; only a connection-level failure (DNS/TCP/TLS/timeout, which `fetch_url` reports as `status: -1`) counts as unreachable. Append failures to `validation_errors`. +2. For entries with `type: path` that passed step 1, request the specific `check_target` resource and expect an actual `[200, 301, 302]` response. Append failures to `validation_errors`. +3. Assert both checks passed (`ignore_errors: true`, registers `connectivity_validation`), following the same pattern as `cpu_validation`/`memory_validation`/etc. in `verify-host.yml`. -Called from `verify-mongodb.yml`, `verify-redis.yml`, `verify-platform.yml` and `verify-gateway.yml`. +Called from `verify-mongodb.yml`, `verify-redis.yml`, `verify-sentinel.yml`, `verify-platform.yml`, and `verify-gateway.yml`. ## Key Variables @@ -62,7 +61,6 @@ Called from `verify-mongodb.yml`, `verify-redis.yml`, `verify-platform.yml` and | `offline_target_node_root` | `/var/tmp` | `defaults/main/offline.yml` | Root on target nodes for offline package staging. | | `offline_control_node_root` | `{{ playbook_dir }}/files` | `defaults/main/offline.yml` | Root on control node where offline packages are staged. | | `offline_itential_packages_path` | `itential_packages/{{ ansible_distribution \| lower }}_{{ ansible_distribution_major_version }}` | `defaults/main/offline.yml` | OS-specific subdirectory under the offline roots. | -| `common_required_repositories` | See `vars/main.yml` | `vars/main.yml` | List of required public repositories per component, consumed by `verify-connectivity.yml`. Keep in sync with the README table. | ## Non-Fatal Verify Design diff --git a/roles/common/tasks/verify-connectivity.yml b/roles/common/tasks/verify-connectivity.yml index 72801954..6257b5ae 100644 --- a/roles/common/tasks/verify-connectivity.yml +++ b/roles/common/tasks/verify-connectivity.yml @@ -6,17 +6,13 @@ # rows, which are the control node's responsibility, not a target host's). # # Expects one variable from the caller: -# - component_name: must match a "component" value in common_required_repositories -# (roles/common/vars/main.yml), e.g. "Gateway", "Platform", "MongoDB", "Redis", "Vault" +# - required_repositories: list of repository dicts -- each component owns its own list in +# roles//vars/main.yml (e.g. redis_required_repositories), with url, type +# (bare|path), optional check_target, and notes. # # Appends a message to validation_errors for every failure, then sets connectivity_validation # via an ignore_errors assert, following the same pattern as verify-host.yml. -- name: Select required repositories for this component - ansible.builtin.set_fact: - component_required_repositories: "{{ common_required_repositories - | selectattr('component', 'equalto', component_name) | list }}" - - name: Check baseline reachability of required repositories ansible.builtin.uri: url: "{{ item.url }}" @@ -27,7 +23,7 @@ validate_certs: true timeout: 10 register: repository_reachability - loop: "{{ component_required_repositories }}" + loop: "{{ required_repositories }}" loop_control: label: "{{ item.url }}" ignore_errors: true diff --git a/roles/common/vars/main.yml b/roles/common/vars/main.yml deleted file mode 100644 index 0108e778..00000000 --- a/roles/common/vars/main.yml +++ /dev/null @@ -1,88 +0,0 @@ -# Copyright (c) 2026, Itential, Inc -# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) ---- -# Mirrors the "Required Public Repositories" table in README.md, excluding the -# "Ansible Control Node" rows (those are the control node's responsibility, not a target -# host's, and are not checked by verify-connectivity.yml). Keep this list in sync with -# README.md if that table changes. -# -# component: matches the component_name passed in to verify-connectivity.yml -# url: the bare location from the README table -# type: "bare" checks only that the URL is reachable (any HTTP response counts). -# "path" additionally checks a real, specific resource (check_target) that the -# component actually needs, expecting a genuine successful response. -# check_target: required when type is "path" -# notes: carried over from the README table's Notes column -common_required_repositories: - - component: "Gateway" - url: "https://registry.aws.itential.com" - type: bare - notes: "Itential packages" - - component: "Gateway" - url: "https://galaxy.ansible.com" - type: bare - notes: "Community Ansible collections" - - component: "Gateway" - url: "https://pypi.org" - type: bare - notes: "Python modules" - - - component: "Platform" - url: "https://registry.aws.itential.com" - type: bare - notes: "Itential packages" - - component: "Platform" - url: "https://registry.npmjs.org" - type: bare - notes: "Core NPM packages" - - component: "Platform" - url: "https://pypi.org" - type: bare - notes: "Python modules" - - component: "Platform" - url: "https://gitlab.com" - type: path - check_target: "https://gitlab.com/itentialopensource/adapters" - notes: "Platform Opensource Adapters NodeJS source repository" - - - component: "MongoDB" - url: "https://repo.mongodb.org" - type: path - check_target: "https://repo.mongodb.org/yum/redhat/" - notes: "MongoDB YUM RPMs" - - component: "MongoDB" - url: "https://www.mongodb.org" - type: bare - notes: "MongoDB YUM RPMs" - - component: "MongoDB" - url: "https://pgp.mongodb.org" - type: bare - notes: "MongoDB YUM repository GPG Key" - - component: "MongoDB" - url: "https://pgp.mongodb.com" - type: bare - notes: "MongoDB YUM repository GPG Key" - - component: "MongoDB" - url: "https://pypi.org" - type: bare - notes: "Python modules" - - - component: "Redis" - url: "http://rpms.remirepo.net" - type: path - check_target: "http://rpms.remirepo.net/enterprise/" - notes: "Redis YUM RPMs" - - component: "Redis" - url: "https://dl.fedoraproject.org" - type: path - check_target: "https://dl.fedoraproject.org/pub/epel/" - notes: "EPEL YUM RPMs" - - component: "Redis" - url: "https://github.com" - type: path - check_target: "https://codeload.github.com/redis/redis/tar.gz/refs/heads/unstable" - notes: "Redis source packages" - - component: "Redis" - url: "https://codeload.github.com" - type: bare - notes: "Redis source packages" diff --git a/roles/gateway/CLAUDE.md b/roles/gateway/CLAUDE.md index b6444a8c..7626274c 100644 --- a/roles/gateway/CLAUDE.md +++ b/roles/gateway/CLAUDE.md @@ -35,7 +35,7 @@ Installs and configures Itential Automation Gateway (IAG). Handles Python virtua ## Other Entry Points -- `tasks/verify-gateway.yml` (invoked by `playbooks/verify_gateway.yml`, tags_from `verify-gateway`) — checks connectivity to Gateway's required public repositories (`common_required_repositories` filtered to `component: "Gateway"`) via `common:verify-connectivity`. Unlike the other components' verify flows, it does not call `common:verify-host` (no `gateway_hw_specs` exists yet), so it skips OS/CPU/RAM/disk/proxy validation entirely. Its final assert uses `ignore_errors: true` and sets a per-host `verification_passed` fact instead of failing hard, for the same reason described in `roles/common/CLAUDE.md`'s "Non-Fatal Verify Design" section — so that a Gateway failure doesn't abort `playbooks/verify.yml` before the other imported verify playbooks run. +- `tasks/verify-gateway.yml` (invoked by `playbooks/verify_gateway.yml`, tags_from `verify-gateway`) — checks connectivity to Gateway's required public repositories (`gateway_required_repositories`, passed to `common:verify-connectivity` as `required_repositories`) via `common:verify-connectivity`. Unlike the other components' verify flows, it does not call `common:verify-host` (no `gateway_hw_specs` exists yet), so it skips OS/CPU/RAM/disk/proxy validation entirely. Its final assert uses `ignore_errors: true` and sets a per-host `verification_passed` fact instead of failing hard, for the same reason described in `roles/common/CLAUDE.md`'s "Non-Fatal Verify Design" section — so that a Gateway failure doesn't abort `playbooks/verify.yml` before the other imported verify playbooks run. ## Key Variables @@ -98,6 +98,12 @@ Installs and configures Itential Automation Gateway (IAG). Handles Python virtua | `gateway_offline_control_node_wheels_dir` | `{{ gateway_control_node_root }}/wheels` | Wheels source dir (control) | | `gateway_offline_control_node_collections_dir` | `{{ gateway_control_node_root }}/collections` | Collections source dir (control) | +### vars/main.yml + +| Variable | Purpose | +|----------|---------| +| `gateway_required_repositories` | List of repository dicts (`url`, `type`, optional `check_target`, `notes`) checked by `verify-gateway.yml` via `common:verify-connectivity`. Mirrors the Gateway rows of the README "Required Public Repositories" table. Previously this list lived in `roles/common/vars/main.yml`; `common` no longer has a `vars/` directory. | + ## TLS Configuration HTTPS is **enabled by default** (`gateway_https_enabled: true`, `gateway_pki_copy_certs: true`). diff --git a/roles/gateway/tasks/verify-gateway.yml b/roles/gateway/tasks/verify-gateway.yml index 32ac61c5..b41edadf 100644 --- a/roles/gateway/tasks/verify-gateway.yml +++ b/roles/gateway/tasks/verify-gateway.yml @@ -15,7 +15,7 @@ name: common tasks_from: verify-connectivity vars: - component_name: "Gateway" + required_repositories: "{{ gateway_required_repositories }}" - name: Display failed validation results ansible.builtin.debug: diff --git a/roles/gateway/vars/main.yml b/roles/gateway/vars/main.yml new file mode 100644 index 00000000..87d5f81a --- /dev/null +++ b/roles/gateway/vars/main.yml @@ -0,0 +1,15 @@ +# Copyright (c) 2026, Itential, Inc +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) +--- +# Public repositories required for installing Itential Gateway, checked by the verify_gateway +# playbook. Mirrors the Gateway rows of the "Required Public Repositories" table in README.md. +gateway_required_repositories: + - url: "https://registry.aws.itential.com" + type: bare + notes: "Itential packages" + - url: "https://galaxy.ansible.com" + type: bare + notes: "Community Ansible collections" + - url: "https://pypi.org" + type: bare + notes: "Python modules" diff --git a/roles/mongodb/CLAUDE.md b/roles/mongodb/CLAUDE.md index cddbad92..9832131a 100644 --- a/roles/mongodb/CLAUDE.md +++ b/roles/mongodb/CLAUDE.md @@ -104,6 +104,12 @@ Conditionally includes (in order): | `mongodb_tls_cert_src` | `{{ mongodb_pki_src_dir }}/{{ mongodb_tls_server_cert_file }}` | Cert source | | `mongodb_tls_ca_src` | `{{ mongodb_pki_src_dir }}/{{ mongodb_tls_ca_file }}` | CA source | +### vars/main.yml + +| Variable | Purpose | +|----------|---------| +| `mongodb_required_repositories` | List of repository dicts (`url`, `type`, optional `check_target`, `notes`) checked by `verify-mongodb.yml` via `common:verify-connectivity`. Mirrors the MongoDB rows of the README "Required Public Repositories" table. | + ## TLS Configuration TLS is **enabled by default** (`mongodb_tls_enabled: true`, `mongodb_tls_copy_certs: true`). diff --git a/roles/mongodb/tasks/verify-mongodb.yml b/roles/mongodb/tasks/verify-mongodb.yml index 0751c294..aed2c6d8 100644 --- a/roles/mongodb/tasks/verify-mongodb.yml +++ b/roles/mongodb/tasks/verify-mongodb.yml @@ -25,7 +25,7 @@ name: common tasks_from: verify-connectivity vars: - component_name: "MongoDB" + required_repositories: "{{ mongodb_required_repositories }}" - name: Report MongoDB results ansible.builtin.include_role: diff --git a/roles/mongodb/vars/main.yml b/roles/mongodb/vars/main.yml index 3aa723a3..99ecf144 100644 --- a/roles/mongodb/vars/main.yml +++ b/roles/mongodb/vars/main.yml @@ -6,3 +6,23 @@ mongodb_port_default: 27017 # The name of the mongo replica set mongodb_replset_name_default: rs0 + +# Public repositories required for installing MongoDB, checked by the verify_mongodb playbook. +# Mirrors the MongoDB rows of the "Required Public Repositories" table in README.md. +mongodb_required_repositories: + - url: "https://repo.mongodb.org" + type: path + check_target: "https://repo.mongodb.org/yum/redhat/" + notes: "MongoDB YUM RPMs" + - url: "https://www.mongodb.org" + type: bare + notes: "MongoDB YUM RPMs" + - url: "https://pgp.mongodb.org" + type: bare + notes: "MongoDB YUM repository GPG Key" + - url: "https://pgp.mongodb.com" + type: bare + notes: "MongoDB YUM repository GPG Key" + - url: "https://pypi.org" + type: bare + notes: "Python modules" diff --git a/roles/platform/CLAUDE.md b/roles/platform/CLAUDE.md index c64d199f..efd4d43e 100644 --- a/roles/platform/CLAUDE.md +++ b/roles/platform/CLAUDE.md @@ -168,6 +168,12 @@ Installs and configures Itential Platform (IAP). Handles OS user/directory setup | `platform_log_max_file_size` | `1048576` | Max log file size (bytes; ~1 MB) | | `platform_log_filename` | `platform.log` | Primary log filename | +### vars/main.yml + +| Variable | Purpose | +|----------|---------| +| `platform_required_repositories` | List of repository dicts (`url`, `type`, optional `check_target`, `notes`) checked by `verify-platform.yml` via `common:verify-connectivity`. Mirrors the Platform rows of the README "Required Public Repositories" table. | + ## TLS Configuration HTTPS and MongoDB client TLS are both **enabled by default** (copy-certs flags default to `true`). diff --git a/roles/platform/tasks/verify-platform.yml b/roles/platform/tasks/verify-platform.yml index 306f6273..677c8d95 100644 --- a/roles/platform/tasks/verify-platform.yml +++ b/roles/platform/tasks/verify-platform.yml @@ -24,7 +24,7 @@ name: common tasks_from: verify-connectivity vars: - component_name: "Platform" + required_repositories: "{{ platform_required_repositories }}" - name: Verify TLS certs for platform webserver are defined ansible.builtin.assert: diff --git a/roles/platform/vars/main.yml b/roles/platform/vars/main.yml index 66ccb020..65175e13 100644 --- a/roles/platform/vars/main.yml +++ b/roles/platform/vars/main.yml @@ -19,3 +19,20 @@ platform_vault_role_id_property: "${ITENTIAL_VAULT_ROLE_ID}" # What to write in application properties file for Secret ID (defaults to env var reference) platform_vault_secret_id_property: "${ITENTIAL_VAULT_SECRET_ID}" + +# Public repositories required for installing Itential Platform, checked by the verify_platform +# playbook. Mirrors the Platform rows of the "Required Public Repositories" table in README.md. +platform_required_repositories: + - url: "https://registry.aws.itential.com" + type: bare + notes: "Itential packages" + - url: "https://registry.npmjs.org" + type: bare + notes: "Core NPM packages" + - url: "https://pypi.org" + type: bare + notes: "Python modules" + - url: "https://gitlab.com" + type: path + check_target: "https://gitlab.com/itentialopensource/adapters" + notes: "Platform Opensource Adapters NodeJS source repository" diff --git a/roles/redis/CLAUDE.md b/roles/redis/CLAUDE.md index 1152ed38..594ae156 100644 --- a/roles/redis/CLAUDE.md +++ b/roles/redis/CLAUDE.md @@ -102,6 +102,12 @@ Installs and configures Redis and Redis Sentinel for use with Itential Platform. | All `_dest` vars | Built from base dir + filename | Destination paths on target | | All `_src` vars | Built from `redis_pki_src_dir` + filename | Source paths on control node | +### vars/main.yml + +| Variable | Purpose | +|----------|---------| +| `redis_required_repositories` | List of repository dicts (`url`, `type`, optional `check_target`, `notes`) checked by `verify-redis.yml` and `verify-sentinel.yml` via `common:verify-connectivity`. Mirrors the Redis rows of the README "Required Public Repositories" table. | + ## TLS Configuration TLS is **disabled by default** (`redis_tls_enabled: false`). diff --git a/roles/redis/tasks/verify-redis.yml b/roles/redis/tasks/verify-redis.yml index c6de23e0..27519b64 100644 --- a/roles/redis/tasks/verify-redis.yml +++ b/roles/redis/tasks/verify-redis.yml @@ -25,7 +25,7 @@ name: common tasks_from: verify-connectivity vars: - component_name: "Redis" + required_repositories: "{{ redis_required_repositories }}" - name: Report Redis results ansible.builtin.include_role: diff --git a/roles/redis/tasks/verify-sentinel.yml b/roles/redis/tasks/verify-sentinel.yml index 87d82fda..94a1e735 100644 --- a/roles/redis/tasks/verify-sentinel.yml +++ b/roles/redis/tasks/verify-sentinel.yml @@ -25,7 +25,7 @@ name: common tasks_from: verify-connectivity vars: - component_name: "Redis" + required_repositories: "{{ redis_required_repositories }}" - name: Report Redis Sentinel results ansible.builtin.include_role: diff --git a/roles/redis/vars/main.yml b/roles/redis/vars/main.yml index 4ae3075f..76c408aa 100644 --- a/roles/redis/vars/main.yml +++ b/roles/redis/vars/main.yml @@ -11,3 +11,23 @@ redis_data_dir_default: /var/lib/redis # Default ports redis_port_default: 6379 redis_sentinel_port_default: 26379 + +# Public repositories required for installing Redis (and Redis Sentinel, which is installed +# from the same source/package), checked by the verify_redis playbook. Mirrors the Redis rows +# of the "Required Public Repositories" table in README.md. +redis_required_repositories: + - url: "http://rpms.remirepo.net" + type: path + check_target: "http://rpms.remirepo.net/enterprise/" + notes: "Redis YUM RPMs" + - url: "https://dl.fedoraproject.org" + type: path + check_target: "https://dl.fedoraproject.org/pub/epel/" + notes: "EPEL YUM RPMs" + - url: "https://github.com" + type: path + check_target: "https://codeload.github.com/redis/redis/tar.gz/refs/heads/unstable" + notes: "Redis source packages" + - url: "https://codeload.github.com" + type: bare + notes: "Redis source packages" From cdf2cf8a9cb0ce96c68eea87d8012b86e23db63d Mon Sep 17 00:00:00 2001 From: Kevin Velarde Date: Thu, 13 Aug 2026 10:16:31 -0600 Subject: [PATCH 5/5] Add verify summary for each host --- .gitignore | 1 + CLAUDE.md | 2 +- playbooks/verify.yml | 5 +++ roles/common/CLAUDE.md | 10 +++--- roles/common/tasks/verify-results.yml | 12 +++++++ roles/gateway/CLAUDE.md | 7 +++- roles/gateway/tasks/verify-gateway.yml | 44 +++++++----------------- roles/gateway/vars/main.yml | 14 ++++++++ roles/mongodb/tasks/verify-mongodb.yml | 2 ++ roles/platform/tasks/verify-platform.yml | 2 ++ roles/redis/tasks/verify-redis.yml | 2 ++ roles/redis/tasks/verify-sentinel.yml | 2 ++ 12 files changed, 66 insertions(+), 37 deletions(-) diff --git a/.gitignore b/.gitignore index a34c8e8e..0131655a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ cspell.json .ansible/* +.claude/* .vscode/* .DS_Store **.DS_Store diff --git a/CLAUDE.md b/CLAUDE.md index c0ab8da3..83e1b835 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -49,7 +49,7 @@ Also requires the `jmespath` Python module on the control node. | `verify_redis.yml` | `itential.deployer.verify_redis` | Pre-install verification for Redis hosts | | `verify_mongodb.yml` | `itential.deployer.verify_mongodb` | Pre-install verification for MongoDB hosts | | `verify_platform.yml` | `itential.deployer.verify_platform` | Pre-install verification for Platform hosts | -| `verify_gateway.yml` | `itential.deployer.verify_gateway` | Pre-install verification (required repository connectivity only) for Gateway hosts | +| `verify_gateway.yml` | `itential.deployer.verify_gateway` | Pre-install verification for Gateway hosts | | `download_packages_site.yml` | `itential.deployer.download_packages_site` | Download all packages for offline install (Platform stack + Gateway) | | `download_packages_platform_site.yml` | `itential.deployer.download_packages_platform_site` | Download all packages for Platform stack offline install | | `download_packages_platform.yml` | `itential.deployer.download_packages_platform` | Download Platform packages for offline install | diff --git a/playbooks/verify.yml b/playbooks/verify.yml index 05957c60..f22d5f8f 100644 --- a/playbooks/verify.yml +++ b/playbooks/verify.yml @@ -23,6 +23,11 @@ hosts: all gather_facts: false tasks: + - name: Display per-component verification failures for this host + ansible.builtin.debug: + var: component_validation_errors + when: component_validation_errors is defined and component_validation_errors | length > 0 + - name: Fail if any verification check failed for this host ansible.builtin.assert: that: verification_passed | default(true) diff --git a/roles/common/CLAUDE.md b/roles/common/CLAUDE.md index b62ec3ef..7c900a78 100644 --- a/roles/common/CLAUDE.md +++ b/roles/common/CLAUDE.md @@ -8,9 +8,9 @@ Provides shared default variables consumed by all other roles. Has no `main.yml` There is no `tasks/main.yml`. The task files are: -- `tasks/verify-host.yml` — imported by `verify-mongodb`, `verify-redis`, and `verify-platform` task files via `tasks_from:`. Checks OS/arch/HW specs/proxy and initializes `validation_errors`. Not called by Gateway's `verify-gateway.yml` (Gateway has no `gateway_hw_specs`/HW requirements defined yet). +- `tasks/verify-host.yml` — imported by `verify-mongodb`, `verify-redis`, `verify-platform`, and `verify-gateway` task files via `tasks_from:`. Checks OS/arch/HW specs/proxy and initializes `validation_errors`. - `tasks/verify-connectivity.yml` — imported by `verify-mongodb`, `verify-redis`, `verify-platform`, and `verify-gateway` (all four components) via `tasks_from:`. Checks outbound connectivity to the URLs in the caller's own `_required_repositories` list, passed in as `required_repositories`. See "verify-connectivity.yml Logic" below. -- `tasks/verify-results.yml` — imported by `verify-mongodb`, `verify-redis`, and `verify-platform` (not Gateway, which does its own minimal assert) to print `validation_errors` and assert `cpu_validation`/`memory_validation`/`disk_validation`/`os_validation`/`arch_validation`/`proxy_validation`/`connectivity_validation` all passed. Each lookup is wrapped in `| default({'failed': false})` before the `is failed`/`is not failed` test, since not every caller registers every one of these (e.g. `verify-sentinel.yml` never calls `verify-connectivity.yml`, so `connectivity_validation` is undefined there). The final assert itself uses `ignore_errors: true` and sets a per-host `verification_passed` fact — see "Non-Fatal Verify Design" below. +- `tasks/verify-results.yml` — imported by `verify-mongodb`, `verify-redis`, `verify-sentinel`, `verify-platform`, and `verify-gateway` (all five verify task files) to print `validation_errors` and assert `cpu_validation`/`memory_validation`/`disk_validation`/`os_validation`/`arch_validation`/`proxy_validation`/`connectivity_validation` all passed. Each lookup is wrapped in `| default({'failed': false})` before the `is failed`/`is not failed` test, since not every caller registers every one of these (e.g. `verify-sentinel.yml` never calls `verify-connectivity.yml`, so `connectivity_validation` is undefined there). The final assert itself uses `ignore_errors: true` and sets a per-host `verification_passed` fact — see "Non-Fatal Verify Design" below. It also expects a `component_name` var from the caller (e.g. `"Redis"`, `"Redis Sentinel"`, `"MongoDB"`, `"Platform"`, `"Gateway"`) and merges this component's `validation_errors` into a per-host `component_validation_errors` dict keyed by that name, so `playbooks/verify.yml`'s final report can show which component failed which check, not just an overall pass/fail. None of these task files are called by any role's `main.yml`. @@ -32,7 +32,9 @@ Execution order: 6. Check for proxy settings in env vars, `/etc/environment`, `/etc/profile.d/` 7. Assert all validations passed (this is `verify-results.yml`'s final assert, not part of this file) -The hardware specs dicts (e.g., `redis_hw_specs`, `platform_hw_specs`, `mongodb_hw_specs`) are defined in `roles//vars/platform-release-.yml` and keyed by `env` value. +The hardware specs dicts (e.g., `redis_hw_specs`, `platform_hw_specs`, `mongodb_hw_specs`) are defined in `roles//vars/platform-release-.yml` and keyed by `env` value. `gateway_hw_specs` is the exception — it's defined in `roles/gateway/vars/main.yml` instead, since Gateway's hardware requirements are not release-specific. + +`verify-gateway.yml` now calls `common:verify-host` too (all four components — Redis, MongoDB, Platform, Gateway — follow the identical `verify-host` → `verify-connectivity` → `verify-results` sequence). ## verify-connectivity.yml Logic @@ -64,7 +66,7 @@ Called from `verify-mongodb.yml`, `verify-redis.yml`, `verify-sentinel.yml`, `ve ## Non-Fatal Verify Design -`playbooks/verify.yml` chains `verify_redis`/`verify_mongodb`/`verify_platform`/`verify_gateway` together via `import_playbook`. Ansible aborts the *entire* `ansible-playbook` run (not just the current play) the moment a play ends with 100% of its hosts failed — this happens even for later plays targeting completely unrelated hosts. So every task in the verify chain that can fail on a genuine per-host/per-component result (OS/arch checks, the final combined assert in `verify-results.yml`, Gateway's own final assert) uses `ignore_errors: true` + `register:` instead of failing hard, and the final assert in each also sets a per-host `verification_passed` fact. `playbooks/verify.yml` ends with one extra `hosts: all` play ("Report Overall Verification Result") that asserts `verification_passed | default(true)` for real (no `ignore_errors`) — since nothing runs after it, failing there is safe and is what actually gives the overall run a non-zero exit code, while still letting every component's checks run against every host regardless of earlier failures. +`playbooks/verify.yml` chains `verify_redis`/`verify_mongodb`/`verify_platform`/`verify_gateway` together via `import_playbook`. Ansible aborts the *entire* `ansible-playbook` run (not just the current play) the moment a play ends with 100% of its hosts failed — this happens even for later plays targeting completely unrelated hosts. So every task in the verify chain that can fail on a genuine per-host/per-component result (OS/arch checks, the final combined assert in `verify-results.yml`, Gateway's own final assert) uses `ignore_errors: true` + `register:` instead of failing hard, and the final assert in each also sets a per-host `verification_passed` fact and merges `validation_errors` into a per-host `component_validation_errors` dict (keyed by `component_name`, via `combine()` so a later-running component can't erase an earlier one's failures on the same host). `playbooks/verify.yml` ends with one extra `hosts: all` play ("Report Overall Verification Result") that prints `component_validation_errors` (when non-empty) and then asserts `verification_passed | default(true)` for real (no `ignore_errors`) — since nothing runs after it, failing there is safe and is what actually gives the overall run a non-zero exit code, while still letting every component's checks run against every host regardless of earlier failures. The two asserts in `verify-host.yml` that check `platform_release`/`env` are deliberately left hard-failing — they indicate the inventory itself is misconfigured (not a specific host failing a real check), typically affects every host identically, and there's little value in continuing past it. diff --git a/roles/common/tasks/verify-results.yml b/roles/common/tasks/verify-results.yml index 3bc135bc..203c0c35 100644 --- a/roles/common/tasks/verify-results.yml +++ b/roles/common/tasks/verify-results.yml @@ -2,6 +2,10 @@ # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) --- +# Expects one variable from the caller: +# - component_name: label used to group this component's errors in component_validation_errors +# for playbooks/verify.yml's final report, e.g. "Redis", "Redis Sentinel", "MongoDB", "Platform" +# # Not every caller registers every one of these (e.g. verify-sentinel.yml skips # verify-connectivity.yml entirely), so each lookup is defaulted to a non-failed result # before the "failed" test runs -- that test raises an error on a plain undefined value. @@ -42,3 +46,11 @@ - name: Record overall verification result for this host ansible.builtin.set_fact: verification_passed: "{{ (verification_passed | default(true)) and (component_verification is not failed) }}" + +# Keyed by component_name and merged via combine() rather than overwritten, since a host can +# be checked by more than one component (e.g. an all-in-one host) -- lets playbooks/verify.yml's +# final report show exactly which checks failed for which component, not just a pass/fail flag. +- name: Record this component's validation errors for the final report + ansible.builtin.set_fact: + component_validation_errors: "{{ (component_validation_errors | default({})) | combine({component_name: validation_errors}) }}" + when: validation_errors | length > 0 diff --git a/roles/gateway/CLAUDE.md b/roles/gateway/CLAUDE.md index 7626274c..8a0da52a 100644 --- a/roles/gateway/CLAUDE.md +++ b/roles/gateway/CLAUDE.md @@ -35,7 +35,7 @@ Installs and configures Itential Automation Gateway (IAG). Handles Python virtua ## Other Entry Points -- `tasks/verify-gateway.yml` (invoked by `playbooks/verify_gateway.yml`, tags_from `verify-gateway`) — checks connectivity to Gateway's required public repositories (`gateway_required_repositories`, passed to `common:verify-connectivity` as `required_repositories`) via `common:verify-connectivity`. Unlike the other components' verify flows, it does not call `common:verify-host` (no `gateway_hw_specs` exists yet), so it skips OS/CPU/RAM/disk/proxy validation entirely. Its final assert uses `ignore_errors: true` and sets a per-host `verification_passed` fact instead of failing hard, for the same reason described in `roles/common/CLAUDE.md`'s "Non-Fatal Verify Design" section — so that a Gateway failure doesn't abort `playbooks/verify.yml` before the other imported verify playbooks run. +- `tasks/verify-gateway.yml` (invoked by `playbooks/verify_gateway.yml`, tags_from `verify-gateway`) — now follows the same three-step pattern as `verify-redis.yml`/`verify-mongodb.yml`/`verify-platform.yml`: `common:verify-host` (OS/arch/CPU/RAM/disk/proxy, against `gateway_hw_specs`), `common:verify-connectivity` (`gateway_required_repositories`, passed as `required_repositories`), then `common:verify-results` (combined assert + reporting). Passes `component_name: "Gateway"` to each. `verify-results.yml`'s final assert uses `ignore_errors: true` and sets a per-host `verification_passed` fact and merges this component's errors into `component_validation_errors`, for the same reason described in `roles/common/CLAUDE.md`'s "Non-Fatal Verify Design" section — so that a Gateway failure doesn't abort `playbooks/verify.yml` before the other imported verify playbooks run. ## Key Variables @@ -103,6 +103,11 @@ Installs and configures Itential Automation Gateway (IAG). Handles Python virtua | Variable | Purpose | |----------|---------| | `gateway_required_repositories` | List of repository dicts (`url`, `type`, optional `check_target`, `notes`) checked by `verify-gateway.yml` via `common:verify-connectivity`. Mirrors the Gateway rows of the README "Required Public Repositories" table. Previously this list lived in `roles/common/vars/main.yml`; `common` no longer has a `vars/` directory. | +| `gateway_hw_specs` | CPU/RAM/disk requirements per `env` (`dev`/`test`/`prod`), checked by `verify-gateway.yml` via `common:verify-host`. Unlike `redis_hw_specs`/`mongodb_hw_specs`/`platform_hw_specs`, this is defined in `vars/main.yml` rather than a `vars/platform-release-.yml` file, since Gateway's hardware requirements are not release-specific. | + +Hardware specs for `verify` playbook (`gateway_hw_specs`): +- dev: 4 CPU, 16 GB RAM, 10 GB disk +- test/prod: 16 CPU, 32 GB RAM, 50 GB disk ## TLS Configuration diff --git a/roles/gateway/tasks/verify-gateway.yml b/roles/gateway/tasks/verify-gateway.yml index b41edadf..d55f28e8 100644 --- a/roles/gateway/tasks/verify-gateway.yml +++ b/roles/gateway/tasks/verify-gateway.yml @@ -1,14 +1,13 @@ # Copyright (c) 2026, Itential, Inc # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) --- -# Unlike verify-mongodb.yml/verify-redis.yml/verify-platform.yml, this does not call -# common:verify-host — Gateway has no documented hardware-spec requirements (gateway_hw_specs) -# in this collection yet. This task file only verifies connectivity to Gateway's required -# public repositories. - -- name: Initialize validation errors list - ansible.builtin.set_fact: - validation_errors: [] +- name: Verify Gateway host requirements + ansible.builtin.include_role: + name: common + tasks_from: verify-host + vars: + component_name: "Gateway" + hw_specs_var_name: "gateway_hw_specs" - name: Verify connectivity to required public repositories ansible.builtin.include_role: @@ -17,26 +16,9 @@ vars: required_repositories: "{{ gateway_required_repositories }}" -- name: Display failed validation results - ansible.builtin.debug: - msg: "{{ validation_errors }}" - when: connectivity_validation is failed - -- name: Verify that all tests passed - ansible.builtin.assert: - that: - - "connectivity_validation is not failed" - fail_msg: "See above, assertions not passed! ✗" - success_msg: "All assertions passed! ✓" - ignore_errors: true - register: component_verification - -# Recorded per-host so playbooks/verify.yml can do one final, non-fatal-until-the-end -# assert after every component has been checked, instead of aborting the whole -# ansible-playbook run the moment one component's checks fail on a host. ANDed with any -# prior value (default true) rather than overwritten -- a host can belong to more than one -# component group (e.g. an all-in-one host), and a later component passing must not erase -# an earlier component's failure on the same host. -- name: Record overall verification result for this host - ansible.builtin.set_fact: - verification_passed: "{{ (verification_passed | default(true)) and (component_verification is not failed) }}" +- name: Report Gateway results + ansible.builtin.include_role: + name: common + tasks_from: verify-results + vars: + component_name: "Gateway" diff --git a/roles/gateway/vars/main.yml b/roles/gateway/vars/main.yml index 87d5f81a..9b514e22 100644 --- a/roles/gateway/vars/main.yml +++ b/roles/gateway/vars/main.yml @@ -1,6 +1,20 @@ # Copyright (c) 2026, Itential, Inc # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) --- +gateway_hw_specs: + "dev": + "cpu_count": 4 + "ram_size": 16 + "disk_size": 10 + "test": + "cpu_count": 16 + "ram_size": 32 + "disk_size": 50 + "prod": + "cpu_count": 16 + "ram_size": 32 + "disk_size": 50 + # Public repositories required for installing Itential Gateway, checked by the verify_gateway # playbook. Mirrors the Gateway rows of the "Required Public Repositories" table in README.md. gateway_required_repositories: diff --git a/roles/mongodb/tasks/verify-mongodb.yml b/roles/mongodb/tasks/verify-mongodb.yml index aed2c6d8..ba7a2ed5 100644 --- a/roles/mongodb/tasks/verify-mongodb.yml +++ b/roles/mongodb/tasks/verify-mongodb.yml @@ -31,3 +31,5 @@ ansible.builtin.include_role: name: common tasks_from: verify-results + vars: + component_name: "MongoDB" diff --git a/roles/platform/tasks/verify-platform.yml b/roles/platform/tasks/verify-platform.yml index 677c8d95..cbf238d7 100644 --- a/roles/platform/tasks/verify-platform.yml +++ b/roles/platform/tasks/verify-platform.yml @@ -73,3 +73,5 @@ ansible.builtin.include_role: name: common tasks_from: verify-results + vars: + component_name: "Platform" diff --git a/roles/redis/tasks/verify-redis.yml b/roles/redis/tasks/verify-redis.yml index 27519b64..92c8e7b2 100644 --- a/roles/redis/tasks/verify-redis.yml +++ b/roles/redis/tasks/verify-redis.yml @@ -31,3 +31,5 @@ ansible.builtin.include_role: name: common tasks_from: verify-results + vars: + component_name: "Redis" diff --git a/roles/redis/tasks/verify-sentinel.yml b/roles/redis/tasks/verify-sentinel.yml index 94a1e735..008d2b09 100644 --- a/roles/redis/tasks/verify-sentinel.yml +++ b/roles/redis/tasks/verify-sentinel.yml @@ -31,3 +31,5 @@ ansible.builtin.include_role: name: common tasks_from: verify-results + vars: + component_name: "Redis Sentinel"