feat(#2398): dagify hivectl restart (RestartScoped)

hivectl restart --agent NAME previously composed stop() then start() as
two separate client-side daemon calls glued by CLI-process control flow
— not one DAG, and a dropped hivectl connection mid-restart (ssh drop,
Ctrl-C) left the agent stopped with no automatic follow-up. mara flagged
this as the first target for the 'dagify hivectl commands' issue.

New HostRequest::RestartScoped{scope, graceful} handles it server-side:
each targeted agent now rides exactly one atomic Restart-template DAG
(same one hivectl agents restart / restart-all already use) in the
common non-graceful case. --graceful has no single-DAG template yet, so
it submits the graceful-stop DAGs, awaits them server-side, then submits
the start DAGs — still one daemon call end to end, just not yet a single
DAG (noted as a follow-up). Infra containers restart synchronously as
before (no lease/DAG concept for them).

CLI-side restart() now just makes the one call + waits, same output
shape as before via render_lifecycle.
This commit is contained in:
atlas 2026-07-14 19:57:50 +02:00 committed by mara
commit 4cfa040154
3 changed files with 118 additions and 10 deletions

View file

@ -39,6 +39,23 @@ pub enum HostRequest {
/// wrapper for `hivectl agents restart-all`; iterates the live
/// container list and restarts each one.
RestartAll,
/// Restart containers hive-wide (`hivectl restart`), scoped like
/// `Stop`/`Start`. Each targeted agent rides exactly one DAG server-side
/// (the `Restart` template, hard stop + reconcile — or, when `graceful`
/// is set, a submitted graceful-stop DAG the server itself waits out
/// before submitting the start DAG) rather than the old client-side
/// stop-then-start composition: a dropped `hivectl` connection mid-way
/// used to leave the agent stopped with no automatic follow-up, since
/// nothing durable remembered "finish the restart" once the CLI process
/// was gone. Infra containers have no lease/DAG and restart
/// synchronously (stop then start), same as before. Scope semantics
/// match `Stop`/`Start` (all-false = everything).
RestartScoped {
#[serde(default)]
scope: LifecycleScope,
#[serde(default)]
graceful: bool,
},
/// Apply pending config to a managed container.
Rebuild { name: String },
/// List managed containers.