hivectl: add hive-wide start/stop verbs
`hivectl stop` brings the whole hive down in one operator action — all
sub-agents plus the ci/forge/gateway/matrix infra containers — and
`hivectl start` brings it back up. Scope flags (--agents, --agent <name>,
--ci, --forge, --gateway, --matrix) narrow the set; a bare invocation
targets everything. hive-c0re never stops itself.
- hive-sh4re: HostRequest::{Stop,Start} + LifecycleScope wire type;
priv_proto InfraAction + ControlInfraContainer + the
CONTROLLABLE_INFRA_CONTAINERS allowlist (adds hive-matrix, excludes
hive-c0re).
- hive-priv: control_infra_container handler (systemctl <verb>
container@<name>, allowlist-validated root-side).
- hive-c0re: handle_stop / handle_start fan out agents via lifecycle and
infra via hive-priv; per-target failures are aggregated. Infra
systemctl routes through hive-priv (the privsep boundary).
- The --graceful flag is threaded through Stop now; the per-agent quiesce
itself lands with the graceful-agent-stop work.
This commit is contained in:
parent
5b99eb1f8f
commit
fbb48ed3ce
6 changed files with 407 additions and 7 deletions
|
|
@ -21,9 +21,9 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use hive_sh4re::priv_proto::{
|
||||
AGENT_PREFIX, AGENT_STATE_ROOT, BindMount, JournalQuery, MANAGER_NAME, META_DIR,
|
||||
NetworkIsolation, PRIV_SOCK, PrivEvent, PrivRequest, PrivResponse, PrivStream, PrivStreamLine,
|
||||
RESTARTABLE_INFRA_CONTAINERS, SIBLING_CONTAINERS,
|
||||
AGENT_PREFIX, AGENT_STATE_ROOT, BindMount, CONTROLLABLE_INFRA_CONTAINERS, InfraAction,
|
||||
JournalQuery, MANAGER_NAME, META_DIR, NetworkIsolation, PRIV_SOCK, PrivEvent, PrivRequest,
|
||||
PrivResponse, PrivStream, PrivStreamLine, RESTARTABLE_INFRA_CONTAINERS, SIBLING_CONTAINERS,
|
||||
};
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::net::unix::OwnedWriteHalf;
|
||||
|
|
@ -268,6 +268,11 @@ async fn exec(req: PrivRequest, writer: &mut OwnedWriteHalf) -> Result<(String,
|
|||
PrivRequest::RestartInfraContainer { ref container } => {
|
||||
restart_infra_container(container).await
|
||||
}
|
||||
|
||||
PrivRequest::ControlInfraContainer {
|
||||
ref container,
|
||||
action,
|
||||
} => control_infra_container(container, action).await,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -423,6 +428,37 @@ async fn restart_infra_container(container: &str) -> Result<(String, String)> {
|
|||
))
|
||||
}
|
||||
|
||||
/// `ControlInfraContainer` — start/stop/restart a hive infrastructure
|
||||
/// container via `systemctl <verb> container@<container>.service`. The
|
||||
/// `container` is validated against `CONTROLLABLE_INFRA_CONTAINERS` here,
|
||||
/// root-side; this is the authoritative allowlist (hive-c0re itself can
|
||||
/// never appear in it, so a hive-wide stop can't sever the daemon socket
|
||||
/// the request arrived on).
|
||||
async fn control_infra_container(container: &str, action: InfraAction) -> Result<(String, String)> {
|
||||
if !CONTROLLABLE_INFRA_CONTAINERS.contains(&container) {
|
||||
bail!("container {container:?} is not a controllable hive infra container");
|
||||
}
|
||||
let verb = action.systemctl_verb();
|
||||
let unit = format!("container@{container}.service");
|
||||
let out = Command::new("systemctl")
|
||||
.args([verb, &unit])
|
||||
.output()
|
||||
.await
|
||||
.with_context(|| format!("systemctl {verb} {unit}"))?;
|
||||
if !out.status.success() {
|
||||
bail!(
|
||||
"systemctl {verb} {unit} exited {}: {}",
|
||||
out.status,
|
||||
String::from_utf8_lossy(&out.stderr).trim()
|
||||
);
|
||||
}
|
||||
tracing::info!(target: "infra-control", "{verb} {unit}");
|
||||
Ok((
|
||||
String::from_utf8_lossy(&out.stdout).into_owned(),
|
||||
String::from_utf8_lossy(&out.stderr).into_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Shared helper for `WriteAgentForgeToken` and `WriteAgentMatrixToken`.
|
||||
/// Writes `content` to `AGENT_STATE_ROOT/<agent_name>/state/<filename>`,
|
||||
/// chowns to the agent user (derived from the state dir's existing owner),
|
||||
|
|
|
|||
Loading…
Reference in a new issue