build agents only from internal/meta; delete the hive's local meta flake
The hive no longer renders, locks or commits a meta flake. A rebuild's MetaSync node picks the internal/meta commit and records it with the hyperhive and config pins it holds; Prebuild, Swap and Create build from it, and a head that does not pin the agent's current config fails the build naming the agent. hive-priv's meta_rev is required. Removed: meta.rs sync/lock/render, the manual-rebuild relock, the boot sweep's hyperhive bump (MetaLock), the update-meta-inputs form and its panel, MetaWindow, the hive's hyperhive_rev setting. Kept: agent_set::refresh holds what sync_agents also did (topology.json, the gateway's agent-socket map and nginx include), called from spawn, destroy, rebuild and the migration. The boot reconcile and the stale-start upgrade compare the agent's marker with the hyperhive pin of internal/meta; the dashboard's deployed chip reads the config marker a successful build writes.
This commit is contained in:
parent
cea4827918
commit
4aa11b3a94
1 changed files with 1042 additions and 3793 deletions
|
|
@ -55,31 +55,6 @@ fn emit_rebuilt_tails(builder: &JobBuilder, agent: &str, roots: &[Handle<'_>]) {
|
|||
);
|
||||
}
|
||||
|
||||
/// Declare one rebuild subgraph per agent onto the builder a running
|
||||
/// [`NodeKind::MetaLock`] was handed.
|
||||
///
|
||||
/// **Into the emitter's own builder, not as new DAGs.** Growing in-DAG is what
|
||||
/// roots each subgraph on the `MetaLock`, so the whole sweep (or meta-update
|
||||
/// cascade) stays one unit of work the operator can watch and cancel, and every
|
||||
/// rebuild builds against the lock the emitter just bumped.
|
||||
///
|
||||
/// Same reason as [`fanned_out_mechanical`] for living here: this was the
|
||||
/// second construction site declaring nodes inline in an executor.
|
||||
pub(crate) fn grown_rebuilds(builder: &JobBuilder, agents: &[String], relock: bool) {
|
||||
for agent in agents {
|
||||
rebuild_nodes(builder, agent, relock, None);
|
||||
}
|
||||
}
|
||||
|
||||
/// As [`grown_rebuilds`], but each agent gets its `Signal` → `Drain` window
|
||||
/// before being stopped. The boot sweep's flavour: it stops agents that were
|
||||
/// mid-turn when the host came up, so they drain rather than being cut off.
|
||||
pub(crate) fn grown_graceful_rebuilds(builder: &JobBuilder, agents: &[String], relock: bool) {
|
||||
for agent in agents {
|
||||
graceful_rebuild_nodes(builder, agent, relock, None);
|
||||
}
|
||||
}
|
||||
|
||||
/// Declare the mechanical node a [`NodeKind::Reconcile`] planner fans out
|
||||
/// (`Start` / `Stop`) onto the builder it was handed while running.
|
||||
///
|
||||
|
|
@ -162,7 +137,7 @@ pub(crate) fn pause_quiesce<'a>(builder: &'a JobBuilder, agent: &str) -> Handle<
|
|||
/// cover every node in the subgraph without the caller knowing its shape.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub(crate) struct RebuildRoots<'a> {
|
||||
/// The meta-repo preamble.
|
||||
/// The preamble.
|
||||
pub meta_sync: Handle<'a>,
|
||||
/// The brace holding the agent lease and the build slot — its roll-up
|
||||
/// carries the whole mechanical subtree (`Prebuild`, the quiesce chain,
|
||||
|
|
@ -182,13 +157,9 @@ impl<'a> RebuildRoots<'a> {
|
|||
/// The rebuild node subtree — three group roots (`MetaSync`, the `AgentWindow`
|
||||
/// brace, `Reconcile`). `after`, when given, is the node it chains behind. The
|
||||
/// shape itself is in `docs/scheduler/coordinator.md`; the code below is the source of
|
||||
/// truth for it, so only the three choices a reader would otherwise undo are
|
||||
/// truth for it, so only the two choices a reader would otherwise undo are
|
||||
/// called out here:
|
||||
///
|
||||
/// - **`MetaSync` is a sibling root, not the brace's parent** — it owns the
|
||||
/// *global* `MetaWindow`, and a resource is held across the holder's whole
|
||||
/// subtree, so parenting the rebuild under it would serialise every agent's
|
||||
/// nix build behind one hive-wide window.
|
||||
/// - **`StopForUpdate` waits on `Prebuild` as well as `Drain`** — running the
|
||||
/// drain early is the win; taking the container *down* early is pure downtime.
|
||||
/// - **`Reconcile` is a top-level root, not a child** — so it survives the
|
||||
|
|
@ -198,15 +169,12 @@ impl<'a> RebuildRoots<'a> {
|
|||
fn rebuild_subtree<'a>(
|
||||
builder: &'a JobBuilder,
|
||||
agent: &str,
|
||||
relock: bool,
|
||||
graceful: bool,
|
||||
after: Option<Handle<'a>>,
|
||||
) -> RebuildRoots<'a> {
|
||||
let a = || agent.to_owned();
|
||||
|
||||
let mut meta_sync = builder
|
||||
.node(NodeKind::MetaSync { agent: a(), relock })
|
||||
.needs(Resource::MetaWindow);
|
||||
let mut meta_sync = builder.node(NodeKind::MetaSync { agent: a() });
|
||||
if let Some(after) = after {
|
||||
meta_sync = meta_sync.after_ok(after);
|
||||
}
|
||||
|
|
@ -263,17 +231,15 @@ fn rebuild_subtree<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
/// The rebuild subtree, stopping the agent outright — the shape five of the six
|
||||
/// call sites want. `relock` re-locks the meta flake inside `MetaSync`; `after`,
|
||||
/// when given, is the node this subgraph chains behind. See
|
||||
/// [`rebuild_subtree`] for the structure.
|
||||
/// The rebuild subtree, stopping the agent outright — the shape most call
|
||||
/// sites want. `after`, when given, is the node this subgraph chains behind.
|
||||
/// See [`rebuild_subtree`] for the structure.
|
||||
pub(crate) fn rebuild_nodes<'a>(
|
||||
builder: &'a JobBuilder,
|
||||
agent: &str,
|
||||
relock: bool,
|
||||
after: Option<Handle<'a>>,
|
||||
) -> RebuildRoots<'a> {
|
||||
rebuild_subtree(builder, agent, relock, false, after)
|
||||
rebuild_subtree(builder, agent, false, after)
|
||||
}
|
||||
|
||||
/// As [`rebuild_nodes`], but the agent gets a `Signal` → `Drain` window to
|
||||
|
|
@ -286,17 +252,15 @@ pub(crate) fn rebuild_nodes<'a>(
|
|||
pub(crate) fn graceful_rebuild_nodes<'a>(
|
||||
builder: &'a JobBuilder,
|
||||
agent: &str,
|
||||
relock: bool,
|
||||
after: Option<Handle<'a>>,
|
||||
) -> RebuildRoots<'a> {
|
||||
rebuild_subtree(builder, agent, relock, true, after)
|
||||
rebuild_subtree(builder, agent, true, after)
|
||||
}
|
||||
|
||||
/// One uniform rebuild shape — no `was_running` branch. `StopForUpdate`
|
||||
/// noops when already down; the tail `Reconcile` auto-noops the start
|
||||
/// when `wanted = Offline` (a rebuild of a deliberately-stopped agent
|
||||
/// leaves it stopped). `relock = false` only for meta-update cascade
|
||||
/// children.
|
||||
/// leaves it stopped).
|
||||
///
|
||||
/// Closed by an [`NodeKind::EmitRebuilt`] tail edged onto all three group-roots
|
||||
/// (`MetaSync`, the `AgentWindow` brace, `Reconcile`) — the brace's roll-up
|
||||
|
|
@ -307,8 +271,8 @@ pub(crate) fn graceful_rebuild_nodes<'a>(
|
|||
///
|
||||
/// Returns those three roots, so a caller that needs to wait on the rebuild can
|
||||
/// name them.
|
||||
pub fn rebuild(builder: &JobBuilder, agent: &str, relock: bool) -> Vec<hive_jobq::NodeGuid> {
|
||||
let roots = rebuild_nodes(builder, agent, relock, None);
|
||||
pub fn rebuild(builder: &JobBuilder, agent: &str) -> Vec<hive_jobq::NodeGuid> {
|
||||
let roots = rebuild_nodes(builder, agent, None);
|
||||
emit_rebuilt_tails(builder, agent, &roots.all());
|
||||
vec![
|
||||
roots.meta_sync.guid(),
|
||||
|
|
@ -318,7 +282,7 @@ pub fn rebuild(builder: &JobBuilder, agent: &str, relock: bool) -> Vec<hive_jobq
|
|||
}
|
||||
|
||||
/// First deploy of an agent this hive has never seen, asked for by the swarm:
|
||||
/// `Provision` (proposed/applied repos, state subvolume, meta registration)
|
||||
/// `Provision` (proposed/applied repos, state subvolume, agent-set registration)
|
||||
/// then `Create` (`nixos-container create`), drop-in write, then `Reconcile`
|
||||
/// starts the container (`wanted = Up`, seeded by
|
||||
/// `swarm_status::queue_first_deploy`). All-or-nothing: `Provision`
|
||||
|
|
@ -330,9 +294,7 @@ pub fn rebuild(builder: &JobBuilder, agent: &str, relock: bool) -> Vec<hive_jobq
|
|||
/// Returns the group root so a caller can wait on the whole subtree.
|
||||
pub fn first_deploy(builder: &JobBuilder, agent: &str) -> Vec<hive_jobq::NodeGuid> {
|
||||
let a = || agent.to_owned();
|
||||
let provision = builder
|
||||
.node(NodeKind::Provision { agent: a() })
|
||||
.needs(Resource::MetaWindow);
|
||||
let provision = builder.node(NodeKind::Provision { agent: a() });
|
||||
let create = builder
|
||||
.node(NodeKind::Create { agent: a() })
|
||||
.needs(Resource::BuildSlot)
|
||||
|
|
@ -403,31 +365,10 @@ pub fn destroy(builder: &JobBuilder, agent: &str, purge: bool) {
|
|||
.after_ok(last);
|
||||
}
|
||||
|
||||
/// Meta-input lock bump. The `MetaLock` executor grows one rebuild subgraph
|
||||
/// per affected agent into *this same* DAG on completion (declared onto the
|
||||
/// builder it was handed) — appended *after* the bump lands so their prebuilds
|
||||
/// run against the post-bump lock, and a failed bump appends nothing
|
||||
/// (replacing the old fan-out-child-DAGs dance).
|
||||
/// `transient = Rebuilding` because those appended subgraphs are rebuilds:
|
||||
/// it's applied per-agent at claim time (the `MetaLock` head needs no lease,
|
||||
/// so the "hyperhive" pseudo-agent gets no pill), giving each cascade agent
|
||||
/// crash-watch suppression during its `Swap` — the property the old child
|
||||
/// `Rebuild` DAGs carried via their own transient.
|
||||
pub fn meta_update(builder: &JobBuilder, inputs: Vec<String>) {
|
||||
let _lock = builder
|
||||
.node(NodeKind::MetaLock {
|
||||
sweep: false,
|
||||
fanout: None,
|
||||
inputs,
|
||||
})
|
||||
.needs(Resource::BuildSlot)
|
||||
.needs(Resource::MetaWindow);
|
||||
}
|
||||
|
||||
// The boot is assembled inline in `workers/auto_update.rs::submit_boot_tree`
|
||||
// as ONE `Boot` DAG (a sweep `MetaLock` root that grows rebuild subgraphs
|
||||
// in-DAG, plus a `Reconcile` root per drifted agent) — no anchor node and no
|
||||
// per-agent child DAGs.
|
||||
// as ONE `Boot` DAG (a graceful rebuild subgraph per stale agent, plus a
|
||||
// `Reconcile` root per drifted agent) — no anchor node and no per-agent child
|
||||
// DAGs.
|
||||
|
||||
/// One matrix user/space sweep. Holding [`Resource::MatrixSweep`] is what keeps
|
||||
/// two passes from overlapping, whichever caller submitted each.
|
||||
|
|
|
|||
Loading…
Reference in a new issue