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
|
|
@ -58,6 +58,68 @@ pub enum HostRequest {
|
|||
child: String,
|
||||
new_parent: Option<String>,
|
||||
},
|
||||
/// Stop managed containers hive-wide in one operator action
|
||||
/// (`hivectl stop`): agents plus the selected infra containers. `scope`
|
||||
/// selects which classes; an all-false scope means **everything** (the
|
||||
/// bare `hivectl stop`). `graceful` runs the per-agent quiesce (graceful
|
||||
/// agent stop, issue tracker `graceful agent stop`) instead of a hard
|
||||
/// stop. Agents stop via the lifecycle path; infra containers via the
|
||||
/// host `container@<name>` units.
|
||||
Stop {
|
||||
#[serde(default)]
|
||||
scope: LifecycleScope,
|
||||
#[serde(default)]
|
||||
graceful: bool,
|
||||
},
|
||||
/// Start managed containers hive-wide — the inverse of `Stop`
|
||||
/// (`hivectl start`). Same `scope` semantics (all-false = everything);
|
||||
/// no graceful flag (start is unconditional).
|
||||
Start {
|
||||
#[serde(default)]
|
||||
scope: LifecycleScope,
|
||||
},
|
||||
}
|
||||
|
||||
/// Selects which container classes a hive-wide [`HostRequest::Stop`] /
|
||||
/// [`HostRequest::Start`] touches. An all-false scope means **everything**
|
||||
/// (the bare `hivectl stop` / `start`); set individual fields to restrict
|
||||
/// (e.g. only `agents` → just the sub-agent containers). `agents` covers
|
||||
/// every managed sub-agent container; the rest are the named infra
|
||||
/// containers (`hive-ci`, `hive-forge`, `hive-gateway`, `hive-matrix`).
|
||||
//
|
||||
// A flat bag of independent flag toggles — one bool per selectable
|
||||
// container class — is exactly the right shape here: each maps 1:1 to a
|
||||
// `hivectl` `--ci` / `--forge` / `--gateway` / `--matrix` flag, and they're
|
||||
// orthogonal (any subset is valid), so a state machine or two-variant enums
|
||||
// would only obscure the mapping. Hence the `struct_excessive_bools` allow.
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct LifecycleScope {
|
||||
/// All sub-agent containers (`--agents`).
|
||||
#[serde(default)]
|
||||
pub agents: bool,
|
||||
/// Specific sub-agents by logical name (`--agent <name>`, repeatable).
|
||||
/// Additive with the rest of the scope; redundant when `agents` is set
|
||||
/// (which already covers every sub-agent).
|
||||
#[serde(default)]
|
||||
pub agent_names: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub ci: bool,
|
||||
#[serde(default)]
|
||||
pub forge: bool,
|
||||
#[serde(default)]
|
||||
pub gateway: bool,
|
||||
#[serde(default)]
|
||||
pub matrix: bool,
|
||||
}
|
||||
|
||||
impl LifecycleScope {
|
||||
/// True when nothing is explicitly selected — interpreted as "all
|
||||
/// classes" (the bare `hivectl stop` / `start` with no scope flags).
|
||||
pub fn is_everything(&self) -> bool {
|
||||
!(self.agents || self.ci || self.forge || self.gateway || self.matrix)
|
||||
&& self.agent_names.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
|
|||
|
|
@ -27,6 +27,37 @@ pub const SIBLING_CONTAINERS: &[&str] = &["hive-forge", "hive-matrix", "hive-gat
|
|||
/// it is the authoritative allowlist regardless of what the caller sends.
|
||||
pub const RESTARTABLE_INFRA_CONTAINERS: &[&str] = &["hive-ci", "hive-gateway", "hive-forge"];
|
||||
|
||||
/// Infra containers hive-c0re may stop/start/restart hive-wide for the
|
||||
/// `hivectl stop` / `hivectl start` operator flow. Superset of
|
||||
/// [`RESTARTABLE_INFRA_CONTAINERS`]: it adds `hive-matrix`, because a full
|
||||
/// stop is a deliberate operator action (unlike the disruptive mid-sync
|
||||
/// *restart* the `infra_admin` MCP path forbids). `hive-c0re` is still
|
||||
/// excluded — it runs the daemon servicing the request and must never stop
|
||||
/// itself. hive-priv re-validates against this list root-side.
|
||||
pub const CONTROLLABLE_INFRA_CONTAINERS: &[&str] =
|
||||
&["hive-ci", "hive-gateway", "hive-forge", "hive-matrix"];
|
||||
|
||||
/// Lifecycle verb for [`PrivRequest::ControlInfraContainer`]. Maps directly
|
||||
/// to `systemctl <verb> container@<container>.service`.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum InfraAction {
|
||||
Start,
|
||||
Stop,
|
||||
Restart,
|
||||
}
|
||||
|
||||
impl InfraAction {
|
||||
/// The `systemctl` subcommand this action maps to.
|
||||
pub fn systemctl_verb(self) -> &'static str {
|
||||
match self {
|
||||
InfraAction::Start => "start",
|
||||
InfraAction::Stop => "stop",
|
||||
InfraAction::Restart => "restart",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Host path of the meta flake. The flake ref for agent `<name>` is
|
||||
/// `{META_DIR}#{name}`, derived by `hive-priv` — never passed over the wire.
|
||||
pub const META_DIR: &str = "/var/lib/hyperhive/meta";
|
||||
|
|
@ -316,6 +347,16 @@ pub enum PrivRequest {
|
|||
/// [`RESTARTABLE_INFRA_CONTAINERS`].
|
||||
container: String,
|
||||
},
|
||||
|
||||
/// Start/stop/restart a hive infrastructure container on the host via
|
||||
/// `systemctl <action> container@<container>.service`. hive-priv
|
||||
/// validates `container` against [`CONTROLLABLE_INFRA_CONTAINERS`]
|
||||
/// root-side. Generalises [`PrivRequest::RestartInfraContainer`] for the
|
||||
/// hive-wide `hivectl stop` / `hivectl start` operator flow.
|
||||
ControlInfraContainer {
|
||||
container: String,
|
||||
action: InfraAction,
|
||||
},
|
||||
}
|
||||
|
||||
/// Response from the privileged helper.
|
||||
|
|
|
|||
Loading…
Reference in a new issue