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