Watch
0
0
Fork
You've already forked hyperhive
0

drop the proposed config repo: applied seeds from forge main, and the dead pending-init topology list goes with it

This commit is contained in:
damocles 2026-11-01 14:25:46 +01:00 • committed by mara
commit 25295fd33f

View file

@ -102,19 +102,14 @@ pub fn all_agents_in(topo: &BTreeSet<String>) -> Vec<String> {
/// caller. The write is atomic, as a sorted array (`BTreeSet` iterates in
/// key order) to keep git diffs minimal.
///
/// `pending` lists agents that have a provisioned proposed config repo
/// but no container yet (provisioned, not yet spawned). They are KEPT
/// (not dropped) so an agent that exists on disk but has never booted is
/// still a name the hive knows about.
///
/// Fails without writing when the existing file can't be read or parsed:
/// rebuilding it from the live set alone would drop every pending name.
pub fn reconcile(agent_names: &[String], pending: &[String]) -> std::io::Result<bool> {
reconcile_at(&topology_path(), agent_names, pending)
/// rebuilding it from the live set alone would silently rewrite it.
pub fn reconcile(agent_names: &[String]) -> std::io::Result<bool> {
reconcile_at(&topology_path(), agent_names)
}
fn reconcile_at(path: &Path, agent_names: &[String], pending: &[String]) -> std::io::Result<bool> {
let (next, changed) = apply_reconcile(&read_from(path)?, agent_names, pending);
fn reconcile_at(path: &Path, agent_names: &[String]) -> std::io::Result<bool> {
let (next, changed) = apply_reconcile(&read_from(path)?, agent_names);
if changed {
super::write_map(path, &next)?;
}
@ -122,13 +117,12 @@ fn reconcile_at(path: &Path, agent_names: &[String], pending: &[String]) -> std:
}
/// Pure form of [`reconcile`] for unit tests. Adds missing live agents,
/// drops entries for agents that are neither live nor pending-init, and
/// reports whether anything changed.
/// drops entries for agents that are not live, and reports whether anything
/// changed.
#[must_use]
pub fn apply_reconcile(
current: &BTreeSet<String>,
agent_names: &[String],
pending: &[String],
) -> (BTreeSet<String>, bool) {
let mut next = current.clone();
let mut changed = false;
@ -137,8 +131,7 @@ pub fn apply_reconcile(
changed = true;
}
}
let known: std::collections::HashSet<&String> =
agent_names.iter().chain(pending.iter()).collect();
let known: std::collections::HashSet<&String> = agent_names.iter().collect();
next.retain(|name| {
let keep = known.contains(name);
if !keep {
@ -166,7 +159,7 @@ mod tests {
#[test]
fn apply_reconcile_adds_missing_live_agent() {
let live = vec!["alice".to_owned(), "newbie".to_owned()];
let (next, changed) = apply_reconcile(&BTreeSet::new(), &live, &[]);
let (next, changed) = apply_reconcile(&BTreeSet::new(), &live);
assert!(changed);
assert!(next.contains("newbie"));
assert!(next.contains("alice"));
@ -175,30 +168,17 @@ mod tests {
#[test]
fn apply_reconcile_drops_vanished_agent() {
let live = vec!["alice".to_owned()];
let (next, changed) = apply_reconcile(&roster_three(), &live, &[]);
let (next, changed) = apply_reconcile(&roster_three(), &live);
assert!(changed);
assert!(!next.contains("bob"));
assert!(!next.contains("carol"));
assert!(next.contains("alice"));
}
#[test]
fn apply_reconcile_keeps_pending_init_agent() {
// `dora` was provisioned but has no container yet, so it's absent
// from the live set. It must NOT be dropped.
let mut roster = roster_three();
roster.insert("dora".to_owned());
let live = vec!["alice".to_owned(), "bob".to_owned(), "carol".to_owned()];
let pending = vec!["dora".to_owned()];
let (next, changed) = apply_reconcile(&roster, &live, &pending);
assert!(!changed, "no change expected: {next:?}");
assert!(next.contains("dora"));
}
#[test]
fn apply_reconcile_is_a_noop_when_already_in_sync() {
let live = vec!["alice".to_owned(), "bob".to_owned(), "carol".to_owned()];
let (next, changed) = apply_reconcile(&roster_three(), &live, &[]);
let (next, changed) = apply_reconcile(&roster_three(), &live);
assert!(!changed);
assert_eq!(next, roster_three());
}
@ -255,8 +235,7 @@ mod tests {
fn reconcile_leaves_a_corrupt_file_untouched() {
let (_dir, path) = corrupt_file();
let live = vec!["alice".to_owned(), "bob".to_owned()];
let err =
reconcile_at(&path, &live, &[]).expect_err("a corrupt file must not be rewritten");
let err = reconcile_at(&path, &live).expect_err("a corrupt file must not be rewritten");
assert_eq!(err.kind(), std::io::ErrorKind::InvalidData);
assert_eq!(std::fs::read(&path).expect("read"), TRUNCATED.as_bytes());
}
@ -273,7 +252,7 @@ mod tests {
let path = dir.path().join(TOPOLOGY_FILE);
assert!(read_from(&path).expect("missing file").is_empty());
let live = vec!["alice".to_owned(), "bob".to_owned(), "carol".to_owned()];
assert!(reconcile_at(&path, &live, &[]).expect("reconcile"));
assert!(reconcile_at(&path, &live).expect("reconcile"));
assert_eq!(read_from(&path).expect("read"), roster_three());
let entries: Vec<_> = std::fs::read_dir(dir.path())
.expect("read_dir")