Merge pull request #299 from nix-community/module-tests

allow using the disko test framework via the module
This commit is contained in:
Jörg Thalheim 2023-07-21 13:37:39 +01:00 committed by GitHub
commit f2248036d2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 138 additions and 79 deletions

View file

@ -9,7 +9,7 @@
partitions = {
boot = {
size = "1M";
type = "EF02";
type = "EF02"; # for grub MBR
};
ESP = {
size = "100M";
@ -37,7 +37,7 @@
partitions = {
boot = {
size = "1M";
type = "EF02";
type = "EF02"; # for grub MBR
};
ESP = {
size = "128M";

View file

@ -12,6 +12,7 @@
name = "ESP";
start = "1M";
end = "128MiB";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";

View file

@ -10,6 +10,7 @@
ESP = {
start = "1MiB";
end = "128MiB";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";

View file

@ -10,7 +10,7 @@
partitions = {
boot = {
size = "1M";
type = "EF02";
type = "EF02"; # for grub MBR
};
root = {
size = "100%";

View file

@ -8,11 +8,12 @@
partitions = {
boot = {
size = "1M";
type = "EF02";
type = "EF02"; # for grub MBR
};
ESP = {
name = "ESP";
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";

View file

@ -9,10 +9,11 @@
partitions = {
boot = {
size = "1M";
type = "EF02";
type = "EF02"; # for grub MBR
};
ESP = {
size = "512M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";

View file

@ -9,6 +9,7 @@
partitions = {
ESP = {
size = "100M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";

View file

@ -9,6 +9,7 @@
partitions = {
boot = {
size = "100M";
type = "EF00";
content = {
type = "mdraid";
name = "boot";
@ -33,6 +34,7 @@
boot = {
name = "boot";
size = "100M";
type = "EF00";
content = {
type = "mdraid";
name = "boot";

View file

@ -9,6 +9,7 @@
partitions = {
boot = {
size = "100M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";

View file

@ -9,7 +9,7 @@
partitions = {
boot = {
size = "1M";
type = "EF02";
type = "EF02"; # for grub MBR
};
mdadm = {
size = "100%";
@ -29,7 +29,7 @@
partitions = {
boot = {
size = "1M";
type = "EF02";
type = "EF02"; # for grub MBR
};
mdadm = {
size = "100%";

View file

@ -9,6 +9,7 @@
partitions = {
ESP = {
size = "100M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";

View file

@ -9,6 +9,7 @@
partitions = {
ESP = {
size = "100M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";

View file

@ -9,6 +9,7 @@
partitions = {
ESP = {
size = "100M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";

View file

@ -9,6 +9,7 @@
partitions = {
ESP = {
size = "64M";
type = "EF00";
content = {
type = "filesystem";
format = "vfat";

View file

@ -407,7 +407,7 @@ let
${cfg.config._create}
'';
system.build.mountScriptNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "disko-mount" ''
mountScriptNoDeps = (diskoLib.writeCheckedBash { inherit pkgs checked; noDeps = true; }) "disko-mount" ''
${cfg.config._mount}
'';

View file

@ -10,21 +10,27 @@ let
# basically changes all the disk.*.devices to something like /dev/vda or /dev/vdb etc.
prepareDiskoConfig = toplevel: devices:
let
cleanedTopLevel = lib.filterAttrsRecursive (n: _: !lib.hasPrefix "_" n) toplevel;
preparedDisks = lib.foldlAttrs (acc: n: v: {
devices = lib.tail acc.devices;
value = acc.value // {
${n} = v // {
grub-devices = acc.grub-devices ++ (lib.optional (lib.any (part: (part.type or "") == "EF02") (lib.attrValues (v.content.partitions or {}))) (lib.head acc.devices));
disks = acc.disks // {
"${n}" = v // {
device = lib.head acc.devices;
content = v.content // { device = lib.head acc.devices; };
};
};
}) {
inherit devices;
value = {};
} toplevel.disko.devices.disk;
grub-devices = [];
disks = {};
} cleanedTopLevel.disko.devices.disk;
in
toplevel // {
disko.devices = toplevel.disko.devices // {
disk = preparedDisks.value;
cleanedTopLevel // {
boot.loader.grub.devices = if (preparedDisks.grub-devices != []) then preparedDisks.grub-devices else [ "nodev" ];
disko.devices = cleanedTopLevel.disko.devices // {
disk = preparedDisks.disks;
};
};
@ -32,13 +38,12 @@ let
makeDiskoTest =
{ name
, disko-config
, nixos-config ? null
, extendModules ? null
, pkgs ? import <nixpkgs> { }
, extraTestScript ? ""
, bootCommands ? ""
, extraInstallerConfig ? { }
, extraSystemConfig ? { }
, grub-devices ? [ "nodev" ]
, efi ? true
, postDisko ? ""
, testMode ? "module" # can be one of direct module cli
@ -50,9 +55,14 @@ let
inherit pkgs;
inherit (pkgs) system;
};
devices = [ "/dev/vda" "/dev/vdb" "/dev/vdc" "/dev/vdd" "/dev/vde" "/dev/vdf"];
# for installation we skip /dev/vda because it is the test runner disk
importedDiskoConfig = import disko-config;
devices = [ "/dev/vda" "/dev/vdb" "/dev/vdc" "/dev/vdd" "/dev/vde" "/dev/vdf"];
importedDiskoConfig = if builtins.isPath disko-config then
import disko-config
else
disko-config;
diskoConfigWithArgs = if builtins.isFunction importedDiskoConfig then
importedDiskoConfig { inherit lib; }
else
@ -63,24 +73,15 @@ let
testConfigBooted = testlib.prepareDiskoConfig diskoConfigWithArgs devices;
tsp-generator = pkgs.callPackage ../. { checked = true; };
tsp-create = (tsp-generator.createScript testConfigInstall) pkgs;
tsp-format = (tsp-generator.formatScript testConfigInstall) pkgs;
tsp-mount = (tsp-generator.mountScript testConfigInstall) pkgs;
tsp-disko = (tsp-generator.diskoScript testConfigInstall) pkgs;
tsp-config = tsp-generator.config testConfigBooted;
num-disks = builtins.length (lib.attrNames testConfigBooted.disko.devices.disk);
installed-system = { modulesPath, ... }: {
# we always want the bind-mounted nix store. otherwise tests take forever
fileSystems."/nix/store" = lib.mkForce {
device = "nix-store";
fsType = "9p";
neededForBoot = true;
options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
};
imports = (if nixos-config != null then [
nixos-config
] else [
(lib.optionalAttrs (testMode == "direct" || testMode == "cli") tsp-config)
installed-system = { modulesPath, ... }: {
imports = [
(lib.optionalAttrs (testMode == "direct") tsp-config)
(lib.optionalAttrs (testMode == "module") {
disko.enableConfig = true;
imports = [
@ -88,34 +89,56 @@ let
testConfigBooted
];
})
{ # config for tests to make them run faster or work at all
documentation.enable = false;
hardware.enableAllFirmware = lib.mkForce false;
networking.hostId = "8425e349"; # from profiles/base.nix, needed for zfs
boot.zfs.devNodes = "/dev/disk/by-uuid"; # needed because /dev/disk/by-id is empty in qemu-vms
boot.initrd.preDeviceCommands = ''
echo -n 'secretsecret' > /tmp/secret.key
'';
boot.consoleLogLevel = lib.mkForce 100;
boot.loader.grub = {
devices = grub-devices;
efiSupport = efi;
efiInstallAsRemovable = efi;
};
}
]) ++ [
(modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests
(modulesPath + "/profiles/qemu-guest.nix")
extraSystemConfig
];
# config for tests to make them run faster or work at all
documentation.enable = false;
hardware.enableAllFirmware = lib.mkForce false;
networking.hostId = "8425e349"; # from profiles/base.nix, needed for zfs
boot.initrd.preDeviceCommands = ''
echo -n 'secretsecret' > /tmp/secret.key
'';
boot.consoleLogLevel = lib.mkForce 100;
boot.loader.systemd-boot.enable = lib.mkDefault efi;
};
installed-system-eval = eval-config {
modules = [ installed-system ];
inherit (pkgs) system;
};
installedTopLevel = installed-system-eval.config.system.build.toplevel;
installedTopLevel = ((if extendModules != null then extendModules else installed-system-eval.extendModules) {
modules = [{
imports = [
extraSystemConfig
({ modulesPath, ... }: {
imports = [
(modulesPath + "/testing/test-instrumentation.nix") # we need these 2 modules always to be able to run the tests
(modulesPath + "/profiles/qemu-guest.nix")
];
disko.devices = lib.mkForce testConfigBooted.disko.devices;
})
];
# since we boot on a different machine, the efi payload needs to be portable
boot.loader.grub.efiInstallAsRemovable = efi;
boot.loader.grub.efiSupport = efi;
boot.loader.systemd-boot.graceful = true;
# we always want the bind-mounted nix store. otherwise tests take forever
fileSystems."/nix/store" = lib.mkForce {
device = "nix-store";
fsType = "9p";
neededForBoot = true;
options = [ "trans=virtio" "version=9p2000.L" "cache=loose" ];
};
boot.zfs.devNodes = "/dev/disk/by-uuid"; # needed because /dev/disk/by-id is empty in qemu-vms
# grub will install to these devices, we need to force those or we are offset by 1
boot.loader.grub.devices = lib.mkForce testConfigInstall.boot.loader.grub.devices;
}];
}).config.system.build.toplevel;
in
makeTest' {
name = "disko-${name}";
@ -132,13 +155,6 @@ let
devices = testConfigInstall.disko.devices;
};
})
(lib.optionalAttrs (testMode == "cli") {
imports = [ (modulesPath + "/installer/cd-dvd/channel.nix") ];
system.extraDependencies = [
((pkgs.callPackage ../. { checked = true; }).createScript testConfigInstall pkgs)
((pkgs.callPackage ../. { checked = true; }).mountScript testConfigInstall pkgs)
];
})
(modulesPath + "/profiles/base.nix")
(modulesPath + "/profiles/minimal.nix")
extraInstallerConfig
@ -156,6 +172,10 @@ let
connect-timeout = 1;
};
networking.hostId = lib.mkIf (
(testConfigInstall ? networking.hostId) && (testConfigInstall.networking.hostId != null)
) testConfigInstall.networking.hostId;
virtualisation.emptyDiskImages = builtins.genList (_: 4096) num-disks;
# useful for debugging via repl
@ -181,28 +201,19 @@ let
machine.succeed("echo -n 'additionalSecret' > /tmp/additionalSecret.key")
machine.succeed("echo -n 'secretsecret' > /tmp/secret.key")
${lib.optionalString (testMode == "direct") ''
machine.succeed("${tsp-create}")
# running direct mode
machine.succeed("${tsp-format}")
machine.succeed("${tsp-mount}")
machine.succeed("${tsp-mount}") # verify that the command is idempotent
machine.succeed("${tsp-disko}") # verify that we can destroy and recreate
''}
${lib.optionalString (testMode == "module") ''
# running module mode
machine.succeed("${nodes.machine.system.build.formatScript}")
machine.succeed("${nodes.machine.system.build.mountScript}")
machine.succeed("${nodes.machine.system.build.mountScript}") # verify that the command is idempotent
machine.succeed("${nodes.machine.system.build.diskoScript}") # verify that we can destroy and recreate again
''}
${lib.optionalString (testMode == "cli") ''
# TODO use the disko cli here
# machine.succeed("${../.}/disko --no-pkgs --mode create ${disko-config}")
# machine.succeed("${../.}/disko --no-pkgs --mode mount ${disko-config}")
# machine.succeed("${../.}/disko --no-pkgs --mode mount ${disko-config}") # verify that the command is idempotent
# machine.succeed("${../.}/disko --no-pkgs --mode zap_create_mount ${disko-config}") # verify that we can destroy and recreate again
machine.succeed("${tsp-create}")
machine.succeed("${tsp-mount}")
machine.succeed("${tsp-mount}") # verify that the command is idempotent
machine.succeed("${tsp-disko}") # verify that we can destroy and recreate
''}
${postDisko}

View file

@ -129,11 +129,14 @@ in
_config = lib.mkOption {
internal = true;
readOnly = true;
default = map
default = (map
(partition:
lib.optional (partition.content != null) partition.content._config
)
(lib.attrValues config.partitions);
(lib.attrValues config.partitions))
++ (lib.optional (lib.any (part: part.type == "EF02") (lib.attrValues config.partitions)) {
boot.loader.grub.devices = [ config.device ];
});
description = "NixOS configuration";
};
_pkgs = lib.mkOption {

View file

@ -1,8 +1,10 @@
{ config, lib, pkgs, ... }:
{ config, lib, pkgs, extendModules, ... }@args:
let
diskoLib = import ./lib {
inherit lib;
rootMountPoint = config.disko.rootMountPoint;
makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix");
eval-config = import (pkgs.path + "/nixos/lib/eval-config.nix");
};
cfg = config.disko;
in
@ -34,6 +36,16 @@ in
type = lib.types.bool;
default = false;
};
tests = {
efi = lib.mkOption {
description = ''
Whether efi is enabled for the `system.build.installTest`.
We try to automatically detect efi based on the configured bootloader.
'';
type = lib.types.bool;
default = config.boot.loader.systemd-boot.enable || config.boot.loader.grub.efiSupport;
};
};
};
config = lib.mkIf (cfg.devices.disk != { }) {
system.build = (cfg.devices._scripts { inherit pkgs; checked = cfg.checkScripts; }) // {
@ -41,6 +53,14 @@ in
# we keep this old outputs for compatibility
disko = builtins.trace "the .disko output is deprecated, plase use .diskoScript instead" cfg.devices._scripts.diskoScript;
diskoNoDeps = builtins.trace "the .diskoNoDeps output is deprecated, plase use .diskoScriptNoDeps instead" cfg.devices._scripts.diskoScriptNoDeps;
installTest = diskoLib.testLib.makeDiskoTest {
inherit extendModules pkgs;
name = "${config.networking.hostName}-disko";
disko-config = builtins.removeAttrs config ["_module"];
testMode = "direct";
efi = cfg.tests.efi;
};
};

View file

@ -9,4 +9,8 @@ makeDiskoTest {
machine.succeed("test -b /dev/md/boot");
machine.succeed("mountpoint /boot");
'';
extraSystemConfig = {
# sadly systemd-boot fails to install to a raid /boot device
boot.loader.systemd-boot.enable = false;
};
}

View file

@ -8,7 +8,7 @@ makeDiskoTest {
extraSystemConfig = {
fileSystems."/zfs_legacy_fs".options = [ "nofail" ]; # TODO find out why we need this!
};
testMode = "cli";
testMode = "direct";
extraTestScript = ''
machine.succeed("test -b /dev/zroot/zfs_testvolume");
machine.succeed("test -b /dev/md/raid1p1");
@ -19,7 +19,15 @@ makeDiskoTest {
machine.succeed("mountpoint /ext4onzfs");
machine.succeed("mountpoint /ext4_on_lvm");
'';
extraSystemConfig = {
imports = [
../module.nix
];
};
extraInstallerConfig = {
boot.kernelModules = [ "dm-raid" "dm-mirror" ];
imports = [
../module.nix
];
};
}

View file

@ -9,5 +9,4 @@ makeDiskoTest {
machine.succeed("mountpoint /");
'';
efi = false;
grub-devices = [ "/dev/vdb" ];
}

View file

@ -11,4 +11,8 @@ makeDiskoTest {
extraInstallerConfig = {
boot.kernelModules = [ "dm-raid0" "dm-mirror" ];
};
extraSystemConfig = {
# sadly systemd-boot fails to install to a raid /boot device
boot.loader.systemd-boot.enable = false;
};
}

View file

@ -10,5 +10,4 @@ makeDiskoTest {
machine.succeed("mountpoint /");
'';
efi = false;
grub-devices = [ "/dev/vdb" "/dev/vdc" ];
}

View file

@ -9,5 +9,4 @@ makeDiskoTest {
machine.succeed("mountpoint /");
'';
efi = false;
grub-devices = [ "/dev/vdb" ];
}