at 25.11-pre 63 kB view raw
1{ 2 system ? builtins.currentSystem, 3 config ? { }, 4 pkgs ? import ../.. { inherit system config; }, 5 systemdStage1 ? false, 6}: 7 8with import ../lib/testing-python.nix { inherit system pkgs; }; 9with pkgs.lib; 10 11let 12 13 # The configuration to install. 14 makeConfig = 15 { 16 bootLoader, 17 grubDevice, 18 grubIdentifier, 19 grubUseEfi, 20 extraConfig, 21 forceGrubReinstallCount ? 0, 22 withTestInstrumentation ? true, 23 clevisTest, 24 }: 25 pkgs.writeText "configuration.nix" '' 26 { config, lib, pkgs, modulesPath, ... }: 27 28 { imports = 29 [ ./hardware-configuration.nix 30 ${ 31 if !withTestInstrumentation then 32 "" # Still included, but via installer/flake.nix 33 else 34 "<nixpkgs/nixos/modules/testing/test-instrumentation.nix>" 35 } 36 ]; 37 38 networking.hostName = "thatworked"; 39 40 documentation.enable = false; 41 42 # To ensure that we can rebuild the grub configuration on the nixos-rebuild 43 system.extraDependencies = with pkgs; [ stdenvNoCC ]; 44 45 ${optionalString systemdStage1 "boot.initrd.systemd.enable = true;"} 46 47 ${optionalString (bootLoader == "grub") '' 48 boot.loader.grub.extraConfig = "serial; terminal_output serial"; 49 ${ 50 if grubUseEfi then 51 '' 52 boot.loader.grub.device = "nodev"; 53 boot.loader.grub.efiSupport = true; 54 boot.loader.grub.efiInstallAsRemovable = true; # XXX: needed for OVMF? 55 '' 56 else 57 '' 58 boot.loader.grub.device = "${grubDevice}"; 59 boot.loader.grub.fsIdentifier = "${grubIdentifier}"; 60 '' 61 } 62 63 boot.loader.grub.configurationLimit = 100 + ${toString forceGrubReinstallCount}; 64 ''} 65 66 ${optionalString (bootLoader == "systemd-boot") '' 67 boot.loader.systemd-boot.enable = true; 68 ''} 69 70 boot.initrd.secrets."/etc/secret" = "/etc/nixos/secret"; 71 72 ${optionalString clevisTest '' 73 boot.kernelParams = [ "console=tty0" "ip=192.168.1.1:::255.255.255.0::eth1:none" ]; 74 boot.initrd = { 75 availableKernelModules = [ "tpm_tis" ]; 76 clevis = { enable = true; useTang = true; }; 77 network.enable = true; 78 }; 79 ''} 80 81 users.users.alice = { 82 isNormalUser = true; 83 home = "/home/alice"; 84 description = "Alice Foobar"; 85 }; 86 87 hardware.enableAllFirmware = lib.mkForce false; 88 89 ${replaceStrings [ "\n" ] [ "\n " ] extraConfig} 90 } 91 ''; 92 93 # The test script boots a NixOS VM, installs NixOS on an empty hard 94 # disk, and then reboot from the hard disk. It's parameterized with 95 # a test script fragment `createPartitions', which must create 96 # partitions and filesystems. 97 testScriptFun = 98 { 99 bootLoader, 100 createPartitions, 101 grubDevice, 102 grubUseEfi, 103 grubIdentifier, 104 postInstallCommands, 105 postBootCommands, 106 extraConfig, 107 testSpecialisationConfig, 108 testFlakeSwitch, 109 testByAttrSwitch, 110 clevisTest, 111 clevisFallbackTest, 112 disableFileSystems, 113 }: 114 let 115 startTarget = '' 116 ${optionalString clevisTest "tpm.start()"} 117 target.start() 118 ${postBootCommands} 119 target.wait_for_unit("multi-user.target") 120 ''; 121 in 122 '' 123 ${optionalString clevisTest '' 124 import os 125 import subprocess 126 127 tpm_folder = os.environ['NIX_BUILD_TOP'] 128 129 class Tpm: 130 def __init__(self): 131 self.start() 132 133 def start(self): 134 self.proc = subprocess.Popen(["${pkgs.swtpm}/bin/swtpm", 135 "socket", 136 "--tpmstate", f"dir={tpm_folder}/swtpm", 137 "--ctrl", f"type=unixio,path={tpm_folder}/swtpm-sock", 138 "--tpm2" 139 ]) 140 141 # Check whether starting swtpm failed 142 try: 143 exit_code = self.proc.wait(timeout=0.2) 144 if exit_code is not None and exit_code != 0: 145 raise Exception("failed to start swtpm") 146 except subprocess.TimeoutExpired: 147 pass 148 149 """Check whether the swtpm process exited due to an error""" 150 def check(self): 151 exit_code = self.proc.poll() 152 if exit_code is not None and exit_code != 0: 153 raise Exception("swtpm process died") 154 155 156 os.mkdir(f"{tpm_folder}/swtpm") 157 tpm = Tpm() 158 tpm.check() 159 ''} 160 161 installer.start() 162 ${optionalString clevisTest '' 163 tang.start() 164 tang.wait_for_unit("sockets.target") 165 tang.systemctl("start network-online.target") 166 tang.wait_for_unit("network-online.target") 167 installer.systemctl("start network-online.target") 168 installer.wait_for_unit("network-online.target") 169 ''} 170 installer.wait_for_unit("multi-user.target") 171 172 with subtest("Assert readiness of login prompt"): 173 installer.succeed("echo hello") 174 175 with subtest("Wait for hard disks to appear in /dev"): 176 installer.succeed("udevadm settle") 177 178 ${createPartitions} 179 180 with subtest("Create the NixOS configuration"): 181 installer.succeed("nixos-generate-config ${optionalString disableFileSystems "--no-filesystems"} --root /mnt") 182 installer.succeed("cat /mnt/etc/nixos/hardware-configuration.nix >&2") 183 installer.copy_from_host( 184 "${ 185 makeConfig { 186 inherit 187 bootLoader 188 grubDevice 189 grubIdentifier 190 grubUseEfi 191 extraConfig 192 clevisTest 193 ; 194 } 195 }", 196 "/mnt/etc/nixos/configuration.nix", 197 ) 198 installer.copy_from_host("${pkgs.writeText "secret" "secret"}", "/mnt/etc/nixos/secret") 199 200 ${optionalString clevisTest '' 201 with subtest("Create the Clevis secret with Tang"): 202 installer.systemctl("start network-online.target") 203 installer.wait_for_unit("network-online.target") 204 installer.succeed('echo -n password | clevis encrypt sss \'{"t": 2, "pins": {"tpm2": {}, "tang": {"url": "http://192.168.1.2"}}}\' -y > /mnt/etc/nixos/clevis-secret.jwe')''} 205 206 ${optionalString clevisFallbackTest '' 207 with subtest("Shutdown Tang to check fallback to interactive prompt"): 208 tang.shutdown() 209 ''} 210 211 with subtest("Perform the installation"): 212 installer.succeed("nixos-install < /dev/null >&2") 213 214 with subtest("Do it again to make sure it's idempotent"): 215 installer.succeed("nixos-install < /dev/null >&2") 216 217 with subtest("Check that we can build things in nixos-enter"): 218 installer.succeed( 219 """ 220 nixos-enter -- nix-build --option substitute false -E 'derivation { 221 name = "t"; 222 builder = "/bin/sh"; 223 args = ["-c" "echo nixos-enter build > $out"]; 224 system = builtins.currentSystem; 225 preferLocalBuild = true; 226 }' 227 """ 228 ) 229 230 ${postInstallCommands} 231 232 with subtest("Shutdown system after installation"): 233 installer.succeed("umount -R /mnt") 234 installer.succeed("sync") 235 installer.shutdown() 236 237 # We're actually the same machine, just booting differently this time. 238 target.state_dir = installer.state_dir 239 240 # Now see if we can boot the installation. 241 ${startTarget} 242 243 with subtest("Assert that /boot get mounted"): 244 target.wait_for_unit("local-fs.target") 245 ${ 246 if bootLoader == "grub" then 247 ''target.succeed("test -e /boot/grub")'' 248 else 249 ''target.succeed("test -e /boot/loader/loader.conf")'' 250 } 251 252 with subtest("Check whether /root has correct permissions"): 253 assert "700" in target.succeed("stat -c '%a' /root") 254 255 with subtest("Assert swap device got activated"): 256 # uncomment once https://bugs.freedesktop.org/show_bug.cgi?id=86930 is resolved 257 target.wait_for_unit("swap.target") 258 target.succeed("cat /proc/swaps | grep -q /dev") 259 260 with subtest("Check that the store is in good shape"): 261 target.succeed("nix-store --verify --check-contents >&2") 262 263 with subtest("Check whether the channel works"): 264 target.succeed("nix-env -iA nixos.procps >&2") 265 assert ".nix-profile" in target.succeed("type -tP ps | tee /dev/stderr") 266 267 with subtest( 268 "Check that the daemon works, and that non-root users can run builds " 269 "(this will build a new profile generation through the daemon)" 270 ): 271 target.succeed("su alice -l -c 'nix-env -iA nixos.procps' >&2") 272 273 with subtest("Configure system with writable Nix store on next boot"): 274 # we're not using copy_from_host here because the installer image 275 # doesn't know about the host-guest sharing mechanism. 276 target.copy_from_host_via_shell( 277 "${ 278 makeConfig { 279 inherit 280 bootLoader 281 grubDevice 282 grubIdentifier 283 grubUseEfi 284 extraConfig 285 clevisTest 286 ; 287 forceGrubReinstallCount = 1; 288 } 289 }", 290 "/etc/nixos/configuration.nix", 291 ) 292 293 with subtest("Check whether nixos-rebuild works"): 294 target.succeed("nixos-rebuild switch >&2") 295 296 with subtest("Test nixos-option"): 297 kernel_modules = target.succeed("nixos-option boot.initrd.kernelModules") 298 assert "virtio_console" in kernel_modules 299 assert "List of modules" in kernel_modules 300 assert "qemu-guest.nix" in kernel_modules 301 302 target.shutdown() 303 304 # Check whether a writable store build works 305 ${startTarget} 306 307 # we're not using copy_from_host here because the installer image 308 # doesn't know about the host-guest sharing mechanism. 309 target.copy_from_host_via_shell( 310 "${ 311 makeConfig { 312 inherit 313 bootLoader 314 grubDevice 315 grubIdentifier 316 grubUseEfi 317 extraConfig 318 clevisTest 319 ; 320 forceGrubReinstallCount = 2; 321 } 322 }", 323 "/etc/nixos/configuration.nix", 324 ) 325 target.succeed("nixos-rebuild boot >&2") 326 target.shutdown() 327 328 # And just to be sure, check that the target still boots after "nixos-rebuild switch". 329 ${startTarget} 330 target.wait_for_unit("network.target") 331 332 # Sanity check, is it the configuration.nix we generated? 333 hostname = target.succeed("hostname").strip() 334 assert hostname == "thatworked" 335 336 target.shutdown() 337 338 # Tests for validating clone configuration entries in grub menu 339 '' 340 + optionalString testSpecialisationConfig '' 341 # Reboot target 342 ${startTarget} 343 344 with subtest("Booted configuration name should be 'Home'"): 345 # This is not the name that shows in the grub menu. 346 # The default configuration is always shown as "Default" 347 target.succeed("cat /run/booted-system/configuration-name >&2") 348 assert "Home" in target.succeed("cat /run/booted-system/configuration-name") 349 350 with subtest("We should **not** find a file named /etc/gitconfig"): 351 target.fail("test -e /etc/gitconfig") 352 353 with subtest("Set grub to boot the second configuration"): 354 target.succeed("grub-reboot 1") 355 356 target.shutdown() 357 358 # Reboot target 359 ${startTarget} 360 361 with subtest("Booted configuration name should be Work"): 362 target.succeed("cat /run/booted-system/configuration-name >&2") 363 assert "Work" in target.succeed("cat /run/booted-system/configuration-name") 364 365 with subtest("We should find a file named /etc/gitconfig"): 366 target.succeed("test -e /etc/gitconfig") 367 368 target.shutdown() 369 '' 370 + optionalString testByAttrSwitch '' 371 with subtest("Configure system with attribute set"): 372 target.succeed(""" 373 mkdir /root/my-config 374 mv /etc/nixos/hardware-configuration.nix /root/my-config/ 375 rm /etc/nixos/configuration.nix 376 """) 377 target.copy_from_host_via_shell( 378 "${ 379 makeConfig { 380 inherit 381 bootLoader 382 grubDevice 383 grubIdentifier 384 grubUseEfi 385 extraConfig 386 clevisTest 387 ; 388 forceGrubReinstallCount = 1; 389 withTestInstrumentation = false; 390 } 391 }", 392 "/root/my-config/configuration.nix", 393 ) 394 target.copy_from_host_via_shell( 395 "${./installer/byAttrWithChannel.nix}", 396 "/root/my-config/default.nix", 397 ) 398 with subtest("Switch to attribute set based config with channels"): 399 target.succeed("nixos-rebuild switch --file /root/my-config/default.nix") 400 401 target.shutdown() 402 403 ${startTarget} 404 405 target.succeed(""" 406 rm /root/my-config/default.nix 407 """) 408 target.copy_from_host_via_shell( 409 "${./installer/byAttrNoChannel.nix}", 410 "/root/my-config/default.nix", 411 ) 412 413 target.succeed(""" 414 pkgs=$(readlink -f /nix/var/nix/profiles/per-user/root/channels)/nixos 415 if ! [[ -e $pkgs/pkgs/top-level/default.nix ]]; then 416 echo 1>&2 "$pkgs does not seem to be a nixpkgs source. Please fix the test so that pkgs points to a nixpkgs source."; 417 exit 1; 418 fi 419 sed -e s^@nixpkgs@^$pkgs^ -i /root/my-config/default.nix 420 421 """) 422 423 with subtest("Switch to attribute set based config without channels"): 424 target.succeed("nixos-rebuild switch --file /root/my-config/default.nix") 425 426 target.shutdown() 427 428 ${startTarget} 429 430 with subtest("nix-channel command is not available anymore"): 431 target.succeed("! which nix-channel") 432 433 with subtest("builtins.nixPath is now empty"): 434 target.succeed(""" 435 [[ "[ ]" == "$(nix-instantiate builtins.nixPath --eval --expr)" ]] 436 """) 437 438 with subtest("<nixpkgs> does not resolve"): 439 target.succeed(""" 440 ! nix-instantiate '<nixpkgs>' --eval --expr 441 """) 442 443 with subtest("Evaluate attribute set based config in fresh env without nix-channel"): 444 target.succeed("nixos-rebuild switch --file /root/my-config/default.nix") 445 446 with subtest("Evaluate attribute set based config in fresh env without channel profiles"): 447 target.succeed(""" 448 ( 449 exec 1>&2 450 mkdir -p /root/restore 451 mv -v /root/.nix-channels /root/restore/ 452 mv -v ~/.nix-defexpr /root/restore/ 453 mkdir -p /root/restore/channels 454 mv -v /nix/var/nix/profiles/per-user/root/channels* /root/restore/channels/ 455 ) 456 """) 457 target.succeed("nixos-rebuild switch --file /root/my-config/default.nix") 458 '' 459 + optionalString (testByAttrSwitch && testFlakeSwitch) '' 460 with subtest("Restore channel profiles"): 461 target.succeed(""" 462 ( 463 exec 1>&2 464 mv -v /root/restore/.nix-channels /root/ 465 mv -v /root/restore/.nix-defexpr ~/.nix-defexpr 466 mv -v /root/restore/channels/* /nix/var/nix/profiles/per-user/root/ 467 rm -vrf /root/restore 468 ) 469 """) 470 471 with subtest("Restore /etc/nixos"): 472 target.succeed(""" 473 mv -v /root/my-config/hardware-configuration.nix /etc/nixos/ 474 """) 475 target.copy_from_host_via_shell( 476 "${ 477 makeConfig { 478 inherit 479 bootLoader 480 grubDevice 481 grubIdentifier 482 grubUseEfi 483 extraConfig 484 clevisTest 485 ; 486 forceGrubReinstallCount = 1; 487 } 488 }", 489 "/etc/nixos/configuration.nix", 490 ) 491 492 with subtest("Restore /root/my-config"): 493 target.succeed(""" 494 rm -vrf /root/my-config 495 """) 496 497 '' 498 + optionalString (testByAttrSwitch && !testFlakeSwitch) '' 499 target.shutdown() 500 '' 501 + optionalString testFlakeSwitch '' 502 ${startTarget} 503 504 with subtest("Configure system with flake"): 505 # TODO: evaluate as user? 506 target.succeed(""" 507 mkdir /root/my-config 508 mv /etc/nixos/hardware-configuration.nix /root/my-config/ 509 rm /etc/nixos/configuration.nix 510 """) 511 target.copy_from_host_via_shell( 512 "${ 513 makeConfig { 514 inherit 515 bootLoader 516 grubDevice 517 grubIdentifier 518 grubUseEfi 519 extraConfig 520 clevisTest 521 ; 522 forceGrubReinstallCount = 1; 523 withTestInstrumentation = false; 524 } 525 }", 526 "/root/my-config/configuration.nix", 527 ) 528 target.copy_from_host_via_shell( 529 "${./installer/flake.nix}", 530 "/root/my-config/flake.nix", 531 ) 532 target.succeed(""" 533 # for some reason the image does not have `pkgs.path`, so 534 # we use readlink to find a Nixpkgs source. 535 pkgs=$(readlink -f /nix/var/nix/profiles/per-user/root/channels)/nixos 536 if ! [[ -e $pkgs/pkgs/top-level/default.nix ]]; then 537 echo 1>&2 "$pkgs does not seem to be a nixpkgs source. Please fix the test so that pkgs points to a nixpkgs source."; 538 exit 1; 539 fi 540 sed -e s^@nixpkgs@^$pkgs^ -i /root/my-config/flake.nix 541 """) 542 543 with subtest("Switch to flake based config"): 544 target.succeed("nixos-rebuild switch --flake /root/my-config#xyz 2>&1 | tee activation-log >&2") 545 546 target.succeed(""" 547 cat -n activation-log >&2 548 """) 549 550 target.succeed(""" 551 grep -F '/root/.nix-defexpr/channels exists, but channels have been disabled.' activation-log 552 """) 553 target.succeed(""" 554 grep -F '/nix/var/nix/profiles/per-user/root/channels exists, but channels have been disabled.' activation-log 555 """) 556 target.succeed(""" 557 grep -F '/root/.nix-defexpr/channels exists, but channels have been disabled.' activation-log 558 """) 559 target.succeed(""" 560 grep -F 'Due to https://github.com/NixOS/nix/issues/9574, Nix may still use these channels when NIX_PATH is unset.' activation-log 561 """) 562 target.succeed("rm activation-log") 563 564 # Perform the suggested cleanups we've just seen in the log 565 # TODO after https://github.com/NixOS/nix/issues/9574: don't remove them yet 566 target.succeed(""" 567 rm -rf /root/.nix-defexpr/channels /nix/var/nix/profiles/per-user/root/channels /root/.nix-defexpr/channels 568 """) 569 570 571 target.shutdown() 572 573 ${startTarget} 574 575 with subtest("nix-channel command is not available anymore"): 576 target.succeed("! which nix-channel") 577 578 # Note that the channel profile is still present on disk, but configured 579 # not to be used. 580 # TODO after issue https://github.com/NixOS/nix/issues/9574: re-enable this assertion 581 # I believe what happens is 582 # - because of the issue, we've removed the `nix-path =` line from nix.conf 583 # - the "backdoor" shell is not a proper session and does not have `NIX_PATH=""` set 584 # - seeing no nix path settings at all, Nix loads its hardcoded default value, 585 # which is unfortunately non-empty 586 # Or maybe it's the new default NIX_PATH?? :( 587 # with subtest("builtins.nixPath is now empty"): 588 # target.succeed(""" 589 # ( 590 # set -x; 591 # [[ "[ ]" == "$(nix-instantiate builtins.nixPath --eval --expr)" ]]; 592 # ) 593 # """) 594 595 with subtest("<nixpkgs> does not resolve"): 596 target.succeed(""" 597 ! nix-instantiate '<nixpkgs>' --eval --expr 598 """) 599 600 with subtest("Evaluate flake config in fresh env without nix-channel"): 601 target.succeed("nixos-rebuild switch --flake /root/my-config#xyz") 602 603 with subtest("Evaluate flake config in fresh env without channel profiles"): 604 target.succeed(""" 605 ( 606 exec 1>&2 607 rm -vf /root/.nix-channels 608 rm -vrf ~/.nix-defexpr 609 rm -vrf /nix/var/nix/profiles/per-user/root/channels* 610 ) 611 """) 612 target.succeed("nixos-rebuild switch --flake /root/my-config#xyz | tee activation-log >&2") 613 target.succeed("cat -n activation-log >&2") 614 target.succeed("! grep -F '/root/.nix-defexpr/channels' activation-log") 615 target.succeed("! grep -F 'but channels have been disabled' activation-log") 616 target.succeed("! grep -F 'https://github.com/NixOS/nix/issues/9574' activation-log") 617 618 target.shutdown() 619 ''; 620 621 makeInstallerTest = 622 name: 623 { 624 createPartitions, 625 postInstallCommands ? "", 626 postBootCommands ? "", 627 extraConfig ? "", 628 extraInstallerConfig ? { }, 629 bootLoader ? "grub", # either "grub" or "systemd-boot" 630 grubDevice ? "/dev/vda", 631 grubIdentifier ? "uuid", 632 grubUseEfi ? false, 633 enableOCR ? false, 634 meta ? { }, 635 passthru ? { }, 636 testSpecialisationConfig ? false, 637 testFlakeSwitch ? false, 638 testByAttrSwitch ? false, 639 clevisTest ? false, 640 clevisFallbackTest ? false, 641 disableFileSystems ? false, 642 selectNixPackage ? pkgs: pkgs.nixVersions.stable, 643 }: 644 let 645 isEfi = bootLoader == "systemd-boot" || (bootLoader == "grub" && grubUseEfi); 646 in 647 makeTest { 648 inherit enableOCR passthru; 649 name = "installer-" + name; 650 meta = { 651 # put global maintainers here, individuals go into makeInstallerTest fkt call 652 maintainers = (meta.maintainers or [ ]); 653 # non-EFI tests can only run on x86 654 platforms = 655 if isEfi then 656 platforms.linux 657 else 658 [ 659 "x86_64-linux" 660 "i686-linux" 661 ]; 662 }; 663 nodes = 664 let 665 commonConfig = { 666 # builds stuff in the VM, needs more juice 667 virtualisation.diskSize = 8 * 1024; 668 virtualisation.cores = 8; 669 virtualisation.memorySize = 2048; 670 671 # both installer and target need to use the same drive 672 virtualisation.diskImage = "./target.qcow2"; 673 674 # and the same TPM options 675 virtualisation.qemu.options = mkIf (clevisTest) [ 676 "-chardev socket,id=chrtpm,path=$NIX_BUILD_TOP/swtpm-sock" 677 "-tpmdev emulator,id=tpm0,chardev=chrtpm" 678 "-device tpm-tis,tpmdev=tpm0" 679 ]; 680 }; 681 in 682 { 683 # The configuration of the system used to run "nixos-install". 684 installer = 685 { config, pkgs, ... }: 686 { 687 imports = [ 688 commonConfig 689 ../modules/profiles/installation-device.nix 690 ../modules/profiles/base.nix 691 extraInstallerConfig 692 ./common/auto-format-root-device.nix 693 ]; 694 695 # In systemdStage1, also automatically format the device backing the 696 # root filesystem. 697 virtualisation.fileSystems."/".autoFormat = systemdStage1; 698 699 boot.initrd.systemd.enable = systemdStage1; 700 701 # Use a small /dev/vdb as the root disk for the 702 # installer. This ensures the target disk (/dev/vda) is 703 # the same during and after installation. 704 virtualisation.emptyDiskImages = [ 512 ]; 705 virtualisation.rootDevice = "/dev/vdb"; 706 707 nix.package = selectNixPackage pkgs; 708 hardware.enableAllFirmware = mkForce false; 709 710 # The test cannot access the network, so any packages we 711 # need must be included in the VM. 712 system.extraDependencies = 713 with pkgs; 714 [ 715 bintools 716 brotli 717 brotli.dev 718 brotli.lib 719 desktop-file-utils 720 docbook5 721 docbook_xsl_ns 722 kbd.dev 723 kmod.dev 724 libarchive.dev 725 libxml2.bin 726 libxslt.bin 727 nixos-artwork.wallpapers.simple-dark-gray-bottom 728 ntp 729 perlPackages.ConfigIniFiles 730 perlPackages.FileSlurp 731 perlPackages.JSON 732 perlPackages.ListCompare 733 perlPackages.XMLLibXML 734 # make-options-doc/default.nix 735 (python3.withPackages (p: [ p.mistune ])) 736 shared-mime-info 737 sudo 738 switch-to-configuration-ng 739 texinfo 740 unionfs-fuse 741 xorg.lndir 742 shellcheck-minimal 743 744 # Only the out output is included here, which is what is 745 # required to build the NixOS udev rules 746 # See the comment in services/hardware/udev.nix 747 systemdMinimal.out 748 749 # add curl so that rather than seeing the test attempt to download 750 # curl's tarball, we see what it's trying to download 751 curl 752 ] 753 ++ optionals (bootLoader == "grub") ( 754 let 755 zfsSupport = extraInstallerConfig.boot.supportedFilesystems.zfs or false; 756 in 757 [ 758 (pkgs.grub2.override { inherit zfsSupport; }) 759 (pkgs.grub2_efi.override { inherit zfsSupport; }) 760 pkgs.nixos-artwork.wallpapers.simple-dark-gray-bootloader 761 pkgs.perlPackages.FileCopyRecursive 762 pkgs.perlPackages.XMLSAX 763 pkgs.perlPackages.XMLSAXBase 764 ] 765 ) 766 ++ optionals (bootLoader == "systemd-boot") [ 767 pkgs.zstd.bin 768 pkgs.mypy 769 config.boot.bootspec.package 770 ] 771 ++ optionals clevisTest [ pkgs.klibc ] 772 ++ optional systemdStage1 pkgs.chroot-realpath; 773 774 nix.settings = { 775 substituters = mkForce [ ]; 776 hashed-mirrors = null; 777 connect-timeout = 1; 778 }; 779 }; 780 781 target = { 782 imports = [ commonConfig ]; 783 virtualisation.useBootLoader = true; 784 virtualisation.useEFIBoot = isEfi; 785 virtualisation.useDefaultFilesystems = false; 786 virtualisation.efi.keepVariables = false; 787 788 virtualisation.fileSystems."/" = { 789 device = "/dev/disk/by-label/this-is-not-real-and-will-never-be-used"; 790 fsType = "ext4"; 791 }; 792 }; 793 } 794 // optionalAttrs clevisTest { 795 tang = { 796 services.tang = { 797 enable = true; 798 listenStream = [ "80" ]; 799 ipAddressAllow = [ "192.168.1.0/24" ]; 800 }; 801 networking.firewall.allowedTCPPorts = [ 80 ]; 802 }; 803 }; 804 805 testScript = testScriptFun { 806 inherit 807 bootLoader 808 createPartitions 809 postInstallCommands 810 postBootCommands 811 grubDevice 812 grubIdentifier 813 grubUseEfi 814 extraConfig 815 testSpecialisationConfig 816 testFlakeSwitch 817 testByAttrSwitch 818 clevisTest 819 clevisFallbackTest 820 disableFileSystems 821 ; 822 }; 823 }; 824 825 makeLuksRootTest = 826 name: luksFormatOpts: 827 makeInstallerTest name { 828 createPartitions = '' 829 installer.succeed( 830 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 831 + " mkpart primary ext2 1M 100MB" # /boot 832 + " mkpart primary linux-swap 100M 1024M" 833 + " mkpart primary 1024M -1s", # LUKS 834 "udevadm settle", 835 "mkswap /dev/vda2 -L swap", 836 "swapon -L swap", 837 "modprobe dm_mod dm_crypt", 838 "echo -n supersecret | cryptsetup luksFormat ${luksFormatOpts} -q /dev/vda3 -", 839 "echo -n supersecret | cryptsetup luksOpen --key-file - /dev/vda3 cryptroot", 840 "mkfs.ext3 -L nixos /dev/mapper/cryptroot", 841 "mount LABEL=nixos /mnt", 842 "mkfs.ext3 -L boot /dev/vda1", 843 "mkdir -p /mnt/boot", 844 "mount LABEL=boot /mnt/boot", 845 ) 846 ''; 847 extraConfig = '' 848 boot.kernelParams = lib.mkAfter [ "console=tty0" ]; 849 ''; 850 enableOCR = true; 851 postBootCommands = '' 852 target.wait_for_text("[Pp]assphrase for") 853 target.send_chars("supersecret\n") 854 ''; 855 }; 856 857 # The (almost) simplest partitioning scheme: a swap partition and 858 # one big filesystem partition. 859 simple-test-config = { 860 createPartitions = '' 861 installer.succeed( 862 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 863 + " mkpart primary linux-swap 1M 1024M" 864 + " mkpart primary ext2 1024M -1s", 865 "udevadm settle", 866 "mkswap /dev/vda1 -L swap", 867 "swapon -L swap", 868 "mkfs.ext3 -L nixos /dev/vda2", 869 "mount LABEL=nixos /mnt", 870 ) 871 ''; 872 }; 873 874 simple-test-config-flake = simple-test-config // { 875 testFlakeSwitch = true; 876 }; 877 878 simple-test-config-by-attr = simple-test-config // { 879 testByAttrSwitch = true; 880 }; 881 882 simple-test-config-from-by-attr-to-flake = simple-test-config // { 883 testByAttrSwitch = true; 884 testFlakeSwitch = true; 885 }; 886 887 simple-uefi-grub-config = { 888 createPartitions = '' 889 installer.succeed( 890 "flock /dev/vda parted --script /dev/vda -- mklabel gpt" 891 + " mkpart ESP fat32 1M 100MiB" # /boot 892 + " set 1 boot on" 893 + " mkpart primary linux-swap 100MiB 1024MiB" 894 + " mkpart primary ext2 1024MiB -1MiB", # / 895 "udevadm settle", 896 "mkswap /dev/vda2 -L swap", 897 "swapon -L swap", 898 "mkfs.ext3 -L nixos /dev/vda3", 899 "mount LABEL=nixos /mnt", 900 "mkfs.vfat -n BOOT /dev/vda1", 901 "mkdir -p /mnt/boot", 902 "mount LABEL=BOOT /mnt/boot", 903 ) 904 ''; 905 bootLoader = "grub"; 906 grubUseEfi = true; 907 }; 908 909 specialisation-test-extraconfig = { 910 extraConfig = '' 911 environment.systemPackages = [ pkgs.grub2 ]; 912 boot.loader.grub.configurationName = "Home"; 913 specialisation.work.configuration = { 914 boot.loader.grub.configurationName = lib.mkForce "Work"; 915 916 environment.etc = { 917 "gitconfig".text = " 918 [core] 919 gitproxy = none for work.com 920 "; 921 }; 922 }; 923 ''; 924 testSpecialisationConfig = true; 925 }; 926 # disable zfs so we can support latest kernel if needed 927 no-zfs-module = { 928 nixpkgs.overlays = [ 929 (final: super: { 930 zfs = super.zfs.overrideAttrs (_: { 931 meta.platforms = [ ]; 932 }); 933 }) 934 ]; 935 }; 936 937 mkClevisBcachefsTest = 938 { 939 fallback ? false, 940 }: 941 makeInstallerTest "clevis-bcachefs${optionalString fallback "-fallback"}" { 942 clevisTest = true; 943 clevisFallbackTest = fallback; 944 enableOCR = fallback; 945 extraInstallerConfig = { 946 imports = [ no-zfs-module ]; 947 boot.supportedFilesystems = [ "bcachefs" ]; 948 environment.systemPackages = with pkgs; [ 949 keyutils 950 clevis 951 ]; 952 }; 953 createPartitions = '' 954 installer.succeed( 955 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 956 + " mkpart primary ext2 1M 100MB" 957 + " mkpart primary linux-swap 100M 1024M" 958 + " mkpart primary 1024M -1s", 959 "udevadm settle", 960 "mkswap /dev/vda2 -L swap", 961 "swapon -L swap", 962 "keyctl link @u @s", 963 "echo -n password | mkfs.bcachefs -L root --encrypted /dev/vda3", 964 "echo -n password | bcachefs unlock /dev/vda3", 965 "echo -n password | mount -t bcachefs /dev/vda3 /mnt", 966 "mkfs.ext3 -L boot /dev/vda1", 967 "mkdir -p /mnt/boot", 968 "mount LABEL=boot /mnt/boot", 969 "udevadm settle") 970 ''; 971 extraConfig = '' 972 boot.initrd.clevis.devices."/dev/vda3".secretFile = "/etc/nixos/clevis-secret.jwe"; 973 974 # We override what nixos-generate-config has generated because we do 975 # not know the UUID in advance. 976 fileSystems."/" = lib.mkForce { device = "/dev/vda3"; fsType = "bcachefs"; }; 977 ''; 978 postBootCommands = optionalString fallback '' 979 target.wait_for_text("enter passphrase for") 980 target.send_chars("password\n") 981 ''; 982 }; 983 984 mkClevisLuksTest = 985 { 986 fallback ? false, 987 }: 988 makeInstallerTest "clevis-luks${optionalString fallback "-fallback"}" { 989 clevisTest = true; 990 clevisFallbackTest = fallback; 991 enableOCR = fallback; 992 extraInstallerConfig = { 993 environment.systemPackages = with pkgs; [ clevis ]; 994 }; 995 createPartitions = '' 996 installer.succeed( 997 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 998 + " mkpart primary ext2 1M 100MB" 999 + " mkpart primary linux-swap 100M 1024M" 1000 + " mkpart primary 1024M -1s", 1001 "udevadm settle", 1002 "mkswap /dev/vda2 -L swap", 1003 "swapon -L swap", 1004 "modprobe dm_mod dm_crypt", 1005 "echo -n password | cryptsetup luksFormat -q /dev/vda3 -", 1006 "echo -n password | cryptsetup luksOpen --key-file - /dev/vda3 crypt-root", 1007 "mkfs.ext3 -L nixos /dev/mapper/crypt-root", 1008 "mount LABEL=nixos /mnt", 1009 "mkfs.ext3 -L boot /dev/vda1", 1010 "mkdir -p /mnt/boot", 1011 "mount LABEL=boot /mnt/boot", 1012 "udevadm settle") 1013 ''; 1014 extraConfig = '' 1015 boot.initrd.clevis.devices."crypt-root".secretFile = "/etc/nixos/clevis-secret.jwe"; 1016 ''; 1017 postBootCommands = optionalString fallback '' 1018 ${ 1019 if systemdStage1 then 1020 '' 1021 target.wait_for_text("Please enter") 1022 '' 1023 else 1024 '' 1025 target.wait_for_text("Passphrase for") 1026 '' 1027 } 1028 target.send_chars("password\n") 1029 ''; 1030 }; 1031 1032 mkClevisZfsTest = 1033 { 1034 fallback ? false, 1035 parentDataset ? false, 1036 }: 1037 makeInstallerTest 1038 "clevis-zfs${optionalString parentDataset "-parent-dataset"}${optionalString fallback "-fallback"}" 1039 { 1040 clevisTest = true; 1041 clevisFallbackTest = fallback; 1042 enableOCR = fallback; 1043 extraInstallerConfig = { 1044 boot.supportedFilesystems = [ "zfs" ]; 1045 environment.systemPackages = with pkgs; [ clevis ]; 1046 }; 1047 createPartitions = 1048 '' 1049 installer.succeed( 1050 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1051 + " mkpart primary ext2 1M 100MB" 1052 + " mkpart primary linux-swap 100M 1024M" 1053 + " mkpart primary 1024M -1s", 1054 "udevadm settle", 1055 "mkswap /dev/vda2 -L swap", 1056 "swapon -L swap", 1057 '' 1058 + optionalString (!parentDataset) '' 1059 "zpool create -O mountpoint=legacy rpool /dev/vda3", 1060 "echo -n password | zfs create" 1061 + " -o encryption=aes-256-gcm -o keyformat=passphrase rpool/root", 1062 '' 1063 + optionalString (parentDataset) '' 1064 "echo -n password | zpool create -O mountpoint=none -O encryption=on -O keyformat=passphrase rpool /dev/vda3", 1065 "zfs create -o mountpoint=legacy rpool/root", 1066 '' 1067 + '' 1068 "mount -t zfs rpool/root /mnt", 1069 "mkfs.ext3 -L boot /dev/vda1", 1070 "mkdir -p /mnt/boot", 1071 "mount LABEL=boot /mnt/boot", 1072 "udevadm settle") 1073 ''; 1074 extraConfig = 1075 optionalString (!parentDataset) '' 1076 boot.initrd.clevis.devices."rpool/root".secretFile = "/etc/nixos/clevis-secret.jwe"; 1077 '' 1078 + optionalString (parentDataset) '' 1079 boot.initrd.clevis.devices."rpool".secretFile = "/etc/nixos/clevis-secret.jwe"; 1080 '' 1081 + '' 1082 boot.zfs.requestEncryptionCredentials = true; 1083 1084 1085 # Using by-uuid overrides the default of by-id, and is unique 1086 # to the qemu disks, as they don't produce by-id paths for 1087 # some reason. 1088 boot.zfs.devNodes = "/dev/disk/by-uuid/"; 1089 networking.hostId = "00000000"; 1090 ''; 1091 postBootCommands = optionalString fallback '' 1092 ${ 1093 if systemdStage1 then 1094 '' 1095 target.wait_for_text("Enter key for rpool/root") 1096 '' 1097 else 1098 '' 1099 target.wait_for_text("Key load error") 1100 '' 1101 } 1102 target.send_chars("password\n") 1103 ''; 1104 }; 1105 1106in 1107{ 1108 1109 # !!! `parted mkpart' seems to silently create overlapping partitions. 1110 1111 # The (almost) simplest partitioning scheme: a swap partition and 1112 # one big filesystem partition. 1113 simple = makeInstallerTest "simple" ( 1114 simple-test-config 1115 // { 1116 passthru.override = args: makeInstallerTest "simple" simple-test-config // args; 1117 } 1118 ); 1119 1120 switchToFlake = makeInstallerTest "switch-to-flake" simple-test-config-flake; 1121 1122 switchToByAttr = makeInstallerTest "switch-to-by-attr" simple-test-config-by-attr; 1123 1124 switchFromByAttrToFlake = makeInstallerTest "switch-from-by-attr-to-flake" simple-test-config-from-by-attr-to-flake; 1125 1126 # Test cloned configurations with the simple grub configuration 1127 simpleSpecialised = makeInstallerTest "simpleSpecialised" ( 1128 simple-test-config // specialisation-test-extraconfig 1129 ); 1130 1131 # Simple GPT/UEFI configuration using systemd-boot with 3 partitions: ESP, swap & root filesystem 1132 simpleUefiSystemdBoot = makeInstallerTest "simpleUefiSystemdBoot" { 1133 createPartitions = '' 1134 installer.succeed( 1135 "flock /dev/vda parted --script /dev/vda -- mklabel gpt" 1136 + " mkpart ESP fat32 1M 100MiB" # /boot 1137 + " set 1 boot on" 1138 + " mkpart primary linux-swap 100MiB 1024MiB" 1139 + " mkpart primary ext2 1024MiB -1MiB", # / 1140 "udevadm settle", 1141 "mkswap /dev/vda2 -L swap", 1142 "swapon -L swap", 1143 "mkfs.ext3 -L nixos /dev/vda3", 1144 "mount LABEL=nixos /mnt", 1145 "mkfs.vfat -n BOOT /dev/vda1", 1146 "mkdir -p /mnt/boot", 1147 "mount LABEL=BOOT /mnt/boot", 1148 ) 1149 ''; 1150 bootLoader = "systemd-boot"; 1151 }; 1152 1153 simpleUefiGrub = makeInstallerTest "simpleUefiGrub" simple-uefi-grub-config; 1154 1155 # Test cloned configurations with the uefi grub configuration 1156 simpleUefiGrubSpecialisation = makeInstallerTest "simpleUefiGrubSpecialisation" ( 1157 simple-uefi-grub-config // specialisation-test-extraconfig 1158 ); 1159 1160 # Same as the previous, but now with a separate /boot partition. 1161 separateBoot = makeInstallerTest "separateBoot" { 1162 createPartitions = '' 1163 installer.succeed( 1164 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1165 + " mkpart primary ext2 1M 100MB" # /boot 1166 + " mkpart primary linux-swap 100MB 1024M" 1167 + " mkpart primary ext2 1024M -1s", # / 1168 "udevadm settle", 1169 "mkswap /dev/vda2 -L swap", 1170 "swapon -L swap", 1171 "mkfs.ext3 -L nixos /dev/vda3", 1172 "mount LABEL=nixos /mnt", 1173 "mkfs.ext3 -L boot /dev/vda1", 1174 "mkdir -p /mnt/boot", 1175 "mount LABEL=boot /mnt/boot", 1176 ) 1177 ''; 1178 }; 1179 1180 # Same as the previous, but with fat32 /boot. 1181 separateBootFat = makeInstallerTest "separateBootFat" { 1182 createPartitions = '' 1183 installer.succeed( 1184 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1185 + " mkpart primary ext2 1M 100MB" # /boot 1186 + " mkpart primary linux-swap 100MB 1024M" 1187 + " mkpart primary ext2 1024M -1s", # / 1188 "udevadm settle", 1189 "mkswap /dev/vda2 -L swap", 1190 "swapon -L swap", 1191 "mkfs.ext3 -L nixos /dev/vda3", 1192 "mount LABEL=nixos /mnt", 1193 "mkfs.vfat -n BOOT /dev/vda1", 1194 "mkdir -p /mnt/boot", 1195 "mount LABEL=BOOT /mnt/boot", 1196 ) 1197 ''; 1198 }; 1199 1200 # Same as the previous, but with ZFS /boot. 1201 separateBootZfs = makeInstallerTest "separateBootZfs" { 1202 extraInstallerConfig = { 1203 boot.supportedFilesystems = [ "zfs" ]; 1204 }; 1205 1206 extraConfig = '' 1207 # Using by-uuid overrides the default of by-id, and is unique 1208 # to the qemu disks, as they don't produce by-id paths for 1209 # some reason. 1210 boot.zfs.devNodes = "/dev/disk/by-uuid/"; 1211 networking.hostId = "00000000"; 1212 ''; 1213 1214 createPartitions = '' 1215 installer.succeed( 1216 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1217 + " mkpart primary ext2 1M 256MB" # /boot 1218 + " mkpart primary linux-swap 256MB 1280M" 1219 + " mkpart primary ext2 1280M -1s", # / 1220 "udevadm settle", 1221 1222 "mkswap /dev/vda2 -L swap", 1223 "swapon -L swap", 1224 1225 "mkfs.ext4 -L nixos /dev/vda3", 1226 "mount LABEL=nixos /mnt", 1227 1228 # Use as many ZFS features as possible to verify that GRUB can handle them 1229 "zpool create" 1230 " -o compatibility=grub2" 1231 " -O utf8only=on" 1232 " -O normalization=formD" 1233 " -O compression=lz4" # Activate the lz4_compress feature 1234 " -O xattr=sa" 1235 " -O acltype=posixacl" 1236 " bpool /dev/vda1", 1237 "zfs create" 1238 " -o recordsize=1M" # Prepare activating the large_blocks feature 1239 " -o mountpoint=legacy" 1240 " -o relatime=on" 1241 " -o quota=1G" 1242 " -o filesystem_limit=100" # Activate the filesystem_limits features 1243 " bpool/boot", 1244 1245 # Snapshotting the top-level dataset would trigger a bug in GRUB2: https://github.com/openzfs/zfs/issues/13873 1246 "zfs snapshot bpool/boot@snap-1", # Prepare activating the livelist and bookmarks features 1247 "zfs clone bpool/boot@snap-1 bpool/test", # Activate the livelist feature 1248 "zfs bookmark bpool/boot@snap-1 bpool/boot#bookmark", # Activate the bookmarks feature 1249 "zpool checkpoint bpool", # Activate the zpool_checkpoint feature 1250 "mkdir -p /mnt/boot", 1251 "mount -t zfs bpool/boot /mnt/boot", 1252 "touch /mnt/boot/empty", # Activate zilsaxattr feature 1253 "dd if=/dev/urandom of=/mnt/boot/test bs=1M count=1", # Activate the large_blocks feature 1254 1255 # Print out all enabled and active ZFS features (and some other stuff) 1256 "sync /mnt/boot", 1257 "zpool get all bpool >&2", 1258 1259 # Abort early if GRUB2 doesn't like the disks 1260 "grub-probe --target=device /mnt/boot >&2", 1261 ) 1262 ''; 1263 1264 # umount & export bpool before shutdown 1265 # this is a fix for "cannot import 'bpool': pool was previously in use from another system." 1266 postInstallCommands = '' 1267 installer.succeed("umount /mnt/boot") 1268 installer.succeed("zpool export bpool") 1269 ''; 1270 }; 1271 1272 # zfs on / with swap 1273 zfsroot = makeInstallerTest "zfs-root" { 1274 extraInstallerConfig = { 1275 boot.supportedFilesystems = [ "zfs" ]; 1276 }; 1277 1278 extraConfig = '' 1279 boot.supportedFilesystems = [ "zfs" ]; 1280 1281 # Using by-uuid overrides the default of by-id, and is unique 1282 # to the qemu disks, as they don't produce by-id paths for 1283 # some reason. 1284 boot.zfs.devNodes = "/dev/disk/by-uuid/"; 1285 networking.hostId = "00000000"; 1286 ''; 1287 1288 createPartitions = '' 1289 installer.succeed( 1290 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1291 + " mkpart primary 1M 100MB" # /boot 1292 + " mkpart primary linux-swap 100M 1024M" 1293 + " mkpart primary 1024M -1s", # rpool 1294 "udevadm settle", 1295 "mkswap /dev/vda2 -L swap", 1296 "swapon -L swap", 1297 "zpool create rpool /dev/vda3", 1298 "zfs create -o mountpoint=legacy rpool/root", 1299 "mount -t zfs rpool/root /mnt", 1300 "zfs create -o mountpoint=legacy rpool/root/usr", 1301 "mkdir /mnt/usr", 1302 "mount -t zfs rpool/root/usr /mnt/usr", 1303 "mkfs.vfat -n BOOT /dev/vda1", 1304 "mkdir /mnt/boot", 1305 "mount LABEL=BOOT /mnt/boot", 1306 "udevadm settle", 1307 ) 1308 ''; 1309 }; 1310 1311 # Create two physical LVM partitions combined into one volume group 1312 # that contains the logical swap and root partitions. 1313 lvm = makeInstallerTest "lvm" { 1314 createPartitions = '' 1315 installer.succeed( 1316 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1317 + " mkpart primary 1M 2048M" # PV1 1318 + " set 1 lvm on" 1319 + " mkpart primary 2048M -1s" # PV2 1320 + " set 2 lvm on", 1321 "udevadm settle", 1322 "pvcreate /dev/vda1 /dev/vda2", 1323 "vgcreate MyVolGroup /dev/vda1 /dev/vda2", 1324 "lvcreate --size 1G --name swap MyVolGroup", 1325 "lvcreate --size 6G --name nixos MyVolGroup", 1326 "mkswap -f /dev/MyVolGroup/swap -L swap", 1327 "swapon -L swap", 1328 "mkfs.xfs -L nixos /dev/MyVolGroup/nixos", 1329 "mount LABEL=nixos /mnt", 1330 ) 1331 ''; 1332 extraConfig = optionalString systemdStage1 '' 1333 boot.initrd.services.lvm.enable = true; 1334 ''; 1335 }; 1336 1337 # Boot off an encrypted root partition with the default LUKS header format 1338 luksroot = makeLuksRootTest "luksroot-format1" ""; 1339 1340 # Boot off an encrypted root partition with LUKS1 format 1341 luksroot-format1 = makeLuksRootTest "luksroot-format1" "--type=LUKS1"; 1342 1343 # Boot off an encrypted root partition with LUKS2 format 1344 luksroot-format2 = makeLuksRootTest "luksroot-format2" "--type=LUKS2"; 1345 1346 # Test whether opening encrypted filesystem with keyfile 1347 # Checks for regression of missing cryptsetup, when no luks device without 1348 # keyfile is configured 1349 encryptedFSWithKeyfile = makeInstallerTest "encryptedFSWithKeyfile" { 1350 createPartitions = '' 1351 installer.succeed( 1352 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1353 + " mkpart primary ext2 1M 100MB" # /boot 1354 + " mkpart primary linux-swap 100M 1024M" 1355 + " mkpart primary 1024M 1280M" # LUKS with keyfile 1356 + " mkpart primary 1280M -1s", 1357 "udevadm settle", 1358 "mkswap /dev/vda2 -L swap", 1359 "swapon -L swap", 1360 "mkfs.ext3 -L nixos /dev/vda4", 1361 "mount LABEL=nixos /mnt", 1362 "mkfs.ext3 -L boot /dev/vda1", 1363 "mkdir -p /mnt/boot", 1364 "mount LABEL=boot /mnt/boot", 1365 "modprobe dm_mod dm_crypt", 1366 "echo -n supersecret > /mnt/keyfile", 1367 "cryptsetup luksFormat -q /dev/vda3 --key-file /mnt/keyfile", 1368 "cryptsetup luksOpen --key-file /mnt/keyfile /dev/vda3 crypt", 1369 "mkfs.ext3 -L test /dev/mapper/crypt", 1370 "cryptsetup luksClose crypt", 1371 "mkdir -p /mnt/test", 1372 ) 1373 ''; 1374 extraConfig = '' 1375 fileSystems."/test" = { 1376 device = "/dev/disk/by-label/test"; 1377 fsType = "ext3"; 1378 encrypted.enable = true; 1379 encrypted.blkDev = "/dev/vda3"; 1380 encrypted.label = "crypt"; 1381 encrypted.keyFile = "/${if systemdStage1 then "sysroot" else "mnt-root"}/keyfile"; 1382 }; 1383 ''; 1384 }; 1385 1386 # Full disk encryption (root, kernel and initrd encrypted) using GRUB, GPT/UEFI, 1387 # LVM-on-LUKS and a keyfile in initrd.secrets to enter the passphrase once 1388 fullDiskEncryption = makeInstallerTest "fullDiskEncryption" { 1389 createPartitions = '' 1390 installer.succeed( 1391 "flock /dev/vda parted --script /dev/vda -- mklabel gpt" 1392 + " mkpart ESP fat32 1M 100MiB" # /boot/efi 1393 + " set 1 boot on" 1394 + " mkpart primary ext2 1024MiB -1MiB", # LUKS 1395 "udevadm settle", 1396 "modprobe dm_mod dm_crypt", 1397 "dd if=/dev/random of=luks.key bs=256 count=1", 1398 "echo -n supersecret | cryptsetup luksFormat -q --pbkdf-force-iterations 1000 --type luks1 /dev/vda2 -", 1399 "echo -n supersecret | cryptsetup luksAddKey -q --pbkdf-force-iterations 1000 --key-file - /dev/vda2 luks.key", 1400 "echo -n supersecret | cryptsetup luksOpen --key-file - /dev/vda2 crypt", 1401 "pvcreate /dev/mapper/crypt", 1402 "vgcreate crypt /dev/mapper/crypt", 1403 "lvcreate -L 100M -n swap crypt", 1404 "lvcreate -l '100%FREE' -n nixos crypt", 1405 "mkfs.vfat -n efi /dev/vda1", 1406 "mkfs.ext4 -L nixos /dev/crypt/nixos", 1407 "mkswap -L swap /dev/crypt/swap", 1408 "mount LABEL=nixos /mnt", 1409 "mkdir -p /mnt/{etc/nixos,boot/efi}", 1410 "mount LABEL=efi /mnt/boot/efi", 1411 "swapon -L swap", 1412 "mv luks.key /mnt/etc/nixos/" 1413 ) 1414 ''; 1415 bootLoader = "grub"; 1416 grubUseEfi = true; 1417 extraConfig = '' 1418 boot.loader.grub.enableCryptodisk = true; 1419 boot.loader.efi.efiSysMountPoint = "/boot/efi"; 1420 1421 boot.initrd.secrets."/luks.key" = "/etc/nixos/luks.key"; 1422 boot.initrd.luks.devices.crypt = 1423 { device = "/dev/vda2"; 1424 keyFile = "/luks.key"; 1425 }; 1426 ''; 1427 enableOCR = true; 1428 postBootCommands = '' 1429 target.wait_for_text("Enter passphrase for") 1430 target.send_chars("supersecret\n") 1431 ''; 1432 }; 1433 1434 swraid = makeInstallerTest "swraid" { 1435 createPartitions = '' 1436 installer.succeed( 1437 "flock /dev/vda parted --script /dev/vda --" 1438 + " mklabel msdos" 1439 + " mkpart primary ext2 1M 100MB" # /boot 1440 + " mkpart extended 100M -1s" 1441 + " mkpart logical 102M 3102M" # md0 (root), first device 1442 + " mkpart logical 3103M 6103M" # md0 (root), second device 1443 + " mkpart logical 6104M 6360M" # md1 (swap), first device 1444 + " mkpart logical 6361M 6617M", # md1 (swap), second device 1445 "udevadm settle", 1446 "ls -l /dev/vda* >&2", 1447 "cat /proc/partitions >&2", 1448 "udevadm control --stop-exec-queue", 1449 "mdadm --create --force /dev/md0 --metadata 1.2 --level=raid1 " 1450 + "--raid-devices=2 /dev/vda5 /dev/vda6", 1451 "mdadm --create --force /dev/md1 --metadata 1.2 --level=raid1 " 1452 + "--raid-devices=2 /dev/vda7 /dev/vda8", 1453 "udevadm control --start-exec-queue", 1454 "udevadm settle", 1455 "mkswap -f /dev/md1 -L swap", 1456 "swapon -L swap", 1457 "mkfs.ext3 -L nixos /dev/md0", 1458 "mount LABEL=nixos /mnt", 1459 "mkfs.ext3 -L boot /dev/vda1", 1460 "mkdir /mnt/boot", 1461 "mount LABEL=boot /mnt/boot", 1462 "udevadm settle", 1463 ) 1464 ''; 1465 postBootCommands = '' 1466 target.fail("dmesg | grep 'immediate safe mode'") 1467 ''; 1468 }; 1469 1470 bcache = makeInstallerTest "bcache" { 1471 createPartitions = '' 1472 installer.succeed( 1473 "flock /dev/vda parted --script /dev/vda --" 1474 + " mklabel msdos" 1475 + " mkpart primary ext2 1M 100MB" # /boot 1476 + " mkpart primary 100MB 512MB " # swap 1477 + " mkpart primary 512MB 1024MB" # Cache (typically SSD) 1478 + " mkpart primary 1024MB -1s ", # Backing device (typically HDD) 1479 "modprobe bcache", 1480 "udevadm settle", 1481 "make-bcache -B /dev/vda4 -C /dev/vda3", 1482 "udevadm settle", 1483 "mkfs.ext3 -L nixos /dev/bcache0", 1484 "mount LABEL=nixos /mnt", 1485 "mkfs.ext3 -L boot /dev/vda1", 1486 "mkdir /mnt/boot", 1487 "mount LABEL=boot /mnt/boot", 1488 "mkswap -f /dev/vda2 -L swap", 1489 "swapon -L swap", 1490 ) 1491 ''; 1492 }; 1493 1494 bcachefsSimple = makeInstallerTest "bcachefs-simple" { 1495 extraInstallerConfig = { 1496 boot.supportedFilesystems = [ "bcachefs" ]; 1497 imports = [ no-zfs-module ]; 1498 }; 1499 1500 createPartitions = '' 1501 installer.succeed( 1502 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1503 + " mkpart primary ext2 1M 100MB" # /boot 1504 + " mkpart primary linux-swap 100M 1024M" # swap 1505 + " mkpart primary 1024M -1s", # / 1506 "udevadm settle", 1507 "mkswap /dev/vda2 -L swap", 1508 "swapon -L swap", 1509 "mkfs.bcachefs -L root /dev/vda3", 1510 "mount -t bcachefs /dev/vda3 /mnt", 1511 "mkfs.ext3 -L boot /dev/vda1", 1512 "mkdir -p /mnt/boot", 1513 "mount /dev/vda1 /mnt/boot", 1514 ) 1515 ''; 1516 }; 1517 1518 bcachefsEncrypted = makeInstallerTest "bcachefs-encrypted" { 1519 extraInstallerConfig = { 1520 boot.supportedFilesystems = [ "bcachefs" ]; 1521 1522 # disable zfs so we can support latest kernel if needed 1523 imports = [ no-zfs-module ]; 1524 1525 environment.systemPackages = with pkgs; [ keyutils ]; 1526 }; 1527 1528 extraConfig = '' 1529 boot.kernelParams = lib.mkAfter [ "console=tty0" ]; 1530 ''; 1531 1532 enableOCR = true; 1533 postBootCommands = '' 1534 # Enter it wrong once 1535 target.wait_for_text("enter passphrase for ") 1536 target.send_chars("wrong\n") 1537 # Then enter it right. 1538 target.wait_for_text("enter passphrase for ") 1539 target.send_chars("password\n") 1540 ''; 1541 1542 createPartitions = '' 1543 installer.succeed( 1544 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1545 + " mkpart primary ext2 1M 100MB" # /boot 1546 + " mkpart primary linux-swap 100M 1024M" # swap 1547 + " mkpart primary 1024M -1s", # / 1548 "udevadm settle", 1549 "mkswap /dev/vda2 -L swap", 1550 "swapon -L swap", 1551 "echo password | mkfs.bcachefs -L root --encrypted /dev/vda3", 1552 "echo password | bcachefs unlock -k session /dev/vda3", 1553 "echo password | mount -t bcachefs /dev/vda3 /mnt", 1554 "mkfs.ext3 -L boot /dev/vda1", 1555 "mkdir -p /mnt/boot", 1556 "mount /dev/vda1 /mnt/boot", 1557 ) 1558 ''; 1559 }; 1560 1561 bcachefsMulti = makeInstallerTest "bcachefs-multi" { 1562 extraInstallerConfig = { 1563 boot.supportedFilesystems = [ "bcachefs" ]; 1564 1565 # disable zfs so we can support latest kernel if needed 1566 imports = [ no-zfs-module ]; 1567 }; 1568 1569 createPartitions = '' 1570 installer.succeed( 1571 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1572 + " mkpart primary ext2 1M 100MB" # /boot 1573 + " mkpart primary linux-swap 100M 1024M" # swap 1574 + " mkpart primary 1024M 4096M" # / 1575 + " mkpart primary 4096M -1s", # / 1576 "udevadm settle", 1577 "mkswap /dev/vda2 -L swap", 1578 "swapon -L swap", 1579 "mkfs.bcachefs -L root --metadata_replicas 2 --foreground_target ssd --promote_target ssd --background_target hdd --label ssd /dev/vda3 --label hdd /dev/vda4", 1580 "mount -t bcachefs /dev/vda3:/dev/vda4 /mnt", 1581 "mkfs.ext3 -L boot /dev/vda1", 1582 "mkdir -p /mnt/boot", 1583 "mount /dev/vda1 /mnt/boot", 1584 ) 1585 ''; 1586 }; 1587 1588 # Test using labels to identify volumes in grub 1589 simpleLabels = makeInstallerTest "simpleLabels" { 1590 createPartitions = '' 1591 installer.succeed( 1592 "sgdisk -Z /dev/vda", 1593 "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", 1594 "mkswap /dev/vda2 -L swap", 1595 "swapon -L swap", 1596 "mkfs.ext4 -L root /dev/vda3", 1597 "mount LABEL=root /mnt", 1598 ) 1599 ''; 1600 grubIdentifier = "label"; 1601 }; 1602 1603 # Test using the provided disk name within grub 1604 # TODO: Fix udev so the symlinks are unneeded in /dev/disks 1605 simpleProvided = makeInstallerTest "simpleProvided" { 1606 createPartitions = '' 1607 uuid = "$(blkid -s UUID -o value /dev/vda2)" 1608 installer.succeed( 1609 "sgdisk -Z /dev/vda", 1610 "sgdisk -n 1:0:+1M -n 2:0:+100M -n 3:0:+1G -N 4 -t 1:ef02 -t 2:8300 " 1611 + "-t 3:8200 -t 4:8300 -c 2:boot -c 4:root /dev/vda", 1612 "mkswap /dev/vda3 -L swap", 1613 "swapon -L swap", 1614 "mkfs.ext4 -L boot /dev/vda2", 1615 "mkfs.ext4 -L root /dev/vda4", 1616 ) 1617 installer.execute(f"ln -s ../../vda2 /dev/disk/by-uuid/{uuid}") 1618 installer.execute("ln -s ../../vda4 /dev/disk/by-label/root") 1619 installer.succeed( 1620 "mount /dev/disk/by-label/root /mnt", 1621 "mkdir /mnt/boot", 1622 f"mount /dev/disk/by-uuid/{uuid} /mnt/boot", 1623 ) 1624 ''; 1625 grubIdentifier = "provided"; 1626 }; 1627 1628 # Simple btrfs grub testing 1629 btrfsSimple = makeInstallerTest "btrfsSimple" { 1630 createPartitions = '' 1631 installer.succeed( 1632 "sgdisk -Z /dev/vda", 1633 "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", 1634 "mkswap /dev/vda2 -L swap", 1635 "swapon -L swap", 1636 "mkfs.btrfs -L root /dev/vda3", 1637 "mount LABEL=root /mnt", 1638 ) 1639 ''; 1640 }; 1641 1642 # Test to see if we can detect /boot and /nix on subvolumes 1643 btrfsSubvols = makeInstallerTest "btrfsSubvols" { 1644 createPartitions = '' 1645 installer.succeed( 1646 "sgdisk -Z /dev/vda", 1647 "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", 1648 "mkswap /dev/vda2 -L swap", 1649 "swapon -L swap", 1650 "mkfs.btrfs -L root /dev/vda3", 1651 "btrfs device scan", 1652 "mount LABEL=root /mnt", 1653 "btrfs subvol create /mnt/boot", 1654 "btrfs subvol create /mnt/nixos", 1655 "btrfs subvol create /mnt/nixos/default", 1656 "umount /mnt", 1657 "mount -o defaults,subvol=nixos/default LABEL=root /mnt", 1658 "mkdir /mnt/boot", 1659 "mount -o defaults,subvol=boot LABEL=root /mnt/boot", 1660 ) 1661 ''; 1662 }; 1663 1664 # Test to see if we can detect default and aux subvolumes correctly 1665 btrfsSubvolDefault = makeInstallerTest "btrfsSubvolDefault" { 1666 createPartitions = '' 1667 installer.succeed( 1668 "sgdisk -Z /dev/vda", 1669 "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", 1670 "mkswap /dev/vda2 -L swap", 1671 "swapon -L swap", 1672 "mkfs.btrfs -L root /dev/vda3", 1673 "btrfs device scan", 1674 "mount LABEL=root /mnt", 1675 "btrfs subvol create /mnt/badpath", 1676 "btrfs subvol create /mnt/badpath/boot", 1677 "btrfs subvol create /mnt/nixos", 1678 "btrfs subvol set-default " 1679 + "$(btrfs subvol list /mnt | grep 'nixos' | awk '{print $2}') /mnt", 1680 "umount /mnt", 1681 "mount -o defaults LABEL=root /mnt", 1682 "mkdir -p /mnt/badpath/boot", # Help ensure the detection mechanism 1683 # is actually looking up subvolumes 1684 "mkdir /mnt/boot", 1685 "mount -o defaults,subvol=badpath/boot LABEL=root /mnt/boot", 1686 ) 1687 ''; 1688 }; 1689 1690 # Test to see if we can deal with subvols that need to be escaped in fstab 1691 btrfsSubvolEscape = makeInstallerTest "btrfsSubvolEscape" { 1692 createPartitions = '' 1693 installer.succeed( 1694 "sgdisk -Z /dev/vda", 1695 "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", 1696 "mkswap /dev/vda2 -L swap", 1697 "swapon -L swap", 1698 "mkfs.btrfs -L root /dev/vda3", 1699 "btrfs device scan", 1700 "mount LABEL=root /mnt", 1701 "btrfs subvol create '/mnt/nixos in space'", 1702 "btrfs subvol create /mnt/boot", 1703 "umount /mnt", 1704 "mount -o 'defaults,subvol=nixos in space' LABEL=root /mnt", 1705 "mkdir /mnt/boot", 1706 "mount -o defaults,subvol=boot LABEL=root /mnt/boot", 1707 ) 1708 ''; 1709 }; 1710} 1711// { 1712 clevisBcachefs = mkClevisBcachefsTest { }; 1713 clevisBcachefsFallback = mkClevisBcachefsTest { fallback = true; }; 1714 clevisLuks = mkClevisLuksTest { }; 1715 clevisLuksFallback = mkClevisLuksTest { fallback = true; }; 1716 clevisZfs = mkClevisZfsTest { }; 1717 clevisZfsFallback = mkClevisZfsTest { fallback = true; }; 1718 clevisZfsParentDataset = mkClevisZfsTest { parentDataset = true; }; 1719 clevisZfsParentDatasetFallback = mkClevisZfsTest { 1720 parentDataset = true; 1721 fallback = true; 1722 }; 1723} 1724// optionalAttrs systemdStage1 { 1725 stratisRoot = makeInstallerTest "stratisRoot" { 1726 createPartitions = '' 1727 installer.succeed( 1728 "sgdisk --zap-all /dev/vda", 1729 "sgdisk --new=1:0:+100M --typecode=0:ef00 /dev/vda", # /boot 1730 "sgdisk --new=2:0:+1G --typecode=0:8200 /dev/vda", # swap 1731 "sgdisk --new=3:0:+5G --typecode=0:8300 /dev/vda", # / 1732 "udevadm settle", 1733 1734 "mkfs.vfat /dev/vda1", 1735 "mkswap /dev/vda2 -L swap", 1736 "swapon -L swap", 1737 "stratis pool create my-pool /dev/vda3", 1738 "stratis filesystem create my-pool nixos", 1739 "udevadm settle", 1740 1741 "mount /dev/stratis/my-pool/nixos /mnt", 1742 "mkdir -p /mnt/boot", 1743 "mount /dev/vda1 /mnt/boot" 1744 ) 1745 ''; 1746 bootLoader = "systemd-boot"; 1747 extraInstallerConfig = 1748 { modulesPath, ... }: 1749 { 1750 config = { 1751 services.stratis.enable = true; 1752 environment.systemPackages = [ 1753 pkgs.stratis-cli 1754 pkgs.thin-provisioning-tools 1755 pkgs.lvm2.bin 1756 pkgs.stratisd.initrd 1757 ]; 1758 }; 1759 }; 1760 }; 1761 1762 gptAutoRoot = 1763 let 1764 rootPartType = 1765 { 1766 ia32 = "44479540-F297-41B2-9AF7-D131D5F0458A"; 1767 x64 = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709"; 1768 arm = "69DAD710-2CE4-4E3C-B16C-21A1D49ABED3"; 1769 aa64 = "B921B045-1DF0-41C3-AF44-4C6F280D3FAE"; 1770 } 1771 .${pkgs.stdenv.hostPlatform.efiArch}; 1772 in 1773 makeInstallerTest "gptAutoRoot" { 1774 disableFileSystems = true; 1775 createPartitions = '' 1776 installer.succeed( 1777 "sgdisk --zap-all /dev/vda", 1778 "sgdisk --new=1:0:+100M --typecode=0:ef00 /dev/vda", # /boot 1779 "sgdisk --new=2:0:+1G --typecode=0:8200 /dev/vda", # swap 1780 "sgdisk --new=3:0:+5G --typecode=0:${rootPartType} /dev/vda", # / 1781 "udevadm settle", 1782 1783 "mkfs.vfat /dev/vda1", 1784 "mkswap /dev/vda2 -L swap", 1785 "swapon -L swap", 1786 "mkfs.ext4 -L root /dev/vda3", 1787 "udevadm settle", 1788 1789 "mount /dev/vda3 /mnt", 1790 "mkdir -p /mnt/boot", 1791 "mount /dev/vda1 /mnt/boot" 1792 ) 1793 ''; 1794 bootLoader = "systemd-boot"; 1795 extraConfig = '' 1796 boot.initrd.systemd.root = "gpt-auto"; 1797 boot.initrd.supportedFilesystems = ["ext4"]; 1798 ''; 1799 }; 1800}