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
11 # The configuration to install.
12 makeConfig = { bootLoader, grubVersion, grubDevice, grubIdentifier, grubUseEfi
13 , extraConfig, forceGrubReinstallCount ? 0
14 }:
15 pkgs.writeText "configuration.nix" ''
16 { config, lib, pkgs, modulesPath, ... }:
17
18 { imports =
19 [ ./hardware-configuration.nix
20 <nixpkgs/nixos/modules/testing/test-instrumentation.nix>
21 ];
22
23 # To ensure that we can rebuild the grub configuration on the nixos-rebuild
24 system.extraDependencies = with pkgs; [ stdenvNoCC ];
25
26 ${optionalString (bootLoader == "grub") ''
27 boot.loader.grub.version = ${toString grubVersion};
28 ${optionalString (grubVersion == 1) ''
29 boot.loader.grub.splashImage = null;
30 ''}
31
32 boot.loader.grub.extraConfig = "serial; terminal_output serial";
33 ${if grubUseEfi then ''
34 boot.loader.grub.device = "nodev";
35 boot.loader.grub.efiSupport = true;
36 boot.loader.grub.efiInstallAsRemovable = true; # XXX: needed for OVMF?
37 '' else ''
38 boot.loader.grub.device = "${grubDevice}";
39 boot.loader.grub.fsIdentifier = "${grubIdentifier}";
40 ''}
41
42 boot.loader.grub.configurationLimit = 100 + ${toString forceGrubReinstallCount};
43 ''}
44
45 ${optionalString (bootLoader == "systemd-boot") ''
46 boot.loader.systemd-boot.enable = true;
47 ''}
48
49 users.users.alice = {
50 isNormalUser = true;
51 home = "/home/alice";
52 description = "Alice Foobar";
53 };
54
55 hardware.enableAllFirmware = lib.mkForce false;
56
57 ${replaceChars ["\n"] ["\n "] extraConfig}
58 }
59 '';
60
61
62 # The test script boots a NixOS VM, installs NixOS on an empty hard
63 # disk, and then reboot from the hard disk. It's parameterized with
64 # a test script fragment `createPartitions', which must create
65 # partitions and filesystems.
66 testScriptFun = { bootLoader, createPartitions, grubVersion, grubDevice, grubUseEfi
67 , grubIdentifier, preBootCommands, postBootCommands, extraConfig
68 , testSpecialisationConfig
69 }:
70 let iface = if grubVersion == 1 then "ide" else "virtio";
71 isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi);
72 bios = if pkgs.stdenv.isAarch64 then "QEMU_EFI.fd" else "OVMF.fd";
73 in if !isEfi && !pkgs.stdenv.hostPlatform.isx86 then
74 throw "Non-EFI boot methods are only supported on i686 / x86_64"
75 else ''
76 def assemble_qemu_flags():
77 flags = "-cpu max"
78 ${if (system == "x86_64-linux" || system == "i686-linux")
79 then ''flags += " -m 1500"''
80 else ''flags += " -m 1000 -enable-kvm -machine virt,gic-version=host"''
81 }
82 return flags
83
84
85 qemu_flags = {"qemuFlags": assemble_qemu_flags()}
86
87 hd_flags = {
88 "hdaInterface": "${iface}",
89 "hda": "vm-state-machine/machine.qcow2",
90 }
91 ${optionalString isEfi ''
92 hd_flags.update(
93 bios="${pkgs.OVMF.fd}/FV/${bios}"
94 )''
95 }
96 default_flags = {**hd_flags, **qemu_flags}
97
98
99 def create_machine_named(name):
100 return create_machine({**default_flags, "name": name})
101
102
103 machine.start()
104
105 with subtest("Assert readiness of login prompt"):
106 machine.succeed("echo hello")
107
108 with subtest("Wait for hard disks to appear in /dev"):
109 machine.succeed("udevadm settle")
110
111 ${createPartitions}
112
113 with subtest("Create the NixOS configuration"):
114 machine.succeed("nixos-generate-config --root /mnt")
115 machine.succeed("cat /mnt/etc/nixos/hardware-configuration.nix >&2")
116 machine.copy_from_host(
117 "${ makeConfig {
118 inherit bootLoader grubVersion grubDevice grubIdentifier
119 grubUseEfi extraConfig;
120 }
121 }",
122 "/mnt/etc/nixos/configuration.nix",
123 )
124
125 with subtest("Perform the installation"):
126 machine.succeed("nixos-install < /dev/null >&2")
127
128 with subtest("Do it again to make sure it's idempotent"):
129 machine.succeed("nixos-install < /dev/null >&2")
130
131 with subtest("Shutdown system after installation"):
132 machine.succeed("umount /mnt/boot || true")
133 machine.succeed("umount /mnt")
134 machine.succeed("sync")
135 machine.shutdown()
136
137 # Now see if we can boot the installation.
138 machine = create_machine_named("boot-after-install")
139
140 # For example to enter LUKS passphrase.
141 ${preBootCommands}
142
143 with subtest("Assert that /boot get mounted"):
144 machine.wait_for_unit("local-fs.target")
145 ${if bootLoader == "grub"
146 then ''machine.succeed("test -e /boot/grub")''
147 else ''machine.succeed("test -e /boot/loader/loader.conf")''
148 }
149
150 with subtest("Check whether /root has correct permissions"):
151 assert "700" in machine.succeed("stat -c '%a' /root")
152
153 with subtest("Assert swap device got activated"):
154 # uncomment once https://bugs.freedesktop.org/show_bug.cgi?id=86930 is resolved
155 machine.wait_for_unit("swap.target")
156 machine.succeed("cat /proc/swaps | grep -q /dev")
157
158 with subtest("Check that the store is in good shape"):
159 machine.succeed("nix-store --verify --check-contents >&2")
160
161 with subtest("Check whether the channel works"):
162 machine.succeed("nix-env -iA nixos.procps >&2")
163 assert ".nix-profile" in machine.succeed("type -tP ps | tee /dev/stderr")
164
165 with subtest(
166 "Check that the daemon works, and that non-root users can run builds "
167 "(this will build a new profile generation through the daemon)"
168 ):
169 machine.succeed("su alice -l -c 'nix-env -iA nixos.procps' >&2")
170
171 with subtest("Configure system with writable Nix store on next boot"):
172 # we're not using copy_from_host here because the installer image
173 # doesn't know about the host-guest sharing mechanism.
174 machine.copy_from_host_via_shell(
175 "${ makeConfig {
176 inherit bootLoader grubVersion grubDevice grubIdentifier
177 grubUseEfi extraConfig;
178 forceGrubReinstallCount = 1;
179 }
180 }",
181 "/etc/nixos/configuration.nix",
182 )
183
184 with subtest("Check whether nixos-rebuild works"):
185 machine.succeed("nixos-rebuild switch >&2")
186
187 # FIXME: Nix 2.4 broke nixos-option, someone has to fix it.
188 # with subtest("Test nixos-option"):
189 # kernel_modules = machine.succeed("nixos-option boot.initrd.kernelModules")
190 # assert "virtio_console" in kernel_modules
191 # assert "List of modules" in kernel_modules
192 # assert "qemu-guest.nix" in kernel_modules
193
194 machine.shutdown()
195
196 # Check whether a writable store build works
197 machine = create_machine_named("rebuild-switch")
198 ${preBootCommands}
199 machine.wait_for_unit("multi-user.target")
200
201 # we're not using copy_from_host here because the installer image
202 # doesn't know about the host-guest sharing mechanism.
203 machine.copy_from_host_via_shell(
204 "${ makeConfig {
205 inherit bootLoader grubVersion grubDevice grubIdentifier
206 grubUseEfi extraConfig;
207 forceGrubReinstallCount = 2;
208 }
209 }",
210 "/etc/nixos/configuration.nix",
211 )
212 machine.succeed("nixos-rebuild boot >&2")
213 machine.shutdown()
214
215 # And just to be sure, check that the machine still boots after
216 # "nixos-rebuild switch".
217 machine = create_machine_named("boot-after-rebuild-switch")
218 ${preBootCommands}
219 machine.wait_for_unit("network.target")
220 ${postBootCommands}
221 machine.shutdown()
222
223 # Tests for validating clone configuration entries in grub menu
224 ''
225 + optionalString testSpecialisationConfig ''
226 # Reboot Machine
227 machine = create_machine_named("clone-default-config")
228 ${preBootCommands}
229 machine.wait_for_unit("multi-user.target")
230
231 with subtest("Booted configuration name should be 'Home'"):
232 # This is not the name that shows in the grub menu.
233 # The default configuration is always shown as "Default"
234 machine.succeed("cat /run/booted-system/configuration-name >&2")
235 assert "Home" in machine.succeed("cat /run/booted-system/configuration-name")
236
237 with subtest("We should **not** find a file named /etc/gitconfig"):
238 machine.fail("test -e /etc/gitconfig")
239
240 with subtest("Set grub to boot the second configuration"):
241 machine.succeed("grub-reboot 1")
242
243 ${postBootCommands}
244 machine.shutdown()
245
246 # Reboot Machine
247 machine = create_machine_named("clone-alternate-config")
248 ${preBootCommands}
249
250 machine.wait_for_unit("multi-user.target")
251 with subtest("Booted configuration name should be Work"):
252 machine.succeed("cat /run/booted-system/configuration-name >&2")
253 assert "Work" in machine.succeed("cat /run/booted-system/configuration-name")
254
255 with subtest("We should find a file named /etc/gitconfig"):
256 machine.succeed("test -e /etc/gitconfig")
257
258 ${postBootCommands}
259 machine.shutdown()
260 '';
261
262
263 makeInstallerTest = name:
264 { createPartitions, preBootCommands ? "", postBootCommands ? "", extraConfig ? ""
265 , extraInstallerConfig ? {}
266 , bootLoader ? "grub" # either "grub" or "systemd-boot"
267 , grubVersion ? 2, grubDevice ? "/dev/vda", grubIdentifier ? "uuid", grubUseEfi ? false
268 , enableOCR ? false, meta ? {}
269 , testSpecialisationConfig ? false
270 }:
271 makeTest {
272 inherit enableOCR;
273 name = "installer-" + name;
274 meta = with pkgs.lib.maintainers; {
275 # put global maintainers here, individuals go into makeInstallerTest fkt call
276 maintainers = (meta.maintainers or []);
277 };
278 nodes = {
279
280 # The configuration of the machine used to run "nixos-install".
281 machine = { pkgs, ... }: {
282 imports = [
283 ../modules/profiles/installation-device.nix
284 ../modules/profiles/base.nix
285 extraInstallerConfig
286 ];
287
288 # builds stuff in the VM, needs more juice
289 virtualisation.diskSize = 8 * 1024;
290 virtualisation.cores = 8;
291 virtualisation.memorySize = 3096;
292
293 # Use a small /dev/vdb as the root disk for the
294 # installer. This ensures the target disk (/dev/vda) is
295 # the same during and after installation.
296 virtualisation.emptyDiskImages = [ 512 ];
297 virtualisation.bootDevice =
298 if grubVersion == 1 then "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive2" else "/dev/vdb";
299 virtualisation.qemu.diskInterface =
300 if grubVersion == 1 then "scsi" else "virtio";
301
302 boot.loader.systemd-boot.enable = mkIf (bootLoader == "systemd-boot") true;
303
304 hardware.enableAllFirmware = mkForce false;
305
306 # The test cannot access the network, so any packages we
307 # need must be included in the VM.
308 system.extraDependencies = with pkgs; [
309 desktop-file-utils
310 docbook5
311 docbook_xsl_ns
312 libxml2.bin
313 libxslt.bin
314 nixos-artwork.wallpapers.simple-dark-gray-bottom
315 ntp
316 perlPackages.ListCompare
317 perlPackages.XMLLibXML
318 shared-mime-info
319 sudo
320 texinfo
321 unionfs-fuse
322 xorg.lndir
323
324 # add curl so that rather than seeing the test attempt to download
325 # curl's tarball, we see what it's trying to download
326 curl
327 ]
328 ++ optional (bootLoader == "grub" && grubVersion == 1) pkgs.grub
329 ++ optionals (bootLoader == "grub" && grubVersion == 2) (let
330 zfsSupport = lib.any (x: x == "zfs")
331 (extraInstallerConfig.boot.supportedFilesystems or []);
332 in [
333 (pkgs.grub2.override { inherit zfsSupport; })
334 (pkgs.grub2_efi.override { inherit zfsSupport; })
335 ]);
336
337 nix.binaryCaches = mkForce [ ];
338 nix.extraOptions = ''
339 hashed-mirrors =
340 connect-timeout = 1
341 '';
342 };
343
344 };
345
346 testScript = testScriptFun {
347 inherit bootLoader createPartitions preBootCommands postBootCommands
348 grubVersion grubDevice grubIdentifier grubUseEfi extraConfig
349 testSpecialisationConfig;
350 };
351 };
352
353 makeLuksRootTest = name: luksFormatOpts: makeInstallerTest name {
354 createPartitions = ''
355 machine.succeed(
356 "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
357 + " mkpart primary ext2 1M 50MB" # /boot
358 + " mkpart primary linux-swap 50M 1024M"
359 + " mkpart primary 1024M -1s", # LUKS
360 "udevadm settle",
361 "mkswap /dev/vda2 -L swap",
362 "swapon -L swap",
363 "modprobe dm_mod dm_crypt",
364 "echo -n supersecret | cryptsetup luksFormat ${luksFormatOpts} -q /dev/vda3 -",
365 "echo -n supersecret | cryptsetup luksOpen --key-file - /dev/vda3 cryptroot",
366 "mkfs.ext3 -L nixos /dev/mapper/cryptroot",
367 "mount LABEL=nixos /mnt",
368 "mkfs.ext3 -L boot /dev/vda1",
369 "mkdir -p /mnt/boot",
370 "mount LABEL=boot /mnt/boot",
371 )
372 '';
373 extraConfig = ''
374 boot.kernelParams = lib.mkAfter [ "console=tty0" ];
375 '';
376 enableOCR = true;
377 preBootCommands = ''
378 machine.start()
379 machine.wait_for_text("Passphrase for")
380 machine.send_chars("supersecret\n")
381 '';
382 };
383
384 # The (almost) simplest partitioning scheme: a swap partition and
385 # one big filesystem partition.
386 simple-test-config = {
387 createPartitions = ''
388 machine.succeed(
389 "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
390 + " mkpart primary linux-swap 1M 1024M"
391 + " mkpart primary ext2 1024M -1s",
392 "udevadm settle",
393 "mkswap /dev/vda1 -L swap",
394 "swapon -L swap",
395 "mkfs.ext3 -L nixos /dev/vda2",
396 "mount LABEL=nixos /mnt",
397 )
398 '';
399 };
400
401 simple-uefi-grub-config = {
402 createPartitions = ''
403 machine.succeed(
404 "flock /dev/vda parted --script /dev/vda -- mklabel gpt"
405 + " mkpart ESP fat32 1M 100MiB" # /boot
406 + " set 1 boot on"
407 + " mkpart primary linux-swap 100MiB 1024MiB"
408 + " mkpart primary ext2 1024MiB -1MiB", # /
409 "udevadm settle",
410 "mkswap /dev/vda2 -L swap",
411 "swapon -L swap",
412 "mkfs.ext3 -L nixos /dev/vda3",
413 "mount LABEL=nixos /mnt",
414 "mkfs.vfat -n BOOT /dev/vda1",
415 "mkdir -p /mnt/boot",
416 "mount LABEL=BOOT /mnt/boot",
417 )
418 '';
419 bootLoader = "grub";
420 grubUseEfi = true;
421 };
422
423 specialisation-test-extraconfig = {
424 extraConfig = ''
425 environment.systemPackages = [ pkgs.grub2 ];
426 boot.loader.grub.configurationName = "Home";
427 specialisation.work.configuration = {
428 boot.loader.grub.configurationName = lib.mkForce "Work";
429
430 environment.etc = {
431 "gitconfig".text = "
432 [core]
433 gitproxy = none for work.com
434 ";
435 };
436 };
437 '';
438 testSpecialisationConfig = true;
439 };
440
441
442in {
443
444 # !!! `parted mkpart' seems to silently create overlapping partitions.
445
446
447 # The (almost) simplest partitioning scheme: a swap partition and
448 # one big filesystem partition.
449 simple = makeInstallerTest "simple" simple-test-config;
450
451 # Test cloned configurations with the simple grub configuration
452 simpleSpecialised = makeInstallerTest "simpleSpecialised" (simple-test-config // specialisation-test-extraconfig);
453
454 # Simple GPT/UEFI configuration using systemd-boot with 3 partitions: ESP, swap & root filesystem
455 simpleUefiSystemdBoot = makeInstallerTest "simpleUefiSystemdBoot" {
456 createPartitions = ''
457 machine.succeed(
458 "flock /dev/vda parted --script /dev/vda -- mklabel gpt"
459 + " mkpart ESP fat32 1M 50MiB" # /boot
460 + " set 1 boot on"
461 + " mkpart primary linux-swap 50MiB 1024MiB"
462 + " mkpart primary ext2 1024MiB -1MiB", # /
463 "udevadm settle",
464 "mkswap /dev/vda2 -L swap",
465 "swapon -L swap",
466 "mkfs.ext3 -L nixos /dev/vda3",
467 "mount LABEL=nixos /mnt",
468 "mkfs.vfat -n BOOT /dev/vda1",
469 "mkdir -p /mnt/boot",
470 "mount LABEL=BOOT /mnt/boot",
471 )
472 '';
473 bootLoader = "systemd-boot";
474 };
475
476 simpleUefiGrub = makeInstallerTest "simpleUefiGrub" simple-uefi-grub-config;
477
478 # Test cloned configurations with the uefi grub configuration
479 simpleUefiGrubSpecialisation = makeInstallerTest "simpleUefiGrubSpecialisation" (simple-uefi-grub-config // specialisation-test-extraconfig);
480
481 # Same as the previous, but now with a separate /boot partition.
482 separateBoot = makeInstallerTest "separateBoot" {
483 createPartitions = ''
484 machine.succeed(
485 "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
486 + " mkpart primary ext2 1M 50MB" # /boot
487 + " mkpart primary linux-swap 50MB 1024M"
488 + " mkpart primary ext2 1024M -1s", # /
489 "udevadm settle",
490 "mkswap /dev/vda2 -L swap",
491 "swapon -L swap",
492 "mkfs.ext3 -L nixos /dev/vda3",
493 "mount LABEL=nixos /mnt",
494 "mkfs.ext3 -L boot /dev/vda1",
495 "mkdir -p /mnt/boot",
496 "mount LABEL=boot /mnt/boot",
497 )
498 '';
499 };
500
501 # Same as the previous, but with fat32 /boot.
502 separateBootFat = makeInstallerTest "separateBootFat" {
503 createPartitions = ''
504 machine.succeed(
505 "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
506 + " mkpart primary ext2 1M 50MB" # /boot
507 + " mkpart primary linux-swap 50MB 1024M"
508 + " mkpart primary ext2 1024M -1s", # /
509 "udevadm settle",
510 "mkswap /dev/vda2 -L swap",
511 "swapon -L swap",
512 "mkfs.ext3 -L nixos /dev/vda3",
513 "mount LABEL=nixos /mnt",
514 "mkfs.vfat -n BOOT /dev/vda1",
515 "mkdir -p /mnt/boot",
516 "mount LABEL=BOOT /mnt/boot",
517 )
518 '';
519 };
520
521 # zfs on / with swap
522 zfsroot = makeInstallerTest "zfs-root" {
523 extraInstallerConfig = {
524 boot.supportedFilesystems = [ "zfs" ];
525 };
526
527 extraConfig = ''
528 boot.supportedFilesystems = [ "zfs" ];
529
530 # Using by-uuid overrides the default of by-id, and is unique
531 # to the qemu disks, as they don't produce by-id paths for
532 # some reason.
533 boot.zfs.devNodes = "/dev/disk/by-uuid/";
534 networking.hostId = "00000000";
535 '';
536
537 createPartitions = ''
538 machine.succeed(
539 "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
540 + " mkpart primary linux-swap 1M 1024M"
541 + " mkpart primary 1024M -1s",
542 "udevadm settle",
543 "mkswap /dev/vda1 -L swap",
544 "swapon -L swap",
545 "zpool create rpool /dev/vda2",
546 "zfs create -o mountpoint=legacy rpool/root",
547 "mount -t zfs rpool/root /mnt",
548 "udevadm settle",
549 )
550 '';
551 };
552
553 # Create two physical LVM partitions combined into one volume group
554 # that contains the logical swap and root partitions.
555 lvm = makeInstallerTest "lvm" {
556 createPartitions = ''
557 machine.succeed(
558 "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
559 + " mkpart primary 1M 2048M" # PV1
560 + " set 1 lvm on"
561 + " mkpart primary 2048M -1s" # PV2
562 + " set 2 lvm on",
563 "udevadm settle",
564 "sleep 1",
565 "pvcreate /dev/vda1 /dev/vda2",
566 "sleep 1",
567 "vgcreate MyVolGroup /dev/vda1 /dev/vda2",
568 "sleep 1",
569 "lvcreate --size 1G --name swap MyVolGroup",
570 "sleep 1",
571 "lvcreate --size 3G --name nixos MyVolGroup",
572 "sleep 1",
573 "mkswap -f /dev/MyVolGroup/swap -L swap",
574 "swapon -L swap",
575 "mkfs.xfs -L nixos /dev/MyVolGroup/nixos",
576 "mount LABEL=nixos /mnt",
577 )
578 '';
579 postBootCommands = ''
580 assert "loaded active" in machine.succeed(
581 "systemctl list-units 'lvm2-pvscan@*' -ql --no-legend | tee /dev/stderr"
582 )
583 '';
584 };
585
586 # Boot off an encrypted root partition with the default LUKS header format
587 luksroot = makeLuksRootTest "luksroot-format1" "";
588
589 # Boot off an encrypted root partition with LUKS1 format
590 luksroot-format1 = makeLuksRootTest "luksroot-format1" "--type=LUKS1";
591
592 # Boot off an encrypted root partition with LUKS2 format
593 luksroot-format2 = makeLuksRootTest "luksroot-format2" "--type=LUKS2";
594
595 # Test whether opening encrypted filesystem with keyfile
596 # Checks for regression of missing cryptsetup, when no luks device without
597 # keyfile is configured
598 encryptedFSWithKeyfile = makeInstallerTest "encryptedFSWithKeyfile" {
599 createPartitions = ''
600 machine.succeed(
601 "flock /dev/vda parted --script /dev/vda -- mklabel msdos"
602 + " mkpart primary ext2 1M 50MB" # /boot
603 + " mkpart primary linux-swap 50M 1024M"
604 + " mkpart primary 1024M 1280M" # LUKS with keyfile
605 + " mkpart primary 1280M -1s",
606 "udevadm settle",
607 "mkswap /dev/vda2 -L swap",
608 "swapon -L swap",
609 "mkfs.ext3 -L nixos /dev/vda4",
610 "mount LABEL=nixos /mnt",
611 "mkfs.ext3 -L boot /dev/vda1",
612 "mkdir -p /mnt/boot",
613 "mount LABEL=boot /mnt/boot",
614 "modprobe dm_mod dm_crypt",
615 "echo -n supersecret > /mnt/keyfile",
616 "cryptsetup luksFormat -q /dev/vda3 --key-file /mnt/keyfile",
617 "cryptsetup luksOpen --key-file /mnt/keyfile /dev/vda3 crypt",
618 "mkfs.ext3 -L test /dev/mapper/crypt",
619 "cryptsetup luksClose crypt",
620 "mkdir -p /mnt/test",
621 )
622 '';
623 extraConfig = ''
624 fileSystems."/test" = {
625 device = "/dev/disk/by-label/test";
626 fsType = "ext3";
627 encrypted.enable = true;
628 encrypted.blkDev = "/dev/vda3";
629 encrypted.label = "crypt";
630 encrypted.keyFile = "/mnt-root/keyfile";
631 };
632 '';
633 };
634
635 swraid = makeInstallerTest "swraid" {
636 createPartitions = ''
637 machine.succeed(
638 "flock /dev/vda parted --script /dev/vda --"
639 + " mklabel msdos"
640 + " mkpart primary ext2 1M 100MB" # /boot
641 + " mkpart extended 100M -1s"
642 + " mkpart logical 102M 3102M" # md0 (root), first device
643 + " mkpart logical 3103M 6103M" # md0 (root), second device
644 + " mkpart logical 6104M 6360M" # md1 (swap), first device
645 + " mkpart logical 6361M 6617M", # md1 (swap), second device
646 "udevadm settle",
647 "ls -l /dev/vda* >&2",
648 "cat /proc/partitions >&2",
649 "udevadm control --stop-exec-queue",
650 "mdadm --create --force /dev/md0 --metadata 1.2 --level=raid1 "
651 + "--raid-devices=2 /dev/vda5 /dev/vda6",
652 "mdadm --create --force /dev/md1 --metadata 1.2 --level=raid1 "
653 + "--raid-devices=2 /dev/vda7 /dev/vda8",
654 "udevadm control --start-exec-queue",
655 "udevadm settle",
656 "mkswap -f /dev/md1 -L swap",
657 "swapon -L swap",
658 "mkfs.ext3 -L nixos /dev/md0",
659 "mount LABEL=nixos /mnt",
660 "mkfs.ext3 -L boot /dev/vda1",
661 "mkdir /mnt/boot",
662 "mount LABEL=boot /mnt/boot",
663 "udevadm settle",
664 )
665 '';
666 preBootCommands = ''
667 machine.start()
668 machine.fail("dmesg | grep 'immediate safe mode'")
669 '';
670 };
671
672 bcache = makeInstallerTest "bcache" {
673 createPartitions = ''
674 machine.succeed(
675 "flock /dev/vda parted --script /dev/vda --"
676 + " mklabel msdos"
677 + " mkpart primary ext2 1M 50MB" # /boot
678 + " mkpart primary 50MB 512MB " # swap
679 + " mkpart primary 512MB 1024MB" # Cache (typically SSD)
680 + " mkpart primary 1024MB -1s ", # Backing device (typically HDD)
681 "modprobe bcache",
682 "udevadm settle",
683 "make-bcache -B /dev/vda4 -C /dev/vda3",
684 "echo /dev/vda3 > /sys/fs/bcache/register",
685 "echo /dev/vda4 > /sys/fs/bcache/register",
686 "udevadm settle",
687 "mkfs.ext3 -L nixos /dev/bcache0",
688 "mount LABEL=nixos /mnt",
689 "mkfs.ext3 -L boot /dev/vda1",
690 "mkdir /mnt/boot",
691 "mount LABEL=boot /mnt/boot",
692 "mkswap -f /dev/vda2 -L swap",
693 "swapon -L swap",
694 )
695 '';
696 };
697
698 # Test a basic install using GRUB 1.
699 grub1 = makeInstallerTest "grub1" rec {
700 createPartitions = ''
701 machine.succeed(
702 "flock ${grubDevice} parted --script ${grubDevice} -- mklabel msdos"
703 + " mkpart primary linux-swap 1M 1024M"
704 + " mkpart primary ext2 1024M -1s",
705 "udevadm settle",
706 "mkswap ${grubDevice}-part1 -L swap",
707 "swapon -L swap",
708 "mkfs.ext3 -L nixos ${grubDevice}-part2",
709 "mount LABEL=nixos /mnt",
710 "mkdir -p /mnt/tmp",
711 )
712 '';
713 grubVersion = 1;
714 # /dev/sda is not stable, even when the SCSI disk number is.
715 grubDevice = "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive1";
716 };
717
718 # Test using labels to identify volumes in grub
719 simpleLabels = makeInstallerTest "simpleLabels" {
720 createPartitions = ''
721 machine.succeed(
722 "sgdisk -Z /dev/vda",
723 "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",
724 "mkswap /dev/vda2 -L swap",
725 "swapon -L swap",
726 "mkfs.ext4 -L root /dev/vda3",
727 "mount LABEL=root /mnt",
728 )
729 '';
730 grubIdentifier = "label";
731 };
732
733 # Test using the provided disk name within grub
734 # TODO: Fix udev so the symlinks are unneeded in /dev/disks
735 simpleProvided = makeInstallerTest "simpleProvided" {
736 createPartitions = ''
737 uuid = "$(blkid -s UUID -o value /dev/vda2)"
738 machine.succeed(
739 "sgdisk -Z /dev/vda",
740 "sgdisk -n 1:0:+1M -n 2:0:+100M -n 3:0:+1G -N 4 -t 1:ef02 -t 2:8300 "
741 + "-t 3:8200 -t 4:8300 -c 2:boot -c 4:root /dev/vda",
742 "mkswap /dev/vda3 -L swap",
743 "swapon -L swap",
744 "mkfs.ext4 -L boot /dev/vda2",
745 "mkfs.ext4 -L root /dev/vda4",
746 )
747 machine.execute(f"ln -s ../../vda2 /dev/disk/by-uuid/{uuid}")
748 machine.execute("ln -s ../../vda4 /dev/disk/by-label/root")
749 machine.succeed(
750 "mount /dev/disk/by-label/root /mnt",
751 "mkdir /mnt/boot",
752 f"mount /dev/disk/by-uuid/{uuid} /mnt/boot",
753 )
754 '';
755 grubIdentifier = "provided";
756 };
757
758 # Simple btrfs grub testing
759 btrfsSimple = makeInstallerTest "btrfsSimple" {
760 createPartitions = ''
761 machine.succeed(
762 "sgdisk -Z /dev/vda",
763 "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",
764 "mkswap /dev/vda2 -L swap",
765 "swapon -L swap",
766 "mkfs.btrfs -L root /dev/vda3",
767 "mount LABEL=root /mnt",
768 )
769 '';
770 };
771
772 # Test to see if we can detect /boot and /nix on subvolumes
773 btrfsSubvols = makeInstallerTest "btrfsSubvols" {
774 createPartitions = ''
775 machine.succeed(
776 "sgdisk -Z /dev/vda",
777 "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",
778 "mkswap /dev/vda2 -L swap",
779 "swapon -L swap",
780 "mkfs.btrfs -L root /dev/vda3",
781 "btrfs device scan",
782 "mount LABEL=root /mnt",
783 "btrfs subvol create /mnt/boot",
784 "btrfs subvol create /mnt/nixos",
785 "btrfs subvol create /mnt/nixos/default",
786 "umount /mnt",
787 "mount -o defaults,subvol=nixos/default LABEL=root /mnt",
788 "mkdir /mnt/boot",
789 "mount -o defaults,subvol=boot LABEL=root /mnt/boot",
790 )
791 '';
792 };
793
794 # Test to see if we can detect default and aux subvolumes correctly
795 btrfsSubvolDefault = makeInstallerTest "btrfsSubvolDefault" {
796 createPartitions = ''
797 machine.succeed(
798 "sgdisk -Z /dev/vda",
799 "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",
800 "mkswap /dev/vda2 -L swap",
801 "swapon -L swap",
802 "mkfs.btrfs -L root /dev/vda3",
803 "btrfs device scan",
804 "mount LABEL=root /mnt",
805 "btrfs subvol create /mnt/badpath",
806 "btrfs subvol create /mnt/badpath/boot",
807 "btrfs subvol create /mnt/nixos",
808 "btrfs subvol set-default "
809 + "$(btrfs subvol list /mnt | grep 'nixos' | awk '{print $2}') /mnt",
810 "umount /mnt",
811 "mount -o defaults LABEL=root /mnt",
812 "mkdir -p /mnt/badpath/boot", # Help ensure the detection mechanism
813 # is actually looking up subvolumes
814 "mkdir /mnt/boot",
815 "mount -o defaults,subvol=badpath/boot LABEL=root /mnt/boot",
816 )
817 '';
818 };
819}