fn sh_squote(s: &str) -> String {
format!("'{}'", s.replace('\'', r"'\''"))
}
pub const INSTALL_SCRIPT_TEMPLATE: &str = include_str!("control_plane_install.sh");
pub fn build_install_script(version: &str, url: &str, sha256: &str, sudo: bool) -> String {
let sudo_kw = if sudo { "sudo" } else { "" };
format!(
"SUDO={sudo_kw}\nURL={url}\nSHA={sha}\nVER={ver}\n{body}",
sudo_kw = sudo_kw,
url = sh_squote(url),
sha = sh_squote(sha256),
ver = sh_squote(version),
body = INSTALL_SCRIPT_TEMPLATE,
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn script_embeds_the_signed_digest_and_url() {
let url = "https://cdn.yah.dev/yubaba/0.8.19/yubaba-0.8.19-x86_64-unknown-linux-musl.tar.gz";
let sha = "abc123def456";
let s = build_install_script("0.8.19", url, sha, false);
assert!(s.contains(sha), "script must carry the manifest sha256");
assert!(s.contains(url), "script must carry the manifest url");
assert!(s.contains("sha256sum -c -"), "script must verify the digest");
}
#[test]
fn script_is_atomic_and_installs_the_whole_pair() {
let s = build_install_script("0.8.19", "u", "d", false);
assert!(s.contains("mv -f"), "install must be an atomic rename");
assert!(s.contains(".roll-new.$$"), "install must stage to a temp name");
for target in [
"/usr/local/bin/yubaba",
"/usr/local/bin/kamaji",
"/etc/systemd/system/yubaba.slice",
"/etc/systemd/system/kamaji.service",
"/etc/systemd/system/yubaba.service",
] {
assert!(s.contains(target), "script must install {target}");
}
let k = s.find("restart kamaji.service").unwrap();
let y = s.find("restart yubaba.service").unwrap();
assert!(k < y, "kamaji must restart before yubaba");
assert!(s.contains("daemon-reload"));
}
fn executable_lines(script: &str) -> String {
script
.lines()
.filter(|l| !l.trim_start().starts_with('#'))
.collect::<Vec<_>>()
.join("\n")
}
#[test]
fn script_never_touches_durable_state() {
let s = executable_lines(&build_install_script("0.8.19", "u", "d", true));
assert!(!s.contains("identity.json"), "must not touch host identity");
assert!(!s.contains("/var/lib/yah-cloud"), "must not touch state dir");
assert!(!s.contains("raft"), "must not touch the raft log dir");
assert!(!s.contains("rm -rf /"), "must not wipe system paths");
}
#[test]
fn script_anchors_every_file_it_replaces_before_replacing_it() {
let s = build_install_script("0.8.19", "u", "d", true);
let first_anchor = s.find("anchor /usr/local/bin/yubaba").expect("anchors");
let first_install = s.find("install_atomic \"$D/yubaba\"").expect("installs");
assert!(
first_anchor < first_install,
"anchors must be written before anything is replaced"
);
assert!(s.contains(r#"STAMP="$(date -u +%Y%m%d)""#));
for target in [
"/usr/local/bin/yubaba",
"/usr/local/bin/kamaji",
"/etc/systemd/system/yubaba.slice",
"/etc/systemd/system/kamaji.service",
"/etc/systemd/system/yubaba.service",
] {
assert!(
s.contains(&format!("anchor {target}\n")),
"every installed path needs a rollback anchor, missing {target}"
);
}
}
#[test]
fn anchoring_is_idempotent_within_a_day() {
let s = build_install_script("0.8.19", "u", "d", true);
assert!(
s.contains(r#"if [ ! -e "$1.rollback-$STAMP" ]; then"#),
"anchor must refuse to overwrite an existing same-day anchor"
);
}
#[test]
fn success_is_asserted_by_content_not_by_version_string() {
let s = build_install_script("0.8.19", "u", "d", true);
for pair in [
r#"assert_installed_bytes "$D/yubaba" /usr/local/bin/yubaba"#,
r#"assert_installed_bytes "$D/kamaji" /usr/local/bin/kamaji"#,
] {
assert!(s.contains(pair), "missing content assertion: {pair}");
}
let body = &s[s.find("assert_installed_bytes() {").expect("assert fn")..];
assert!(
body.contains("content assertion FAILED") && body.contains("exit 1"),
"a content mismatch must exit nonzero so the caller sees a failed roll"
);
assert!(
s.find("assert_installed_bytes \"$D/yubaba\"").unwrap()
< s.find("systemctl restart kamaji.service").unwrap(),
"content must be proved before the supervision tree restarts"
);
}
#[test]
fn the_template_is_the_only_copy_of_the_body() {
let s = build_install_script("0.8.19", "u", "d", false);
assert!(
s.ends_with(INSTALL_SCRIPT_TEMPLATE),
"the built script must be prologue + the shared template, verbatim"
);
let prologue = &s[..s.len() - INSTALL_SCRIPT_TEMPLATE.len()];
assert_eq!(
prologue.lines().count(),
4,
"the prologue is exactly URL/SHA/VER/SUDO — anything else belongs in the template"
);
}
#[test]
fn sudo_prefix_tracks_the_caller() {
let s = build_install_script("0.8.19", "u", "d", true);
assert!(s.contains("SUDO=sudo"));
let s = build_install_script("0.8.19", "u", "d", false);
assert!(s.contains("SUDO=\n") || s.contains("SUDO="));
}
}