1# Tests building and running a GUID Partition Table (GPT) appliance image.
2# "Appliance" here means that the image does not contain the normal NixOS
3# infrastructure of a system profile and cannot be re-built via
4# `nixos-rebuild`.
5
6{ lib, ... }:
7
8let
9 rootPartitionLabel = "root";
10
11 bootLoaderConfigPath = "/loader/entries/nixos.conf";
12 kernelPath = "/EFI/nixos/kernel.efi";
13 initrdPath = "/EFI/nixos/initrd.efi";
14in
15{
16 name = "appliance-gpt-image";
17
18 meta.maintainers = with lib.maintainers; [ nikstur ];
19
20 nodes.machine = { config, lib, pkgs, ... }: {
21
22 imports = [ ../modules/image/repart.nix ];
23
24 virtualisation.directBoot.enable = false;
25 virtualisation.mountHostNixStore = false;
26 virtualisation.useEFIBoot = true;
27
28 # Disable boot loaders because we install one "manually".
29 # TODO(raitobezarius): revisit this when #244907 lands
30 boot.loader.grub.enable = false;
31
32 virtualisation.fileSystems = lib.mkForce {
33 "/" = {
34 device = "/dev/disk/by-partlabel/${rootPartitionLabel}";
35 fsType = "ext4";
36 };
37 };
38
39 image.repart = {
40 name = "appliance-gpt-image";
41 partitions = {
42 "esp" = {
43 contents =
44 let
45 efiArch = config.nixpkgs.hostPlatform.efiArch;
46 in
47 {
48 "/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source =
49 "${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi";
50
51 # TODO: create an abstraction for Boot Loader Specification (BLS) entries.
52 "${bootLoaderConfigPath}".source = pkgs.writeText "nixos.conf" ''
53 title NixOS
54 linux ${kernelPath}
55 initrd ${initrdPath}
56 options init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}
57 '';
58
59 "${kernelPath}".source =
60 "${config.boot.kernelPackages.kernel}/${config.system.boot.loader.kernelFile}";
61
62 "${initrdPath}".source =
63 "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
64 };
65 repartConfig = {
66 Type = "esp";
67 Format = "vfat";
68 # Minimize = "guess" seems to not work very vell for vfat
69 # partitons. It's better to set a sensible default instead. The
70 # aarch64 kernel seems to generally be a little bigger than the
71 # x86_64 kernel. To stay on the safe side, leave some more slack
72 # for every platform other than x86_64.
73 SizeMinBytes = if config.nixpkgs.hostPlatform.isx86_64 then "64M" else "96M";
74 };
75 };
76 "root" = {
77 storePaths = [ config.system.build.toplevel ];
78 repartConfig = {
79 Type = "root";
80 Format = config.fileSystems."/".fsType;
81 Label = rootPartitionLabel;
82 Minimize = "guess";
83 };
84 };
85 };
86 };
87 };
88
89 testScript = { nodes, ... }: ''
90 import os
91 import subprocess
92 import tempfile
93
94 tmp_disk_image = tempfile.NamedTemporaryFile()
95
96 subprocess.run([
97 "${nodes.machine.virtualisation.qemu.package}/bin/qemu-img",
98 "create",
99 "-f",
100 "qcow2",
101 "-b",
102 "${nodes.machine.system.build.image}/image.raw",
103 "-F",
104 "raw",
105 tmp_disk_image.name,
106 ])
107
108 # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image.
109 os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name
110
111 bootctl_status = machine.succeed("bootctl status")
112 assert "${bootLoaderConfigPath}" in bootctl_status
113 assert "${kernelPath}" in bootctl_status
114 assert "${initrdPath}" in bootctl_status
115 '';
116}