1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
use std::fs;
use std::os::unix::fs::symlink;
use std::path::Path;
use alt_controller_image::initramfs::{
CommandRunner, InitramfsBuilder, InitramfsRecipe, InitramfsRequest, Invocation, KernelVersion,
MakeInitrdBuilder,
};
use tempfile::tempdir;
#[test]
fn discovers_the_single_rt_kernel_and_renders_a_sorted_oem_recipe() {
let rootfs = tempdir().expect("rootfs directory");
fs::create_dir(rootfs.path().join("boot")).expect("boot directory");
fs::write(rootfs.path().join("boot/vmlinuz-6.12.8-alt1.rt1"), "kernel").expect("RT kernel");
fs::write(rootfs.path().join("boot/vmlinuz-6.12.8-alt1.std"), "kernel").expect("non-RT kernel");
let kernel = KernelVersion::discover_rt(rootfs.path()).expect("discover RT kernel");
assert_eq!(kernel.as_str(), "6.12.8-alt1.rt1");
assert_eq!(
InitramfsRecipe::new(["rootfs", "compress"], ["virtio_blk.ko", "ext4"])
.expect("valid recipe")
.render(),
"FEATURES += compress rootfs\nMODULES += ext4 virtio_blk.ko\n"
);
}
#[test]
fn diagnoses_missing_or_ambiguous_rt_kernels() {
let rootfs = tempdir().expect("rootfs directory");
fs::create_dir(rootfs.path().join("boot")).expect("boot directory");
let missing = KernelVersion::discover_rt(rootfs.path()).expect_err("missing RT kernel");
assert!(missing.to_string().contains("no RT kernel image"));
fs::write(rootfs.path().join("boot/vmlinuz-6.12-rt1"), "kernel").expect("first RT kernel");
fs::write(rootfs.path().join("boot/vmlinuz-6.11-rt1"), "kernel").expect("second RT kernel");
let ambiguous = KernelVersion::discover_rt(rootfs.path()).expect_err("ambiguous RT kernel");
assert!(ambiguous.to_string().contains("multiple RT kernel images"));
}
#[test]
fn kernel_discovery_ignores_non_regular_vmlinuz_entries() {
let rootfs = tempdir().expect("rootfs directory");
let boot = rootfs.path().join("boot");
fs::create_dir(&boot).expect("boot directory");
fs::create_dir(boot.join("vmlinuz-6.12-rt-directory")).expect("misleading RT directory");
symlink("/not-a-kernel", boot.join("vmlinuz-6.12-rt-symlink")).expect("misleading RT symlink");
fs::write(boot.join("vmlinuz-6.12-rt-real"), "kernel").expect("RT kernel");
let kernel = KernelVersion::discover_rt(rootfs.path()).expect("discover regular RT kernel");
assert_eq!(kernel.as_str(), "6.12-rt-real");
}
#[test]
fn make_initrd_adapter_uses_typed_chroot_arguments_and_records_output_digest() {
let rootfs = tempdir().expect("rootfs directory");
fs::create_dir(rootfs.path().join("boot")).expect("boot directory");
fs::write(rootfs.path().join("boot/vmlinuz-6.12-rt1"), "kernel").expect("RT kernel");
fs::write(
rootfs.path().join("boot/initrd-6.12-rt1.img"),
b"generated initrd",
)
.expect("generated initrd");
let request = InitramfsRequest::discover(rootfs.path()).expect("initramfs request");
let mut builder = MakeInitrdBuilder::new(RecordingRunner::default());
let result = builder.build(&request).expect("build initramfs");
assert_eq!(
builder.runner().invocations,
vec![Invocation::new(
"chroot",
[
rootfs.path().as_os_str().to_owned(),
"make-initrd".into(),
"-k".into(),
"6.12-rt1".into(),
],
)]
);
assert_eq!(result.initrd_path(), Path::new("boot/initrd-6.12-rt1.img"));
assert_eq!(
result.sha256(),
"aec42bc86f526931d7c7f01ecba8d643dbb748f9102539a99db5d4855ee4435f"
);
assert_eq!(
fs::read_link(rootfs.path().join("boot/vmlinuz")).expect("kernel boot symlink"),
Path::new("vmlinuz-6.12-rt1")
);
assert_eq!(
fs::read_link(rootfs.path().join("boot/initrd.img")).expect("initrd boot symlink"),
Path::new("initrd-6.12-rt1.img")
);
}
#[test]
fn initramfs_adapter_refuses_to_replace_a_non_symlink_boot_alias() {
let rootfs = tempdir().expect("rootfs directory");
let boot = rootfs.path().join("boot");
fs::create_dir(&boot).expect("boot directory");
fs::write(boot.join("vmlinuz-6.12-rt1"), "kernel").expect("RT kernel");
fs::write(boot.join("initrd-6.12-rt1.img"), b"generated initrd").expect("generated initrd");
fs::write(boot.join("initrd.img"), "do not replace").expect("protected boot alias");
let request = InitramfsRequest::discover(rootfs.path()).expect("initramfs request");
let mut builder = MakeInitrdBuilder::new(RecordingRunner::default());
let error = builder
.build(&request)
.expect_err("regular boot alias must be protected");
assert!(
error
.to_string()
.contains("refusing to replace non-symlink boot alias")
);
assert_eq!(
fs::read_to_string(boot.join("initrd.img")).expect("protected alias"),
"do not replace"
);
}
#[derive(Debug, Default)]
struct RecordingRunner {
invocations: Vec<Invocation>,
}
impl CommandRunner for RecordingRunner {
fn run(&mut self, invocation: Invocation) -> anyhow::Result<()> {
self.invocations.push(invocation);
Ok(())
}
}
|