pin internal/meta's hyperhive on the controller's rev and rebuild agents when it moves
The swarm controller offers its own running rev (services.hyperhive.c0re. hyperhiveRev) as the hyperhive pin of internal/meta and takes it only when the forge compare API shows it descends from the committed pin; merges to hyperhive main no longer move the pin, and the first commit pins main's head. When a commit moves the pin, every placed agent gets a TriggerDeploy. A deploy after a meta update no longer waits on it succeeding (after_any), so a down builder does not hold back hives that render locally.
This commit is contained in:
parent
5950363a10
commit
22e2d30581
10 changed files with 1027 additions and 148 deletions
|
|
@ -421,3 +421,135 @@ pub fn agent_web_port(name: &str) -> u16 {
|
|||
// Modulo of a u32 by a u16's value is guaranteed < u16::MAX, so try_from never fails.
|
||||
WEB_PORT_BASE + u16::try_from(fnv1a(name) % u32::from(WEB_PORT_RANGE)).unwrap_or(0)
|
||||
}
|
||||
|
||||
/// Whether `rev` is a full git commit id: 40 lowercase hex digits.
|
||||
#[must_use]
|
||||
pub fn is_rev(rev: &str) -> bool {
|
||||
rev.len() == 40 && rev.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))
|
||||
}
|
||||
|
||||
/// The commit `input` is pinned to in a flake [`render_flake`] rendered:
|
||||
/// the `<rev>` of its `<input>.url = "git+<url>?rev=<rev>";` line. `None` when
|
||||
/// the flake has no such input, or its rev is not a commit id.
|
||||
#[must_use]
|
||||
pub fn pinned_rev<'a>(flake_nix: &'a str, input: &str) -> Option<&'a str> {
|
||||
let prefix = format!(" {input}.url = \"");
|
||||
let url = flake_nix
|
||||
.lines()
|
||||
.find_map(|l| l.strip_prefix(prefix.as_str()))?
|
||||
.split_once('"')?
|
||||
.0;
|
||||
let (_, rev) = url.split_once("?rev=")?;
|
||||
let rev = rev.split('&').next()?;
|
||||
is_rev(rev).then_some(rev)
|
||||
}
|
||||
|
||||
/// Whether a flake [`render_flake`] rendered builds agent `name` from the
|
||||
/// config commit `config_head`. `None` is an archetype agent, which has no
|
||||
/// config input to compare.
|
||||
#[must_use]
|
||||
pub fn builds_agent_at(flake_nix: &str, name: &str, config_head: Option<&str>) -> bool {
|
||||
let attr = format!("\n {name} = mkAgent {{ name = \"{name}\";");
|
||||
if !flake_nix.contains(&attr) {
|
||||
return false;
|
||||
}
|
||||
config_head.is_none_or(|head| pinned_rev(flake_nix, &format!("agent-{name}")) == Some(head))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use swarm_wire::agent_archetype::ArchetypeRef;
|
||||
|
||||
use super::*;
|
||||
|
||||
const HYPERHIVE: &str = "1111111111111111111111111111111111111111";
|
||||
const ARGUS: &str = "2222222222222222222222222222222222222222";
|
||||
|
||||
fn spec(name: &str, archetype: Option<ArchetypeRef>, pin: Option<AgentPin>) -> AgentSpec {
|
||||
AgentSpec {
|
||||
name: name.to_owned(),
|
||||
port: agent_web_port(name),
|
||||
archetype,
|
||||
pin,
|
||||
}
|
||||
}
|
||||
|
||||
fn rendered() -> String {
|
||||
let url = |repo: &str, rev: &str| {
|
||||
pinned_git_url(&format!("https://f.t/{repo}.git"), Some(rev)).unwrap()
|
||||
};
|
||||
render_flake(
|
||||
&url("hyperhive/hyperhive", HYPERHIVE),
|
||||
&[
|
||||
spec(
|
||||
"argus",
|
||||
None,
|
||||
Some(AgentPin {
|
||||
url: url("agent-configs/argus", ARGUS),
|
||||
canonical_inputs: vec![],
|
||||
}),
|
||||
),
|
||||
spec(
|
||||
"rev",
|
||||
Some(ArchetypeRef {
|
||||
source: "hyperhive".to_owned(),
|
||||
name: "reviewer".to_owned(),
|
||||
}),
|
||||
None,
|
||||
),
|
||||
],
|
||||
&[],
|
||||
false,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_rendered_flake_names_the_commit_each_input_is_pinned_to() {
|
||||
let flake = rendered();
|
||||
assert_eq!(pinned_rev(&flake, "hyperhive"), Some(HYPERHIVE));
|
||||
assert_eq!(pinned_rev(&flake, "agent-argus"), Some(ARGUS));
|
||||
assert_eq!(
|
||||
pinned_rev(&flake, "agent-rev"),
|
||||
None,
|
||||
"an archetype has no input"
|
||||
);
|
||||
assert_eq!(pinned_rev(&flake, "agent-ghost"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_pin_that_is_not_a_commit_id_is_no_pin() {
|
||||
let flake = " hyperhive.url = \"git+https://f.t/h.git?rev=main\";\n";
|
||||
assert_eq!(pinned_rev(flake, "hyperhive"), None);
|
||||
assert_eq!(
|
||||
pinned_rev(
|
||||
" hyperhive.url = \"git+https://f.t/h.git\";\n",
|
||||
"hyperhive"
|
||||
),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_agent_is_built_only_at_the_config_head_the_flake_pins() {
|
||||
let flake = rendered();
|
||||
assert!(builds_agent_at(&flake, "argus", Some(ARGUS)));
|
||||
assert!(
|
||||
!builds_agent_at(&flake, "argus", Some(HYPERHIVE)),
|
||||
"a stale pin"
|
||||
);
|
||||
assert!(
|
||||
!builds_agent_at(&flake, "ghost", Some(ARGUS)),
|
||||
"an agent the flake lacks"
|
||||
);
|
||||
assert!(builds_agent_at(&flake, "rev", None));
|
||||
assert!(!builds_agent_at(&flake, "ghost", None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_agent_whose_name_ends_another_s_is_not_that_agent() {
|
||||
let flake = rendered();
|
||||
assert!(!builds_agent_at(&flake, "gus", Some(ARGUS)));
|
||||
assert!(!builds_agent_at(&flake, "v", None));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue