Skip to content

fix(infra): discover every managed GCP zone and fail closed on empty Ansible role groups - #2408

Merged
Peyton-Spencer merged 3 commits into
ditto-assistant:mainfrom
diorwave:fix/ansible-gcp-zone-discovery-547
Sep 28, 2026
Merged

Peyton-Spencer merged 3 commits into
ditto-assistant:mainfrom
diorwave:fix/ansible-gcp-zone-discovery-547

Conversation

@diorwave

Copy link
Copy Markdown
Collaborator

Refs #547. Implements zone discovery, per-host IAP zones, the empty-group guard and multi-zone CI fixtures. Remaining: supporting and documenting a short-lived gcloud access-token workflow next to ADC. auth_kind: application is set directly in inventory/gcp.yml, and options set in the file take precedence over GCP_AUTH_KIND/GCP_ACCESS_TOKEN, so this needs its own design decision.

Problem

infra/ansible/inventory/gcp.yml only listed us-central1-a. Terraform (stacks/gcp-platform) also places the legacy prod screener in us-central1-c (var.screener_prod_zone) and spreads the regional screener MIG over us-central1-a/b/c/f (var.screener_fleet_zones), so the inventory silently left those hosts out. Every group_vars/role_*.yml also hard-coded gcp_zone: us-central1-a for the IAP ProxyCommand, so even a discovered host outside zone a would tunnel to the wrong zone. A play whose group matches no hosts is skipped and the run exits 0, so a missing host looked like a successful converge.

Change

  • inventory/gcp.yml now lists exactly the four us-central1 zones Terraform uses, and adds gcp_zone: zone to compose, so each host's IAP tunnel uses its instance's own zone.
  • Removed the static gcp_zone from all ten group_vars/role_*.yml. Both static fallback inventories (validator-static.yml, screener-capacity-static.yml) now set gcp_zone: us-central1-a per host. This matches Terraform's var.zone. A host with no zone now fails when the variable is templated instead of falling back to zone a.
  • New playbooks/require-hosts.yml runs first in every gcp-*.yml playbook (import_playbook with required_group set to the group that playbook targets). It fails when that group has no hosts. It targets localhost:all and intersects with the play's hosts, so it also runs under --limit and catches a limit that falls outside the group. assert runs on the controller and connects to no host. A --limit naming an absent host already fails in ansible-core ("leaves us with no hosts to target").
  • New CI fixture tests/gcp-inventory/: instance-shaped hosts in zones a/b/c/f, a constructed config that replays gcp.yml's keyed_groups/compose, and the real group_vars via a symlink. tests/gcp-inventory.yml checks four things: gcp.yml zones equal the quoted zone literals in stacks/gcp-platform/*.tf; the fixture's compose/keyed_groups equal gcp.yml's; every host's gcp_zone equals its instance zone and its ProxyCommand says --zone=<that zone> (e.g. us-central1-c for ditto-screener-prod); and the guard accepts a multi-zone group. The new infra-ci.yml step also requires the guard to fail on an empty group and on a --limit outside the group. Nothing calls GCP.

Verification

All commands were run from infra/ansible with uvx --from ansible-core==2.21.2:

  • ansible-inventory -i tests/gcp-inventory --graph: role_platform_app, role_screener, and role_screener_fleet (b1, f1) were grouped as expected.
  • ansible-playbook -i tests/gcp-inventory tests/gcp-inventory.yml: passed (localhost ok=4, 4 fixture hosts ok=1).
  • The same test with origin/main's inventory/gcp.yml failed with inventory/gcp.yml zones ['us-central1-a'] != Terraform zones [...]. With gcp_zone compose removed, it failed. With a static gcp_zone re-added to role_screener.yml, it still passed, which confirms the composed host var takes precedence.
  • ansible-playbook -i tests/gcp-inventory playbooks/require-hosts.yml -e required_group=role_validator_prod: exit 2, "role_validator_prod resolved to no hosts".
  • ... -e required_group=role_screener --limit ditto-platform-prod: exit 2. --limit ditto-screener-prod: exit 0, and no host was contacted.
  • ansible-playbook -i inventory/validator-static.yml playbooks/gcp-screener.yml: failed at the guard before any host play.
  • ansible -i inventory/{validator,screener-capacity}-static.yml all -m debug -a var=ansible_ssh_common_args: both rendered --zone=us-central1-a.
  • Ran the new CI step's script locally with bash -euo pipefail: exit 0.
  • ansible-playbook --syntax-check -i inventory/validator-static.yml on every playbook in the CI list, plus gcp-pg.yml, gcp-coding-hosted-runtime.yml and tests/gcp-inventory.yml: all passed.
  • python3 tests/test_docker_apt_key_pin.py: OK. ansible-playbook --check -i tests/coding-hosted-postgres-inventory.yml tests/coding-hosted-postgres.yml: failed=0.

@ditto-code ditto-code Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto Review · Beta

Review completed for bac0066bdf6877aaf30b7e1eab19acaa286aacd1.

No actionable findings in the reviewed files. This is not an approval or proof that the PR is bug-free.

Configure Ditto Review. Inference is billed through your Ditto Router endpoint; targeted verification is included during beta.

@Peyton-Spencer
Peyton-Spencer merged commit d11fc1d into ditto-assistant:main Sep 28, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants