summaryrefslogtreecommitdiff
path: root/tests/initramfs.rs
blob: 429411a0a9d8699072774bb1f037147fa9139b2b (plain)
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
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(())
    }
}