Watch
0
0
Fork
You've already forked hyperhive
0

bind a container apply to the closure realised for the agent, and record the deployed pins on drift

hive-priv applies only the path its last RealiseClosure returned for that agent. On drift the swap and create record the pins of the commit the closure was built from.
This commit is contained in:
atlas 2026-10-29 22:36:58 +01:00
commit 6b800600e2

View file

@ -129,6 +129,15 @@ impl Host for FakeHost {
Ok(self.built.clone())
}
async fn pins_at(&self, name: &str, rev: &str) -> anyhow::Result<MetaBuild> {
self.step("pins_at", format!("pins_at {name} {rev}"))?;
Ok(MetaBuild {
rev: rev.to_owned(),
hyperhive: "dh".to_owned(),
config: Some("dc".to_owned()),
})
}
async fn realise_closure(&self, name: &str, store_path: &str) -> anyhow::Result<()> {
self.record(format!("realise {name} {store_path}"));
if self.fails("realise") {
@ -531,14 +540,46 @@ async fn swap_applies_a_closure_built_from_another_commit_with_a_warning() {
let id = node_id();
let (result, log) = logging(run_swap(&host, &coord, "alice", id)).await;
result.expect("drift is not a refusal");
// The pins of the commit the closure came from are read before the apply
// and recorded after it, in place of the ones this hive chose; the
// bookkeeping node then marks those.
assert_eq!(
host.calls().last(),
Some(&format!("swap_update alice {CLOSURE} node={}", id.get()))
host.calls(),
[
"recorded_rev alice".to_owned(),
"closure_record alice".to_owned(),
format!("realise alice {CLOSURE}"),
format!("pins_at alice {OTHER_REV}"),
format!("swap_update alice {CLOSURE} node={}", id.get()),
format!("record_meta alice {OTHER_REV}"),
]
);
assert_eq!(log.at(tracing::Level::WARN).len(), 1);
assert!(log.at(tracing::Level::ERROR).is_empty());
}
/// Pins that can't be read, or an apply that fails, leave the record alone.
#[tokio::test]
async fn a_drifted_swap_records_pins_only_for_an_apply_that_happened() {
for failing in ["pins_at", "swap_update"] {
let (_dir, coord) = coordinator();
let host = FakeHost::down().built(OTHER_REV, true).failing(failing);
run_swap(&host, &coord, "alice", node_id())
.await
.expect_err(failing);
let calls = host.calls();
assert!(
!calls.iter().any(|c| c.starts_with("record_meta")),
"{failing}: {calls:?}"
);
assert_eq!(
calls.iter().any(|c| c.starts_with("swap_update")),
failing == "swap_update",
"{failing}: {calls:?}"
);
}
}
/// A failed update fails the node and nothing else: the swap never restarts or
/// stops the container itself — recovery is the DAG's tail `Reconcile`.
#[tokio::test]
@ -897,9 +938,18 @@ async fn create_uses_a_closure_built_from_another_commit_with_a_warning() {
let (result, log) = logging(run_create(&host, "alice")).await;
result.expect("drift is not a refusal");
let calls = host.calls();
assert!(
calls.contains(&format!("create_only alice {CLOSURE}")),
"{calls:?}"
// Created from the swarm's closure, and marked as the commit it was built
// from, not the one this hive chose.
assert_eq!(
calls,
[
"resolve_meta alice".to_owned(),
"closure_record alice".to_owned(),
format!("realise alice {CLOSURE}"),
format!("pins_at alice {OTHER_REV}"),
format!("create_only alice {CLOSURE}"),
format!("mark_deployed alice {OTHER_REV}"),
]
);
assert_eq!(log.at(tracing::Level::WARN).len(), 1);
assert!(log.at(tracing::Level::ERROR).is_empty());