hive-c0re: scrub lifecycle + meta cookies (#715 batch 9)
This commit is contained in:
parent
6d7ec90843
commit
b0495c5167
2 changed files with 73 additions and 86 deletions
|
|
@ -94,19 +94,19 @@ pub async fn sync_agents(
|
|||
// Reconcile topology.json against the live agent set — adds
|
||||
// entries for newly-spawned agents (default: manager as parent,
|
||||
// manager itself as root) and drops removed agents. Operator
|
||||
// overrides via the write API (#361 follow-up) are preserved
|
||||
// because reconcile only fills in missing entries. Idempotent;
|
||||
// when nothing changed the file isn't touched.
|
||||
// overrides via the write API are preserved because reconcile
|
||||
// only fills in missing entries. Idempotent; when nothing changed
|
||||
// the file isn't touched.
|
||||
let agent_names: Vec<String> = agents.iter().map(|a| a.name.clone()).collect();
|
||||
let topology_changed = crate::topology::reconcile(&agent_names)
|
||||
.with_context(|| format!("reconcile {}", crate::topology::topology_path().display()))?;
|
||||
|
||||
// Refresh /var/lib/hyperhive/agent-ports.json so the hive-gateway
|
||||
// nginx sees the new agent set (#15 / #740). The file is the
|
||||
// single source of truth for which agents the gateway proxies to,
|
||||
// since the gateway container lives in system config and can't be
|
||||
// rebuilt from meta-flake events. Atomic write (tmp + rename) so
|
||||
// a partial write never trips the gateway's read.
|
||||
// nginx sees the new agent set. The file is the single source of
|
||||
// truth for which agents the gateway proxies to, since the
|
||||
// gateway container lives in system config and can't be rebuilt
|
||||
// from meta-flake events. Atomic write (tmp + rename) so a
|
||||
// partial write never trips the gateway's read.
|
||||
if let Err(e) = crate::agent_ports::write(&agent_names) {
|
||||
// Best-effort: a failed write doesn't block the meta-flake
|
||||
// regen + container ops that follow. The gateway falls back
|
||||
|
|
@ -117,10 +117,11 @@ pub async fn sync_agents(
|
|||
|
||||
// Refresh /var/lib/hyperhive/agent-sockets.json — sibling to the
|
||||
// ports map, drives the gateway's unix-socket upstreams once
|
||||
// agents opt in to `HIVE_WEB_SOCKET` (PR #800 / #784 phase 1).
|
||||
// Coexists with the TCP-port map during the transition: the
|
||||
// gateway picks the socket upstream when one exists, falls back
|
||||
// to the TCP port otherwise. Same best-effort + non-fatal shape.
|
||||
// agents opt in to `HIVE_WEB_SOCKET`. Coexists with the TCP-port
|
||||
// map during the transition: the gateway picks the socket
|
||||
// upstream when one exists, falls back to the TCP port otherwise.
|
||||
// Same best-effort + non-fatal shape. See
|
||||
// `docs/gateway.md::Per-agent unix-socket upstream`.
|
||||
if let Err(e) = crate::agent_sockets::write(&agent_names) {
|
||||
tracing::warn!(error = ?e, "agent_sockets::write failed (non-fatal)");
|
||||
}
|
||||
|
|
@ -301,7 +302,7 @@ fn render_flake(
|
|||
/// of these as a top-level input in its own `flake.nix` will get a
|
||||
/// `follows = "<name>"` line emitted in meta — collapsing the
|
||||
/// otherwise-separate-but-identical `nixpkgs_N` nodes into a single
|
||||
/// meta-level reference (#355).
|
||||
/// meta-level reference.
|
||||
const CANONICAL_INPUTS: &[&str] = &["nixpkgs", "nixpkgs-unstable"];
|
||||
|
||||
/// Env vars hive-c0re forwards from its own systemd unit env into every
|
||||
|
|
@ -391,10 +392,8 @@ where
|
|||
// resolves transitively to hyperhive's pin. One channel decision
|
||||
// in the whole tree, no second source to drift.
|
||||
//
|
||||
// Per mara via triage on #619: "all nixpkgs follow the one
|
||||
// hyperhive was deployed with — if not, that's what we should
|
||||
// fix." This is the fix (flip from the concrete-url shape
|
||||
// #619 originally shipped with).
|
||||
// All nixpkgs follow the one hyperhive was deployed with —
|
||||
// anything else would drift.
|
||||
//
|
||||
// Operators who want to slide the whole swarm onto a different
|
||||
// channel do it at the host level via
|
||||
|
|
@ -402,9 +401,9 @@ where
|
|||
// makes hyperhive's nixpkgs = the host's nixpkgs and cascades
|
||||
// through to every agent.
|
||||
//
|
||||
// closes #317 invariant still satisfied — `nixpkgs` is still a
|
||||
// single canonical name in the meta tree, it just resolves
|
||||
// through hyperhive instead of being its own root input.
|
||||
// `nixpkgs` is still a single canonical name in the meta tree,
|
||||
// it just resolves through hyperhive instead of being its own
|
||||
// root input.
|
||||
let _ = writeln!(out, " hyperhive.url = \"{hyperhive_flake}\";");
|
||||
out.push_str(" nixpkgs.follows = \"hyperhive/nixpkgs\";\n");
|
||||
out.push_str(" nixpkgs-unstable.follows = \"hyperhive/nixpkgs-unstable\";\n");
|
||||
|
|
@ -417,11 +416,11 @@ where
|
|||
// For each canonical input the agent declares in its own
|
||||
// `flake.nix` (detected by reading its applied `flake.lock`),
|
||||
// emit `inputs.agent-<name>.inputs.<canon>.follows = "<canon>"`.
|
||||
// Collapses three otherwise-separate-but-identical nixpkgs
|
||||
// nodes (root + agent-bitburner's + agent-dmatrix's) into one
|
||||
// (closes #355). Skipped silently for agents that don't
|
||||
// declare the input — emitting follows on a non-existent
|
||||
// input would error at `nix flake lock` time.
|
||||
// Collapses otherwise-separate-but-identical nixpkgs nodes
|
||||
// (root + every agent's own nixpkgs) into one. Skipped
|
||||
// silently for agents that don't declare the input — emitting
|
||||
// follows on a non-existent input would error at
|
||||
// `nix flake lock` time.
|
||||
for canon in lookup(&spec.name) {
|
||||
let _ = writeln!(
|
||||
out,
|
||||
|
|
@ -452,13 +451,12 @@ where
|
|||
modules = [
|
||||
input.nixosModules.default
|
||||
{
|
||||
# Drop root (#658): the harness service inside the
|
||||
# container runs as a non-root unix user named after
|
||||
# the agent (`damocles`, `iris`, `hm1nd`, …). UID
|
||||
# auto-assigned by NixOS per mara on #658; the per-
|
||||
# agent override here is what makes `hyperhive.user.name`
|
||||
# match the agent's identity instead of the harness-
|
||||
# base default of `"agent"`.
|
||||
# The harness service inside the container runs as a
|
||||
# non-root unix user named after the agent (`damocles`,
|
||||
# `iris`, `hm1nd`, …). UID auto-assigned by NixOS; the
|
||||
# per-agent override here is what makes
|
||||
# `hyperhive.user.name` match the agent's identity
|
||||
# instead of the harness-base default of `"agent"`.
|
||||
hyperhive.user.name = name;
|
||||
programs.git.config.user = {
|
||||
name = name;
|
||||
|
|
@ -470,7 +468,7 @@ where
|
|||
# shells); `systemd.globalEnvironment` is the analogue for
|
||||
# systemd units so tea-login / forge-avatar-sync /
|
||||
# matrix-avatar-sync etc. can read `$HYPERHIVE_STATE_DIR`
|
||||
# without each service having to redeclare it (#604).
|
||||
# without each service having to redeclare it.
|
||||
environment.variables = {
|
||||
HIVE_LABEL = name;
|
||||
HYPERHIVE_STATE_DIR = "/agents/${name}/state";
|
||||
|
|
@ -506,9 +504,9 @@ where
|
|||
// → in-container accessors fall back to None / defaults gracefully.
|
||||
//
|
||||
// - HIVE_FORGE_URL: agents poll this for Forgejo notifications.
|
||||
// - HYPERHIVE_HIVE_DOMAIN: machine-readable hive DNS (#589).
|
||||
// - HYPERHIVE_HIVE_DOMAIN: machine-readable hive DNS.
|
||||
// - HYPERHIVE_HIVE_NAME / HYPERHIVE_SWARM_NAME: human display
|
||||
// names for hive + swarm (#701).
|
||||
// names for hive + swarm.
|
||||
for (var, val) in forwarded_env_vars() {
|
||||
let escaped = val.replace('\\', "\\\\").replace('"', "\\\"");
|
||||
let _ = writeln!(out, " {var} = \"{escaped}\";");
|
||||
|
|
@ -526,9 +524,8 @@ where
|
|||
);
|
||||
// Pull the topology map once and look up each agent's parent. An
|
||||
// empty / absent topology.json yields `parent = null` for everyone
|
||||
// — equivalent to the pre-#361 status quo (every container at root).
|
||||
// `meta::sync_agents` seeds the file on first run with manager as
|
||||
// root + everyone else under manager.
|
||||
// (every container at root). `meta::sync_agents` seeds the file
|
||||
// on first run with manager as root + everyone else under manager.
|
||||
let topology = crate::topology::read();
|
||||
for spec in agents {
|
||||
let parent_attr = topology
|
||||
|
|
@ -648,8 +645,8 @@ mod tests {
|
|||
);
|
||||
// Meta's `nixpkgs` + `nixpkgs-unstable` are aliases for
|
||||
// hyperhive's sub-inputs. Single channel-pin authority:
|
||||
// hyperhive's own flake.nix. Per mara via triage on #619:
|
||||
// "all nixpkgs follow the one hyperhive was deployed with."
|
||||
// hyperhive's own flake.nix. All nixpkgs follow the one
|
||||
// hyperhive was deployed with.
|
||||
assert!(
|
||||
out.contains("nixpkgs.follows = \"hyperhive/nixpkgs\""),
|
||||
"missing nixpkgs follows alias:\n{out}"
|
||||
|
|
@ -659,8 +656,8 @@ mod tests {
|
|||
"missing nixpkgs-unstable follows alias:\n{out}"
|
||||
);
|
||||
// And conversely: no literal channel ref baked into meta. If
|
||||
// this fails, someone reintroduced a hardcoded ref — see
|
||||
// #619 follow-up rationale for why that drifts.
|
||||
// this fails, someone reintroduced a hardcoded ref — would
|
||||
// drift away from hyperhive's pin.
|
||||
assert!(
|
||||
!out.contains("nixpkgs.url ="),
|
||||
"no literal `nixpkgs.url` should be emitted (hyperhive owns the pin):\n{out}"
|
||||
|
|
|
|||
Loading…
Reference in a new issue