hyperhive/hivectl/src/power.rs

71 lines
2.7 KiB
Rust

//! `hivectl stop` / `start` / `restart` — hive-wide + scoped container
//! lifecycle. Each verb makes one daemon call and renders the queued DAGs.
use std::path::Path;
use anyhow::{Context as _, Result};
use crate::dag_progress::wait_for_nodes;
use crate::util::render_lifecycle;
pub(crate) async fn stop(
socket: &Path,
scope: hive_host_sock::LifecycleScope,
graceful: bool,
no_wait: bool,
) -> Result<()> {
let resp = crate::client::request(
socket,
hive_host_sock::HostRequest::Stop { scope, graceful },
)
.await
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
// Render first, but even when an infra failure makes it bail,
// watch the already-queued agent DAGs before surfacing the error —
// they run regardless.
let rendered = render_lifecycle(&resp, "stop queued");
wait_for_nodes(socket, resp.queued_dags.unwrap_or_default(), no_wait).await?;
rendered
}
pub(crate) async fn start(
socket: &Path,
scope: hive_host_sock::LifecycleScope,
no_wait: bool,
) -> Result<()> {
let resp = crate::client::request(socket, hive_host_sock::HostRequest::Start { scope })
.await
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
let rendered = render_lifecycle(&resp, "start queued");
wait_for_nodes(socket, resp.queued_dags.unwrap_or_default(), no_wait).await?;
rendered
}
/// Restart — one `RestartScoped` daemon call, server-side DAG-based (see
/// issue tracker "dagify hivectl commands"). Each targeted agent rides
/// exactly one atomic DAG queued up front — `Restart` (mechanical stop +
/// reconcile), or `GracefulRestart` with `--graceful` (signal → drain →
/// mechanical stop → reconcile); infra containers restart synchronously.
/// No "submit one DAG, wait for it, submit another" composition on either
/// side of the wire: unlike the old client-side stop-then-start compose, a
/// dropped `hivectl` connection mid-restart no longer leaves an agent
/// stopped with no automatic follow-up — the daemon owns the whole
/// sequence once this call is made.
///
/// No `--no-wait` here on purpose, same as before: the operator wants to
/// see the restart actually land, not just get queued.
pub(crate) async fn restart(
socket: &Path,
scope: hive_host_sock::LifecycleScope,
graceful: bool,
) -> Result<()> {
let resp = crate::client::request(
socket,
hive_host_sock::HostRequest::RestartScoped { scope, graceful },
)
.await
.with_context(|| format!("connect to daemon socket {}", socket.display()))?;
let rendered = render_lifecycle(&resp, "restart queued");
wait_for_nodes(socket, resp.queued_dags.unwrap_or_default(), false).await?;
rendered
}