Merge pull request from nikstur/qemu-vm-refactoring

qemu-vm.nix refactoring
This commit is contained in:
lassulus 2024-07-18 19:34:51 +02:00 committed by GitHub
commit a62c421981
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 90 additions and 69 deletions
nixos

View File

@ -393,7 +393,7 @@ in
The path (inside the VM) to the device containing the EFI System Partition (ESP). 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 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.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 # After booting, register the closure of the paths in
# `virtualisation.additionalPaths' in the Nix database in the VM. This # `virtualisation.additionalPaths' in the Nix database in the VM. This
# allows Nix operations to work in the VM. The path to the # allows Nix operations to work in the VM. The path to the
@ -1101,8 +1069,7 @@ in
''; '';
boot.initrd.availableKernelModules = 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"; ++ optional (cfg.tpm.enable) "tpm_tis";
virtualisation.additionalPaths = [ config.system.build.toplevel ]; virtualisation.additionalPaths = [ config.system.build.toplevel ];
@ -1110,7 +1077,9 @@ in
virtualisation.sharedDirectories = { virtualisation.sharedDirectories = {
nix-store = mkIf cfg.mountHostNixStore { nix-store = mkIf cfg.mountHostNixStore {
source = builtins.storeDir; 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"; securityModel = "none";
}; };
xchg = { xchg = {
@ -1220,10 +1189,7 @@ in
virtualisation.fileSystems = let virtualisation.fileSystems = let
mkSharedDir = tag: share: mkSharedDir = tag: share:
{ {
name = name = share.target;
if tag == "nix-store" && cfg.writableStore
then "/nix/.ro-store"
else share.target;
value.device = tag; value.device = tag;
value.fsType = "9p"; value.fsType = "9p";
value.neededForBoot = true; value.neededForBoot = true;
@ -1248,7 +1214,17 @@ in
# Sync with systemd's tmp.mount; # Sync with systemd's tmp.mount;
options = [ "mode=1777" "strictatime" "nosuid" "nodev" "size=${toString config.boot.tmp.tmpfsSize}" ]; 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}"; device = "/dev/disk/by-label/${nixStoreFilesystemLabel}";
neededForBoot = true; neededForBoot = true;
options = [ "ro" ]; options = [ "ro" ];
@ -1258,40 +1234,13 @@ in
options = [ "mode=0755" ]; options = [ "mode=0755" ];
neededForBoot = true; 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; device = cfg.bootPartition;
fsType = "vfat"; 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) [ ]; swapDevices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) [ ];
boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) {}; boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) {};

View File

@ -804,6 +804,7 @@ in {
qemu-vm-restrictnetwork = handleTest ./qemu-vm-restrictnetwork.nix {}; qemu-vm-restrictnetwork = handleTest ./qemu-vm-restrictnetwork.nix {};
qemu-vm-volatile-root = runTest ./qemu-vm-volatile-root.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-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 = handleTest ./qgis.nix { qgisPackage = pkgs.qgis; };
qgis-ltr = handleTest ./qgis.nix { qgisPackage = pkgs.qgis-ltr; }; qgis-ltr = handleTest ./qgis.nix { qgisPackage = pkgs.qgis-ltr; };
qownnotes = handleTest ./qownnotes.nix {}; qownnotes = handleTest ./qownnotes.nix {};

View File

@ -0,0 +1,71 @@
{ 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"
'';
}