From 87cd533a328750587e9545c12e4a81f7af67a8a4 Mon Sep 17 00:00:00 2001 From: T0astBread Date: Wed, 22 Jun 2022 04:03:43 +0200 Subject: [PATCH 1/3] nixos/qemu-vm: allow custom partitions and filesystems in VM Potential use cases for disabling `useDefaultFilesystems` include: - Testing with uncommon filesystem layouts - Testing scenarios where swapping occurs - Testing with LUKS-encrypted disks Closes #177963 --- nixos/modules/virtualisation/qemu-vm.nix | 84 ++++++++++++++---------- 1 file changed, 51 insertions(+), 33 deletions(-) diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index f622897aa620..e87f540fd57c 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -684,6 +684,21 @@ in ''; }; + virtualisation.useDefaultFilesystems = + mkOption { + type = types.bool; + default = true; + description = + '' + If enabled, the boot disk of the virtual machine will be + formatted and mounted with the default filesystems for + testing. Swap devices and LUKS will be disabled. + + If disabled, a root filesystem has to be specified and + formatted (for example in the initial ramdisk). + ''; + }; + virtualisation.efiVars = mkOption { type = types.str; @@ -754,13 +769,13 @@ in ); boot.loader.grub.gfxmodeBios = with cfg.resolution; "${toString x}x${toString y}"; - boot.initrd.extraUtilsCommands = lib.mkIf (!config.boot.initrd.systemd.enable) + boot.initrd.extraUtilsCommands = lib.mkIf (cfg.useDefaultFilesystems && !config.boot.initrd.systemd.enable) '' # We need mke2fs in the initrd. copy_bin_and_libs ${pkgs.e2fsprogs}/bin/mke2fs ''; - boot.initrd.postDeviceCommands = lib.mkIf (!config.boot.initrd.systemd.enable) + boot.initrd.postDeviceCommands = lib.mkIf (cfg.useDefaultFilesystems && !config.boot.initrd.systemd.enable) '' # If the disk image appears to be empty, run mke2fs to # initialise. @@ -930,38 +945,41 @@ in }; in mkVMOverride (cfg.fileSystems // - { + optionalAttrs cfg.useDefaultFilesystems { "/".device = cfg.bootDevice; "/".fsType = "ext4"; "/".autoFormat = true; - - "/tmp" = mkIf config.boot.tmpOnTmpfs - { device = "tmpfs"; - fsType = "tmpfs"; - neededForBoot = true; - # Sync with systemd's tmp.mount; - options = [ "mode=1777" "strictatime" "nosuid" "nodev" "size=${toString config.boot.tmpOnTmpfsSize}" ]; - }; - - "/nix/${if cfg.writableStore then ".ro-store" else "store"}" = - mkIf cfg.useNixStoreImage - { device = "${lookupDriveDeviceName "nix-store" cfg.qemu.drives}"; - neededForBoot = true; - options = [ "ro" ]; - }; - - "/nix/.rw-store" = mkIf (cfg.writableStore && cfg.writableStoreUseTmpfs) - { fsType = "tmpfs"; - options = [ "mode=0755" ]; - neededForBoot = true; - }; - - "/boot" = mkIf cfg.useBootLoader - # see note [Disk layout with `useBootLoader`] - { device = "${lookupDriveDeviceName "boot" cfg.qemu.drives}2"; # 2 for e.g. `vdb2`, as created in `bootDisk` - fsType = "vfat"; - noCheck = true; # fsck fails on a r/o filesystem - }; + } // + optionalAttrs config.boot.tmpOnTmpfs { + "/tmp" = { + device = "tmpfs"; + fsType = "tmpfs"; + neededForBoot = true; + # Sync with systemd's tmp.mount; + options = [ "mode=1777" "strictatime" "nosuid" "nodev" "size=${toString config.boot.tmpOnTmpfsSize}" ]; + }; + } // + optionalAttrs cfg.useNixStoreImage { + "/nix/${if cfg.writableStore then ".ro-store" else "store"}" = { + device = "${lookupDriveDeviceName "nix-store" cfg.qemu.drives}"; + neededForBoot = true; + options = [ "ro" ]; + }; + } // + optionalAttrs (cfg.writableStore && cfg.writableStoreUseTmpfs) { + "/nix/.rw-store" = { + fsType = "tmpfs"; + options = [ "mode=0755" ]; + neededForBoot = true; + }; + } // + optionalAttrs cfg.useBootLoader { + # see note [Disk layout with `useBootLoader`] + "/boot" = { + device = "${lookupDriveDeviceName "boot" cfg.qemu.drives}2"; # 2 for e.g. `vdb2`, as created in `bootDisk` + fsType = "vfat"; + noCheck = true; # fsck fails on a r/o filesystem + }; } // lib.mapAttrs' mkSharedDir cfg.sharedDirectories); boot.initrd.systemd = lib.mkIf (config.boot.initrd.systemd.enable && cfg.writableStore) { @@ -986,8 +1004,8 @@ in }; }; - swapDevices = mkVMOverride [ ]; - boot.initrd.luks.devices = mkVMOverride {}; + swapDevices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) [ ]; + boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) {}; # Don't run ntpd in the guest. It should get the correct time from KVM. services.timesyncd.enable = false; From 4c77ffb38fcdb2accf3760966e4e6bc8628316b4 Mon Sep 17 00:00:00 2001 From: T0astBread Date: Wed, 22 Jun 2022 04:10:44 +0200 Subject: [PATCH 2/3] nixos/tests: add non-default-filesystems test --- nixos/tests/all-tests.nix | 1 + nixos/tests/non-default-filesystems.nix | 54 +++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 nixos/tests/non-default-filesystems.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index fa88ad524070..b8df54bcdd2b 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -382,6 +382,7 @@ in { nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { inherit evalMinimalConfig; }; node-red = handleTest ./node-red.nix {}; nomad = handleTest ./nomad.nix {}; + non-default-filesystems = handleTest ./non-default-filesystems.nix {}; noto-fonts = handleTest ./noto-fonts.nix {}; novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {}; nsd = handleTest ./nsd.nix {}; diff --git a/nixos/tests/non-default-filesystems.nix b/nixos/tests/non-default-filesystems.nix new file mode 100644 index 000000000000..7fa75aaad724 --- /dev/null +++ b/nixos/tests/non-default-filesystems.nix @@ -0,0 +1,54 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: +{ + name = "non-default-filesystems"; + + nodes.machine = + { config, pkgs, lib, ... }: + let + disk = config.virtualisation.bootDevice; + in + { + virtualisation.useDefaultFilesystems = false; + + boot.initrd.availableKernelModules = [ "btrfs" ]; + boot.supportedFilesystems = [ "btrfs" ]; + + boot.initrd.postDeviceCommands = '' + FSTYPE=$(blkid -o value -s TYPE ${disk} || true) + if test -z "$FSTYPE"; then + modprobe btrfs + ${pkgs.btrfs-progs}/bin/mkfs.btrfs ${disk} + + mkdir /nixos + mount -t btrfs ${disk} /nixos + + ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/root + ${pkgs.btrfs-progs}/bin/btrfs subvolume create /nixos/home + + umount /nixos + fi + ''; + + virtualisation.fileSystems = { + "/" = { + device = disk; + fsType = "btrfs"; + options = [ "subvol=/root" ]; + }; + + "/home" = { + device = disk; + fsType = "btrfs"; + options = [ "subvol=/home" ]; + }; + }; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + + with subtest("BTRFS filesystems are mounted correctly"): + machine.succeed("grep -E '/dev/vda / btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/root 0 0' /proc/mounts") + machine.succeed("grep -E '/dev/vda /home btrfs rw,relatime,space_cache=v2,subvolid=[0-9]+,subvol=/home 0 0' /proc/mounts") + ''; +}) From 5249031660b21610e291272fd2a9ebd172fda812 Mon Sep 17 00:00:00 2001 From: T0astBread Date: Wed, 22 Jun 2022 04:11:23 +0200 Subject: [PATCH 3/3] nixos/tests: add swap-partition test --- nixos/tests/all-tests.nix | 1 + nixos/tests/swap-partition.nix | 48 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 nixos/tests/swap-partition.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index b8df54bcdd2b..62a6864b948a 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -516,6 +516,7 @@ in { step-ca = handleTestOn ["x86_64-linux"] ./step-ca.nix {}; strongswan-swanctl = handleTest ./strongswan-swanctl.nix {}; sudo = handleTest ./sudo.nix {}; + swap-partition = handleTest ./swap-partition.nix {}; sway = handleTest ./sway.nix {}; switchTest = handleTest ./switch-test.nix {}; sympa = handleTest ./sympa.nix {}; diff --git a/nixos/tests/swap-partition.nix b/nixos/tests/swap-partition.nix new file mode 100644 index 000000000000..2279630b57b8 --- /dev/null +++ b/nixos/tests/swap-partition.nix @@ -0,0 +1,48 @@ +import ./make-test-python.nix ({ lib, pkgs, ... }: +{ + name = "swap-partition"; + + nodes.machine = + { config, pkgs, lib, ... }: + { + virtualisation.useDefaultFilesystems = false; + + virtualisation.bootDevice = "/dev/vda1"; + + boot.initrd.postDeviceCommands = '' + if ! test -b /dev/vda1; then + ${pkgs.parted}/bin/parted --script /dev/vda -- mklabel msdos + ${pkgs.parted}/bin/parted --script /dev/vda -- mkpart primary 1MiB -250MiB + ${pkgs.parted}/bin/parted --script /dev/vda -- mkpart primary -250MiB 100% + sync + fi + + FSTYPE=$(blkid -o value -s TYPE /dev/vda1 || true) + if test -z "$FSTYPE"; then + ${pkgs.e2fsprogs}/bin/mke2fs -t ext4 -L root /dev/vda1 + ${pkgs.util-linux}/bin/mkswap --label swap /dev/vda2 + fi + ''; + + virtualisation.fileSystems = { + "/" = { + device = "/dev/disk/by-label/root"; + fsType = "ext4"; + }; + }; + + swapDevices = [ + { + device = "/dev/disk/by-label/swap"; + } + ]; + }; + + testScript = '' + machine.wait_for_unit("multi-user.target") + + with subtest("Swap is active"): + # Doesn't matter if the numbers reported by `free` are slightly off due to unit conversions. + machine.succeed("free -h | grep -E 'Swap:\s+2[45][0-9]Mi'") + ''; +})