at master 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 = mkIf (!isEfi) [ 655 "x86_64-linux" 656 "x86_64-darwin" 657 "i686-linux" 658 ]; 659 }; 660 nodes = 661 let 662 commonConfig = { 663 # builds stuff in the VM, needs more juice 664 virtualisation.diskSize = 8 * 1024; 665 virtualisation.cores = 8; 666 virtualisation.memorySize = 2048; 667 668 # both installer and target need to use the same drive 669 virtualisation.diskImage = "./target.qcow2"; 670 671 # and the same TPM options 672 virtualisation.qemu.options = mkIf (clevisTest) [ 673 "-chardev socket,id=chrtpm,path=$NIX_BUILD_TOP/swtpm-sock" 674 "-tpmdev emulator,id=tpm0,chardev=chrtpm" 675 "-device tpm-tis,tpmdev=tpm0" 676 ]; 677 }; 678 in 679 { 680 # The configuration of the system used to run "nixos-install". 681 installer = 682 { config, pkgs, ... }: 683 { 684 imports = [ 685 commonConfig 686 ../modules/profiles/installation-device.nix 687 ../modules/profiles/base.nix 688 extraInstallerConfig 689 ./common/auto-format-root-device.nix 690 ]; 691 692 # In systemdStage1, also automatically format the device backing the 693 # root filesystem. 694 virtualisation.fileSystems."/".autoFormat = systemdStage1; 695 696 boot.initrd.systemd.enable = systemdStage1; 697 698 # Use a small /dev/vdb as the root disk for the 699 # installer. This ensures the target disk (/dev/vda) is 700 # the same during and after installation. 701 virtualisation.emptyDiskImages = [ 512 ]; 702 virtualisation.rootDevice = "/dev/vdb"; 703 704 nix.package = selectNixPackage pkgs; 705 hardware.enableAllFirmware = mkForce false; 706 707 # The test cannot access the network, so any packages we 708 # need must be included in the VM. 709 system.extraDependencies = 710 with pkgs; 711 [ 712 # TODO: Remove this when we can install systems 713 # without `stdenv`. 714 stdenv 715 716 bintools 717 brotli 718 brotli.dev 719 brotli.lib 720 desktop-file-utils 721 docbook5 722 docbook_xsl_ns 723 kbd.dev 724 kmod.dev 725 libarchive.dev 726 libxml2.bin 727 libxslt.bin 728 nixos-artwork.wallpapers.simple-dark-gray-bottom 729 (nixos-rebuild-ng.override { 730 withNgSuffix = false; 731 withReexec = true; 732 }) 733 ntp 734 perlPackages.ConfigIniFiles 735 perlPackages.FileSlurp 736 perlPackages.JSON 737 perlPackages.ListCompare 738 perlPackages.XMLLibXML 739 # make-options-doc/default.nix 740 (python3.withPackages (p: [ p.mistune ])) 741 shared-mime-info 742 sudo 743 switch-to-configuration-ng 744 texinfo 745 unionfs-fuse 746 xorg.lndir 747 shellcheck-minimal 748 749 # Only the out output is included here, which is what is 750 # required to build the NixOS udev rules 751 # See the comment in services/hardware/udev.nix 752 systemdMinimal.out 753 754 # add curl so that rather than seeing the test attempt to download 755 # curl's tarball, we see what it's trying to download 756 curl 757 ] 758 ++ optionals (bootLoader == "grub") ( 759 let 760 zfsSupport = extraInstallerConfig.boot.supportedFilesystems.zfs or false; 761 in 762 [ 763 (pkgs.grub2.override { inherit zfsSupport; }) 764 (pkgs.grub2_efi.override { inherit zfsSupport; }) 765 pkgs.nixos-artwork.wallpapers.simple-dark-gray-bootloader 766 pkgs.perlPackages.FileCopyRecursive 767 pkgs.perlPackages.XMLSAX 768 pkgs.perlPackages.XMLSAXBase 769 ] 770 ) 771 ++ optionals (bootLoader == "systemd-boot") [ 772 pkgs.zstd.bin 773 pkgs.mypy 774 config.boot.bootspec.package 775 ] 776 ++ optionals clevisTest [ pkgs.klibc ] 777 ++ optional systemdStage1 config.system.nixos-init.package; 778 779 nix.settings = { 780 substituters = mkForce [ ]; 781 hashed-mirrors = null; 782 connect-timeout = 1; 783 }; 784 }; 785 786 target = { 787 imports = [ commonConfig ]; 788 virtualisation.useBootLoader = true; 789 virtualisation.useEFIBoot = isEfi; 790 virtualisation.useDefaultFilesystems = false; 791 virtualisation.efi.keepVariables = false; 792 793 virtualisation.fileSystems."/" = { 794 device = "/dev/disk/by-label/this-is-not-real-and-will-never-be-used"; 795 fsType = "ext4"; 796 }; 797 }; 798 } 799 // optionalAttrs clevisTest { 800 tang = { 801 services.tang = { 802 enable = true; 803 listenStream = [ "80" ]; 804 ipAddressAllow = [ "192.168.1.0/24" ]; 805 }; 806 networking.firewall.allowedTCPPorts = [ 80 ]; 807 }; 808 }; 809 810 testScript = testScriptFun { 811 inherit 812 bootLoader 813 createPartitions 814 postInstallCommands 815 postBootCommands 816 grubDevice 817 grubIdentifier 818 grubUseEfi 819 extraConfig 820 testSpecialisationConfig 821 testFlakeSwitch 822 testByAttrSwitch 823 clevisTest 824 clevisFallbackTest 825 disableFileSystems 826 ; 827 }; 828 }; 829 830 makeLuksRootTest = 831 name: luksFormatOpts: 832 makeInstallerTest name { 833 createPartitions = '' 834 installer.succeed( 835 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 836 + " mkpart primary ext2 1M 100MB" # /boot 837 + " mkpart primary linux-swap 100M 1024M" 838 + " mkpart primary 1024M -1s", # LUKS 839 "udevadm settle", 840 "mkswap /dev/vda2 -L swap", 841 "swapon -L swap", 842 "modprobe dm_mod dm_crypt", 843 "echo -n supersecret | cryptsetup luksFormat ${luksFormatOpts} -q /dev/vda3 -", 844 "echo -n supersecret | cryptsetup luksOpen --key-file - /dev/vda3 cryptroot", 845 "mkfs.ext3 -L nixos /dev/mapper/cryptroot", 846 "mount LABEL=nixos /mnt", 847 "mkfs.ext3 -L boot /dev/vda1", 848 "mkdir -p /mnt/boot", 849 "mount LABEL=boot /mnt/boot", 850 ) 851 ''; 852 extraConfig = '' 853 boot.kernelParams = lib.mkAfter [ "console=tty0" ]; 854 ''; 855 enableOCR = true; 856 postBootCommands = '' 857 target.wait_for_text("[Pp]assphrase for") 858 target.send_chars("supersecret\n") 859 ''; 860 }; 861 862 # The (almost) simplest partitioning scheme: a swap partition and 863 # one big filesystem partition. 864 simple-test-config = { 865 createPartitions = '' 866 installer.succeed( 867 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 868 + " mkpart primary linux-swap 1M 1024M" 869 + " mkpart primary ext2 1024M -1s", 870 "udevadm settle", 871 "mkswap /dev/vda1 -L swap", 872 "swapon -L swap", 873 "mkfs.ext3 -L nixos /dev/vda2", 874 "mount LABEL=nixos /mnt", 875 ) 876 ''; 877 }; 878 879 simple-test-config-flake = simple-test-config // { 880 testFlakeSwitch = true; 881 }; 882 883 simple-test-config-by-attr = simple-test-config // { 884 testByAttrSwitch = true; 885 }; 886 887 simple-test-config-from-by-attr-to-flake = simple-test-config // { 888 testByAttrSwitch = true; 889 testFlakeSwitch = true; 890 }; 891 892 simple-uefi-grub-config = { 893 createPartitions = '' 894 installer.succeed( 895 "flock /dev/vda parted --script /dev/vda -- mklabel gpt" 896 + " mkpart ESP fat32 1M 100MiB" # /boot 897 + " set 1 boot on" 898 + " mkpart primary linux-swap 100MiB 1024MiB" 899 + " mkpart primary ext2 1024MiB -1MiB", # / 900 "udevadm settle", 901 "mkswap /dev/vda2 -L swap", 902 "swapon -L swap", 903 "mkfs.ext3 -L nixos /dev/vda3", 904 "mount LABEL=nixos /mnt", 905 "mkfs.vfat -n BOOT /dev/vda1", 906 "mkdir -p /mnt/boot", 907 "mount LABEL=BOOT /mnt/boot", 908 ) 909 ''; 910 bootLoader = "grub"; 911 grubUseEfi = true; 912 }; 913 914 specialisation-test-extraconfig = { 915 extraConfig = '' 916 environment.systemPackages = [ pkgs.grub2 ]; 917 boot.loader.grub.configurationName = "Home"; 918 specialisation.work.configuration = { 919 boot.loader.grub.configurationName = lib.mkForce "Work"; 920 921 environment.etc = { 922 "gitconfig".text = " 923 [core] 924 gitproxy = none for work.com 925 "; 926 }; 927 }; 928 ''; 929 testSpecialisationConfig = true; 930 }; 931 # disable zfs so we can support latest kernel if needed 932 no-zfs-module = { 933 nixpkgs.overlays = [ 934 (final: super: { 935 zfs = super.zfs.overrideAttrs (_: { 936 meta.platforms = [ ]; 937 }); 938 }) 939 ]; 940 }; 941 942 mkClevisBcachefsTest = 943 { 944 fallback ? false, 945 }: 946 makeInstallerTest "clevis-bcachefs${optionalString fallback "-fallback"}" { 947 clevisTest = true; 948 clevisFallbackTest = fallback; 949 enableOCR = fallback; 950 extraInstallerConfig = { 951 imports = [ no-zfs-module ]; 952 boot.supportedFilesystems = [ "bcachefs" ]; 953 environment.systemPackages = with pkgs; [ 954 keyutils 955 clevis 956 ]; 957 }; 958 createPartitions = '' 959 installer.succeed( 960 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 961 + " mkpart primary ext2 1M 100MB" 962 + " mkpart primary linux-swap 100M 1024M" 963 + " mkpart primary 1024M -1s", 964 "udevadm settle", 965 "mkswap /dev/vda2 -L swap", 966 "swapon -L swap", 967 "keyctl link @u @s", 968 "echo -n password | mkfs.bcachefs -L root --encrypted /dev/vda3", 969 "echo -n password | bcachefs unlock /dev/vda3", 970 "echo -n password | mount -t bcachefs /dev/vda3 /mnt", 971 "mkfs.ext3 -L boot /dev/vda1", 972 "mkdir -p /mnt/boot", 973 "mount LABEL=boot /mnt/boot", 974 "udevadm settle") 975 ''; 976 extraConfig = '' 977 boot.initrd.clevis.devices."/dev/vda3".secretFile = "/etc/nixos/clevis-secret.jwe"; 978 979 # We override what nixos-generate-config has generated because we do 980 # not know the UUID in advance. 981 fileSystems."/" = lib.mkForce { device = "/dev/vda3"; fsType = "bcachefs"; }; 982 ''; 983 postBootCommands = optionalString fallback '' 984 target.wait_for_text("enter passphrase for") 985 target.send_chars("password\n") 986 ''; 987 }; 988 989 mkClevisLuksTest = 990 { 991 fallback ? false, 992 }: 993 makeInstallerTest "clevis-luks${optionalString fallback "-fallback"}" { 994 clevisTest = true; 995 clevisFallbackTest = fallback; 996 enableOCR = fallback; 997 extraInstallerConfig = { 998 environment.systemPackages = with pkgs; [ clevis ]; 999 }; 1000 createPartitions = '' 1001 installer.succeed( 1002 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1003 + " mkpart primary ext2 1M 100MB" 1004 + " mkpart primary linux-swap 100M 1024M" 1005 + " mkpart primary 1024M -1s", 1006 "udevadm settle", 1007 "mkswap /dev/vda2 -L swap", 1008 "swapon -L swap", 1009 "modprobe dm_mod dm_crypt", 1010 "echo -n password | cryptsetup luksFormat -q /dev/vda3 -", 1011 "echo -n password | cryptsetup luksOpen --key-file - /dev/vda3 crypt-root", 1012 "mkfs.ext3 -L nixos /dev/mapper/crypt-root", 1013 "mount LABEL=nixos /mnt", 1014 "mkfs.ext3 -L boot /dev/vda1", 1015 "mkdir -p /mnt/boot", 1016 "mount LABEL=boot /mnt/boot", 1017 "udevadm settle") 1018 ''; 1019 extraConfig = '' 1020 boot.initrd.clevis.devices."crypt-root".secretFile = "/etc/nixos/clevis-secret.jwe"; 1021 ''; 1022 postBootCommands = optionalString fallback '' 1023 ${ 1024 if systemdStage1 then 1025 '' 1026 target.wait_for_text("Please enter") 1027 '' 1028 else 1029 '' 1030 target.wait_for_text("Passphrase for") 1031 '' 1032 } 1033 target.send_chars("password\n") 1034 ''; 1035 }; 1036 1037 mkClevisZfsTest = 1038 { 1039 fallback ? false, 1040 parentDataset ? false, 1041 }: 1042 makeInstallerTest 1043 "clevis-zfs${optionalString parentDataset "-parent-dataset"}${optionalString fallback "-fallback"}" 1044 { 1045 clevisTest = true; 1046 clevisFallbackTest = fallback; 1047 enableOCR = fallback; 1048 extraInstallerConfig = { 1049 boot.supportedFilesystems = [ "zfs" ]; 1050 environment.systemPackages = with pkgs; [ clevis ]; 1051 }; 1052 createPartitions = '' 1053 installer.succeed( 1054 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1055 + " mkpart primary ext2 1M 100MB" 1056 + " mkpart primary linux-swap 100M 1024M" 1057 + " mkpart primary 1024M -1s", 1058 "udevadm settle", 1059 "mkswap /dev/vda2 -L swap", 1060 "swapon -L swap", 1061 '' 1062 + optionalString (!parentDataset) '' 1063 "zpool create -O mountpoint=legacy rpool /dev/vda3", 1064 "echo -n password | zfs create" 1065 + " -o encryption=aes-256-gcm -o keyformat=passphrase rpool/root", 1066 '' 1067 + optionalString (parentDataset) '' 1068 "echo -n password | zpool create -O mountpoint=none -O encryption=on -O keyformat=passphrase rpool /dev/vda3", 1069 "zfs create -o mountpoint=legacy rpool/root", 1070 '' 1071 + '' 1072 "mount -t zfs rpool/root /mnt", 1073 "mkfs.ext3 -L boot /dev/vda1", 1074 "mkdir -p /mnt/boot", 1075 "mount LABEL=boot /mnt/boot", 1076 "udevadm settle") 1077 ''; 1078 extraConfig = 1079 optionalString (!parentDataset) '' 1080 boot.initrd.clevis.devices."rpool/root".secretFile = "/etc/nixos/clevis-secret.jwe"; 1081 '' 1082 + optionalString (parentDataset) '' 1083 boot.initrd.clevis.devices."rpool".secretFile = "/etc/nixos/clevis-secret.jwe"; 1084 '' 1085 + '' 1086 boot.zfs.requestEncryptionCredentials = true; 1087 1088 1089 # Using by-uuid overrides the default of by-id, and is unique 1090 # to the qemu disks, as they don't produce by-id paths for 1091 # some reason. 1092 boot.zfs.devNodes = "/dev/disk/by-uuid/"; 1093 networking.hostId = "00000000"; 1094 ''; 1095 postBootCommands = optionalString fallback '' 1096 ${ 1097 if systemdStage1 then 1098 '' 1099 target.wait_for_text("Enter key for rpool${optionalString (!parentDataset) "/root"}") 1100 '' 1101 else 1102 '' 1103 target.wait_for_text("Key load error") 1104 '' 1105 } 1106 target.send_chars("password\n") 1107 ''; 1108 }; 1109 1110in 1111{ 1112 1113 # !!! `parted mkpart' seems to silently create overlapping partitions. 1114 1115 # The (almost) simplest partitioning scheme: a swap partition and 1116 # one big filesystem partition. 1117 simple = makeInstallerTest "simple" ( 1118 simple-test-config 1119 // { 1120 passthru.override = args: makeInstallerTest "simple" (simple-test-config // args); 1121 } 1122 ); 1123 1124 switchToFlake = makeInstallerTest "switch-to-flake" simple-test-config-flake; 1125 1126 switchToByAttr = makeInstallerTest "switch-to-by-attr" simple-test-config-by-attr; 1127 1128 switchFromByAttrToFlake = makeInstallerTest "switch-from-by-attr-to-flake" simple-test-config-from-by-attr-to-flake; 1129 1130 # Test cloned configurations with the simple grub configuration 1131 simpleSpecialised = makeInstallerTest "simpleSpecialised" ( 1132 simple-test-config // specialisation-test-extraconfig 1133 ); 1134 1135 # Simple GPT/UEFI configuration using systemd-boot with 3 partitions: ESP, swap & root filesystem 1136 simpleUefiSystemdBoot = makeInstallerTest "simpleUefiSystemdBoot" { 1137 createPartitions = '' 1138 installer.succeed( 1139 "flock /dev/vda parted --script /dev/vda -- mklabel gpt" 1140 + " mkpart ESP fat32 1M 100MiB" # /boot 1141 + " set 1 boot on" 1142 + " mkpart primary linux-swap 100MiB 1024MiB" 1143 + " mkpart primary ext2 1024MiB -1MiB", # / 1144 "udevadm settle", 1145 "mkswap /dev/vda2 -L swap", 1146 "swapon -L swap", 1147 "mkfs.ext3 -L nixos /dev/vda3", 1148 "mount LABEL=nixos /mnt", 1149 "mkfs.vfat -n BOOT /dev/vda1", 1150 "mkdir -p /mnt/boot", 1151 "mount LABEL=BOOT /mnt/boot", 1152 ) 1153 ''; 1154 bootLoader = "systemd-boot"; 1155 }; 1156 1157 simpleUefiGrub = makeInstallerTest "simpleUefiGrub" simple-uefi-grub-config; 1158 1159 # Test cloned configurations with the uefi grub configuration 1160 simpleUefiGrubSpecialisation = makeInstallerTest "simpleUefiGrubSpecialisation" ( 1161 simple-uefi-grub-config // specialisation-test-extraconfig 1162 ); 1163 1164 # Same as the previous, but now with a separate /boot partition. 1165 separateBoot = makeInstallerTest "separateBoot" { 1166 createPartitions = '' 1167 installer.succeed( 1168 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1169 + " mkpart primary ext2 1M 100MB" # /boot 1170 + " mkpart primary linux-swap 100MB 1024M" 1171 + " mkpart primary ext2 1024M -1s", # / 1172 "udevadm settle", 1173 "mkswap /dev/vda2 -L swap", 1174 "swapon -L swap", 1175 "mkfs.ext3 -L nixos /dev/vda3", 1176 "mount LABEL=nixos /mnt", 1177 "mkfs.ext3 -L boot /dev/vda1", 1178 "mkdir -p /mnt/boot", 1179 "mount LABEL=boot /mnt/boot", 1180 ) 1181 ''; 1182 }; 1183 1184 # Same as the previous, but with fat32 /boot. 1185 separateBootFat = makeInstallerTest "separateBootFat" { 1186 createPartitions = '' 1187 installer.succeed( 1188 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1189 + " mkpart primary ext2 1M 100MB" # /boot 1190 + " mkpart primary linux-swap 100MB 1024M" 1191 + " mkpart primary ext2 1024M -1s", # / 1192 "udevadm settle", 1193 "mkswap /dev/vda2 -L swap", 1194 "swapon -L swap", 1195 "mkfs.ext3 -L nixos /dev/vda3", 1196 "mount LABEL=nixos /mnt", 1197 "mkfs.vfat -n BOOT /dev/vda1", 1198 "mkdir -p /mnt/boot", 1199 "mount LABEL=BOOT /mnt/boot", 1200 ) 1201 ''; 1202 }; 1203 1204 # Same as the previous, but with ZFS /boot. 1205 separateBootZfs = makeInstallerTest "separateBootZfs" { 1206 extraInstallerConfig = { 1207 boot.supportedFilesystems = [ "zfs" ]; 1208 }; 1209 1210 extraConfig = '' 1211 # Using by-uuid overrides the default of by-id, and is unique 1212 # to the qemu disks, as they don't produce by-id paths for 1213 # some reason. 1214 boot.zfs.devNodes = "/dev/disk/by-uuid/"; 1215 networking.hostId = "00000000"; 1216 ''; 1217 1218 createPartitions = '' 1219 installer.succeed( 1220 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1221 + " mkpart primary ext2 1M 256MB" # /boot 1222 + " mkpart primary linux-swap 256MB 1280M" 1223 + " mkpart primary ext2 1280M -1s", # / 1224 "udevadm settle", 1225 1226 "mkswap /dev/vda2 -L swap", 1227 "swapon -L swap", 1228 1229 "mkfs.ext4 -L nixos /dev/vda3", 1230 "mount LABEL=nixos /mnt", 1231 1232 # Use as many ZFS features as possible to verify that GRUB can handle them 1233 "zpool create" 1234 " -o compatibility=grub2" 1235 " -O utf8only=on" 1236 " -O normalization=formD" 1237 " -O compression=lz4" # Activate the lz4_compress feature 1238 " -O xattr=sa" 1239 " -O acltype=posixacl" 1240 " bpool /dev/vda1", 1241 "zfs create" 1242 " -o recordsize=1M" # Prepare activating the large_blocks feature 1243 " -o mountpoint=legacy" 1244 " -o relatime=on" 1245 " -o quota=1G" 1246 " -o filesystem_limit=100" # Activate the filesystem_limits features 1247 " bpool/boot", 1248 1249 # Snapshotting the top-level dataset would trigger a bug in GRUB2: https://github.com/openzfs/zfs/issues/13873 1250 "zfs snapshot bpool/boot@snap-1", # Prepare activating the livelist and bookmarks features 1251 "zfs clone bpool/boot@snap-1 bpool/test", # Activate the livelist feature 1252 "zfs bookmark bpool/boot@snap-1 bpool/boot#bookmark", # Activate the bookmarks feature 1253 "zpool checkpoint bpool", # Activate the zpool_checkpoint feature 1254 "mkdir -p /mnt/boot", 1255 "mount -t zfs bpool/boot /mnt/boot", 1256 "touch /mnt/boot/empty", # Activate zilsaxattr feature 1257 "dd if=/dev/urandom of=/mnt/boot/test bs=1M count=1", # Activate the large_blocks feature 1258 1259 # Print out all enabled and active ZFS features (and some other stuff) 1260 "sync /mnt/boot", 1261 "zpool get all bpool >&2", 1262 1263 # Abort early if GRUB2 doesn't like the disks 1264 "grub-probe --target=device /mnt/boot >&2", 1265 ) 1266 ''; 1267 1268 # umount & export bpool before shutdown 1269 # this is a fix for "cannot import 'bpool': pool was previously in use from another system." 1270 postInstallCommands = '' 1271 installer.succeed("umount /mnt/boot") 1272 installer.succeed("zpool export bpool") 1273 ''; 1274 }; 1275 1276 # zfs on / with swap 1277 zfsroot = makeInstallerTest "zfs-root" { 1278 extraInstallerConfig = { 1279 boot.supportedFilesystems = [ "zfs" ]; 1280 }; 1281 1282 extraConfig = '' 1283 boot.supportedFilesystems = [ "zfs" ]; 1284 1285 # Using by-uuid overrides the default of by-id, and is unique 1286 # to the qemu disks, as they don't produce by-id paths for 1287 # some reason. 1288 boot.zfs.devNodes = "/dev/disk/by-uuid/"; 1289 networking.hostId = "00000000"; 1290 ''; 1291 1292 createPartitions = '' 1293 installer.succeed( 1294 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1295 + " mkpart primary 1M 100MB" # /boot 1296 + " mkpart primary linux-swap 100M 1024M" 1297 + " mkpart primary 1024M -1s", # rpool 1298 "udevadm settle", 1299 "mkswap /dev/vda2 -L swap", 1300 "swapon -L swap", 1301 "zpool create rpool /dev/vda3", 1302 "zfs create -o mountpoint=legacy rpool/root", 1303 "mount -t zfs rpool/root /mnt", 1304 "zfs create -o mountpoint=legacy rpool/root/usr", 1305 "mkdir /mnt/usr", 1306 "mount -t zfs rpool/root/usr /mnt/usr", 1307 "mkfs.vfat -n BOOT /dev/vda1", 1308 "mkdir /mnt/boot", 1309 "mount LABEL=BOOT /mnt/boot", 1310 "udevadm settle", 1311 ) 1312 ''; 1313 }; 1314 1315 # Create two physical LVM partitions combined into one volume group 1316 # that contains the logical swap and root partitions. 1317 lvm = makeInstallerTest "lvm" { 1318 createPartitions = '' 1319 installer.succeed( 1320 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1321 + " mkpart primary 1M 2048M" # PV1 1322 + " set 1 lvm on" 1323 + " mkpart primary 2048M -1s" # PV2 1324 + " set 2 lvm on", 1325 "udevadm settle", 1326 "pvcreate /dev/vda1 /dev/vda2", 1327 "vgcreate MyVolGroup /dev/vda1 /dev/vda2", 1328 "lvcreate --size 1G --name swap MyVolGroup", 1329 "lvcreate --size 6G --name nixos MyVolGroup", 1330 "mkswap -f /dev/MyVolGroup/swap -L swap", 1331 "swapon -L swap", 1332 "mkfs.xfs -L nixos /dev/MyVolGroup/nixos", 1333 "mount LABEL=nixos /mnt", 1334 ) 1335 ''; 1336 extraConfig = optionalString systemdStage1 '' 1337 boot.initrd.services.lvm.enable = true; 1338 ''; 1339 }; 1340 1341 # Boot off an encrypted root partition with the default LUKS header format 1342 luksroot = makeLuksRootTest "luksroot-format1" ""; 1343 1344 # Boot off an encrypted root partition with LUKS1 format 1345 luksroot-format1 = makeLuksRootTest "luksroot-format1" "--type=LUKS1"; 1346 1347 # Boot off an encrypted root partition with LUKS2 format 1348 luksroot-format2 = makeLuksRootTest "luksroot-format2" "--type=LUKS2"; 1349 1350 # Test whether opening encrypted filesystem with keyfile 1351 # Checks for regression of missing cryptsetup, when no luks device without 1352 # keyfile is configured 1353 encryptedFSWithKeyfile = makeInstallerTest "encryptedFSWithKeyfile" { 1354 createPartitions = '' 1355 installer.succeed( 1356 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1357 + " mkpart primary ext2 1M 100MB" # /boot 1358 + " mkpart primary linux-swap 100M 1024M" 1359 + " mkpart primary 1024M 1280M" # LUKS with keyfile 1360 + " mkpart primary 1280M -1s", 1361 "udevadm settle", 1362 "mkswap /dev/vda2 -L swap", 1363 "swapon -L swap", 1364 "mkfs.ext3 -L nixos /dev/vda4", 1365 "mount LABEL=nixos /mnt", 1366 "mkfs.ext3 -L boot /dev/vda1", 1367 "mkdir -p /mnt/boot", 1368 "mount LABEL=boot /mnt/boot", 1369 "modprobe dm_mod dm_crypt", 1370 "echo -n supersecret > /mnt/keyfile", 1371 "cryptsetup luksFormat -q /dev/vda3 --key-file /mnt/keyfile", 1372 "cryptsetup luksOpen --key-file /mnt/keyfile /dev/vda3 crypt", 1373 "mkfs.ext3 -L test /dev/mapper/crypt", 1374 "cryptsetup luksClose crypt", 1375 "mkdir -p /mnt/test", 1376 ) 1377 ''; 1378 extraConfig = '' 1379 fileSystems."/test" = { 1380 device = "/dev/disk/by-label/test"; 1381 fsType = "ext3"; 1382 encrypted.enable = true; 1383 encrypted.blkDev = "/dev/vda3"; 1384 encrypted.label = "crypt"; 1385 encrypted.keyFile = "/${if systemdStage1 then "sysroot" else "mnt-root"}/keyfile"; 1386 }; 1387 ''; 1388 }; 1389 1390 # Full disk encryption (root, kernel and initrd encrypted) using GRUB, GPT/UEFI, 1391 # LVM-on-LUKS and a keyfile in initrd.secrets to enter the passphrase once 1392 fullDiskEncryption = makeInstallerTest "fullDiskEncryption" { 1393 createPartitions = '' 1394 installer.succeed( 1395 "flock /dev/vda parted --script /dev/vda -- mklabel gpt" 1396 + " mkpart ESP fat32 1M 100MiB" # /boot/efi 1397 + " set 1 boot on" 1398 + " mkpart primary ext2 1024MiB -1MiB", # LUKS 1399 "udevadm settle", 1400 "modprobe dm_mod dm_crypt", 1401 "dd if=/dev/random of=luks.key bs=256 count=1", 1402 "echo -n supersecret | cryptsetup luksFormat -q --pbkdf-force-iterations 1000 --type luks1 /dev/vda2 -", 1403 "echo -n supersecret | cryptsetup luksAddKey -q --pbkdf-force-iterations 1000 --key-file - /dev/vda2 luks.key", 1404 "echo -n supersecret | cryptsetup luksOpen --key-file - /dev/vda2 crypt", 1405 "pvcreate /dev/mapper/crypt", 1406 "vgcreate crypt /dev/mapper/crypt", 1407 "lvcreate -L 100M -n swap crypt", 1408 "lvcreate -l '100%FREE' -n nixos crypt", 1409 "mkfs.vfat -n efi /dev/vda1", 1410 "mkfs.ext4 -L nixos /dev/crypt/nixos", 1411 "mkswap -L swap /dev/crypt/swap", 1412 "mount LABEL=nixos /mnt", 1413 "mkdir -p /mnt/{etc/nixos,boot/efi}", 1414 "mount LABEL=efi /mnt/boot/efi", 1415 "swapon -L swap", 1416 "mv luks.key /mnt/etc/nixos/" 1417 ) 1418 ''; 1419 bootLoader = "grub"; 1420 grubUseEfi = true; 1421 extraConfig = '' 1422 boot.loader.grub.enableCryptodisk = true; 1423 boot.loader.efi.efiSysMountPoint = "/boot/efi"; 1424 1425 boot.initrd.secrets."/luks.key" = "/etc/nixos/luks.key"; 1426 boot.initrd.luks.devices.crypt = 1427 { device = "/dev/vda2"; 1428 keyFile = "/luks.key"; 1429 }; 1430 ''; 1431 enableOCR = true; 1432 postBootCommands = '' 1433 target.wait_for_text("Enter passphrase for") 1434 target.send_chars("supersecret\n") 1435 ''; 1436 }; 1437 1438 swraid = makeInstallerTest "swraid" { 1439 createPartitions = '' 1440 installer.succeed( 1441 "flock /dev/vda parted --script /dev/vda --" 1442 + " mklabel msdos" 1443 + " mkpart primary ext2 1M 100MB" # /boot 1444 + " mkpart extended 100M -1s" 1445 + " mkpart logical 102M 3102M" # md0 (root), first device 1446 + " mkpart logical 3103M 6103M" # md0 (root), second device 1447 + " mkpart logical 6104M 6360M" # md1 (swap), first device 1448 + " mkpart logical 6361M 6617M", # md1 (swap), second device 1449 "udevadm settle", 1450 "ls -l /dev/vda* >&2", 1451 "cat /proc/partitions >&2", 1452 "udevadm control --stop-exec-queue", 1453 "mdadm --create --force /dev/md0 --metadata 1.2 --level=raid1 " 1454 + "--raid-devices=2 /dev/vda5 /dev/vda6", 1455 "mdadm --create --force /dev/md1 --metadata 1.2 --level=raid1 " 1456 + "--raid-devices=2 /dev/vda7 /dev/vda8", 1457 "udevadm control --start-exec-queue", 1458 "udevadm settle", 1459 "mkswap -f /dev/md1 -L swap", 1460 "swapon -L swap", 1461 "mkfs.ext3 -L nixos /dev/md0", 1462 "mount LABEL=nixos /mnt", 1463 "mkfs.ext3 -L boot /dev/vda1", 1464 "mkdir /mnt/boot", 1465 "mount LABEL=boot /mnt/boot", 1466 "udevadm settle", 1467 ) 1468 ''; 1469 postBootCommands = '' 1470 target.fail("dmesg | grep 'immediate safe mode'") 1471 ''; 1472 }; 1473 1474 bcache = makeInstallerTest "bcache" { 1475 createPartitions = '' 1476 installer.succeed( 1477 "flock /dev/vda parted --script /dev/vda --" 1478 + " mklabel msdos" 1479 + " mkpart primary ext2 1M 100MB" # /boot 1480 + " mkpart primary 100MB 512MB " # swap 1481 + " mkpart primary 512MB 1024MB" # Cache (typically SSD) 1482 + " mkpart primary 1024MB -1s ", # Backing device (typically HDD) 1483 "modprobe bcache", 1484 "udevadm settle", 1485 "make-bcache -B /dev/vda4 -C /dev/vda3", 1486 "udevadm settle", 1487 "mkfs.ext3 -L nixos /dev/bcache0", 1488 "mount LABEL=nixos /mnt", 1489 "mkfs.ext3 -L boot /dev/vda1", 1490 "mkdir /mnt/boot", 1491 "mount LABEL=boot /mnt/boot", 1492 "mkswap -f /dev/vda2 -L swap", 1493 "swapon -L swap", 1494 ) 1495 ''; 1496 }; 1497 1498 bcachefsSimple = makeInstallerTest "bcachefs-simple" { 1499 extraInstallerConfig = { 1500 boot.supportedFilesystems = [ "bcachefs" ]; 1501 imports = [ no-zfs-module ]; 1502 }; 1503 1504 createPartitions = '' 1505 installer.succeed( 1506 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1507 + " mkpart primary ext2 1M 100MB" # /boot 1508 + " mkpart primary linux-swap 100M 1024M" # swap 1509 + " mkpart primary 1024M -1s", # / 1510 "udevadm settle", 1511 "mkswap /dev/vda2 -L swap", 1512 "swapon -L swap", 1513 "mkfs.bcachefs -L root /dev/vda3", 1514 "mount -t bcachefs /dev/vda3 /mnt", 1515 "mkfs.ext3 -L boot /dev/vda1", 1516 "mkdir -p /mnt/boot", 1517 "mount /dev/vda1 /mnt/boot", 1518 ) 1519 ''; 1520 }; 1521 1522 bcachefsEncrypted = makeInstallerTest "bcachefs-encrypted" { 1523 extraInstallerConfig = { 1524 boot.supportedFilesystems = [ "bcachefs" ]; 1525 1526 # disable zfs so we can support latest kernel if needed 1527 imports = [ no-zfs-module ]; 1528 1529 environment.systemPackages = with pkgs; [ keyutils ]; 1530 }; 1531 1532 extraConfig = '' 1533 boot.kernelParams = lib.mkAfter [ "console=tty0" ]; 1534 ''; 1535 1536 enableOCR = true; 1537 postBootCommands = '' 1538 # Enter it wrong once 1539 target.wait_for_text("enter passphrase for ") 1540 target.send_chars("wrong\n") 1541 # Then enter it right. 1542 target.wait_for_text("enter passphrase for ") 1543 target.send_chars("password\n") 1544 ''; 1545 1546 createPartitions = '' 1547 installer.succeed( 1548 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1549 + " mkpart primary ext2 1M 100MB" # /boot 1550 + " mkpart primary linux-swap 100M 1024M" # swap 1551 + " mkpart primary 1024M -1s", # / 1552 "udevadm settle", 1553 "mkswap /dev/vda2 -L swap", 1554 "swapon -L swap", 1555 "echo password | mkfs.bcachefs -L root --encrypted /dev/vda3", 1556 "echo password | bcachefs unlock -k session /dev/vda3", 1557 "echo password | mount -t bcachefs /dev/vda3 /mnt", 1558 "mkfs.ext3 -L boot /dev/vda1", 1559 "mkdir -p /mnt/boot", 1560 "mount /dev/vda1 /mnt/boot", 1561 ) 1562 ''; 1563 }; 1564 1565 bcachefsMulti = makeInstallerTest "bcachefs-multi" { 1566 extraInstallerConfig = { 1567 boot.supportedFilesystems = [ "bcachefs" ]; 1568 1569 # disable zfs so we can support latest kernel if needed 1570 imports = [ no-zfs-module ]; 1571 }; 1572 1573 createPartitions = '' 1574 installer.succeed( 1575 "flock /dev/vda parted --script /dev/vda -- mklabel msdos" 1576 + " mkpart primary ext2 1M 100MB" # /boot 1577 + " mkpart primary linux-swap 100M 1024M" # swap 1578 + " mkpart primary 1024M 4096M" # / 1579 + " mkpart primary 4096M -1s", # / 1580 "udevadm settle", 1581 "mkswap /dev/vda2 -L swap", 1582 "swapon -L swap", 1583 "mkfs.bcachefs -L root --metadata_replicas 2 --foreground_target ssd --promote_target ssd --background_target hdd --label ssd /dev/vda3 --label hdd /dev/vda4", 1584 "mount -t bcachefs /dev/vda3:/dev/vda4 /mnt", 1585 "mkfs.ext3 -L boot /dev/vda1", 1586 "mkdir -p /mnt/boot", 1587 "mount /dev/vda1 /mnt/boot", 1588 ) 1589 ''; 1590 }; 1591 1592 # Test using labels to identify volumes in grub 1593 simpleLabels = makeInstallerTest "simpleLabels" { 1594 createPartitions = '' 1595 installer.succeed( 1596 "sgdisk -Z /dev/vda", 1597 "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", 1598 "mkswap /dev/vda2 -L swap", 1599 "swapon -L swap", 1600 "mkfs.ext4 -L root /dev/vda3", 1601 "mount LABEL=root /mnt", 1602 ) 1603 ''; 1604 grubIdentifier = "label"; 1605 }; 1606 1607 # Test using the provided disk name within grub 1608 # TODO: Fix udev so the symlinks are unneeded in /dev/disks 1609 simpleProvided = makeInstallerTest "simpleProvided" { 1610 createPartitions = '' 1611 uuid = "$(blkid -s UUID -o value /dev/vda2)" 1612 installer.succeed( 1613 "sgdisk -Z /dev/vda", 1614 "sgdisk -n 1:0:+1M -n 2:0:+100M -n 3:0:+1G -N 4 -t 1:ef02 -t 2:8300 " 1615 + "-t 3:8200 -t 4:8300 -c 2:boot -c 4:root /dev/vda", 1616 "mkswap /dev/vda3 -L swap", 1617 "swapon -L swap", 1618 "mkfs.ext4 -L boot /dev/vda2", 1619 "mkfs.ext4 -L root /dev/vda4", 1620 ) 1621 installer.execute(f"ln -s ../../vda2 /dev/disk/by-uuid/{uuid}") 1622 installer.execute("ln -s ../../vda4 /dev/disk/by-label/root") 1623 installer.succeed( 1624 "mount /dev/disk/by-label/root /mnt", 1625 "mkdir /mnt/boot", 1626 f"mount /dev/disk/by-uuid/{uuid} /mnt/boot", 1627 ) 1628 ''; 1629 grubIdentifier = "provided"; 1630 }; 1631 1632 # Simple btrfs grub testing 1633 btrfsSimple = makeInstallerTest "btrfsSimple" { 1634 createPartitions = '' 1635 installer.succeed( 1636 "sgdisk -Z /dev/vda", 1637 "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", 1638 "mkswap /dev/vda2 -L swap", 1639 "swapon -L swap", 1640 "mkfs.btrfs -L root /dev/vda3", 1641 "mount LABEL=root /mnt", 1642 ) 1643 ''; 1644 }; 1645 1646 # Test to see if we can detect /boot and /nix on subvolumes 1647 btrfsSubvols = makeInstallerTest "btrfsSubvols" { 1648 createPartitions = '' 1649 installer.succeed( 1650 "sgdisk -Z /dev/vda", 1651 "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", 1652 "mkswap /dev/vda2 -L swap", 1653 "swapon -L swap", 1654 "mkfs.btrfs -L root /dev/vda3", 1655 "btrfs device scan", 1656 "mount LABEL=root /mnt", 1657 "btrfs subvol create /mnt/boot", 1658 "btrfs subvol create /mnt/nixos", 1659 "btrfs subvol create /mnt/nixos/default", 1660 "umount /mnt", 1661 "mount -o defaults,subvol=nixos/default LABEL=root /mnt", 1662 "mkdir /mnt/boot", 1663 "mount -o defaults,subvol=boot LABEL=root /mnt/boot", 1664 ) 1665 ''; 1666 }; 1667 1668 # Test to see if we can detect default and aux subvolumes correctly 1669 btrfsSubvolDefault = makeInstallerTest "btrfsSubvolDefault" { 1670 createPartitions = '' 1671 installer.succeed( 1672 "sgdisk -Z /dev/vda", 1673 "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", 1674 "mkswap /dev/vda2 -L swap", 1675 "swapon -L swap", 1676 "mkfs.btrfs -L root /dev/vda3", 1677 "btrfs device scan", 1678 "mount LABEL=root /mnt", 1679 "btrfs subvol create /mnt/badpath", 1680 "btrfs subvol create /mnt/badpath/boot", 1681 "btrfs subvol create /mnt/nixos", 1682 "btrfs subvol set-default " 1683 + "$(btrfs subvol list /mnt | grep 'nixos' | awk '{print $2}') /mnt", 1684 "umount /mnt", 1685 "mount -o defaults LABEL=root /mnt", 1686 "mkdir -p /mnt/badpath/boot", # Help ensure the detection mechanism 1687 # is actually looking up subvolumes 1688 "mkdir /mnt/boot", 1689 "mount -o defaults,subvol=badpath/boot LABEL=root /mnt/boot", 1690 ) 1691 ''; 1692 }; 1693 1694 # Test to see if we can deal with subvols that need to be escaped in fstab 1695 btrfsSubvolEscape = makeInstallerTest "btrfsSubvolEscape" { 1696 createPartitions = '' 1697 installer.succeed( 1698 "sgdisk -Z /dev/vda", 1699 "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", 1700 "mkswap /dev/vda2 -L swap", 1701 "swapon -L swap", 1702 "mkfs.btrfs -L root /dev/vda3", 1703 "btrfs device scan", 1704 "mount LABEL=root /mnt", 1705 "btrfs subvol create '/mnt/nixos in space'", 1706 "btrfs subvol create /mnt/boot", 1707 "umount /mnt", 1708 "mount -o 'defaults,subvol=nixos in space' LABEL=root /mnt", 1709 "mkdir /mnt/boot", 1710 "mount -o defaults,subvol=boot LABEL=root /mnt/boot", 1711 ) 1712 ''; 1713 }; 1714} 1715// { 1716 clevisBcachefs = mkClevisBcachefsTest { }; 1717 clevisBcachefsFallback = mkClevisBcachefsTest { fallback = true; }; 1718 clevisLuks = mkClevisLuksTest { }; 1719 clevisLuksFallback = mkClevisLuksTest { fallback = true; }; 1720 clevisZfs = mkClevisZfsTest { }; 1721 clevisZfsFallback = mkClevisZfsTest { fallback = true; }; 1722 clevisZfsParentDataset = mkClevisZfsTest { parentDataset = true; }; 1723 clevisZfsParentDatasetFallback = mkClevisZfsTest { 1724 parentDataset = true; 1725 fallback = true; 1726 }; 1727} 1728// optionalAttrs systemdStage1 { 1729 stratisRoot = makeInstallerTest "stratisRoot" { 1730 createPartitions = '' 1731 installer.succeed( 1732 "sgdisk --zap-all /dev/vda", 1733 "sgdisk --new=1:0:+100M --typecode=0:ef00 /dev/vda", # /boot 1734 "sgdisk --new=2:0:+1G --typecode=0:8200 /dev/vda", # swap 1735 "sgdisk --new=3:0:+5G --typecode=0:8300 /dev/vda", # / 1736 "udevadm settle", 1737 1738 "mkfs.vfat /dev/vda1", 1739 "mkswap /dev/vda2 -L swap", 1740 "swapon -L swap", 1741 "stratis pool create my-pool /dev/vda3", 1742 "stratis filesystem create my-pool nixos", 1743 "udevadm settle", 1744 1745 "mount /dev/stratis/my-pool/nixos /mnt", 1746 "mkdir -p /mnt/boot", 1747 "mount /dev/vda1 /mnt/boot" 1748 ) 1749 ''; 1750 bootLoader = "systemd-boot"; 1751 extraInstallerConfig = 1752 { modulesPath, ... }: 1753 { 1754 config = { 1755 services.stratis.enable = true; 1756 environment.systemPackages = [ 1757 pkgs.stratis-cli 1758 pkgs.thin-provisioning-tools 1759 pkgs.lvm2.bin 1760 pkgs.stratisd.initrd 1761 ]; 1762 }; 1763 }; 1764 }; 1765 1766 gptAutoRoot = 1767 let 1768 rootPartType = 1769 { 1770 ia32 = "44479540-F297-41B2-9AF7-D131D5F0458A"; 1771 x64 = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709"; 1772 arm = "69DAD710-2CE4-4E3C-B16C-21A1D49ABED3"; 1773 aa64 = "B921B045-1DF0-41C3-AF44-4C6F280D3FAE"; 1774 } 1775 .${pkgs.stdenv.hostPlatform.efiArch}; 1776 in 1777 makeInstallerTest "gptAutoRoot" { 1778 disableFileSystems = true; 1779 createPartitions = '' 1780 installer.succeed( 1781 "sgdisk --zap-all /dev/vda", 1782 "sgdisk --new=1:0:+100M --typecode=0:ef00 /dev/vda", # /boot 1783 "sgdisk --new=2:0:+1G --typecode=0:8200 /dev/vda", # swap 1784 "sgdisk --new=3:0:+5G --typecode=0:${rootPartType} /dev/vda", # / 1785 "udevadm settle", 1786 1787 "mkfs.vfat /dev/vda1", 1788 "mkswap /dev/vda2 -L swap", 1789 "swapon -L swap", 1790 "mkfs.ext4 -L root /dev/vda3", 1791 "udevadm settle", 1792 1793 "mount /dev/vda3 /mnt", 1794 "mkdir -p /mnt/boot", 1795 "mount /dev/vda1 /mnt/boot" 1796 ) 1797 ''; 1798 bootLoader = "systemd-boot"; 1799 extraConfig = '' 1800 boot.initrd.systemd.root = "gpt-auto"; 1801 boot.initrd.supportedFilesystems = ["ext4"]; 1802 ''; 1803 }; 1804}