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