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
|
|
@ -51,7 +51,7 @@ fn insert_named(
|
|||
}
|
||||
|
||||
fn rebuild(builder: &JobBuilder, agent: &str) -> Vec<hive_jobq::NodeGuid> {
|
||||
templates::rebuild(builder, agent, true)
|
||||
templates::rebuild(builder, agent)
|
||||
}
|
||||
|
||||
/// Restart shape with every agent treated as **running** — the online
|
||||
|
|
@ -468,7 +468,7 @@ fn rebuild_chain_is_declared_serial() {
|
|||
fn graceful_rebuild_chain_drains_before_stopping() {
|
||||
let q = JobQueue::new(1);
|
||||
insert(&q, |builder| {
|
||||
templates::graceful_rebuild_nodes(builder, "agent-a", true, None);
|
||||
templates::graceful_rebuild_nodes(builder, "agent-a", None);
|
||||
});
|
||||
// Asserted as full rows, not just kinds: the kind list is identical whether
|
||||
// the quiesce chain runs beside the build or nested under it, so a
|
||||
|
|
@ -515,8 +515,8 @@ fn graceful_rebuild_chain_drains_before_stopping() {
|
|||
}
|
||||
|
||||
/// The non-graceful shape is the default everywhere except the boot sweep:
|
||||
/// a manual rebuild, a meta-update cascade child and a deploy must NOT spend a
|
||||
/// drain window, so `StopForUpdate` waits only on `Prebuild`.
|
||||
/// a manual rebuild and a deploy must NOT spend a drain window, so
|
||||
/// `StopForUpdate` waits only on `Prebuild`.
|
||||
#[test]
|
||||
fn non_graceful_rebuild_has_no_signal_or_drain() {
|
||||
// Read the shape off the queue rather than out of a node list: a declared
|
||||
|
|
@ -524,7 +524,7 @@ fn non_graceful_rebuild_has_no_signal_or_drain() {
|
|||
// observable where it matters — in what the scheduler runs.
|
||||
let q = JobQueue::new(1);
|
||||
insert(&q, |builder| {
|
||||
templates::rebuild_nodes(builder, "agent-a", true, None);
|
||||
templates::rebuild_nodes(builder, "agent-a", None);
|
||||
});
|
||||
assert_eq!(
|
||||
declared_shape(&q)
|
||||
|
|
@ -576,10 +576,10 @@ fn rebuild_chain_declares_its_resources_on_the_brace() {
|
|||
res("reconcile"),
|
||||
],
|
||||
[
|
||||
// The meta preamble takes the global window and *nothing else* —
|
||||
// no slot (it does no nix work) and no lease. It stays a sibling
|
||||
// root so that hive-global window is not held across any build.
|
||||
vec![Resource::MetaWindow],
|
||||
// The preamble declares nothing — no slot (it does no nix work) and
|
||||
// no lease. It stays a sibling root so nothing it holds is held
|
||||
// across a build.
|
||||
vec![],
|
||||
// The brace takes both, atomically, and holds them for its whole
|
||||
// subtree. Hoisting the slot here is not a widening: it already
|
||||
// spanned the entire rebuild when `Prebuild` held it, because a unit
|
||||
|
|
@ -782,44 +782,43 @@ fn boot_sweep_nodes_declare_their_own_resources() {
|
|||
// nothing in this module covered it. When resource derivation moved to the
|
||||
// construction sites, this path was missed and both kinds silently declared
|
||||
// nothing — dropping the agent lease a boot `Reconcile` needs to not race
|
||||
// another DAG's container ops, and letting the sweep `MetaLock` land its
|
||||
// meta commit inside another node's staged deploy window. Nothing failed to
|
||||
// compile; only an exhaustive caller list would have caught it.
|
||||
// another DAG's container ops. Nothing failed to compile; only an
|
||||
// exhaustive caller list would have caught it.
|
||||
let q = JobQueue::new(4);
|
||||
insert(&q, |builder| {
|
||||
crate::workers::auto_update::boot_nodes(
|
||||
builder,
|
||||
true,
|
||||
vec!["stale-agent".to_owned()],
|
||||
&["stale-agent".to_owned()],
|
||||
vec!["drifted-agent".to_owned()],
|
||||
);
|
||||
});
|
||||
|
||||
let mut lock = declared_resources(&q, node_of(&q, "meta_lock"));
|
||||
lock.sort_by_key(|r| format!("{r:?}"));
|
||||
let mut window = declared_resources(&q, node_of(&q, "agent_window"));
|
||||
window.sort_by_key(|r| format!("{r:?}"));
|
||||
assert_eq!(
|
||||
lock,
|
||||
vec![Resource::BuildSlot, Resource::MetaWindow],
|
||||
"the sweep MetaLock runs a nix lock bump and commits to meta"
|
||||
window,
|
||||
vec![
|
||||
Resource::Agent("stale-agent".to_owned()),
|
||||
Resource::BuildSlot
|
||||
],
|
||||
"a swept rebuild holds the agent's lease and a build slot"
|
||||
);
|
||||
|
||||
// Two `reconcile` nodes exist: the swept agent's recovery tail and the
|
||||
// drifted agent's own.
|
||||
assert_eq!(
|
||||
declared_resources(&q, node_of(&q, "reconcile")),
|
||||
vec![Resource::Agent("drifted-agent".to_owned())],
|
||||
declared_resources_of_kind(&q, "reconcile"),
|
||||
vec![
|
||||
vec![Resource::Agent("drifted-agent".to_owned())],
|
||||
vec![Resource::Agent("stale-agent".to_owned())],
|
||||
],
|
||||
"a boot Reconcile touches the container, so it holds that agent's lease"
|
||||
);
|
||||
}
|
||||
|
||||
/// Crash-watch suppression for a cascade rebuild, which the deleted half of
|
||||
/// `meta_update_grows_cascade_in_dag` used to assert via a DAG-level
|
||||
/// `transient` field on the submit-time spec (both the field and the spec type
|
||||
/// are gone).
|
||||
///
|
||||
/// The property is unchanged — a container going down under a rebuild must not
|
||||
/// read as a crash — but it is no longer a DAG-level declaration: each node
|
||||
/// answers for itself, so the assertion moves to the nodes a cascade actually
|
||||
/// runs. Kept as its own test rather than dropped, because it is the *property*
|
||||
/// that mattered, not the field that used to carry it.
|
||||
/// Crash-watch suppression for a rebuild: a container going down under a
|
||||
/// rebuild must not read as a crash. Each node answers for itself, so the
|
||||
/// assertion is on the nodes a rebuild actually runs.
|
||||
#[test]
|
||||
fn rebuild_chain_nodes_suppress_crash_watch() {
|
||||
for kind in [
|
||||
|
|
@ -990,26 +989,15 @@ fn a_fanned_out_mechanical_node_declares_its_agent_lease() {
|
|||
);
|
||||
}
|
||||
|
||||
/// A running `MetaLock` grows one rebuild subgraph per agent into **its own
|
||||
/// DAG**, rooted on itself — not as child DAGs. That is what keeps a boot sweep
|
||||
/// (or a meta-update cascade) one unit of work, with every rebuild building
|
||||
/// against the lock the emitter just bumped.
|
||||
///
|
||||
/// Replaces `grown_subgraph_roots_on_emitter_and_rebases_local_deps` and
|
||||
/// `meta_update_grows_cascade_in_dag`, which differed only in which rebuild
|
||||
/// flavour they grew and each minted a builder by hand to simulate the graft.
|
||||
/// What they were checking is the `grown_*_rebuilds` templates, so this calls
|
||||
/// one — the boot sweep's, since that is the caller that grows a graceful one.
|
||||
///
|
||||
/// That the grafted work lands under the emitter, and that the emitter parks in
|
||||
/// `Finishing` until it settles, is `hive_jobq`'s
|
||||
/// (`a_completing_node_grows_the_work_it_declared`).
|
||||
/// The boot sweep declares one rebuild subgraph per stale agent into **one
|
||||
/// DAG**, each an independent root — not as child DAGs. That keeps a boot sweep
|
||||
/// one unit of work the operator can watch and cancel.
|
||||
#[test]
|
||||
fn a_meta_lock_grows_one_rebuild_subgraph_per_agent() {
|
||||
fn the_boot_sweep_declares_one_rebuild_subgraph_per_stale_agent() {
|
||||
let q = JobQueue::new(4);
|
||||
let agents = vec!["alice".to_owned(), "bob".to_owned()];
|
||||
insert(&q, |builder| {
|
||||
templates::grown_graceful_rebuilds(builder, &agents, true);
|
||||
crate::workers::auto_update::boot_nodes(builder, &agents, Vec::new());
|
||||
});
|
||||
|
||||
// One chain per agent, each an independent group root — so the two rebuild
|
||||
|
|
|
|||
Loading…
Reference in a new issue