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(
137 "zfs set sharesmb=on rpool/shared_smb",
138 "zfs share rpool/shared_smb",
139 "smbclient -gNL localhost | grep rpool_shared_smb",
140 "umount /tmp/mnt",
141 "zpool destroy rpool",
142 )
143
144 with subtest("encryption works"):
145 machine.succeed(
146 'echo password | zpool create -O mountpoint=legacy '
147 + "-O encryption=aes-256-gcm -O keyformat=passphrase automatic /dev/vdb1",
148 "zpool create -O mountpoint=legacy manual /dev/vdc1",
149 "echo otherpass | zfs create "
150 + "-o encryption=aes-256-gcm -o keyformat=passphrase manual/encrypted",
151 "zfs create -o encryption=aes-256-gcm -o keyformat=passphrase "
152 + "-o keylocation=http://localhost/zfskey manual/httpkey",
153 "bootctl set-default nixos-generation-1-specialisation-encryption.conf",
154 "sync",
155 "zpool export automatic",
156 "zpool export manual",
157 )
158 machine.crash()
159 machine.start()
160 machine.wait_for_console_text("Starting password query on")
161 machine.send_console("password\n")
162 machine.wait_for_unit("multi-user.target")
163 machine.succeed(
164 "zfs get -Ho value keystatus manual/encrypted | grep -Fx unavailable",
165 "echo otherpass | zfs load-key manual/encrypted",
166 "systemctl start manual-encrypted.mount",
167 "zfs load-key manual/httpkey",
168 "systemctl start manual-httpkey.mount",
169 "umount /automatic /manual/encrypted /manual/httpkey /manual",
170 "zpool destroy automatic",
171 "zpool destroy manual",
172 )
173
174 with subtest("boot.zfs.forceImportAll works"):
175 machine.succeed(
176 "rm /etc/hostid",
177 "zgenhostid deadcafe",
178 "zpool create forcepool /dev/vdb1 -O mountpoint=legacy",
179 "bootctl set-default nixos-generation-1-specialisation-forcepool.conf",
180 "rm /etc/hostid",
181 "sync",
182 )
183 machine.crash()
184 machine.wait_for_unit("multi-user.target")
185 machine.fail("zpool import forcepool")
186 machine.succeed(
187 "systemctl start forcepool.mount",
188 "mount | grep forcepool",
189 )
190 '' + extraTest;
191
192 };
193
194
195in {
196
197 stable = makeZfsTest "stable" { };
198
199 unstable = makeZfsTest "unstable" {
200 enableUnstable = true;
201 };
202
203 unstableWithSystemdStage1 = makeZfsTest "unstable" {
204 enableUnstable = true;
205 enableSystemdStage1 = true;
206 };
207
208 installer = (import ./installer.nix { }).zfsroot;
209
210 expand-partitions = makeTest {
211 name = "multi-disk-zfs";
212 nodes = {
213 machine = { pkgs, ... }: {
214 environment.systemPackages = [ pkgs.parted ];
215 boot.supportedFilesystems = [ "zfs" ];
216 networking.hostId = "00000000";
217
218 virtualisation = {
219 emptyDiskImages = [ 20480 20480 20480 20480 20480 20480 ];
220 };
221
222 specialisation.resize.configuration = {
223 services.zfs.expandOnBoot = [ "tank" ];
224 };
225 };
226 };
227
228 testScript = { nodes, ... }:
229 ''
230 start_all()
231 machine.wait_for_unit("default.target")
232 print(machine.succeed('mount'))
233
234 print(machine.succeed('parted --script /dev/vdb -- mklabel gpt'))
235 print(machine.succeed('parted --script /dev/vdb -- mkpart primary 1M 70M'))
236
237 print(machine.succeed('parted --script /dev/vdc -- mklabel gpt'))
238 print(machine.succeed('parted --script /dev/vdc -- mkpart primary 1M 70M'))
239
240 print(machine.succeed('zpool create tank mirror /dev/vdb1 /dev/vdc1 mirror /dev/vdd /dev/vde mirror /dev/vdf /dev/vdg'))
241 print(machine.succeed('zpool list -v'))
242 print(machine.succeed('mount'))
243 start_size = int(machine.succeed('df -k --output=size /tank | tail -n1').strip())
244
245 print(machine.succeed("/run/current-system/specialisation/resize/bin/switch-to-configuration test >&2"))
246 machine.wait_for_unit("zpool-expand-pools.service")
247 machine.wait_for_unit("zpool-expand@tank.service")
248
249 print(machine.succeed('zpool list -v'))
250 new_size = int(machine.succeed('df -k --output=size /tank | tail -n1').strip())
251
252 if (new_size - start_size) > 20000000:
253 print("Disk grew appropriately.")
254 else:
255 print(f"Disk went from {start_size} to {new_size}, which doesn't seem right.")
256 exit(1)
257 '';
258 };
259}