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 imageId = "nixos-appliance";
12 imageVersion = "1-rc1";
13in
14{
15 name = "appliance-gpt-image";
16
17 meta.maintainers = with lib.maintainers; [ nikstur ];
18
19 nodes.machine = { config, lib, pkgs, ... }: {
20
21 imports = [ ../modules/image/repart.nix ];
22
23 virtualisation.directBoot.enable = false;
24 virtualisation.mountHostNixStore = false;
25 virtualisation.useEFIBoot = true;
26
27 # Disable boot loaders because we install one "manually".
28 # TODO(raitobezarius): revisit this when #244907 lands
29 boot.loader.grub.enable = false;
30
31 system.image.id = imageId;
32 system.image.version = imageVersion;
33
34 virtualisation.fileSystems = lib.mkForce {
35 "/" = {
36 device = "/dev/disk/by-partlabel/${rootPartitionLabel}";
37 fsType = "ext4";
38 };
39 };
40
41 image.repart = {
42 name = "appliance-gpt-image";
43 # OVMF does not work with the default repart sector size of 4096
44 sectorSize = 512;
45 partitions = {
46 "esp" = {
47 contents =
48 let
49 efiArch = config.nixpkgs.hostPlatform.efiArch;
50 in
51 {
52 "/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source =
53 "${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi";
54
55 "/EFI/Linux/${config.system.boot.loader.ukiFile}".source =
56 "${config.system.build.uki}/${config.system.boot.loader.ukiFile}";
57 };
58 repartConfig = {
59 Type = "esp";
60 Format = "vfat";
61 # Minimize = "guess" seems to not work very vell for vfat
62 # partitons. It's better to set a sensible default instead. The
63 # aarch64 kernel seems to generally be a little bigger than the
64 # x86_64 kernel. To stay on the safe side, leave some more slack
65 # for every platform other than x86_64.
66 SizeMinBytes = if config.nixpkgs.hostPlatform.isx86_64 then "64M" else "96M";
67 };
68 };
69 "root" = {
70 storePaths = [ config.system.build.toplevel ];
71 repartConfig = {
72 Type = "root";
73 Format = config.fileSystems."/".fsType;
74 Label = rootPartitionLabel;
75 Minimize = "guess";
76 };
77 };
78 };
79 };
80 };
81
82 testScript = { nodes, ... }: ''
83 import os
84 import subprocess
85 import tempfile
86
87 tmp_disk_image = tempfile.NamedTemporaryFile()
88
89 subprocess.run([
90 "${nodes.machine.virtualisation.qemu.package}/bin/qemu-img",
91 "create",
92 "-f",
93 "qcow2",
94 "-b",
95 "${nodes.machine.system.build.image}/${nodes.machine.image.repart.imageFile}",
96 "-F",
97 "raw",
98 tmp_disk_image.name,
99 ])
100
101 # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image.
102 os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name
103
104 os_release = machine.succeed("cat /etc/os-release")
105 assert 'IMAGE_ID="${imageId}"' in os_release
106 assert 'IMAGE_VERSION="${imageVersion}"' in os_release
107
108 bootctl_status = machine.succeed("bootctl status")
109 assert "Boot Loader Specification Type #2 (.efi)" in bootctl_status
110 '';
111}