download an agent's cached closure in prebuild, building locally when that fails
This commit is contained in:
parent
b845630023
commit
c16cf1756f
1 changed files with 1032 additions and 81 deletions
|
|
@ -110,6 +110,33 @@ pub fn valid_meta_rev(rev: &str) -> bool {
|
|||
rev.len() == 40 && rev.bytes().all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))
|
||||
}
|
||||
|
||||
/// Whether `path` is a store path in the shape nix prints one: `/nix/store/`,
|
||||
/// 32 lowercase letters or digits, `-`, then a name of the characters store
|
||||
/// names allow. The path crosses the wire in [`PrivRequest::RealiseClosure`],
|
||||
/// so `hive-priv` checks it before it hands it to nix.
|
||||
#[must_use]
|
||||
pub fn valid_closure_path(path: &str) -> bool {
|
||||
let Some(rest) = path.strip_prefix("/nix/store/") else {
|
||||
return false;
|
||||
};
|
||||
let Some((hash, name)) = rest.split_once('-') else {
|
||||
return false;
|
||||
};
|
||||
hash.len() == 32
|
||||
&& hash
|
||||
.bytes()
|
||||
.all(|b| b.is_ascii_lowercase() || b.is_ascii_digit())
|
||||
&& !name.is_empty()
|
||||
&& name.bytes().all(|b| {
|
||||
b.is_ascii_alphanumeric() || matches!(b, b'+' | b'-' | b'.' | b'_' | b'?' | b'=')
|
||||
})
|
||||
}
|
||||
|
||||
/// What a failed [`PrivRequest::RealiseClosure`] says when nix refused a path
|
||||
/// because no trusted key signed it, so a caller can tell that apart from a
|
||||
/// cache that is down.
|
||||
pub const SIGNATURE_REFUSED: &str = "no signature by the swarm cache's key";
|
||||
|
||||
/// The flake the swarm publishes at `internal/meta`, at `rev`:
|
||||
/// `git+<forge_base>/internal/meta.git?rev=<rev>`. `forge_base` is the forge's
|
||||
/// `http` origin, which the hive's git credential helper answers for.
|
||||
|
|
@ -270,6 +297,18 @@ pub enum PrivRequest {
|
|||
meta_rev: String,
|
||||
},
|
||||
|
||||
/// `nix-store --realise <store_path>` from the swarm's binary cache, never
|
||||
/// building: the closure the swarm built for `agent`, downloaded into the
|
||||
/// store ahead of the update that applies it.
|
||||
///
|
||||
/// The request names no cache and no key: hive-priv takes the cache's
|
||||
/// address from its own unit's environment and the key and the pull token
|
||||
/// from files only root can write, and nix accepts the path only with a
|
||||
/// signature by that key. `store_path` is checked by
|
||||
/// [`valid_closure_path`]. A path nix could not substitute answers with an
|
||||
/// error that starts `not available: `.
|
||||
RealiseClosure { agent: String, store_path: String },
|
||||
|
||||
/// `nixos-container destroy <name>`
|
||||
DestroyContainer { name: String },
|
||||
|
||||
|
|
@ -661,7 +700,7 @@ pub enum PrivEvent {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{InfraContainer, PrivRequest, meta_flake_url, valid_meta_rev};
|
||||
use super::{InfraContainer, PrivRequest, meta_flake_url, valid_closure_path, valid_meta_rev};
|
||||
|
||||
#[test]
|
||||
fn infra_control_names_no_c0re_or_swarm_service() {
|
||||
|
|
@ -717,6 +756,46 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_a_store_path_is_a_closure_path() {
|
||||
let hash = "0123456789abcdfghijklmnpqrsvwxyz";
|
||||
for good in [
|
||||
format!("/nix/store/{hash}-nixos-system-atlas-26.05.20261026.718f670"),
|
||||
format!("/nix/store/{hash}-a"),
|
||||
] {
|
||||
assert!(valid_closure_path(&good), "{good}");
|
||||
}
|
||||
for bad in [
|
||||
String::new(),
|
||||
"/nix/store/".to_owned(),
|
||||
format!("/nix/store/{hash}"),
|
||||
format!("/nix/store/{hash}-"),
|
||||
format!("/nix/store/{}-x", &hash[1..]),
|
||||
format!("/nix/store/{hash}a-x"),
|
||||
format!("/nix/store/{}-x", hash.to_uppercase()),
|
||||
format!("/tmp/{hash}-x"),
|
||||
format!("/nix/store/{hash}-x/bin/sh"),
|
||||
format!("/nix/store/{hash}-../x"),
|
||||
format!("/nix/store/{hash}-x y"),
|
||||
format!("/nix/store/{hash}-x\n"),
|
||||
format!("/nix/store/../../etc/{hash}-x"),
|
||||
format!("/nix/store/{hash}-x;id"),
|
||||
] {
|
||||
assert!(!valid_closure_path(&bad), "{bad:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_closure_request_names_an_agent_and_a_path() {
|
||||
let line = r#"{"op":"realise_closure","agent":"atlas","store_path":"/nix/store/x"}"#;
|
||||
assert!(matches!(
|
||||
serde_json::from_str::<PrivRequest>(line),
|
||||
Ok(PrivRequest::RealiseClosure { .. })
|
||||
));
|
||||
let without = r#"{"op":"realise_closure","agent":"atlas"}"#;
|
||||
assert!(serde_json::from_str::<PrivRequest>(without).is_err());
|
||||
}
|
||||
|
||||
/// `meta_rev` is required: there is no local flake to fall back to.
|
||||
#[test]
|
||||
fn a_request_without_meta_rev_is_refused() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue