fix(ansible): write DB inventory as static YAML, not dynamic --list JSON
generate_inventory() emits Ansible's dynamic --list shape (all.hosts is a
LIST, vars under _meta) — valid only as an executable inventory script's
stdout. We were writing it to a static file and passing -i, so Ansible's yaml
plugin rejected it ("Invalid 'hosts' entry for 'all' group, requires a
dictionary, found ...list...") and fell back to implicit localhost → "no hosts
matched". Affected every steward:* scope run; surfaced on the first real
provision.
- New inventory_to_yaml(inv): convert the --list dict → a valid static YAML
inventory (all.hosts dict keyed by host, groups under all.children, group
vars preserved, injected per-host vars like steward_token retained).
- Wire it into runner.trigger_run, host_agent deploy + provision.
- executor writes the file as inventory.yml so the yaml plugin's extension
check reliably claims it.
- generate_inventory unchanged (still the --list dict); conversion happens at
write time. Unit tests added.
Scribe issue #885.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,10 @@
|
||||
Uses SimpleNamespace duck-typed objects so no DB or SQLAlchemy is required.
|
||||
"""
|
||||
from types import SimpleNamespace
|
||||
from steward.ansible.inventory_gen import generate_inventory
|
||||
|
||||
import yaml
|
||||
|
||||
from steward.ansible.inventory_gen import generate_inventory, inventory_to_yaml
|
||||
|
||||
|
||||
def _group(name, ansible_vars=None):
|
||||
@@ -91,3 +94,37 @@ def test_target_in_multiple_groups():
|
||||
assert "web-prod-01" in result["prod"]["hosts"]
|
||||
assert result["_meta"]["hostvars"]["web-prod-01"]["role"] == "web"
|
||||
assert result["_meta"]["hostvars"]["web-prod-01"]["env"] == "production"
|
||||
|
||||
|
||||
# ── inventory_to_yaml: STATIC inventory (hosts is a dict, not a list) ──────────
|
||||
|
||||
def test_yaml_hosts_is_dict_with_hostvars():
|
||||
target = _target("web1", "192.168.1.10", {"ansible_user": "ubuntu"})
|
||||
parsed = yaml.safe_load(inventory_to_yaml(generate_inventory([target])))
|
||||
# The bug was a LIST here; a static YAML inventory requires a dict keyed by host.
|
||||
assert isinstance(parsed["all"]["hosts"], dict)
|
||||
assert parsed["all"]["hosts"]["web1"]["ansible_host"] == "192.168.1.10"
|
||||
assert parsed["all"]["hosts"]["web1"]["ansible_user"] == "ubuntu"
|
||||
|
||||
|
||||
def test_yaml_groups_under_children():
|
||||
grp = _group("db", {"db_port": 5432})
|
||||
t1 = _target("db1", "10.0.0.1", groups=[grp])
|
||||
t2 = _target("db2", "10.0.0.2", groups=[grp])
|
||||
parsed = yaml.safe_load(inventory_to_yaml(generate_inventory([t1, t2])))
|
||||
assert set(parsed["all"]["hosts"]) == {"db1", "db2"}
|
||||
assert set(parsed["all"]["children"]["db"]["hosts"]) == {"db1", "db2"}
|
||||
assert parsed["all"]["children"]["db"]["vars"]["db_port"] == 5432
|
||||
|
||||
|
||||
def test_yaml_injected_hostvars_survive():
|
||||
"""Extra per-host vars injected after generate_inventory (e.g. steward_token)."""
|
||||
inv = generate_inventory([_target("h1", "1.2.3.4")])
|
||||
inv["_meta"]["hostvars"]["h1"]["steward_token"] = "tok with space"
|
||||
parsed = yaml.safe_load(inventory_to_yaml(inv))
|
||||
assert parsed["all"]["hosts"]["h1"]["steward_token"] == "tok with space"
|
||||
|
||||
|
||||
def test_yaml_empty_targets():
|
||||
parsed = yaml.safe_load(inventory_to_yaml(generate_inventory([])))
|
||||
assert parsed == {"all": {"hosts": {}}}
|
||||
|
||||
Reference in New Issue
Block a user