mirror of
https://github.com/nix-community/disko
synced 2024-11-13 23:57:12 +00:00
Merge pull request #713 from nix-community/interactiveVM
add interactive VMs
This commit is contained in:
commit
6433571556
11 changed files with 206 additions and 63 deletions
|
@ -41,9 +41,8 @@ recursive layouts.
|
||||||
|
|
||||||
Disko doesn't require installation: it can be run directly from nix-community
|
Disko doesn't require installation: it can be run directly from nix-community
|
||||||
repository. The [Quickstart Guide](./docs/quickstart.md) documents how to run
|
repository. The [Quickstart Guide](./docs/quickstart.md) documents how to run
|
||||||
Disko in its simplest form when installing NixOS. Alternatively, you can
|
Disko in its simplest form when installing NixOS. Alternatively, you can also
|
||||||
also use the new [disko-install](./docs/disko-install.md) tool, which combines
|
use the new [disko-install](./docs/disko-install.md) tool, which combines `disko` and `nixos-install` into one step.
|
||||||
the `disko` and `nixos-install` into one step.
|
|
||||||
|
|
||||||
For information on other use cases, including upgrading from an older version of
|
For information on other use cases, including upgrading from an older version of
|
||||||
**disko**, using **disko** without NixOS and downloading the module, see the
|
**disko**, using **disko** without NixOS and downloading the module, see the
|
||||||
|
|
17
docs/interactive-vm.md
Normal file
17
docs/interactive-vm.md
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# Running Interactive VMs with disko
|
||||||
|
|
||||||
|
disko now exports its 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
|
|
@ -15,7 +15,8 @@ existing partitions. Dual booting with other operating systems is not supported.
|
||||||
|
|
||||||
### Step 1: Choose a Disk Configuration
|
### Step 1: Choose a Disk Configuration
|
||||||
|
|
||||||
Real-world templates are provided in this [repository](https://github.com/nix-community/disko-templates).
|
Real-world templates are provided in this
|
||||||
|
[repository](https://github.com/nix-community/disko-templates).
|
||||||
|
|
||||||
More disk layouts for all filesystems can be also found in the
|
More disk layouts for all filesystems can be also found in the
|
||||||
[example](https://github.com/nix-community/disko/tree/master/example) directory
|
[example](https://github.com/nix-community/disko/tree/master/example) directory
|
||||||
|
|
|
@ -16,6 +16,8 @@ let
|
||||||
# a version of makeDiskImage which runs outside of the store
|
# a version of makeDiskImage which runs outside of the store
|
||||||
makeDiskImagesScript = args: (import ./make-disk-image.nix ({ inherit diskoLib; } // args)).impure;
|
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; };
|
testLib = import ./tests.nix { inherit lib makeTest eval-config; };
|
||||||
# like lib.types.oneOf but instead of a list takes an attrset
|
# like lib.types.oneOf but instead of a list takes an attrset
|
||||||
# uses the field "type" to find the correct type in the attrset
|
# uses the field "type" to find the correct type in the attrset
|
||||||
|
|
88
lib/interactive-vm.nix
Normal file
88
lib/interactive-vm.nix
Normal file
|
@ -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
|
||||||
|
'';
|
||||||
|
}
|
|
@ -5,6 +5,9 @@
|
||||||
, name ? "${nixosConfig.config.networking.hostName}-disko-images"
|
, name ? "${nixosConfig.config.networking.hostName}-disko-images"
|
||||||
, extraPostVM ? nixosConfig.config.disko.extraPostVM
|
, extraPostVM ? nixosConfig.config.disko.extraPostVM
|
||||||
, checked ? false
|
, checked ? false
|
||||||
|
, copyNixStore ? true
|
||||||
|
, testMode ? false
|
||||||
|
, extraConfig ? { }
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
vmTools = pkgs.vmTools.override {
|
vmTools = pkgs.vmTools.override {
|
||||||
|
@ -16,10 +19,14 @@ let
|
||||||
};
|
};
|
||||||
cleanedConfig = diskoLib.testLib.prepareDiskoConfig nixosConfig.config diskoLib.testLib.devices;
|
cleanedConfig = diskoLib.testLib.prepareDiskoConfig nixosConfig.config diskoLib.testLib.devices;
|
||||||
systemToInstall = nixosConfig.extendModules {
|
systemToInstall = nixosConfig.extendModules {
|
||||||
modules = [{
|
modules = [
|
||||||
|
extraConfig
|
||||||
|
{
|
||||||
|
disko.testMode = true;
|
||||||
disko.devices = lib.mkForce cleanedConfig.disko.devices;
|
disko.devices = lib.mkForce cleanedConfig.disko.devices;
|
||||||
boot.loader.grub.devices = lib.mkForce cleanedConfig.boot.loader.grub.devices;
|
boot.loader.grub.devices = lib.mkForce cleanedConfig.boot.loader.grub.devices;
|
||||||
}];
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
dependencies = with pkgs; [
|
dependencies = with pkgs; [
|
||||||
bash
|
bash
|
||||||
|
@ -49,6 +56,7 @@ let
|
||||||
rootPaths = [ systemToInstall.config.system.build.toplevel ];
|
rootPaths = [ systemToInstall.config.system.build.toplevel ];
|
||||||
};
|
};
|
||||||
partitioner = ''
|
partitioner = ''
|
||||||
|
set -efux
|
||||||
# running udev, stolen from stage-1.sh
|
# running udev, stolen from stage-1.sh
|
||||||
echo "running udev..."
|
echo "running udev..."
|
||||||
ln -sfn /proc/self/fd /dev/fd
|
ln -sfn /proc/self/fd /dev/fd
|
||||||
|
@ -64,10 +72,13 @@ let
|
||||||
udevadm trigger --action=add
|
udevadm trigger --action=add
|
||||||
udevadm settle
|
udevadm settle
|
||||||
|
|
||||||
|
${lib.optionalString testMode ''
|
||||||
|
export IN_DISKO_TEST=1
|
||||||
|
''}
|
||||||
${systemToInstall.config.system.build.diskoScript}
|
${systemToInstall.config.system.build.diskoScript}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
installer = ''
|
installer = lib.optionalString copyNixStore ''
|
||||||
# populate nix db, so nixos-install doesn't complain
|
# populate nix db, so nixos-install doesn't complain
|
||||||
export NIX_STATE_DIR=${systemToInstall.config.disko.rootMountPoint}/nix/var/nix
|
export NIX_STATE_DIR=${systemToInstall.config.disko.rootMountPoint}/nix/var/nix
|
||||||
nix-store --load-db < "${closureInfo}/registration"
|
nix-store --load-db < "${closureInfo}/registration"
|
||||||
|
|
|
@ -45,7 +45,8 @@ in
|
||||||
};
|
};
|
||||||
priority = lib.mkOption {
|
priority = lib.mkOption {
|
||||||
type = lib.types.int;
|
type = lib.types.int;
|
||||||
default = if partition.config.size or "" == "100%" then
|
default =
|
||||||
|
if partition.config.size or "" == "100%" then
|
||||||
9001
|
9001
|
||||||
else if partition.config.type == "EF02" then
|
else if partition.config.type == "EF02" then
|
||||||
# Boot partition should be created first, because some BIOS implementations require it.
|
# Boot partition should be created first, because some BIOS implementations require it.
|
||||||
|
|
|
@ -114,19 +114,23 @@ in
|
||||||
default = ''
|
default = ''
|
||||||
if ! blkid "${config.device}" >/dev/null || ! (blkid "${config.device}" -o export | grep -q '^TYPE='); then
|
if ! blkid "${config.device}" >/dev/null || ! (blkid "${config.device}" -o export | grep -q '^TYPE='); then
|
||||||
${lib.optionalString config.askPassword ''
|
${lib.optionalString config.askPassword ''
|
||||||
set +x
|
|
||||||
askPassword() {
|
askPassword() {
|
||||||
|
if [ -z ''${IN_DISKO_TEST+x} ]; then
|
||||||
|
set +x
|
||||||
echo "Enter password for ${config.device}: "
|
echo "Enter password for ${config.device}: "
|
||||||
IFS= read -r -s password
|
IFS= read -r -s password
|
||||||
echo "Enter password for ${config.device} again to be safe: "
|
echo "Enter password for ${config.device} again to be safe: "
|
||||||
IFS= read -r -s password_check
|
IFS= read -r -s password_check
|
||||||
export password
|
export password
|
||||||
[ "$password" = "$password_check" ]
|
[ "$password" = "$password_check" ]
|
||||||
|
set -x
|
||||||
|
else
|
||||||
|
export password=disko
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
until askPassword; do
|
until askPassword; do
|
||||||
echo "Passwords did not match, please try again."
|
echo "Passwords did not match, please try again."
|
||||||
done
|
done
|
||||||
set -x
|
|
||||||
''}
|
''}
|
||||||
cryptsetup -q luksFormat ${config.device} ${toString config.extraFormatArgs} ${keyFileArgs}
|
cryptsetup -q luksFormat ${config.device} ${toString config.extraFormatArgs} ${keyFileArgs}
|
||||||
${cryptsetupOpen} --persistent
|
${cryptsetupOpen} --persistent
|
||||||
|
@ -147,11 +151,15 @@ in
|
||||||
dev = ''
|
dev = ''
|
||||||
if ! cryptsetup status ${config.name} >/dev/null 2>/dev/null; then
|
if ! cryptsetup status ${config.name} >/dev/null 2>/dev/null; then
|
||||||
${lib.optionalString config.askPassword ''
|
${lib.optionalString config.askPassword ''
|
||||||
|
if [ -z ''${IN_DISKO_TEST+x} ]; then
|
||||||
set +x
|
set +x
|
||||||
echo "Enter password for ${config.device}"
|
echo "Enter password for ${config.device}"
|
||||||
IFS= read -r -s password
|
IFS= read -r -s password
|
||||||
export password
|
export password
|
||||||
set -x
|
set -x
|
||||||
|
else
|
||||||
|
export password=disko
|
||||||
|
fi
|
||||||
''}
|
''}
|
||||||
${cryptsetupOpen}
|
${cryptsetupOpen}
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -125,7 +125,7 @@ in
|
||||||
_config = lib.mkOption {
|
_config = lib.mkOption {
|
||||||
internal = true;
|
internal = true;
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
default = [ { boot.initrd.kernelModules = kernelModules; } ] ++
|
default = [{ boot.initrd.kernelModules = kernelModules; }] ++
|
||||||
map
|
map
|
||||||
(lv: [
|
(lv: [
|
||||||
(lib.optional (lv.content != null) lv.content._config)
|
(lib.optional (lv.content != null) lv.content._config)
|
||||||
|
|
|
@ -59,7 +59,8 @@
|
||||||
# important to prevent accidental shadowing of mount points
|
# important to prevent accidental shadowing of mount points
|
||||||
# since (create order != mount order)
|
# since (create order != mount order)
|
||||||
# -p creates parents automatically
|
# -p creates parents automatically
|
||||||
default = let
|
default =
|
||||||
|
let
|
||||||
createOptions = (lib.optionalAttrs (config.mountpoint != null) { mountpoint = config.mountpoint; }) // config.options;
|
createOptions = (lib.optionalAttrs (config.mountpoint != null) { mountpoint = config.mountpoint; }) // config.options;
|
||||||
# All options defined as PROP_ONETIME or PROP_ONETIME_DEFAULT in https://github.com/openzfs/zfs/blob/master/module/zcommon/zfs_prop.c
|
# All options defined as PROP_ONETIME or PROP_ONETIME_DEFAULT in https://github.com/openzfs/zfs/blob/master/module/zcommon/zfs_prop.c
|
||||||
onetimeProperties = [
|
onetimeProperties = [
|
||||||
|
@ -74,7 +75,8 @@
|
||||||
];
|
];
|
||||||
updateOptions = builtins.removeAttrs config.options onetimeProperties;
|
updateOptions = builtins.removeAttrs config.options onetimeProperties;
|
||||||
mountpoint = config.options.mountpoint or config.mountpoint;
|
mountpoint = config.options.mountpoint or config.mountpoint;
|
||||||
in ''
|
in
|
||||||
|
''
|
||||||
if ! zfs get type ${config._name} >/dev/null 2>&1; then
|
if ! zfs get type ${config._name} >/dev/null 2>&1; then
|
||||||
zfs create -up ${config._name} \
|
zfs create -up ${config._name} \
|
||||||
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-o ${n}=${v}") (createOptions))}
|
${lib.concatStringsSep " " (lib.mapAttrsToList (n: v: "-o ${n}=${v}") (createOptions))}
|
||||||
|
|
14
module.nix
14
module.nix
|
@ -99,6 +99,15 @@ in
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = false;
|
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 = {
|
tests = {
|
||||||
efi = lib.mkOption {
|
efi = lib.mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -151,6 +160,11 @@ in
|
||||||
extraSystemConfig = cfg.tests.extraConfig;
|
extraSystemConfig = cfg.tests.extraConfig;
|
||||||
extraTestScript = cfg.tests.extraChecks;
|
extraTestScript = cfg.tests.extraChecks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
vmWithDisko = diskoLib.makeVMRunner {
|
||||||
|
inherit pkgs;
|
||||||
|
nixosConfig = args;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue