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:
@@ -707,9 +707,9 @@ async def deploy_via_ansible():
|
||||
Uses the core "ansible.run_playbook" capability (no hard import of the
|
||||
runner) and injects a freshly-minted per-host token as an inventory hostvar.
|
||||
"""
|
||||
import json as _json
|
||||
from steward.core.capabilities import has_capability, invoke_capability
|
||||
from steward.ansible.inventory_gen import fetch_scope_targets, generate_inventory
|
||||
from steward.ansible.inventory_gen import (
|
||||
fetch_scope_targets, generate_inventory, inventory_to_yaml)
|
||||
from steward.ansible.sources import BUILTIN_SOURCE_NAME
|
||||
|
||||
if not has_capability("ansible.run_playbook"):
|
||||
@@ -742,7 +742,7 @@ async def deploy_via_ansible():
|
||||
hv = inv["_meta"]["hostvars"].setdefault(name, {})
|
||||
hv["steward_url"] = url
|
||||
hv["steward_token"] = tok
|
||||
inventory_content = _json.dumps(inv)
|
||||
inventory_content = inventory_to_yaml(inv)
|
||||
await db.commit()
|
||||
|
||||
actor_role = UserRole(session.get("user_role", "viewer"))
|
||||
@@ -771,9 +771,9 @@ async def provision_via_ansible():
|
||||
The bootstrap password is passed to the runner as a connection override and
|
||||
is NOT persisted on the run row.
|
||||
"""
|
||||
import json as _json
|
||||
from steward.core.capabilities import has_capability, invoke_capability
|
||||
from steward.ansible.inventory_gen import fetch_scope_targets, generate_inventory
|
||||
from steward.ansible.inventory_gen import (
|
||||
fetch_scope_targets, generate_inventory, inventory_to_yaml)
|
||||
from steward.ansible.sources import BUILTIN_SOURCE_NAME
|
||||
|
||||
if not has_capability("ansible.run_playbook"):
|
||||
@@ -823,7 +823,7 @@ async def provision_via_ansible():
|
||||
# Ansible would shlex-split on whitespace.
|
||||
hv["steward_pubkey"] = pubkey
|
||||
hv["steward_user"] = steward_user
|
||||
inventory_content = _json.dumps(inv)
|
||||
inventory_content = inventory_to_yaml(inv)
|
||||
await db.commit()
|
||||
|
||||
actor_role = UserRole(session.get("user_role", "viewer"))
|
||||
|
||||
Reference in New Issue
Block a user