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