Unverified Commit a62c4219 authored by lassulus's avatar lassulus Committed by GitHub
Browse files

Merge pull request #290730 from nikstur/qemu-vm-refactoring

qemu-vm.nix refactoring
parents ceabda68 b6e354f0
Loading
Loading
Loading
Loading
+18 −69
Original line number Diff line number Diff line
@@ -393,7 +393,7 @@ in
            The path (inside the VM) to the device containing the EFI System Partition (ESP).

            If you are *not* booting from a UEFI firmware, this value is, by
            default, `null`. The ESP is mounted under `/boot`.
            default, `null`. The ESP is mounted to `boot.loader.efi.efiSysMountpoint`.
          '';
      };

@@ -1054,38 +1054,6 @@ in

    boot.loader.supportsInitrdSecrets = mkIf (!cfg.useBootLoader) (mkVMOverride false);

    boot.initrd.postMountCommands = lib.mkIf (!config.boot.initrd.systemd.enable)
      ''
        # Mark this as a NixOS machine.
        mkdir -p $targetRoot/etc
        echo -n > $targetRoot/etc/NIXOS

        # Fix the permissions on /tmp.
        chmod 1777 $targetRoot/tmp

        mkdir -p $targetRoot/boot

        ${optionalString cfg.writableStore ''
          echo "mounting overlay filesystem on /nix/store..."
          mkdir -p -m 0755 $targetRoot/nix/.rw-store/store $targetRoot/nix/.rw-store/work $targetRoot/nix/store
          mount -t overlay overlay $targetRoot/nix/store \
            -o lowerdir=$targetRoot/nix/.ro-store,upperdir=$targetRoot/nix/.rw-store/store,workdir=$targetRoot/nix/.rw-store/work || fail
        ''}
      '';

    systemd.tmpfiles.settings."10-qemu-vm" = lib.mkIf config.boot.initrd.systemd.enable {
      "/etc/NIXOS".f = {
        mode = "0644";
        user = "root";
        group = "root";
      };
      "${config.boot.loader.efi.efiSysMountPoint}".d = {
        mode = "0644";
        user = "root";
        group = "root";
      };
    };

    # After booting, register the closure of the paths in
    # `virtualisation.additionalPaths' in the Nix database in the VM.  This
    # allows Nix operations to work in the VM.  The path to the
@@ -1101,8 +1069,7 @@ in
      '';

    boot.initrd.availableKernelModules =
      optional cfg.writableStore "overlay"
      ++ optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx"
      optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx"
      ++ optional (cfg.tpm.enable) "tpm_tis";

    virtualisation.additionalPaths = [ config.system.build.toplevel ];
@@ -1110,7 +1077,9 @@ in
    virtualisation.sharedDirectories = {
      nix-store = mkIf cfg.mountHostNixStore {
        source = builtins.storeDir;
        target = "/nix/store";
        # Always mount this to /nix/.ro-store because we never want to actually
        # write to the host Nix Store.
        target = "/nix/.ro-store";
        securityModel = "none";
      };
      xchg = {
@@ -1220,10 +1189,7 @@ in
    virtualisation.fileSystems = let
      mkSharedDir = tag: share:
        {
          name =
            if tag == "nix-store" && cfg.writableStore
            then "/nix/.ro-store"
            else share.target;
          name = share.target;
          value.device = tag;
          value.fsType = "9p";
          value.neededForBoot = true;
@@ -1248,7 +1214,17 @@ in
          # Sync with systemd's tmp.mount;
          options = [ "mode=1777" "strictatime" "nosuid" "nodev" "size=${toString config.boot.tmp.tmpfsSize}" ];
        };
        "/nix/${if cfg.writableStore then ".ro-store" else "store"}" = lib.mkIf cfg.useNixStoreImage {
        "/nix/store" = lib.mkIf (cfg.useNixStoreImage || cfg.mountHostNixStore) (if cfg.writableStore then {
          overlay = {
            lowerdir = [ "/nix/.ro-store" ];
            upperdir = "/nix/.rw-store/upper";
            workdir = "/nix/.rw-store/work";
          };
        } else {
          device = "/nix/.ro-store";
          options = [ "bind" ];
        });
        "/nix/.ro-store" = lib.mkIf cfg.useNixStoreImage {
          device = "/dev/disk/by-label/${nixStoreFilesystemLabel}";
          neededForBoot = true;
          options = [ "ro" ];
@@ -1258,40 +1234,13 @@ in
          options = [ "mode=0755" ];
          neededForBoot = true;
        };
        "/boot" = lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null) {
        "${config.boot.loader.efi.efiSysMountPoint}" = lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null) {
          device = cfg.bootPartition;
          fsType = "vfat";
          noCheck = true; # fsck fails on a r/o filesystem
        };
      }
    ];

    boot.initrd.systemd = lib.mkIf (config.boot.initrd.systemd.enable && cfg.writableStore) {
      mounts = [{
        where = "/sysroot/nix/store";
        what = "overlay";
        type = "overlay";
        options = "lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/store,workdir=/sysroot/nix/.rw-store/work";
        wantedBy = ["initrd-fs.target"];
        before = ["initrd-fs.target"];
        requires = ["rw-store.service"];
        after = ["rw-store.service"];
        unitConfig.RequiresMountsFor = "/sysroot/nix/.ro-store";
      }];
      services.rw-store = {
        before = [ "shutdown.target" ];
        conflicts = [ "shutdown.target" ];
        unitConfig = {
          DefaultDependencies = false;
          RequiresMountsFor = "/sysroot/nix/.rw-store";
        };
        serviceConfig = {
          Type = "oneshot";
          ExecStart = "/bin/mkdir -p -m 0755 /sysroot/nix/.rw-store/store /sysroot/nix/.rw-store/work /sysroot/nix/store";
        };
      };
    };

    swapDevices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) [ ];
    boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) {};

+1 −0
Original line number Diff line number Diff line
@@ -804,6 +804,7 @@ in {
  qemu-vm-restrictnetwork = handleTest ./qemu-vm-restrictnetwork.nix {};
  qemu-vm-volatile-root = runTest ./qemu-vm-volatile-root.nix;
  qemu-vm-external-disk-image = runTest ./qemu-vm-external-disk-image.nix;
  qemu-vm-store = runTest ./qemu-vm-store.nix;
  qgis = handleTest ./qgis.nix { qgisPackage = pkgs.qgis; };
  qgis-ltr = handleTest ./qgis.nix { qgisPackage = pkgs.qgis-ltr; };
  qownnotes = handleTest ./qownnotes.nix {};
+71 −0
Original line number Diff line number Diff line
{ lib, ... }: {

  name = "qemu-vm-store";

  meta.maintainers = with lib.maintainers; [ nikstur ];

  nodes = {
    sharedWritable = {
      virtualisation.writableStore = true;
    };

    sharedReadOnly = {
      virtualisation.writableStore = false;
    };

    imageWritable = {
      virtualisation.useNixStoreImage = true;
      virtualisation.writableStore = true;
    };

    imageReadOnly = {
      virtualisation.useNixStoreImage = true;
      virtualisation.writableStore = false;
    };

    fullDisk = {
      virtualisation.useBootLoader = true;
    };
  };

  testScript = ''
    build_derivation = """
      nix-build --option substitute false -E 'derivation {
        name = "t";
        builder = "/bin/sh";
        args = ["-c" "echo something > $out"];
        system = builtins.currentSystem;
        preferLocalBuild = true;
      }'
    """

    start_all()

    with subtest("Nix Store is writable"):
      sharedWritable.succeed(build_derivation)
      imageWritable.succeed(build_derivation)
      fullDisk.succeed(build_derivation)

    with subtest("Nix Store is read only"):
      sharedReadOnly.fail(build_derivation)
      imageReadOnly.fail(build_derivation)

    # Checking whether the fs type is 9P is just a proxy to test whether the
    # Nix Store is shared. If we switch to a different technology (e.g.
    # virtiofs) for sharing, we need to adjust these tests.

    with subtest("Nix store is shared from the host via 9P"):
      sharedWritable.succeed("findmnt --kernel --type 9P /nix/.ro-store")
      sharedReadOnly.succeed("findmnt --kernel --type 9P /nix/.ro-store")

    with subtest("Nix store is not shared via 9P"):
      imageWritable.fail("findmnt --kernel --type 9P /nix/.ro-store")
      imageReadOnly.fail("findmnt --kernel --type 9P /nix/.ro-store")

    with subtest("Nix store is not mounted separately"):
      rootDevice = fullDisk.succeed("stat -c %d /")
      nixStoreDevice = fullDisk.succeed("stat -c %d /nix/store")
      assert rootDevice == nixStoreDevice, "Nix store is mounted separately from the root fs"
  '';

}