1{
2 system ? builtins.currentSystem,
3 config ? { },
4 pkgs ? import ../.. { inherit system config; },
5}:
6
7with import ../lib/testing-python.nix { inherit system pkgs; };
8with pkgs.lib;
9
10let
11 # A testScript fragment that prepares a disk with some empty, unpartitioned
12 # space. and uses it to boot the test with. Takes a single argument `machine`
13 # from which the diskImage is extracted.
14 useDiskImage = machine: ''
15 import os
16 import shutil
17 import subprocess
18 import tempfile
19
20 tmp_disk_image = tempfile.NamedTemporaryFile()
21
22 shutil.copyfile("${machine.system.build.diskImage}/nixos.img", tmp_disk_image.name)
23
24 subprocess.run([
25 "${machine.virtualisation.qemu.package}/bin/qemu-img",
26 "resize",
27 "-f",
28 "raw",
29 tmp_disk_image.name,
30 "+32M",
31 ])
32
33 # Set NIX_DISK_IMAGE so that the qemu script finds the right disk image.
34 os.environ['NIX_DISK_IMAGE'] = tmp_disk_image.name
35 '';
36
37 common =
38 {
39 config,
40 pkgs,
41 lib,
42 ...
43 }:
44 {
45 virtualisation.useDefaultFilesystems = false;
46 virtualisation.fileSystems = {
47 "/" = {
48 device = "/dev/vda2";
49 fsType = "ext4";
50 };
51 };
52
53 # systemd-repart operates on disks with a partition table. The qemu module,
54 # however, creates separate filesystem images without a partition table, so
55 # we have to create a disk image manually.
56 #
57 # This creates two partitions, an ESP available as /dev/vda1 and the root
58 # partition available as /dev/vda2.
59 system.build.diskImage = import ../lib/make-disk-image.nix {
60 inherit config pkgs lib;
61 # Use a raw format disk so that it can be resized before starting the
62 # test VM.
63 format = "raw";
64 # Keep the image as small as possible but leave some room for changes.
65 bootSize = "32M";
66 additionalSpace = "0M";
67 # GPT with an EFI System Partition is the typical use case for
68 # systemd-repart because it does not support MBR.
69 partitionTableType = "efi";
70 # We do not actually care much about the content of the partitions, so we
71 # do not need a bootloader installed.
72 installBootLoader = false;
73 # Improve determinism by not copying a channel.
74 copyChannel = false;
75 };
76 };
77in
78{
79 basic = makeTest {
80 name = "systemd-repart";
81 meta.maintainers = with maintainers; [ nikstur ];
82
83 nodes.machine =
84 { config, pkgs, ... }:
85 {
86 imports = [ common ];
87
88 boot.initrd.systemd.enable = true;
89
90 boot.initrd.systemd.repart.enable = true;
91 systemd.repart.partitions = {
92 "10-root" = {
93 Type = "linux-generic";
94 };
95 };
96 };
97
98 testScript =
99 { nodes, ... }:
100 ''
101 ${useDiskImage nodes.machine}
102
103 machine.start()
104 machine.wait_for_unit("multi-user.target")
105
106 systemd_repart_logs = machine.succeed("journalctl --boot --unit systemd-repart.service")
107 assert "Growing existing partition 1." in systemd_repart_logs
108 '';
109 };
110
111 after-initrd = makeTest {
112 name = "systemd-repart-after-initrd";
113 meta.maintainers = with maintainers; [ nikstur ];
114
115 nodes.machine =
116 { config, pkgs, ... }:
117 {
118 imports = [ common ];
119
120 systemd.repart.enable = true;
121 systemd.repart.partitions = {
122 "10-root" = {
123 Type = "linux-generic";
124 };
125 };
126 };
127
128 testScript =
129 { nodes, ... }:
130 ''
131 ${useDiskImage nodes.machine}
132
133 machine.start()
134 machine.wait_for_unit("multi-user.target")
135
136 systemd_repart_logs = machine.succeed("journalctl --unit systemd-repart.service")
137 assert "Growing existing partition 1." in systemd_repart_logs
138 '';
139 };
140
141 create-root = makeTest {
142 name = "systemd-repart-create-root";
143 meta.maintainers = with maintainers; [ nikstur ];
144
145 nodes.machine =
146 {
147 config,
148 lib,
149 pkgs,
150 ...
151 }:
152 {
153 virtualisation.useDefaultFilesystems = false;
154 virtualisation.mountHostNixStore = false;
155 virtualisation.fileSystems = {
156 "/" = {
157 device = "/dev/disk/by-partlabel/created-root";
158 fsType = "ext4";
159 };
160 "/nix/store" = {
161 device = "/dev/vda2";
162 fsType = "ext4";
163 };
164 };
165
166 # Create an image containing only the Nix store. This enables creating
167 # the root partition with systemd-repart and then successfully booting
168 # into a working system.
169 #
170 # This creates two partitions, an ESP available as /dev/vda1 and the Nix
171 # store available as /dev/vda2.
172 system.build.diskImage = import ../lib/make-disk-image.nix {
173 inherit config pkgs lib;
174 onlyNixStore = true;
175 format = "raw";
176 bootSize = "32M";
177 additionalSpace = "0M";
178 partitionTableType = "efi";
179 installBootLoader = false;
180 copyChannel = false;
181 };
182
183 boot.initrd.systemd.enable = true;
184
185 boot.initrd.systemd.repart.enable = true;
186 boot.initrd.systemd.repart.device = "/dev/vda";
187 systemd.repart.partitions = {
188 "10-root" = {
189 Type = "root";
190 Label = "created-root";
191 Format = "ext4";
192 };
193 };
194 };
195
196 testScript =
197 { nodes, ... }:
198 ''
199 ${useDiskImage nodes.machine}
200
201 machine.start()
202 machine.wait_for_unit("multi-user.target")
203
204 systemd_repart_logs = machine.succeed("journalctl --boot --unit systemd-repart.service")
205 assert "Adding new partition 2 to partition table." in systemd_repart_logs
206 '';
207 };
208}