Watch
0
0
Fork
You've already forked hyperhive
0

gate each deploy on a swarm build of the agent from internal/meta

This commit is contained in:
atlas 2026-10-26 16:40:06 +01:00
commit 5e64048e53

View file

@ -1,5 +1,6 @@
//! The swarm builder's requests: the subjects swarm-controller sends a build,
//! an archetype evaluation and a meta-flake lock on, and what travels each way.
//! an archetype evaluation, a meta-flake lock and an agent build from the meta
//! flake on, and what travels each way.
//!
//! Three crates name these: the controller sends the requests, the builder
//! answers them, and the auth-callout responder grants the subjects to each.
@ -115,6 +116,41 @@ pub enum LockReply {
Failed { error_tail: String },
}
/// The subject an agent build from the swarm meta flake goes to, with the same
/// one publisher and one subscriber as [`SUBJECT`]. A subject of its own, so a
/// builder that predates the request has no subscriber and the controller gets
/// no responders at once.
pub const META_SUBJECT: &str = "$SWARM.builder.build-meta";
/// The swarm meta flake's repo on the forge, as `<org>/<repo>`.
pub const META_REPO_PATH: &str = "internal/meta";
/// One agent built from the swarm meta flake: the toplevel of
/// `nixosConfigurations.<agent>`, with every input at the flake's own lock.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BuildFromMeta {
/// The agent, which is also its name in the meta flake.
pub agent: String,
/// The `internal/meta` commit to build, a full 40-hex id.
pub meta_rev: String,
}
/// The builder's answer to one [`BuildFromMeta`].
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "state", rename_all = "kebab-case")]
pub enum FromMetaReply {
/// The toplevel built; `out_path` is its store path in the builder host's
/// store and `system` the platform the builder host builds for.
Built { out_path: String, system: String },
/// `nix` ran and the build did not finish: the last lines it printed, or
/// the deadline it hit. The controller holds the deploy back.
Failed { error_tail: String },
/// The builder did not get to run `nix`: the build slot stayed busy or the
/// forge token could not be read. Says nothing about the config, so the
/// controller tries again.
Unavailable { reason: String },
}
#[cfg(test)]
mod tests {
use serde_json::json;
@ -199,11 +235,53 @@ mod tests {
}
}
#[test]
fn the_meta_build_request_and_reply_serialize_as_pinned() {
let req = BuildFromMeta {
agent: "atlas".to_owned(),
meta_rev: "abc".to_owned(),
};
let value = serde_json::to_value(&req).expect("encodes");
assert_eq!(value, json!({"agent": "atlas", "meta_rev": "abc"}));
assert_eq!(
serde_json::from_value::<BuildFromMeta>(value).expect("decodes"),
req
);
for (reply, wire) in [
(
FromMetaReply::Built {
out_path: "/nix/store/x".to_owned(),
system: "x86_64-linux".to_owned(),
},
json!({"state": "built", "out_path": "/nix/store/x", "system": "x86_64-linux"}),
),
(
FromMetaReply::Failed {
error_tail: "e".to_owned(),
},
json!({"state": "failed", "error_tail": "e"}),
),
(
FromMetaReply::Unavailable {
reason: "r".to_owned(),
},
json!({"state": "unavailable", "reason": "r"}),
),
] {
assert_eq!(serde_json::to_value(&reply).expect("encodes"), wire);
assert_eq!(
serde_json::from_value::<FromMetaReply>(wire).expect("decodes"),
reply
);
}
}
#[test]
fn the_subjects_are_pinned() {
assert_eq!(SUBJECT, "$SWARM.builder.build");
assert_eq!(ARCHETYPES_SUBJECT, "$SWARM.builder.archetypes");
assert_eq!(LOCK_SUBJECT, "$SWARM.builder.lock-meta");
assert_eq!(META_SUBJECT, "$SWARM.builder.build-meta");
assert_eq!(QUEUE_GROUP, "builder");
}