| 596 |
596 |
|
])
|
| 597 |
597 |
|
}
|
| 598 |
598 |
|
|
|
599 |
+ |
/// Where the installer ISO carries the image it installs.
|
|
600 |
+ |
///
|
|
601 |
+ |
/// dmsquash-live mounts the medium here, and build/make-iso.sh puts the
|
|
602 |
+ |
/// oci-archive at `source/` on it. The two are a pair: renaming either
|
|
603 |
+ |
/// leaves an installer that runs and cannot install.
|
|
604 |
+ |
const LIVE_SOURCE: &str = "/run/initramfs/live/source/alloy.oci";
|
|
605 |
+ |
|
|
606 |
+ |
/// The `--source-imgref` for this run, or `None` when bootc's own default
|
|
607 |
+ |
/// applies.
|
|
608 |
+ |
///
|
|
609 |
+ |
/// Returning an owned skopeo reference rather than a bare path because the
|
|
610 |
+ |
/// transport is part of the answer: bootc reads skopeo formats, and an
|
|
611 |
+ |
/// `oci-archive:` prefix is what makes a file on the medium a thing it can
|
|
612 |
+ |
/// deploy from.
|
|
613 |
+ |
fn live_source_image() -> Option<String> {
|
|
614 |
+ |
source_image_at(std::path::Path::new(LIVE_SOURCE))
|
|
615 |
+ |
}
|
|
616 |
+ |
|
|
617 |
+ |
/// The reference for an archive at `path`, if it is there.
|
|
618 |
+ |
///
|
|
619 |
+ |
/// Split from [`live_source_image`] so the transport prefix can be tested
|
|
620 |
+ |
/// without an installer medium to hand.
|
|
621 |
+ |
fn source_image_at(path: &std::path::Path) -> Option<String> {
|
|
622 |
+ |
path.exists()
|
|
623 |
+ |
.then(|| format!("oci-archive:{}", path.display()))
|
|
624 |
+ |
}
|
|
625 |
+ |
|
| 599 |
626 |
|
/// The whole install, as stages.
|
| 600 |
627 |
|
///
|
| 601 |
628 |
|
/// Two discoveries, nested. bootc decides which partition holds the new root
|
| 611 |
638 |
|
// --wipe is explicit rather than implied by the confirm the user just
|
| 612 |
639 |
|
// answered: the flag that destroys the disk should be visible on the
|
| 613 |
640 |
|
// line the summary displays, not hidden in a default.
|
| 614 |
|
- |
Stage::Run(
|
| 615 |
|
- |
Invocation::new("bootc")
|
| 616 |
|
- |
.args(["install", "to-disk", "--wipe"])
|
| 617 |
|
- |
.arg(disk),
|
| 618 |
|
- |
),
|
|
641 |
+ |
Stage::Run({
|
|
642 |
+ |
let mut install = Invocation::new("bootc").args(["install", "to-disk", "--wipe"]);
|
|
643 |
+ |
// bootc installs a container image, and by default it expects to
|
|
644 |
+ |
// be running inside the one it is installing. On the ISO it is
|
|
645 |
+ |
// not: it runs in the live system, which is a squashfs, and a
|
|
646 |
+ |
// squashfs is not a container image. So the image travels
|
|
647 |
+ |
// separately as an oci-archive and is named here.
|
|
648 |
+ |
//
|
|
649 |
+ |
// Detected rather than configured, because the same binary runs
|
|
650 |
+ |
// in both places. Present means booted from the installer
|
|
651 |
+ |
// medium; absent means running inside a container during
|
|
652 |
+ |
// development, where bootc's own default is already right.
|
|
653 |
+ |
if let Some(source) = live_source_image() {
|
|
654 |
+ |
install = install.args(["--source-imgref", &source]);
|
|
655 |
+ |
}
|
|
656 |
+ |
install.arg(disk)
|
|
657 |
+ |
}),
|
| 619 |
658 |
|
// bootc returns when the install is done, not when the kernel and
|
| 620 |
659 |
|
// udev have caught up with the partition table it wrote. In that
|
| 621 |
660 |
|
// window `lsblk` answers with a `parttype` of null for every
|
| 2434 |
2473 |
|
assert!(error.contains("no root partition"), "{error}");
|
| 2435 |
2474 |
|
}
|
| 2436 |
2475 |
|
|
|
2476 |
+ |
// bootc reads skopeo transports, so the prefix is not decoration: handed
|
|
2477 |
+ |
// a bare path it looks for a registry by that name and fails saying so.
|
|
2478 |
+ |
#[test]
|
|
2479 |
+ |
fn a_source_image_is_named_with_its_transport() {
|
|
2480 |
+ |
let dir = std::env::temp_dir().join("alloy-source-test");
|
|
2481 |
+ |
std::fs::create_dir_all(&dir).unwrap();
|
|
2482 |
+ |
let archive = dir.join("alloy.oci");
|
|
2483 |
+ |
std::fs::write(&archive, b"not really an archive").unwrap();
|
|
2484 |
+ |
|
|
2485 |
+ |
let named = source_image_at(&archive).expect("an archive that is there is named");
|
|
2486 |
+ |
assert!(named.starts_with("oci-archive:"), "{named}");
|
|
2487 |
+ |
assert!(named.ends_with("alloy.oci"), "{named}");
|
|
2488 |
+ |
|
|
2489 |
+ |
std::fs::remove_file(&archive).unwrap();
|
|
2490 |
+ |
}
|
|
2491 |
+ |
|
|
2492 |
+ |
// Absent means running inside a container during development, where
|
|
2493 |
+ |
// bootc installs the image it is running and needs no help.
|
|
2494 |
+ |
#[test]
|
|
2495 |
+ |
fn no_source_image_means_bootc_uses_its_own_default() {
|
|
2496 |
+ |
let missing = std::env::temp_dir().join("alloy-no-such-archive.oci");
|
|
2497 |
+ |
let _ = std::fs::remove_file(&missing);
|
|
2498 |
+ |
assert_eq!(source_image_at(&missing), None);
|
|
2499 |
+ |
}
|
|
2500 |
+ |
|
| 2437 |
2501 |
|
#[test]
|
| 2438 |
2502 |
|
fn a_malformed_listing_is_a_named_failure() {
|
| 2439 |
2503 |
|
assert!(root_partition("not json").unwrap_err().contains("JSON"));
|