fix(ansible): replace steward key on reprovision + distinct agent update path
Provisioning review corrections + the matching frontend, plus breadcrumb header integration. - provision.yml: authorize the managed pubkey with a regexp match on the ' steward-managed' comment so rotating the key REPLACES the host's steward key in place instead of stacking a second authorized entry. Hand-added keys (other comments) are untouched. - update.yml (new): refresh agent.py + restart only. Does NOT rotate the token or rewrite /etc/steward-agent.conf — the host keeps its identity. Asserts the agent is already installed and fails clearly otherwise. - host_agent /update route: runs update.yml as the managed steward user (no token minting). Token rotation stays a deliberate action. - settings/ansible/generate-key honors a safe relative `next` redirect, so an inline trigger elsewhere returns to its page. - Host Agents settings: reworked into a clear lifecycle — an intro card that explains it runs Ansible to deploy the agent (+ inline "generate managed key" warning/trigger when none exists), then three labelled cards: 1 Provision, 2 Install/enroll, 3 Update. Each explains what it does. - base.html: breadcrumb now renders as a kicker line directly above the page title (moved below alerts, tightened margin) so nested and top-level views share one consistent header. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -809,3 +809,38 @@ async def provision_via_ansible():
|
||||
if err:
|
||||
return _error(400, "provision_failed", err)
|
||||
return redirect(url_for("ansible.run_detail", run_id=run.id))
|
||||
|
||||
|
||||
@host_agent_bp.post("/update")
|
||||
@require_role(UserRole.admin)
|
||||
async def update_via_ansible():
|
||||
"""Refresh agent.py on already-installed hosts via the bundled update
|
||||
playbook. Connects as the managed steward account — no token rotation, no
|
||||
config rewrite (the host keeps its existing identity)."""
|
||||
from steward.core.capabilities import has_capability, invoke_capability
|
||||
from steward.ansible.sources import BUILTIN_SOURCE_NAME
|
||||
|
||||
if not has_capability("ansible.run_playbook"):
|
||||
return _error(400, "ansible_unavailable", "Ansible is not available")
|
||||
|
||||
form = await request.form
|
||||
scope = (form.get("inventory_scope", "") or "").strip()
|
||||
if not (scope.startswith("steward:target:")
|
||||
or scope.startswith("steward:group:")
|
||||
or scope == "steward:all"):
|
||||
return _error(400, "bad_scope", "Choose a target or group")
|
||||
|
||||
url = public_base_url(request)
|
||||
actor_role = UserRole(session.get("user_role", "viewer"))
|
||||
run, _source, err = await invoke_capability(
|
||||
"ansible.run_playbook", actor_role,
|
||||
current_app._get_current_object(), # type: ignore[attr-defined]
|
||||
source_name=BUILTIN_SOURCE_NAME,
|
||||
playbook_path="host_agent/update.yml",
|
||||
inventory_scope=scope,
|
||||
params={"extra_vars": [f"steward_url={url}"]},
|
||||
triggered_by=session.get("user_id"),
|
||||
)
|
||||
if err:
|
||||
return _error(400, "update_failed", err)
|
||||
return redirect(url_for("ansible.run_detail", run_id=run.id))
|
||||
|
||||
Reference in New Issue
Block a user