diff --git a/docs/interactive-vm.md b/docs/interactive-vm.md new file mode 100644 index 0000000..fec8107 --- /dev/null +++ b/docs/interactive-vm.md @@ -0,0 +1,17 @@ +# Running Interactive VMs with disko + +disko now exports it's own flavor of interactive VMs (similiar to config.system.build.vm). +Simply import the disko module and build the vm runner with: +``` +nix build -L '.#nixosConfigurations.mymachine.config.system.build.vmWithDisko' +``` + +afterwards you can run the interactive VM with: + +``` +result/bin/disko-vm +``` + +extraConfig that is set in disko.tests.extraConfig is also applied to the interactive VMs. +imageSize of the VMs will be determined by the imageSize in the disk type in your disko config. +memorySize is set by disko.memSize diff --git a/lib/default.nix b/lib/default.nix index f4337f9..58467f6 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -16,6 +16,8 @@ let # a version of makeDiskImage which runs outside of the store makeDiskImagesScript = args: (import ./make-disk-image.nix ({ inherit diskoLib; } // args)).impure; + makeVMRunner = args: (import ./interactive-vm.nix ({ inherit diskoLib; } // args)).pure; + testLib = import ./tests.nix { inherit lib makeTest eval-config; }; # like lib.types.oneOf but instead of a list takes an attrset # uses the field "type" to find the correct type in the attrset diff --git a/lib/interactive-vm.nix b/lib/interactive-vm.nix new file mode 100644 index 0000000..ba20987 --- /dev/null +++ b/lib/interactive-vm.nix @@ -0,0 +1,88 @@ +{ nixosConfig +, diskoLib +, pkgs ? nixosConfig.pkgs +, name ? "${nixosConfig.config.networking.hostName}-disko-images" +, extraConfig ? { } +}: +let + lib = pkgs.lib; + vm_disko = (diskoLib.testLib.prepareDiskoConfig nixosConfig.config diskoLib.testLib.devices).disko; + cfg_ = (lib.evalModules { + modules = lib.singleton { + # _file = toString input; + imports = lib.singleton { disko.devices = vm_disko.devices; }; + options = { + disko.devices = lib.mkOption { + type = diskoLib.toplevel; + }; + disko.testMode = lib.mkOption { + type = lib.types.bool; + default = true; + }; + }; + }; + }).config; + disks = lib.attrValues cfg_.disko.devices.disk; + diskoImages = diskoLib.makeDiskImages { + nixosConfig = nixosConfig; + copyNixStore = false; + extraConfig = { + disko.devices = cfg_.disko.devices; + }; + testMode = true; + }; + rootDisk = { + name = "root"; + file = ''"$tmp"/${(builtins.head disks).name}.qcow2''; + driveExtraOpts.cache = "writeback"; + driveExtraOpts.werror = "report"; + deviceExtraOpts.bootindex = "1"; + deviceExtraOpts.serial = "root"; + }; + otherDisks = map + (disk: { + name = disk.name; + file = ''"$tmp"/${disk.name}.qcow2''; + driveExtraOpts.werror = "report"; + }) + (builtins.tail disks); + vm = (nixosConfig.extendModules { + modules = [ + ({ modulesPath, ... }: { + imports = [ + (modulesPath + "/virtualisation/qemu-vm.nix") + ]; + }) + { + virtualisation.useEFIBoot = nixosConfig.config.disko.tests.efi; + virtualisation.memorySize = nixosConfig.config.disko.memSize; + virtualisation.useDefaultFilesystems = false; + virtualisation.diskImage = null; + virtualisation.qemu.drives = [ rootDisk ] ++ otherDisks; + boot.zfs.devNodes = "/dev/disk/by-uuid"; # needed because /dev/disk/by-id is empty in qemu-vms + boot.zfs.forceImportAll = true; + } + { + # generated from disko config + virtualisation.fileSystems = cfg_.disko.devices._config.fileSystems; + boot = cfg_.disko.devices._config.boot or { }; + swapDevices = cfg_.disko.devices._config.swapDevices or [ ]; + } + nixosConfig.config.disko.tests.extraConfig + ]; + }).config.system.build.vm; +in +{ + pure = pkgs.writeDashBin "disko-vm" '' + set -efux + export tmp=$(mktemp -d) + trap 'rm -rf "$tmp"' EXIT + ${lib.concatMapStringsSep "\n" (disk: '' + ${pkgs.qemu}/bin/qemu-img create -f qcow2 \ + -b ${diskoImages}/${disk.name}.raw \ + -F raw "$tmp"/${disk.name}.qcow2 + '') disks} + set +f + ${vm}/bin/run-*-vm + ''; +} diff --git a/module.nix b/module.nix index 8c8fbc3..9411ca9 100644 --- a/module.nix +++ b/module.nix @@ -69,6 +69,15 @@ in type = lib.types.bool; default = false; }; + testMode = lib.mkOption { + internal = true; + description = '' + this is true if the system is being run in test mode. + like a vm test or an interactive vm + ''; + type = lib.types.bool; + default = false; + }; tests = { efi = lib.mkOption { description = '' @@ -121,6 +130,11 @@ in extraSystemConfig = cfg.tests.extraConfig; extraTestScript = cfg.tests.extraChecks; }; + + vmWithDisko = diskoLib.makeVMRunner { + inherit pkgs; + nixosConfig = args; + }; };