hive-sh4re: type infra containers as an InfraContainer enum
Replace the stringly-typed infra-control path with an InfraContainer enum
(Ci/Forge/Gateway/Matrix). The variants are the allowlist: serde rejects any
unknown or unsafe name (hive-c0re has no variant) at the wire boundary, so
hive-priv no longer needs a root-side SIBLING_CONTAINERS.contains() check on
ControlInfraContainer — the type enforces it, and 'the daemon can't stop
itself' is a compile-time guarantee.
- priv_proto: InfraContainer enum; manual Serialize/Deserialize + FromStr +
unit_name() all key off one mapping, so the wire form ('hive-ci', …) is
unchanged and there's no drift. ControlInfraContainer.container: String ->
InfraContainer.
- hive-priv / priv_client / server.rs: thread the enum; scoped_infra returns
Vec<InfraContainer>; the control handler uses unit_name().
- agent_server: the infra_admin restart gate parses the name via FromStr
instead of a slice .contains().
- SIBLING_CONTAINERS stays (validate_container_name/_system_name still use it
for journals / general container validation); a test keeps the enum and the
slice in lockstep.
This commit is contained in:
parent
6b1dbebe5a
commit
cd025b3790
5 changed files with 146 additions and 71 deletions
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
|||
use std::sync::Arc;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use hive_sh4re::priv_proto::InfraAction;
|
||||
use hive_sh4re::priv_proto::{InfraAction, InfraContainer};
|
||||
use hive_sh4re::{HostRequest, HostResponse, LifecycleScope};
|
||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::net::{UnixListener, UnixStream};
|
||||
|
|
@ -229,7 +229,7 @@ async fn handle_restart_all() -> Result<HostResponse> {
|
|||
async fn handle_stop(
|
||||
coord: &Arc<Coordinator>,
|
||||
agents: &[String],
|
||||
infra: &[&str],
|
||||
infra: &[InfraContainer],
|
||||
graceful: bool,
|
||||
) -> Result<HostResponse> {
|
||||
tracing::info!(?agents, ?infra, graceful, "stop");
|
||||
|
|
@ -267,11 +267,12 @@ async fn handle_stop(
|
|||
}
|
||||
|
||||
for &container in infra {
|
||||
let name = container.unit_name();
|
||||
match crate::priv_client::control_infra_container(container, InfraAction::Stop).await {
|
||||
Ok(()) => ok_items.push(container.to_owned()),
|
||||
Ok(()) => ok_items.push(name.to_owned()),
|
||||
Err(e) => {
|
||||
tracing::warn!(%container, error = ?e, "stop: infra stop failed");
|
||||
errors.push(format!("{container}: {e:#}"));
|
||||
tracing::warn!(%name, error = ?e, "stop: infra stop failed");
|
||||
errors.push(format!("{name}: {e:#}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -283,17 +284,18 @@ async fn handle_stop(
|
|||
/// inverse of [`handle_stop`]. Infra comes up before agents so the agents
|
||||
/// find forge/matrix/gateway ready. Per-target failures aggregated. Callers
|
||||
/// resolve the [`LifecycleScope`] to these explicit name lists up front.
|
||||
async fn handle_start(agents: &[String], infra: &[&str]) -> Result<HostResponse> {
|
||||
async fn handle_start(agents: &[String], infra: &[InfraContainer]) -> Result<HostResponse> {
|
||||
tracing::info!(?agents, ?infra, "start");
|
||||
let mut ok_items: Vec<String> = Vec::new();
|
||||
let mut errors: Vec<String> = Vec::new();
|
||||
|
||||
for &container in infra {
|
||||
let name = container.unit_name();
|
||||
match crate::priv_client::control_infra_container(container, InfraAction::Start).await {
|
||||
Ok(()) => ok_items.push(container.to_owned()),
|
||||
Ok(()) => ok_items.push(name.to_owned()),
|
||||
Err(e) => {
|
||||
tracing::warn!(%container, error = ?e, "start: infra start failed");
|
||||
errors.push(format!("{container}: {e:#}"));
|
||||
tracing::warn!(%name, error = ?e, "start: infra start failed");
|
||||
errors.push(format!("{name}: {e:#}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -333,23 +335,23 @@ async fn scoped_agents(scope: &LifecycleScope) -> Result<Vec<String>> {
|
|||
Ok(set.into_iter().collect())
|
||||
}
|
||||
|
||||
/// Resolve which infra container names a scope targets. An "everything"
|
||||
/// scope (no flags set) selects all controllable infra; otherwise each set
|
||||
/// flag maps to its container. Fixed order for deterministic output.
|
||||
fn scoped_infra(scope: &LifecycleScope) -> Vec<&'static str> {
|
||||
/// Resolve which infra containers a scope targets. An "everything" scope
|
||||
/// (no flags set) selects all controllable infra; otherwise each set flag
|
||||
/// maps to its [`InfraContainer`]. Fixed order for deterministic output.
|
||||
fn scoped_infra(scope: &LifecycleScope) -> Vec<InfraContainer> {
|
||||
let everything = scope.is_everything();
|
||||
let mut out = Vec::new();
|
||||
if everything || scope.ci {
|
||||
out.push("hive-ci");
|
||||
out.push(InfraContainer::Ci);
|
||||
}
|
||||
if everything || scope.forge {
|
||||
out.push("hive-forge");
|
||||
out.push(InfraContainer::Forge);
|
||||
}
|
||||
if everything || scope.gateway {
|
||||
out.push("hive-gateway");
|
||||
out.push(InfraContainer::Gateway);
|
||||
}
|
||||
if everything || scope.matrix {
|
||||
out.push("hive-matrix");
|
||||
out.push(InfraContainer::Matrix);
|
||||
}
|
||||
out
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue