Merge pull request #290730 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

View File

@ -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) {};

View File

@ -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 {};

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"
'';
}