1{ system ? builtins.currentSystem }:
2
3with import ../lib/testing.nix { inherit system; };
4with import ../lib/qemu-flags.nix;
5with pkgs.lib;
6
7let
8
9 # The configuration to install.
10 makeConfig = { grubVersion, grubDevice, grubIdentifier
11 , extraConfig, forceGrubReinstallCount ? 0
12 }:
13 pkgs.writeText "configuration.nix" ''
14 { config, lib, pkgs, modulesPath, ... }:
15
16 { imports =
17 [ ./hardware-configuration.nix
18 <nixpkgs/nixos/modules/testing/test-instrumentation.nix>
19 ];
20
21 boot.loader.grub.version = ${toString grubVersion};
22 ${optionalString (grubVersion == 1) ''
23 boot.loader.grub.splashImage = null;
24 ''}
25 boot.loader.grub.device = "${grubDevice}";
26 boot.loader.grub.extraConfig = "serial; terminal_output.serial";
27 boot.loader.grub.fsIdentifier = "${grubIdentifier}";
28
29 boot.loader.grub.configurationLimit = 100 + ${toString forceGrubReinstallCount};
30
31 hardware.enableAllFirmware = lib.mkForce false;
32
33 ${replaceChars ["\n"] ["\n "] extraConfig}
34 }
35 '';
36
37
38 channelContents = [ pkgs.rlwrap ];
39
40
41 # The test script boots a NixOS VM, installs NixOS on an empty hard
42 # disk, and then reboot from the hard disk. It's parameterized with
43 # a test script fragment `createPartitions', which must create
44 # partitions and filesystems.
45 testScriptFun = { createPartitions, grubVersion, grubDevice
46 , grubIdentifier, preBootCommands, extraConfig
47 }:
48 let
49 iface = if grubVersion == 1 then "scsi" else "virtio";
50 qemuFlags =
51 (if system == "x86_64-linux" then "-m 768 " else "-m 512 ") +
52 (optionalString (system == "x86_64-linux") "-cpu kvm64 ");
53 hdFlags = ''hda => "vm-state-machine/machine.qcow2", hdaInterface => "${iface}", '';
54 in
55 ''
56 $machine->start;
57
58 # Make sure that we get a login prompt etc.
59 $machine->succeed("echo hello");
60 #$machine->waitForUnit('getty@tty2');
61 $machine->waitForUnit("rogue");
62 $machine->waitForUnit("nixos-manual");
63
64 # Wait for hard disks to appear in /dev
65 $machine->succeed("udevadm settle");
66
67 # Partition the disk.
68 ${createPartitions}
69
70 # Create the NixOS configuration.
71 $machine->succeed("nixos-generate-config --root /mnt");
72
73 $machine->succeed("cat /mnt/etc/nixos/hardware-configuration.nix >&2");
74
75 $machine->copyFileFromHost(
76 "${ makeConfig { inherit grubVersion grubDevice grubIdentifier extraConfig; } }",
77 "/mnt/etc/nixos/configuration.nix");
78
79 # Perform the installation.
80 $machine->succeed("nixos-install < /dev/null >&2");
81
82 # Do it again to make sure it's idempotent.
83 $machine->succeed("nixos-install < /dev/null >&2");
84
85 $machine->succeed("umount /mnt/boot || true");
86 $machine->succeed("umount /mnt");
87 $machine->succeed("sync");
88
89 $machine->shutdown;
90
91 # Now see if we can boot the installation.
92 $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}" });
93
94 # For example to enter LUKS passphrase.
95 ${preBootCommands}
96
97 # Did /boot get mounted?
98 $machine->waitForUnit("local-fs.target");
99
100 $machine->succeed("test -e /boot/grub");
101
102 # Check whether /root has correct permissions.
103 $machine->succeed("stat -c '%a' /root") =~ /700/ or die;
104
105 # Did the swap device get activated?
106 # uncomment once https://bugs.freedesktop.org/show_bug.cgi?id=86930 is resolved
107 #$machine->waitForUnit("swap.target");
108 $machine->waitUntilSucceeds("cat /proc/swaps | grep -q /dev");
109
110 # Check whether the channel works.
111 $machine->succeed("nix-env -iA nixos.coreutils >&2");
112 $machine->succeed("type -tP ls | tee /dev/stderr") =~ /.nix-profile/
113 or die "nix-env failed";
114
115 # We need to a writable nix-store on next boot.
116 $machine->copyFileFromHost(
117 "${ makeConfig { inherit grubVersion grubDevice grubIdentifier extraConfig; forceGrubReinstallCount = 1; } }",
118 "/etc/nixos/configuration.nix");
119
120 # Check whether nixos-rebuild works.
121 $machine->succeed("nixos-rebuild switch >&2");
122
123 # Test nixos-option.
124 $machine->succeed("nixos-option boot.initrd.kernelModules | grep virtio_console");
125 $machine->succeed("nixos-option boot.initrd.kernelModules | grep 'List of modules'");
126 $machine->succeed("nixos-option boot.initrd.kernelModules | grep qemu-guest.nix");
127
128 $machine->shutdown;
129
130 # Check whether a writable store build works
131 $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}" });
132 ${preBootCommands}
133 $machine->waitForUnit("multi-user.target");
134 $machine->copyFileFromHost(
135 "${ makeConfig { inherit grubVersion grubDevice grubIdentifier extraConfig; forceGrubReinstallCount = 2; } }",
136 "/etc/nixos/configuration.nix");
137 $machine->succeed("nixos-rebuild boot >&2");
138 $machine->shutdown;
139
140 # And just to be sure, check that the machine still boots after
141 # "nixos-rebuild switch".
142 $machine = createMachine({ ${hdFlags} qemuFlags => "${qemuFlags}" });
143 ${preBootCommands}
144 $machine->waitForUnit("network.target");
145 $machine->shutdown;
146 '';
147
148
149 makeInstallerTest = name:
150 { createPartitions, preBootCommands ? "", extraConfig ? ""
151 , grubVersion ? 2, grubDevice ? "/dev/vda"
152 , grubIdentifier ? "uuid", enableOCR ? false, meta ? {}
153 }:
154 makeTest {
155 inherit enableOCR;
156 name = "installer-" + name;
157 meta = with pkgs.stdenv.lib.maintainers; {
158 # put global maintainers here, individuals go into makeInstallerTest fkt call
159 maintainers = [ wkennington ] ++ (meta.maintainers or []);
160 };
161 nodes = {
162
163 # The configuration of the machine used to run "nixos-install". It
164 # also has a web server that simulates cache.nixos.org.
165 machine =
166 { config, lib, pkgs, ... }:
167
168 { imports =
169 [ ../modules/profiles/installation-device.nix
170 ../modules/profiles/base.nix
171 ];
172
173 virtualisation.diskSize = 8 * 1024;
174 virtualisation.memorySize = 1024;
175 virtualisation.writableStore = true;
176
177 # Use a small /dev/vdb as the root disk for the
178 # installer. This ensures the target disk (/dev/vda) is
179 # the same during and after installation.
180 virtualisation.emptyDiskImages = [ 512 ];
181 virtualisation.bootDevice =
182 if grubVersion == 1 then "/dev/sdb" else "/dev/vdb";
183 virtualisation.qemu.diskInterface =
184 if grubVersion == 1 then "scsi" else "virtio";
185
186 hardware.enableAllFirmware = mkForce false;
187
188 # The test cannot access the network, so any packages we
189 # need must be included in the VM.
190 system.extraDependencies =
191 [ pkgs.sudo
192 pkgs.docbook5
193 pkgs.docbook5_xsl
194 pkgs.unionfs-fuse
195 pkgs.ntp
196 pkgs.nixos-artwork
197 pkgs.gummiboot
198 pkgs.perlPackages.XMLLibXML
199 pkgs.perlPackages.ListCompare
200 ]
201 ++ optional (grubVersion == 1) pkgs.grub
202 ++ optionals (grubVersion == 2) [ pkgs.grub2 pkgs.grub2_efi ];
203
204 nix.binaryCaches = mkForce [ ];
205 };
206
207 };
208
209 testScript = testScriptFun {
210 inherit createPartitions preBootCommands grubVersion
211 grubDevice grubIdentifier extraConfig;
212 };
213 };
214
215
216in {
217
218 # !!! `parted mkpart' seems to silently create overlapping partitions.
219
220
221 # The (almost) simplest partitioning scheme: a swap partition and
222 # one big filesystem partition.
223 simple = makeInstallerTest "simple"
224 { createPartitions =
225 ''
226 $machine->succeed(
227 "parted /dev/vda mklabel msdos",
228 "parted /dev/vda -- mkpart primary linux-swap 1M 1024M",
229 "parted /dev/vda -- mkpart primary ext2 1024M -1s",
230 "udevadm settle",
231 "mkswap /dev/vda1 -L swap",
232 "swapon -L swap",
233 "mkfs.ext3 -L nixos /dev/vda2",
234 "mount LABEL=nixos /mnt",
235 );
236 '';
237 };
238
239 # Same as the previous, but now with a separate /boot partition.
240 separateBoot = makeInstallerTest "separateBoot"
241 { createPartitions =
242 ''
243 $machine->succeed(
244 "parted /dev/vda mklabel msdos",
245 "parted /dev/vda -- mkpart primary ext2 1M 50MB", # /boot
246 "parted /dev/vda -- mkpart primary linux-swap 50MB 1024M",
247 "parted /dev/vda -- mkpart primary ext2 1024M -1s", # /
248 "udevadm settle",
249 "mkswap /dev/vda2 -L swap",
250 "swapon -L swap",
251 "mkfs.ext3 -L nixos /dev/vda3",
252 "mount LABEL=nixos /mnt",
253 "mkfs.ext3 -L boot /dev/vda1",
254 "mkdir -p /mnt/boot",
255 "mount LABEL=boot /mnt/boot",
256 );
257 '';
258 };
259
260 # Same as the previous, but with fat32 /boot.
261 separateBootFat = makeInstallerTest "separateBootFat"
262 { createPartitions =
263 ''
264 $machine->succeed(
265 "parted /dev/vda mklabel msdos",
266 "parted /dev/vda -- mkpart primary ext2 1M 50MB", # /boot
267 "parted /dev/vda -- mkpart primary linux-swap 50MB 1024M",
268 "parted /dev/vda -- mkpart primary ext2 1024M -1s", # /
269 "udevadm settle",
270 "mkswap /dev/vda2 -L swap",
271 "swapon -L swap",
272 "mkfs.ext3 -L nixos /dev/vda3",
273 "mount LABEL=nixos /mnt",
274 "mkfs.vfat -n BOOT /dev/vda1",
275 "mkdir -p /mnt/boot",
276 "mount LABEL=BOOT /mnt/boot",
277 );
278 '';
279 };
280
281 # Create two physical LVM partitions combined into one volume group
282 # that contains the logical swap and root partitions.
283 lvm = makeInstallerTest "lvm"
284 { createPartitions =
285 ''
286 $machine->succeed(
287 "parted /dev/vda mklabel msdos",
288 "parted /dev/vda -- mkpart primary 1M 2048M", # PV1
289 "parted /dev/vda -- set 1 lvm on",
290 "parted /dev/vda -- mkpart primary 2048M -1s", # PV2
291 "parted /dev/vda -- set 2 lvm on",
292 "udevadm settle",
293 "pvcreate /dev/vda1 /dev/vda2",
294 "vgcreate MyVolGroup /dev/vda1 /dev/vda2",
295 "lvcreate --size 1G --name swap MyVolGroup",
296 "lvcreate --size 2G --name nixos MyVolGroup",
297 "mkswap -f /dev/MyVolGroup/swap -L swap",
298 "swapon -L swap",
299 "mkfs.xfs -L nixos /dev/MyVolGroup/nixos",
300 "mount LABEL=nixos /mnt",
301 );
302 '';
303 };
304
305 # Boot off an encrypted root partition
306 luksroot = makeInstallerTest "luksroot"
307 { createPartitions = ''
308 $machine->succeed(
309 "parted /dev/vda mklabel msdos",
310 "parted /dev/vda -- mkpart primary ext2 1M 50MB", # /boot
311 "parted /dev/vda -- mkpart primary linux-swap 50M 1024M",
312 "parted /dev/vda -- mkpart primary 1024M -1s", # LUKS
313 "udevadm settle",
314 "mkswap /dev/vda2 -L swap",
315 "swapon -L swap",
316 "modprobe dm_mod dm_crypt",
317 "echo -n supersecret | cryptsetup luksFormat -q /dev/vda3 -",
318 "echo -n supersecret | cryptsetup luksOpen --key-file - /dev/vda3 cryptroot",
319 "mkfs.ext3 -L nixos /dev/mapper/cryptroot",
320 "mount LABEL=nixos /mnt",
321 "mkfs.ext3 -L boot /dev/vda1",
322 "mkdir -p /mnt/boot",
323 "mount LABEL=boot /mnt/boot",
324 );
325 '';
326 # XXX: Currently, generate-config doesn't detect LUKS yet.
327 extraConfig = ''
328 boot.kernelParams = lib.mkAfter [ "console=tty0" ];
329 boot.initrd.luks.devices = lib.singleton {
330 name = "cryptroot";
331 device = "/dev/vda3";
332 preLVM = true;
333 };
334 '';
335 enableOCR = true;
336 preBootCommands = ''
337 $machine->start;
338 $machine->waitForText(qr/Enter passphrase/);
339 $machine->sendChars("supersecret\n");
340 '';
341 };
342
343 swraid = makeInstallerTest "swraid"
344 { createPartitions =
345 ''
346 $machine->succeed(
347 "parted /dev/vda --"
348 . " mklabel msdos"
349 . " mkpart primary ext2 1M 100MB" # /boot
350 . " mkpart extended 100M -1s"
351 . " mkpart logical 102M 1602M" # md0 (root), first device
352 . " mkpart logical 1603M 3103M" # md0 (root), second device
353 . " mkpart logical 3104M 3360M" # md1 (swap), first device
354 . " mkpart logical 3361M 3617M", # md1 (swap), second device
355 "udevadm settle",
356 "ls -l /dev/vda* >&2",
357 "cat /proc/partitions >&2",
358 "mdadm --create --force /dev/md0 --metadata 1.2 --level=raid1 --raid-devices=2 /dev/vda5 /dev/vda6",
359 "mdadm --create --force /dev/md1 --metadata 1.2 --level=raid1 --raid-devices=2 /dev/vda7 /dev/vda8",
360 "udevadm settle",
361 "mkswap -f /dev/md1 -L swap",
362 "swapon -L swap",
363 "mkfs.ext3 -L nixos /dev/md0",
364 "mount LABEL=nixos /mnt",
365 "mkfs.ext3 -L boot /dev/vda1",
366 "mkdir /mnt/boot",
367 "mount LABEL=boot /mnt/boot",
368 "udevadm settle",
369 "mdadm -W /dev/md0", # wait for sync to finish; booting off an unsynced device tends to fail
370 "mdadm -W /dev/md1",
371 );
372 '';
373 };
374
375 # Test a basic install using GRUB 1.
376 grub1 = makeInstallerTest "grub1"
377 { createPartitions =
378 ''
379 $machine->succeed(
380 "parted /dev/sda mklabel msdos",
381 "parted /dev/sda -- mkpart primary linux-swap 1M 1024M",
382 "parted /dev/sda -- mkpart primary ext2 1024M -1s",
383 "udevadm settle",
384 "mkswap /dev/sda1 -L swap",
385 "swapon -L swap",
386 "mkfs.ext3 -L nixos /dev/sda2",
387 "mount LABEL=nixos /mnt",
388 );
389 '';
390 grubVersion = 1;
391 grubDevice = "/dev/sda";
392 };
393
394 # Test using labels to identify volumes in grub
395 simpleLabels = makeInstallerTest "simpleLabels" {
396 createPartitions = ''
397 $machine->succeed(
398 "sgdisk -Z /dev/vda",
399 "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda",
400 "mkswap /dev/vda2 -L swap",
401 "swapon -L swap",
402 "mkfs.ext4 -L root /dev/vda3",
403 "mount LABEL=root /mnt",
404 );
405 '';
406 grubIdentifier = "label";
407 };
408
409 # Test using the provided disk name within grub
410 # TODO: Fix udev so the symlinks are unneeded in /dev/disks
411 simpleProvided = makeInstallerTest "simpleProvided" {
412 createPartitions = ''
413 my $UUID = "\$(blkid -s UUID -o value /dev/vda2)";
414 $machine->succeed(
415 "sgdisk -Z /dev/vda",
416 "sgdisk -n 1:0:+1M -n 2:0:+100M -n 3:0:+1G -N 4 -t 1:ef02 -t 2:8300 -t 3:8200 -t 4:8300 -c 2:boot -c 4:root /dev/vda",
417 "mkswap /dev/vda3 -L swap",
418 "swapon -L swap",
419 "mkfs.ext4 -L boot /dev/vda2",
420 "mkfs.ext4 -L root /dev/vda4",
421 );
422 $machine->execute("ln -s ../../vda2 /dev/disk/by-uuid/$UUID");
423 $machine->execute("ln -s ../../vda4 /dev/disk/by-label/root");
424 $machine->succeed(
425 "mount /dev/disk/by-label/root /mnt",
426 "mkdir /mnt/boot",
427 "mount /dev/disk/by-uuid/$UUID /mnt/boot"
428 );
429 '';
430 grubIdentifier = "provided";
431 };
432
433 # Simple btrfs grub testing
434 btrfsSimple = makeInstallerTest "btrfsSimple" {
435 createPartitions = ''
436 $machine->succeed(
437 "sgdisk -Z /dev/vda",
438 "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda",
439 "mkswap /dev/vda2 -L swap",
440 "swapon -L swap",
441 "mkfs.btrfs -L root /dev/vda3",
442 "mount LABEL=root /mnt",
443 );
444 '';
445 };
446
447 # Test to see if we can detect /boot and /nix on subvolumes
448 btrfsSubvols = makeInstallerTest "btrfsSubvols" {
449 createPartitions = ''
450 $machine->succeed(
451 "sgdisk -Z /dev/vda",
452 "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda",
453 "mkswap /dev/vda2 -L swap",
454 "swapon -L swap",
455 "mkfs.btrfs -L root /dev/vda3",
456 "btrfs device scan",
457 "mount LABEL=root /mnt",
458 "btrfs subvol create /mnt/boot",
459 "btrfs subvol create /mnt/nixos",
460 "btrfs subvol create /mnt/nixos/default",
461 "umount /mnt",
462 "mount -o defaults,subvol=nixos/default LABEL=root /mnt",
463 "mkdir /mnt/boot",
464 "mount -o defaults,subvol=boot LABEL=root /mnt/boot",
465 );
466 '';
467 };
468
469 # Test to see if we can detect default and aux subvolumes correctly
470 btrfsSubvolDefault = makeInstallerTest "btrfsSubvolDefault" {
471 createPartitions = ''
472 $machine->succeed(
473 "sgdisk -Z /dev/vda",
474 "sgdisk -n 1:0:+1M -n 2:0:+1G -N 3 -t 1:ef02 -t 2:8200 -t 3:8300 -c 3:root /dev/vda",
475 "mkswap /dev/vda2 -L swap",
476 "swapon -L swap",
477 "mkfs.btrfs -L root /dev/vda3",
478 "btrfs device scan",
479 "mount LABEL=root /mnt",
480 "btrfs subvol create /mnt/badpath",
481 "btrfs subvol create /mnt/badpath/boot",
482 "btrfs subvol create /mnt/nixos",
483 "btrfs subvol set-default \$(btrfs subvol list /mnt | grep 'nixos' | awk '{print \$2}') /mnt",
484 "umount /mnt",
485 "mount -o defaults LABEL=root /mnt",
486 "mkdir -p /mnt/badpath/boot", # Help ensure the detection mechanism is actually looking up subvolumes
487 "mkdir /mnt/boot",
488 "mount -o defaults,subvol=badpath/boot LABEL=root /mnt/boot",
489 );
490 '';
491 };
492
493}