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 =
20 {
21 config,
22 lib,
23 pkgs,
24 ...
25 }:
26 {
27
28 imports = [ ../modules/image/repart.nix ];
29
30 virtualisation.directBoot.enable = false;
31 virtualisation.mountHostNixStore = false;
32 virtualisation.useEFIBoot = true;
33
34 # Disable boot loaders because we install one "manually".
35 # TODO(raitobezarius): revisit this when #244907 lands
36 boot.loader.grub.enable = false;
37
38 system.image.id = imageId;
39 system.image.version = imageVersion;
40
41 virtualisation.fileSystems = lib.mkForce {
42 "/" = {
43 device = "/dev/disk/by-partlabel/${rootPartitionLabel}";
44 fsType = "ext4";
45 };
46 };
47
48 image.repart = {
49 name = "appliance-gpt-image";
50 # OVMF does not work with the default repart sector size of 4096
51 sectorSize = 512;
52 partitions = {
53 "esp" = {
54 contents =
55 let
56 efiArch = config.nixpkgs.hostPlatform.efiArch;
57 in
58 {
59 "/EFI/BOOT/BOOT${lib.toUpper efiArch}.EFI".source =
60 "${pkgs.systemd}/lib/systemd/boot/efi/systemd-boot${efiArch}.efi";
61
62 "/EFI/Linux/${config.system.boot.loader.ukiFile}".source =
63 "${config.system.build.uki}/${config.system.boot.loader.ukiFile}";
64 };
65 repartConfig = {
66 Type = "esp";
67 Format = "vfat";
68 # Minimize = "guess" seems to not work very well for vfat
69 # partitions. 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 "swap" = {
77 repartConfig = {
78 Type = "swap";
79 Format = "swap";
80 SizeMinBytes = "10M";
81 SizeMaxBytes = "10M";
82 };
83 };
84 "root" = {
85 storePaths = [ config.system.build.toplevel ];
86 repartConfig = {
87 Type = "root";
88 Format = config.fileSystems."/".fsType;
89 Label = rootPartitionLabel;
90 Minimize = "guess";
91 };
92 };
93 };
94 };
95 };
96
97 testScript =
98 { nodes, ... }:
99 ''
100 import os
101 import subprocess
102 import tempfile
103
104 tmp_disk_image = tempfile.NamedTemporaryFile()
105
106 subprocess.run([
107 "${nodes.machine.virtualisation.qemu.package}/bin/qemu-img",
108 "create",
109 "-f",
110 "qcow2",
111 "-b",
112 "${nodes.machine.system.build.image}/${nodes.machine.image.repart.imageFile}",
113 "-F",
114 "raw",
115 tmp_disk_image.name,
116 ])
117
118 # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image.
119 os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name
120
121 os_release = machine.succeed("cat /etc/os-release")
122 assert 'IMAGE_ID="${imageId}"' in os_release
123 assert 'IMAGE_VERSION="${imageVersion}"' in os_release
124
125 bootctl_status = machine.succeed("bootctl status")
126 assert "Boot Loader Specification Type #2 (.efi)" in bootctl_status
127 '';
128}