1{
2 system ? builtins.currentSystem,
3 config ? { },
4 pkgs ? import ../.. { inherit system config; },
5}:
6
7with import ../lib/testing-python.nix { inherit system pkgs; };
8
9let
10
11 makeZfsTest =
12 {
13 kernelPackages,
14 enableSystemdStage1 ? false,
15 zfsPackage,
16 extraTest ? "",
17 }:
18 makeTest {
19 name = zfsPackage.kernelModuleAttribute;
20 meta = with pkgs.lib.maintainers; {
21 maintainers = [ elvishjerricco ];
22 };
23
24 nodes.machine =
25 {
26 config,
27 pkgs,
28 lib,
29 ...
30 }:
31 let
32 usersharePath = "/var/lib/samba/usershares";
33 in
34 {
35 virtualisation = {
36 emptyDiskImages = [
37 4096
38 4096
39 ];
40 useBootLoader = true;
41 useEFIBoot = true;
42 };
43 boot.loader.systemd-boot.enable = true;
44 boot.loader.timeout = 0;
45 boot.loader.efi.canTouchEfiVariables = true;
46 networking.hostId = "deadbeef";
47 boot.kernelPackages = kernelPackages;
48 boot.zfs.package = zfsPackage;
49 boot.supportedFilesystems = [ "zfs" ];
50 boot.initrd.systemd.enable = enableSystemdStage1;
51
52 environment.systemPackages = [ pkgs.parted ];
53
54 # /dev/disk/by-id doesn't get populated in the NixOS test framework
55 boot.zfs.devNodes = "/dev/disk/by-uuid";
56
57 specialisation.samba.configuration = {
58 services.samba = {
59 enable = true;
60 settings.global = {
61 "registry shares" = true;
62 "usershare path" = "${usersharePath}";
63 "usershare allow guests" = true;
64 "usershare max shares" = "100";
65 "usershare owner only" = false;
66 };
67 };
68 systemd.services.samba-smbd.serviceConfig.ExecStartPre =
69 "${pkgs.coreutils}/bin/mkdir -m +t -p ${usersharePath}";
70 virtualisation.fileSystems = {
71 "/tmp/mnt" = {
72 device = "rpool/root";
73 fsType = "zfs";
74 };
75 };
76 };
77
78 specialisation.encryption.configuration = {
79 boot.zfs.requestEncryptionCredentials = [ "automatic" ];
80 virtualisation.fileSystems."/automatic" = {
81 device = "automatic";
82 fsType = "zfs";
83 };
84 virtualisation.fileSystems."/manual" = {
85 device = "manual";
86 fsType = "zfs";
87 };
88 virtualisation.fileSystems."/manual/encrypted" = {
89 device = "manual/encrypted";
90 fsType = "zfs";
91 options = [ "noauto" ];
92 };
93 virtualisation.fileSystems."/manual/httpkey" = {
94 device = "manual/httpkey";
95 fsType = "zfs";
96 options = [ "noauto" ];
97 };
98 };
99
100 specialisation.forcepool.configuration = {
101 systemd.services.zfs-import-forcepool.wantedBy = lib.mkVMOverride [ "forcepool.mount" ];
102 systemd.targets.zfs.wantedBy = lib.mkVMOverride [ ];
103 boot.zfs.forceImportAll = true;
104 virtualisation.fileSystems."/forcepool" = {
105 device = "forcepool";
106 fsType = "zfs";
107 options = [ "noauto" ];
108 };
109 };
110
111 services.nginx = {
112 enable = true;
113 virtualHosts = {
114 localhost = {
115 locations = {
116 "/zfskey" = {
117 return = ''200 "httpkeyabc"'';
118 };
119 };
120 };
121 };
122 };
123 };
124
125 testScript =
126 ''
127 machine.wait_for_unit("multi-user.target")
128 machine.succeed(
129 "zpool status",
130 "parted --script /dev/vdb mklabel msdos",
131 "parted --script /dev/vdb -- mkpart primary 1024M -1s",
132 "parted --script /dev/vdc mklabel msdos",
133 "parted --script /dev/vdc -- mkpart primary 1024M -1s",
134 )
135
136 with subtest("sharesmb works"):
137 machine.succeed(
138 "zpool create rpool /dev/vdb1",
139 "zfs create -o mountpoint=legacy rpool/root",
140 # shared datasets cannot have legacy mountpoint
141 "zfs create rpool/shared_smb",
142 "bootctl set-default nixos-generation-1-specialisation-samba.conf",
143 "sync",
144 )
145 machine.crash()
146 machine.wait_for_unit("multi-user.target")
147 machine.succeed("zfs set sharesmb=on rpool/shared_smb")
148 machine.succeed(
149 "smbclient -gNL localhost | grep rpool_shared_smb",
150 "umount /tmp/mnt",
151 "zpool destroy rpool",
152 )
153
154 with subtest("encryption works"):
155 machine.succeed(
156 'echo password | zpool create -O mountpoint=legacy '
157 + "-O encryption=aes-256-gcm -O keyformat=passphrase automatic /dev/vdb1",
158 "zpool create -O mountpoint=legacy manual /dev/vdc1",
159 "echo otherpass | zfs create "
160 + "-o encryption=aes-256-gcm -o keyformat=passphrase manual/encrypted",
161 "zfs create -o encryption=aes-256-gcm -o keyformat=passphrase "
162 + "-o keylocation=http://localhost/zfskey manual/httpkey",
163 "bootctl set-default nixos-generation-1-specialisation-encryption.conf",
164 "sync",
165 "zpool export automatic",
166 "zpool export manual",
167 )
168 machine.crash()
169 machine.start()
170 machine.wait_for_console_text("Starting password query on")
171 machine.send_console("password\n")
172 machine.wait_for_unit("multi-user.target")
173 machine.succeed(
174 "zfs get -Ho value keystatus manual/encrypted | grep -Fx unavailable",
175 "echo otherpass | zfs load-key manual/encrypted",
176 "systemctl start manual-encrypted.mount",
177 "zfs load-key manual/httpkey",
178 "systemctl start manual-httpkey.mount",
179 "umount /automatic /manual/encrypted /manual/httpkey /manual",
180 "zpool destroy automatic",
181 "zpool destroy manual",
182 )
183
184 with subtest("boot.zfs.forceImportAll works"):
185 machine.succeed(
186 "rm /etc/hostid",
187 "zgenhostid deadcafe",
188 "zpool create forcepool /dev/vdb1 -O mountpoint=legacy",
189 "bootctl set-default nixos-generation-1-specialisation-forcepool.conf",
190 "rm /etc/hostid",
191 "sync",
192 )
193 machine.crash()
194 machine.wait_for_unit("multi-user.target")
195 machine.fail("zpool import forcepool")
196 machine.succeed(
197 "systemctl start forcepool.mount",
198 "mount | grep forcepool",
199 )
200 ''
201 + extraTest;
202
203 };
204
205in
206{
207
208 series_2_2 = makeZfsTest {
209 zfsPackage = pkgs.zfs_2_2;
210 kernelPackages = pkgs.linuxPackages;
211 };
212
213 series_2_3 = makeZfsTest {
214 zfsPackage = pkgs.zfs_2_3;
215 kernelPackages = pkgs.linuxPackages;
216 };
217
218 unstable = makeZfsTest rec {
219 zfsPackage = pkgs.zfs_unstable;
220 kernelPackages = pkgs.linuxPackages;
221 };
222
223 unstableWithSystemdStage1 = makeZfsTest rec {
224 zfsPackage = pkgs.zfs_unstable;
225 kernelPackages = pkgs.linuxPackages;
226 enableSystemdStage1 = true;
227 };
228
229 installerBoot = (import ./installer.nix { inherit system; }).separateBootZfs;
230 installer = (import ./installer.nix { inherit system; }).zfsroot;
231
232 expand-partitions = makeTest {
233 name = "multi-disk-zfs";
234 nodes = {
235 machine =
236 { pkgs, ... }:
237 {
238 environment.systemPackages = [ pkgs.parted ];
239 boot.supportedFilesystems = [ "zfs" ];
240 networking.hostId = "00000000";
241
242 virtualisation = {
243 emptyDiskImages = [
244 20480
245 20480
246 20480
247 20480
248 20480
249 20480
250 ];
251 };
252
253 specialisation.resize.configuration = {
254 services.zfs.expandOnBoot = [ "tank" ];
255 };
256 };
257 };
258
259 testScript =
260 { nodes, ... }:
261 ''
262 start_all()
263 machine.wait_for_unit("default.target")
264 print(machine.succeed('mount'))
265
266 print(machine.succeed('parted --script /dev/vdb -- mklabel gpt'))
267 print(machine.succeed('parted --script /dev/vdb -- mkpart primary 1M 70M'))
268
269 print(machine.succeed('parted --script /dev/vdc -- mklabel gpt'))
270 print(machine.succeed('parted --script /dev/vdc -- mkpart primary 1M 70M'))
271
272 print(machine.succeed('zpool create tank mirror /dev/vdb1 /dev/vdc1 mirror /dev/vdd /dev/vde mirror /dev/vdf /dev/vdg'))
273 print(machine.succeed('zpool list -v'))
274 print(machine.succeed('mount'))
275 start_size = int(machine.succeed('df -k --output=size /tank | tail -n1').strip())
276
277 print(machine.succeed("/run/current-system/specialisation/resize/bin/switch-to-configuration test >&2"))
278 machine.wait_for_unit("zpool-expand-pools.service")
279 machine.wait_for_unit("zpool-expand@tank.service")
280
281 print(machine.succeed('zpool list -v'))
282 new_size = int(machine.succeed('df -k --output=size /tank | tail -n1').strip())
283
284 if (new_size - start_size) > 20000000:
285 print("Disk grew appropriately.")
286 else:
287 print(f"Disk went from {start_size} to {new_size}, which doesn't seem right.")
288 exit(1)
289 '';
290 };
291}