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