Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/edda

crates/appliance-pid1/src/install.rs

//! **Whether this /data is the one the box had before its last install** (Gunnar's
//! `pid1::{INSTALL_RECORD, InstallRecord, Kept, announce_install_mode}`, moved here).
//! korp-installer (edda c09dd54, the data-survives rule) writes `/boot/korp/install`:
//! `mode=upgrade` + the kept `korp-data` span when it re-ran on a disk it had laid and kept
//! P3; `mode=upgrade` + `kept=data-set` on the boot disk of a multi-disk box; `mode=fresh`
//! otherwise. One `install.mode` line says it, so an operator knows a reinstall did NOT
//! re-open bootstrap. Silent when the record is absent (a container, a live ISO).

/// Where korp-installer wrote the install-mode record (the ESP, mounted at `/boot`).
pub const INSTALL_RECORD: &str = "/boot/korp/install";

/// What korp-installer's `korp/install` record says.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InstallRecord {
    /// `upgrade` / `fresh`; `-` when the line is missing.
    pub mode: String,
    /// What was kept, when the record names it.
    pub kept: Option<Kept>,
}

/// The kept /data, in the two shapes the record can spell.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Kept {
    /// `kept=korp-data` + `kept_partno=` + `kept_bytes=`: the boot disk's P3.
    Partition {
        /// The partition number.
        partno: u32,
        /// Its size in bytes (0 when the record did not say).
        bytes: u64,
    },
    /// `kept=data-set`: the members hold it, the record names no span.
    DataSet,
}

impl InstallRecord {
    /// Pure over the record text (`key=value` lines). A `kept=korp-data` without a
    /// parsable `kept_partno` says nothing about the span: `kept: None`, never `p-`.
    pub fn parse(text: &str) -> Self {
        let get = |k: &str| text.lines().find_map(|l| l.strip_prefix(k).and_then(|r| r.strip_prefix('='))).map(str::trim);
        let mode = get("mode").unwrap_or("-").to_owned();
        let kept = match get("kept") {
            Some("data-set") => Some(Kept::DataSet),
            Some("korp-data") => get("kept_partno").and_then(|p| p.parse().ok()).map(|partno| Kept::Partition { partno, bytes: get("kept_bytes").and_then(|b| b.parse().ok()).unwrap_or(0) }),
            _ => None,
        };
        InstallRecord { mode, kept }
    }

    /// The `install.mode` line (Gunnar's event fields, one line): `set` is the kept data
    /// set's shape as the product measured it (`4×75G`), `-` when unknown.
    pub fn line(&self, set: Option<&str>) -> String {
        match &self.kept {
            Some(Kept::Partition { partno, bytes }) => format!("install.mode mode={} kept=korp-data kept_partno={partno} kept_gib={:.1}", self.mode, *bytes as f64 / (1u64 << 30) as f64),
            Some(Kept::DataSet) => format!("install.mode mode={} kept=data-set kept_set={}", self.mode, set.unwrap_or("-")),
            None => format!("install.mode mode={} kept=-", self.mode),
        }
    }
}

/// **Say the install record at `path`**, once, through `say`; `false` (and silence)
/// when there is none.
pub fn announce_install_mode_at(path: &std::path::Path, set: Option<&str>, say: &dyn Fn(&str)) -> bool {
    let Ok(text) = std::fs::read_to_string(path) else { return false };
    say(&format!("{} (install record read off the ESP)", InstallRecord::parse(&text).line(set)));
    true
}

/// [`announce_install_mode_at`] on the real ESP ([`INSTALL_RECORD`]).
pub fn announce_install_mode(set: Option<&str>, say: &dyn Fn(&str)) -> bool {
    announce_install_mode_at(std::path::Path::new(INSTALL_RECORD), set, say)
}

#[cfg(test)]
mod tests {
    use super::*;

    /// The install record in both shapes korp-installer writes (edda
    /// `install::install_record`), and the shapes that must not turn into a `p-` on the
    /// screen.
    #[test]
    fn the_install_record_parses_both_kept_shapes_and_never_invents_a_span() {
        let single = "mode=upgrade\nkept=korp-data\nkept_partno=3\nkept_first_lba=33693663\nkept_last_lba=505511\nkept_bytes=241591910400\nkept_fs=xfs\n";
        assert_eq!(InstallRecord::parse(single), InstallRecord { mode: "upgrade".into(), kept: Some(Kept::Partition { partno: 3, bytes: 241591910400 }) });
        assert_eq!(InstallRecord::parse("mode=upgrade\nkept=data-set\n"), InstallRecord { mode: "upgrade".into(), kept: Some(Kept::DataSet) });
        assert_eq!(InstallRecord::parse("mode=fresh\n"), InstallRecord { mode: "fresh".into(), kept: None });
        assert_eq!(InstallRecord::parse("mode=upgrade\nkept=korp-data\n").kept, None, "a span-less korp-data record names no partition");
        assert_eq!(InstallRecord::parse("").mode, "-");
    }

    /// The line said for each shape; the set's shape is the product's to measure (Gunnar
    /// reads it off the md sysfs), and an unknown one is `-`, never a made-up number.
    #[test]
    fn the_install_line_names_the_mode_and_what_was_kept() {
        assert_eq!(INSTALL_RECORD, "/boot/korp/install");
        let p = InstallRecord::parse("mode=upgrade\nkept=korp-data\nkept_partno=3\nkept_bytes=241591910400\n");
        assert_eq!(p.line(None), "install.mode mode=upgrade kept=korp-data kept_partno=3 kept_gib=225.0");
        let s = InstallRecord::parse("mode=upgrade\nkept=data-set\n");
        assert_eq!(s.line(Some("4\u{d7}75G")), "install.mode mode=upgrade kept=data-set kept_set=4\u{d7}75G");
        assert_eq!(s.line(None), "install.mode mode=upgrade kept=data-set kept_set=-");
        assert_eq!(InstallRecord::parse("mode=fresh\n").line(None), "install.mode mode=fresh kept=-");
    }

    /// Absent record: nothing is said.
    #[test]
    fn no_record_says_nothing() {
        let d = tempfile::tempdir().unwrap();
        let said = std::cell::RefCell::new(Vec::<String>::new());
        assert!(!announce_install_mode_at(&d.path().join("install"), None, &|l| said.borrow_mut().push(l.to_string())));
        assert!(said.borrow().is_empty());
        std::fs::write(d.path().join("install"), "mode=fresh\n").unwrap();
        assert!(announce_install_mode_at(&d.path().join("install"), None, &|l| said.borrow_mut().push(l.to_string())));
        assert_eq!(said.borrow().as_slice(), ["install.mode mode=fresh kept=- (install record read off the ESP)".to_string()]);
    }
}