#![cfg(feature = "backend-tunnr")]
use std::path::Path;
use std::time::Duration;
use draupnir::kvm::KvmBoot;
use draupnir::{Boot, BootOrder, BootSpec, Lifecycle, Seen};
fn proof_iso() -> String {
std::env::var("PROOF_ISO")
.unwrap_or_else(|_| "/home/rickard/Hämtningar/gunnar-appliance-x86_64.iso".to_string())
}
const MEDIUM_MARKER: &str = "UEFI QEMU DVD-ROM";
fn proof_marker() -> String {
std::env::var("PROOF_MARKER").unwrap_or_else(|_| "gunnar ssh listener bound".to_string())
}
fn missing(what: &str, path: &str) -> bool {
eprintln!("SKIP boot_off_medium_proof: {what} not present at `{path}` — this proof did NOT run");
true
}
fn prerequisites_absent(iso: &str) -> bool {
if !Path::new("/dev/kvm").exists() {
return missing("/dev/kvm", "/dev/kvm");
}
if !Path::new(iso).is_file() {
return missing("the boot medium (ISO)", iso);
}
false
}
#[test]
#[ignore = "REAL BOOT: needs /dev/kvm, qemu-system-x86_64, OVMF and an ISO on disk"]
fn a_kvm_guest_really_boots_off_the_medium_and_says_so_on_its_console() {
let iso = proof_iso();
if prerequisites_absent(&iso) {
return;
}
let spec = BootSpec::iso_boot("iso-kvm-proof", &iso);
spec.validate().expect("the medium spec is well formed");
assert_eq!(spec.boot_order, BootOrder::Medium);
assert_eq!(spec.medium_path(), Some(iso.as_str()));
let backend = KvmBoot::new();
let machine = backend.boot(&spec).expect("a real QEMU/KVM guest starts");
println!("BOOTED off the medium: id={} iso={iso}", machine.id);
let firmware = backend
.await_serial_marker(
&machine,
MEDIUM_MARKER,
Duration::from_secs(60),
Duration::from_millis(250),
)
.expect("the guest this backend booted is addressable");
let marker = proof_marker();
let serving = backend
.await_serial_marker(
&machine,
&marker,
Duration::from_secs(120),
Duration::from_millis(250),
)
.expect("the guest is still addressable");
let still_up = backend.status(&machine);
let console = backend.serial_log(&machine).unwrap_or_default();
println!("---------------- SERIAL CONSOLE ({} bytes) ----------------", console.len());
println!("{console}");
println!("---------------- END SERIAL CONSOLE ----------------");
println!("FIRMWARE: {}", firmware.detail());
println!("SERVING : {}", serving.detail());
println!("STATUS : {still_up:?}");
let _ = backend.power_off(&machine);
assert!(
firmware.saw_marker(),
"the FIRMWARE never named the DVD-ROM — this was not a boot off the medium.\n\
observed: {}\nconsole:\n{console}",
firmware.detail()
);
match &serving {
Seen::Marker { line, after } => {
println!("PROOF: booted off the medium and reached userspace in {after:?}");
println!("PROOF: `{}`", line.trim());
}
other => panic!(
"the system off the medium never printed `{marker}`.\nobserved: {}\nconsole:\n{console}",
other.detail()
),
}
assert_eq!(
still_up.unwrap(),
draupnir::PowerState::On,
"the appliance is still SERVING after it announced itself — an appliance that \
had to exit for this test to pass would be a broken appliance"
);
assert!(
console.len() > 4096,
"the serial capture is a real boot, got {} bytes",
console.len()
);
}
#[test]
#[ignore = "REAL BOOT: needs /dev/kvm, qemu-system-x86_64 and OVMF"]
fn a_blank_disk_boots_no_appliance_and_names_no_medium() {
if !Path::new("/dev/kvm").exists() {
missing("/dev/kvm", "/dev/kvm");
return;
}
let blank = std::env::temp_dir().join(format!("draupnir-blank-{}.raw", std::process::id()));
let f = std::fs::File::create(&blank).expect("create the blank disk");
f.set_len(256 * 1024 * 1024).expect("size the blank disk");
drop(f);
let spec = BootSpec::kvm_boot_installed_disk("blank-disk-red", blank.to_string_lossy());
spec.validate().expect("the second-lifetime spec is well formed");
assert_eq!(spec.medium_path(), None, "the medium is absent, as the leg requires");
let backend = KvmBoot::new();
let machine = backend.boot(&spec).expect("QEMU starts even with nothing to boot");
let seen = backend
.await_serial_marker(
&machine,
&proof_marker(),
Duration::from_secs(25),
Duration::from_millis(500),
)
.expect("addressable");
let console = backend.serial_log(&machine).unwrap_or_default();
println!("---------------- BLANK-DISK CONSOLE ({} bytes) ----------------", console.len());
println!("{console}");
println!("---------------- END BLANK-DISK CONSOLE ----------------");
println!("SEEN: {}", seen.detail());
let _ = backend.power_off(&machine);
let _ = std::fs::remove_file(&blank);
assert!(
!seen.saw_marker(),
"a BLANK disk produced an appliance — then the positive proof proves nothing: {}",
seen.detail()
);
assert!(
!console.contains(MEDIUM_MARKER),
"the medium-less leg still booted a DVD-ROM — the medium was NOT absent:\n{console}"
);
assert!(
!console.contains("GUNNAR APPLIANCE"),
"a blank disk printed the appliance banner:\n{console}"
);
println!("RED CONFIRMED: blank disk, no medium line, no appliance.");
}
#[test]
fn a_spec_naming_a_medium_that_does_not_exist_fails_by_name_without_booting() {
let ghost = "/nonexistent/never-built-by-anyone.iso";
let spec = BootSpec::iso_boot("ghost-iso", ghost);
spec.validate().expect("a nonexistent path is not a malformed spec");
let err = KvmBoot::new()
.boot(&spec)
.expect_err("a medium that is not on disk cannot be booted");
let msg = format!("{err}");
assert!(msg.contains(ghost), "the failure NAMES the missing ISO: {msg}");
assert!(msg.contains("does not exist"), "and says what is wrong: {msg}");
assert!(
msg.contains("ghost-iso"),
"and names the spec that asked for it: {msg}"
);
}
#[test]
fn the_boot_installed_leg_cannot_secretly_keep_the_installer_medium() {
let honest = BootSpec::kvm_boot_installed_disk("appliance", "/var/lib/appliance.qcow2");
honest.validate().expect("the medium-less second lifetime is legal");
assert_eq!(honest.medium_path(), None);
let lie = honest.clone().with_medium("/images/installer.iso");
let err = lie
.validate()
.expect_err("boot-from-disk with the medium attached is refused");
let msg = format!("{err}");
assert!(msg.contains("/images/installer.iso"), "names the medium: {msg}");
assert!(msg.contains("ABSENT"), "states the rule: {msg}");
assert!(msg.contains("second VM lifetime"), "names the fix: {msg}");
}