The forge and matrix are swarm services; per mara's ruling on #5475 (shape a) a hive no longer starts, stops or restarts them. - hive-priv-sock: InfraContainer keeps only Gateway; InfraTarget goes, service_unit maps the gateway to nginx.service directly. A ControlInfraContainer naming Forge/Matrix fails to decode. - hive-host-sock: LifecycleScope drops forge/matrix and refuses unknown fields, so an older hivectl's --forge is an error rather than an all-false scope meaning everything. - hive-c0re: scoped_infra selects only the gateway. - hivectl: --forge/--matrix removed from stop/start/restart. - docs: services.md says how the forge and matrix are managed; security.md, coordinator.md and hivectl-cli.md follow. - hive-host-sock, hivectl/power.rs: Stop and RestartScoped doc comments name the gateway (host nginx.service), not infra containers.
71 lines
2.8 KiB
Rust
71 lines
2.8 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); the gateway (host `nginx.service`) restarts 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
|
|
}
|