From 1237ac36db1a457ae561134d191d2924a9ce5ffc Mon Sep 17 00:00:00 2001 From: lassulus Date: Thu, 25 Aug 2022 13:13:20 +0200 Subject: [PATCH 01/13] fix mdadm mounting, move test to mdadm test --- default.nix | 5 +++-- example/{raid.nix => mdadm.nix} | 1 - tests/{test.nix => mdadm.nix} | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) rename example/{raid.nix => mdadm.nix} (99%) rename tests/{test.nix => mdadm.nix} (90%) diff --git a/default.nix b/default.nix index d20b67e..567324c 100644 --- a/default.nix +++ b/default.nix @@ -137,7 +137,7 @@ let }; mount.devices = q: x: let - z = foldl' recursiveUpdate {} (mapAttrsToList (name: mount-f { device = "/dev/${name}"; }) x.content); + z = foldl' recursiveUpdate {} (mapAttrsToList (name: mount-f { device = "/dev/${name}"; inherit name; }) x.content); # attrValues returns values sorted by name. This is important, because it # ensures that "/" is processed before "/foo" etc. in '' @@ -168,8 +168,9 @@ let mount.noop = q: x: {}; + mount.mdadm = q: x: + mount-f { device = "/dev/md/${q.name}"; } x.content; # TODO maybe we need to do something here? - mount.mdadm = mount.noop; mount.mdraid = mount.noop; mount.partition = q: x: diff --git a/example/raid.nix b/example/mdadm.nix similarity index 99% rename from example/raid.nix rename to example/mdadm.nix index 490ea01..cb0ad91 100644 --- a/example/raid.nix +++ b/example/mdadm.nix @@ -53,7 +53,6 @@ }; } ]; - }; }; }; diff --git a/tests/test.nix b/tests/mdadm.nix similarity index 90% rename from tests/test.nix rename to tests/mdadm.nix index 1384590..37465e4 100644 --- a/tests/test.nix +++ b/tests/mdadm.nix @@ -7,7 +7,7 @@ let inherit pkgs; inherit (pkgs) system; }; - disko-config = import ../example/raid.nix; + disko-config = import ../example/mdadm.nix; tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); in makeTest' { @@ -34,5 +34,6 @@ in makeTest' { machine.succeed("${tsp-mount}"); machine.succeed("${tsp-mount}"); # verify that the command is idempotent machine.succeed("test -b /dev/md/raid1"); + machine.succeed("grep -qs '/mnt/raid' /proc/mounts"); ''; } From dd99e29edc994056d5f700e24a75406115e98dff Mon Sep 17 00:00:00 2001 From: lassulus Date: Thu, 25 Aug 2022 13:14:07 +0200 Subject: [PATCH 02/13] fix cryptsetup luksOpen idempotency, add luks-lvm test --- default.nix | 2 +- example/luks-lvm.nix | 78 ++++++++++++++++++++++++++++++++++++++++++++ tests/luks-lvm.nix | 39 ++++++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 example/luks-lvm.nix create mode 100644 tests/luks-lvm.nix diff --git a/default.nix b/default.nix index 567324c..39e455a 100644 --- a/default.nix +++ b/default.nix @@ -151,7 +151,7 @@ let recursiveUpdate (mount-f { device = "/dev/mapper/${x.name}"; } x.content) {luks.${q.device} = '' - cryptsetup luksOpen ${q.device} ${x.name} ${if builtins.hasAttr "keyfile" x then "--key-file " + x.keyfile else ""} + cryptsetup status ${x.name} >/dev/null 2>/dev/null || cryptsetup luksOpen ${q.device} ${x.name} ${if builtins.hasAttr "keyfile" x then "--key-file " + x.keyfile else ""} '';} ); diff --git a/example/luks-lvm.nix b/example/luks-lvm.nix new file mode 100644 index 0000000..d0b4d26 --- /dev/null +++ b/example/luks-lvm.nix @@ -0,0 +1,78 @@ +{ + type = "devices"; + content = { + vdb = { + type = "table"; + format = "gpt"; + partitions = [ + { + type = "partition"; + part-type = "ESP"; + start = "1MiB"; + end = "100MiB"; + fs-type = "FAT32"; + bootable = true; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + options = [ + "defaults" + ]; + }; + } + { + type = "partition"; + part-type = "primary"; + start = "100MiB"; + end = "100%"; + content = { + type = "luks"; + algo = "aes-xts..."; + name = "crypted"; + keyfile = "/tmp/secret.key"; + extraArgs = [ + "--hash sha512" + "--iter-time 5000" + ]; + content = { + type = "lvm"; + name = "pool"; + lvs = { + root = { + type = "lv"; + size = "100M"; + mountpoint = "/"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + options = [ + "defaults" + ]; + }; + }; + home = { + type = "lv"; + size = "10M"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/home"; + }; + }; + raw = { + type = "lv"; + size = "10M"; + content = { + type = "noop"; + }; + }; + }; + }; + }; + } + ]; + }; + }; +} diff --git a/tests/luks-lvm.nix b/tests/luks-lvm.nix new file mode 100644 index 0000000..3e2c5f2 --- /dev/null +++ b/tests/luks-lvm.nix @@ -0,0 +1,39 @@ +{ makeTest ? import +, pkgs ? (import {}) +}: +let + makeTest' = args: + makeTest args { + inherit pkgs; + inherit (pkgs) system; + }; + disko-config = import ../example/luks-lvm.nix; + tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); + tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); +in makeTest' { + name = "disko"; + + nodes.machine = + { config, pkgs, modulesPath, ... }: + + { + imports = [ + (modulesPath + "/profiles/installation-device.nix") + (modulesPath + "/profiles/base.nix") + ]; + + # speed-up eval + documentation.enable = false; + + virtualisation.emptyDiskImages = [ 512 ]; + }; + + testScript = '' + machine.succeed("echo 'secret' > /tmp/secret.key"); + machine.succeed("${tsp-create}"); + machine.succeed("${tsp-mount}"); + machine.succeed("${tsp-mount}"); # verify that the command is idempotent + machine.succeed("cryptsetup isLuks /dev/vdb2"); + machine.succeed("grep -qs '/mnt/home' /proc/mounts"); + ''; +} From 83fb8f661eb574f32fad1d51bbebd0c36595db01 Mon Sep 17 00:00:00 2001 From: lassulus Date: Thu, 25 Aug 2022 15:08:26 +0200 Subject: [PATCH 03/13] add zfs support/test/example --- default.nix | 65 ++++++++++++++++++++++++++++++++++++++++++++++--- example/zfs.nix | 41 +++++++++++++++++++++++++++++++ tests/zfs.nix | 40 ++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 example/zfs.nix create mode 100644 tests/zfs.nix diff --git a/default.nix b/default.nix index 39e455a..4a9a033 100644 --- a/default.nix +++ b/default.nix @@ -38,6 +38,13 @@ let }; }; + config.zfs_filesystem = q: x: { + fileSystems.${x.mountpoint} = { + device = q.device; + fsType = "zfs"; + }; + }; + config.devices = q: x: foldl' recursiveUpdate {} (mapAttrsToList (name: config-f { device = "/dev/${name}"; }) x.content); @@ -67,8 +74,8 @@ let ''; create.devices = q: x: let - raid-devices = lib.filterAttrs (_: dev: dev.type == "mdadm") x.content; - other-devices = lib.filterAttrs (_: dev: dev.type != "mdadm") x.content; + raid-devices = lib.filterAttrs (_: dev: dev.type == "mdadm" || dev.type == "zpool") x.content; + other-devices = lib.filterAttrs (_: dev: dev.type != "mdadm" && dev.type != "zpool") x.content; in '' ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; }) other-devices)} ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; name = name; }) raid-devices)} @@ -125,6 +132,32 @@ let ${concatStrings (imap (index: create-f (q // { inherit index; })) x.partitions)} ''; + create.zfs = q: x: '' + ZFSDEVICES_${x.pool}="''${ZFSDEVICES_${x.pool}:-}${q.device} " + ''; + + create.zfs_filesystem = q: x: '' + zfs create ${q.pool}/${x.name} \ + ${lib.optionalString (isAttrs x.options or null) (concatStringsSep " " (mapAttrsToList (n: v: "-o ${n}=${v}") x.options))} + ''; + + create.zfs_volume = q: x: '' + zfs create ${q.pool}/${x.name} \ + -V ${x.size} \ + ${lib.optionalString (isAttrs x.options or null) (concatStringsSep " " (mapAttrsToList (n: v: "-o ${n}=${v}") x.options))} + udevadm trigger --subsystem-match=block; udevadm settle + ${create-f { device = "/dev/zvol/${q.pool}/${x.name}"; } x.content} + ''; + + create.zpool = q: x: '' + zpool create ${q.name} \ + ${lib.optionalString (!isNull x.mode || x.mode != "stripe") x.mode} \ + ${lib.optionalString (isAttrs x.options or null) (concatStringsSep " " (mapAttrsToList (n: v: "-o ${n}=${v}") x.options))} \ + ${lib.optionalString (isAttrs x.rootFsOptions or null) (concatStringsSep " " (mapAttrsToList (n: v: "-O ${n}=${v}") x.rootFsOptions))} \ + ''${ZFSDEVICES_${q.name}} + ${concatMapStrings (create-f (q // { pool = q.name; })) x.datasets} + ''; + mount-f = q: x: mount.${x.type} q x; @@ -144,6 +177,8 @@ let ${optionalString (hasAttr "table" z) (concatStringsSep "\n" (attrValues z.table))} ${optionalString (hasAttr "luks" z) (concatStringsSep "\n" (attrValues z.luks))} ${optionalString (hasAttr "lvm" z) (concatStringsSep "\n" (attrValues z.lvm))} + ${optionalString (hasAttr "zpool" z) (concatStringsSep "\n" (attrValues z.zpool))} + ${optionalString (hasAttr "zfs" z) (concatStringsSep "\n" (attrValues z.zfs))} ${optionalString (hasAttr "fs" z) (concatStringsSep "\n" (attrValues z.fs))} ''; @@ -170,7 +205,6 @@ let mount.mdadm = q: x: mount-f { device = "/dev/md/${q.name}"; } x.content; - # TODO maybe we need to do something here? mount.mdraid = mount.noop; mount.partition = q: x: @@ -181,6 +215,31 @@ let (foldl' recursiveUpdate {} (imap (index: mount-f (q // { inherit index; device = helper.device-id q.device; })) x.partitions)) {table.${q.device} = helper.find-device q.device;} ); + + mount.zfs = mount.noop; + + mount.zpool = q: x: ( + recursiveUpdate + (foldl' recursiveUpdate {} (map (mount-f (q // { pool = q.name; })) x.datasets)) + {zpool.${q.device} = '' + zpool list '${q.name}' >/dev/null 2>/dev/null || zpool import '${q.name}' + '';} + ); + + mount.zfs_filesystem = q: x: { + zfs.${x.mountpoint} = '' + if ! findmnt '${q.pool}/${x.name}' /mnt${x.mountpoint} > /dev/null 2>&1; then + mount \ + ${lib.optionalString ((x.options.mountpoint or "") != "legacy") "-o zfsutil"} \ + -t zfs ${q.pool}/${x.name} /mnt${x.mountpoint} \ + -o X-mount.mkdir + fi + ''; + }; + + mount.zfs_volume = q: x: + mount-f { device = "/dev/zvol/${q.pool}/${x.name}"; } x.content; + in { config = config-f {}; create = cfg: '' diff --git a/example/zfs.nix b/example/zfs.nix new file mode 100644 index 0000000..e344289 --- /dev/null +++ b/example/zfs.nix @@ -0,0 +1,41 @@ +{ + type = "devices"; + content = { + vdb = { + type = "zfs"; + pool = "zroot"; + }; + vdc = { + type = "zfs"; + pool = "zroot"; + }; + zroot = { + type = "zpool"; + mode = "mirror"; + datasets = [ + { + type = "zfs_filesystem"; + name = "zfs_fs"; + mountpoint = "/zfs_fs"; + } + { + type = "zfs_filesystem"; + name = "zfs_legacy_fs"; + options.mountpoint = "legacy"; + mountpoint = "/zfs_legacy_fs"; + } + { + type = "zfs_volume"; + name = "zfs_testvolume"; + size = "10M"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/ext4onzfs"; + }; + } + ]; + }; + }; +} + diff --git a/tests/zfs.nix b/tests/zfs.nix new file mode 100644 index 0000000..bc807bc --- /dev/null +++ b/tests/zfs.nix @@ -0,0 +1,40 @@ +{ makeTest ? import +, pkgs ? (import {}) +}: +let + makeTest' = args: + makeTest args { + inherit pkgs; + inherit (pkgs) system; + }; + disko-config = import ../example/zfs.nix; + tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); + tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); +in makeTest' { + name = "disko"; + + nodes.machine = + { config, pkgs, modulesPath, ... }: + + { + imports = [ + (modulesPath + "/profiles/installation-device.nix") + (modulesPath + "/profiles/base.nix") + ]; + + # speed-up eval + documentation.enable = false; + + virtualisation.emptyDiskImages = [ 512 512 ]; + }; + + testScript = '' + machine.succeed("echo 'secret' > /tmp/secret.key"); + machine.succeed("${tsp-create}"); + machine.succeed("${tsp-mount}"); + machine.succeed("${tsp-mount}"); # verify that the command is idempotent + machine.succeed("test -b /dev/zvol/zroot/zfs_testvolume"); + machine.succeed("grep -qs '/mnt/ext4onzfs' /proc/mounts"); + ''; +} + From 0ffaac7913565e497c5193d45fd50de78f3241f0 Mon Sep 17 00:00:00 2001 From: lassulus Date: Thu, 25 Aug 2022 15:16:26 +0200 Subject: [PATCH 04/13] run all tests on ci --- ci.nix | 2 +- flake.nix | 6 +++--- tests/default.nix | 8 ++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 tests/default.nix diff --git a/ci.nix b/ci.nix index cdb3198..3c49ab1 100644 --- a/ci.nix +++ b/ci.nix @@ -3,6 +3,6 @@ let in { test = pkgs.writeScript "test" '' #!/bin/sh - nix-build "${toString ./tests/test.nix}"; + nix-build "${toString ./tests}"; ''; } diff --git a/flake.nix b/flake.nix index 3e7263a..488933d 100644 --- a/flake.nix +++ b/flake.nix @@ -9,11 +9,11 @@ }; checks.x86_64-linux = let pkgs = nixpkgs.legacyPackages.x86_64-linux; - in { + in # Run tests: nix flake check -L - nixos-test = pkgs.callPackage ./tests/test.nix { + import ./tests { + inherit pkgs; makeTest = import (pkgs.path + "/nixos/tests/make-test-python.nix"); }; - }; }; } diff --git a/tests/default.nix b/tests/default.nix new file mode 100644 index 0000000..3b24ba6 --- /dev/null +++ b/tests/default.nix @@ -0,0 +1,8 @@ +{ makeTest ? import +, pkgs ? (import {}) +}@args: +{ + luks-lvm = import ./luks-lvm.nix args; + mdadm = import ./mdadm.nix args; + zfs = import ./zfs.nix args; +} From dadc49133042834dada6eafc98dcb1f7f2c5e43b Mon Sep 17 00:00:00 2001 From: lassulus Date: Thu, 25 Aug 2022 18:36:56 +0200 Subject: [PATCH 05/13] add lvm raid --- default.nix | 43 +++++++++++++++++---------- example/luks-lvm.nix | 69 +++++++++++++++++++++++--------------------- example/lvm-raid.nix | 66 ++++++++++++++++++++++++++++++++++++++++++ tests/lvm-raid.nix | 39 +++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 49 deletions(-) create mode 100644 example/lvm-raid.nix create mode 100644 tests/lvm-raid.nix diff --git a/default.nix b/default.nix index 4a9a033..19333fc 100644 --- a/default.nix +++ b/default.nix @@ -52,10 +52,10 @@ let boot.initrd.luks.devices.${x.name}.device = q.device; } // config-f { device = "/dev/mapper/${x.name}"; } x.content; - config.lv = q: x: - config-f { device = "/dev/mapper/${q.vgname}-${q.name}"; } x.content; + config.lvm_lv = q: x: + config-f { device = "/dev/${q.vgname}/${q.name}"; } x.content; - config.lvm = q: x: + config.lvm_vg = q: x: foldl' recursiveUpdate {} (mapAttrsToList (name: config-f { inherit name; vgname = x.name; }) x.lvs); config.noop = q: x: {}; @@ -74,8 +74,8 @@ let ''; create.devices = q: x: let - raid-devices = lib.filterAttrs (_: dev: dev.type == "mdadm" || dev.type == "zpool") x.content; - other-devices = lib.filterAttrs (_: dev: dev.type != "mdadm" && dev.type != "zpool") x.content; + raid-devices = lib.filterAttrs (_: dev: dev.type == "mdadm" || dev.type == "zpool" || dev.type == "lvm_vg") x.content; + other-devices = lib.filterAttrs (_: dev: dev.type != "mdadm" && dev.type != "zpool" && dev.type != "lvm_vg") x.content; in '' ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; }) other-devices)} ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; name = name; }) raid-devices)} @@ -98,15 +98,24 @@ let ${create-f { device = "/dev/mapper/${x.name}"; } x.content} ''; - create.lv = q: x: '' - lvcreate ${if hasInfix "%" x.size then "-l" else "-L"} ${x.size} -n ${q.name} ${q.vgname} - ${create-f { device = "/dev/mapper/${q.vgname}-${q.name}"; } x.content} + create.lvm_pv = q: x: '' + pvcreate ${q.device} + LVMDEVICES_${x.vg}="''${LVMDEVICES_${x.vg}:-}${q.device} " ''; - create.lvm = q: x: '' - pvcreate ${q.device} - vgcreate ${x.name} ${q.device} - ${concatStrings (mapAttrsToList (name: create-f { inherit name; vgname = x.name; }) x.lvs)} + create.lvm_lv = q: x: '' + lvcreate \ + ${if hasInfix "%" x.size then "-l" else "-L"} ${x.size} \ + -n ${q.name} \ + ${lib.optionalString (!isNull x.lvm_type or null) "--type=${x.lvm_type}"} \ + ${lib.optionalString (!isNull x.extraArgs or null) x.extraArgs} \ + ${q.vgname} + ${create-f { device = "/dev/${q.vgname}/${q.name}"; } x.content} + ''; + + create.lvm_vg = q: x: '' + vgcreate ${q.name} $LVMDEVICES_${q.name} + ${concatStrings (mapAttrsToList (name: create-f { inherit name; vgname = q.name; }) x.lvs)} ''; create.noop = q: x: ""; @@ -190,17 +199,19 @@ let '';} ); - mount.lv = q: x: - mount-f { device = "/dev/mapper/${q.vgname}-${q.name}"; } x.content; + mount.lvm_lv = q: x: + mount-f { device = "/dev/${q.vgname}/${q.name}"; } x.content; - mount.lvm = q: x: ( + mount.lvm_vg = q: x: ( recursiveUpdate - (foldl' recursiveUpdate {} (mapAttrsToList (name: mount-f { inherit name; vgname = x.name; }) x.lvs)) + (foldl' recursiveUpdate {} (mapAttrsToList (name: mount-f { inherit name; vgname = q.name; }) x.lvs)) {lvm.${q.device} = '' vgchange -a y '';} ); + mount.lvm_pv = mount.noop; + mount.noop = q: x: {}; mount.mdadm = q: x: diff --git a/example/luks-lvm.nix b/example/luks-lvm.nix index d0b4d26..22c029e 100644 --- a/example/luks-lvm.nix +++ b/example/luks-lvm.nix @@ -36,43 +36,46 @@ "--iter-time 5000" ]; content = { - type = "lvm"; - name = "pool"; - lvs = { - root = { - type = "lv"; - size = "100M"; - mountpoint = "/"; - content = { - type = "filesystem"; - format = "ext4"; - mountpoint = "/"; - options = [ - "defaults" - ]; - }; - }; - home = { - type = "lv"; - size = "10M"; - content = { - type = "filesystem"; - format = "ext4"; - mountpoint = "/home"; - }; - }; - raw = { - type = "lv"; - size = "10M"; - content = { - type = "noop"; - }; - }; - }; + type = "lvm_pv"; + vg = "pool"; }; }; } ]; }; + pool = { + type = "lvm_vg"; + lvs = { + root = { + type = "lvm_lv"; + size = "100M"; + mountpoint = "/"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + options = [ + "defaults" + ]; + }; + }; + home = { + type = "lvm_lv"; + size = "10M"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/home"; + }; + }; + raw = { + type = "lvm_lv"; + size = "10M"; + content = { + type = "noop"; + }; + }; + }; + }; }; } diff --git a/example/lvm-raid.nix b/example/lvm-raid.nix new file mode 100644 index 0000000..48930ec --- /dev/null +++ b/example/lvm-raid.nix @@ -0,0 +1,66 @@ +{ + type = "devices"; + content = { + vdb = { + type = "table"; + format = "gpt"; + partitions = [ + { + type = "partition"; + part-type = "primary"; + start = "0%"; + end = "100%"; + content = { + type = "lvm_pv"; + vg = "pool"; + }; + } + ]; + }; + vdc = { + type = "table"; + format = "gpt"; + partitions = [ + { + type = "partition"; + part-type = "primary"; + start = "0%"; + end = "100%"; + content = { + type = "lvm_pv"; + vg = "pool"; + }; + } + ]; + }; + pool = { + type = "lvm_vg"; + lvs = { + root = { + type = "lvm_lv"; + size = "100M"; + mountpoint = "/"; + lvm_type = "mirror"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + options = [ + "defaults" + ]; + }; + }; + home = { + type = "lvm_lv"; + size = "10M"; + lvm_type = "raid0"; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/home"; + }; + }; + }; + }; + }; +} diff --git a/tests/lvm-raid.nix b/tests/lvm-raid.nix new file mode 100644 index 0000000..5165645 --- /dev/null +++ b/tests/lvm-raid.nix @@ -0,0 +1,39 @@ +{ makeTest ? import +, pkgs ? (import {}) +}: +let + makeTest' = args: + makeTest args { + inherit pkgs; + inherit (pkgs) system; + }; + disko-config = import ../example/lvm-raid.nix; + tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); + tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); +in makeTest' { + name = "disko"; + + nodes.machine = + { config, pkgs, modulesPath, ... }: + + { + imports = [ + (modulesPath + "/profiles/installation-device.nix") + (modulesPath + "/profiles/base.nix") + ]; + + # speed-up eval + documentation.enable = false; + + virtualisation.emptyDiskImages = [ 512 512 ]; + boot.kernelModules = [ "dm-raid" "dm-mirror" ]; + }; + + testScript = '' + machine.succeed("echo 'secret' > /tmp/secret.key"); + machine.succeed("${tsp-create}"); + machine.succeed("${tsp-mount}"); + machine.succeed("${tsp-mount}"); # verify that the command is idempotent + machine.succeed("grep -qs '/mnt/home' /proc/mounts"); + ''; +} From 81e704b638a8a7158bd9106b7fd592fb982d1864 Mon Sep 17 00:00:00 2001 From: lassulus Date: Thu, 25 Aug 2022 21:46:17 +0200 Subject: [PATCH 06/13] add btrfs subvolumes --- default.nix | 14 +++++++++++++ example/btrfs-subvolumes.nix | 26 +++++++++++++++++++++++ tests/btrfs-subvolumes.nix | 40 ++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 example/btrfs-subvolumes.nix create mode 100644 tests/btrfs-subvolumes.nix diff --git a/default.nix b/default.nix index 19333fc..c6ec47c 100644 --- a/default.nix +++ b/default.nix @@ -69,6 +69,18 @@ let create-f = q: x: create.${x.type} q x; + create.btrfs = q: x: '' + mkfs.btrfs ${q.device} + ${lib.optionalString (!isNull x.subvolumes or null) '' + MNTPOINT=$(mktemp -d) + ( + mount ${q.device} "$MNTPOINT" + trap 'umount $MNTPOINT; rm -rf $MNTPOINT' EXIT + ${concatMapStringsSep "\n" (subvolume: "btrfs subvolume create \"$MNTPOINT\"/${subvolume}") x.subvolumes} + ) + ''} + ''; + create.filesystem = q: x: '' mkfs.${x.format} ${q.device} ''; @@ -178,6 +190,8 @@ let ''; }; + mount.btrfs = mount.filesystem; + mount.devices = q: x: let z = foldl' recursiveUpdate {} (mapAttrsToList (name: mount-f { device = "/dev/${name}"; inherit name; }) x.content); # attrValues returns values sorted by name. This is important, because it diff --git a/example/btrfs-subvolumes.nix b/example/btrfs-subvolumes.nix new file mode 100644 index 0000000..0124c86 --- /dev/null +++ b/example/btrfs-subvolumes.nix @@ -0,0 +1,26 @@ +{ + type = "devices"; + content = { + vdb = { + type = "table"; + format = "gpt"; + partitions = [ + { + type = "partition"; + part-type = "primary"; + start = "0%"; + end = "100%"; + content = { + type = "btrfs"; + mountpoint = "/"; + subvolumes = [ + "/home" + "/test" + ]; + }; + } + ]; + }; + }; +} + diff --git a/tests/btrfs-subvolumes.nix b/tests/btrfs-subvolumes.nix new file mode 100644 index 0000000..6527329 --- /dev/null +++ b/tests/btrfs-subvolumes.nix @@ -0,0 +1,40 @@ +{ makeTest ? import +, pkgs ? (import {}) +}: +let + makeTest' = args: + makeTest args { + inherit pkgs; + inherit (pkgs) system; + }; + disko-config = import ../example/btrfs-subvolumes.nix; + tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); + tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); +in makeTest' { + name = "disko"; + + nodes.machine = + { config, pkgs, modulesPath, ... }: + + { + imports = [ + (modulesPath + "/profiles/installation-device.nix") + (modulesPath + "/profiles/base.nix") + ]; + + # speed-up eval + documentation.enable = false; + + virtualisation.emptyDiskImages = [ 512 ]; + }; + + testScript = '' + machine.succeed("echo 'secret' > /tmp/secret.key"); + machine.succeed("${tsp-create}"); + machine.succeed("${tsp-mount}"); + machine.succeed("${tsp-mount}"); # verify that the command is idempotent + machine.succeed("test -e /mnt/test"); + machine.succeed("btrfs subvolume list /mnt | grep -qs 'path test$'"); + ''; +} + From e4836108d51a4339983c0f88fb4e995e4109bb21 Mon Sep 17 00:00:00 2001 From: lassulus Date: Thu, 25 Aug 2022 21:46:48 +0200 Subject: [PATCH 07/13] create.filesystem: support extraArgs --- default.nix | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/default.nix b/default.nix index c6ec47c..d98e8d6 100644 --- a/default.nix +++ b/default.nix @@ -82,7 +82,9 @@ let ''; create.filesystem = q: x: '' - mkfs.${x.format} ${q.device} + mkfs.${x.format} \ + ${lib.optionalString (!isNull x.extraArgs or null) x.extraArgs} \ + ${q.device} ''; create.devices = q: x: let From 1ac0e76b15a3b13c83d25a78b32dee4070a584b7 Mon Sep 17 00:00:00 2001 From: lassulus Date: Thu, 25 Aug 2022 23:12:49 +0200 Subject: [PATCH 08/13] tests: add lib, autoimport tests --- tests/btrfs-subvolumes.nix | 37 ++++-------------------------- tests/default.nix | 17 ++++++++++---- tests/lib.nix | 47 ++++++++++++++++++++++++++++++++++++++ tests/luks-lvm.nix | 37 ++++-------------------------- tests/lvm-raid.nix | 41 ++++++--------------------------- tests/mdadm.nix | 37 ++++-------------------------- tests/zfs.nix | 38 ++++-------------------------- 7 files changed, 82 insertions(+), 172 deletions(-) create mode 100644 tests/lib.nix diff --git a/tests/btrfs-subvolumes.nix b/tests/btrfs-subvolumes.nix index 6527329..d901b0e 100644 --- a/tests/btrfs-subvolumes.nix +++ b/tests/btrfs-subvolumes.nix @@ -1,38 +1,9 @@ -{ makeTest ? import -, pkgs ? (import {}) +{ pkgs ? (import {}) +, makeDiskoTest ? (pkgs.callPackage ./lib.nix {}).makeDiskoTest }: -let - makeTest' = args: - makeTest args { - inherit pkgs; - inherit (pkgs) system; - }; +makeDiskoTest { disko-config = import ../example/btrfs-subvolumes.nix; - tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); - tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); -in makeTest' { - name = "disko"; - - nodes.machine = - { config, pkgs, modulesPath, ... }: - - { - imports = [ - (modulesPath + "/profiles/installation-device.nix") - (modulesPath + "/profiles/base.nix") - ]; - - # speed-up eval - documentation.enable = false; - - virtualisation.emptyDiskImages = [ 512 ]; - }; - - testScript = '' - machine.succeed("echo 'secret' > /tmp/secret.key"); - machine.succeed("${tsp-create}"); - machine.succeed("${tsp-mount}"); - machine.succeed("${tsp-mount}"); # verify that the command is idempotent + extraTestScript = '' machine.succeed("test -e /mnt/test"); machine.succeed("btrfs subvolume list /mnt | grep -qs 'path test$'"); ''; diff --git a/tests/default.nix b/tests/default.nix index 3b24ba6..14b7a19 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -1,8 +1,15 @@ { makeTest ? import , pkgs ? (import {}) }@args: -{ - luks-lvm = import ./luks-lvm.nix args; - mdadm = import ./mdadm.nix args; - zfs = import ./zfs.nix args; -} +let + lib = pkgs.lib; + makeDiskoTest = (pkgs.callPackage ./lib.nix { inherit makeTest; }).makeDiskoTest; + allTestFilenames = + builtins.map (lib.removeSuffix ".nix") ( + builtins.filter + (x: lib.hasSuffix ".nix" x && x != "default.nix" && x != "lib.nix") + (lib.attrNames (builtins.readDir ./.)) + ); + + allTests = lib.genAttrs (allTestFilenames) (test: import (./. + "/${test}.nix") { inherit makeDiskoTest; }); +in allTests diff --git a/tests/lib.nix b/tests/lib.nix new file mode 100644 index 0000000..b98be69 --- /dev/null +++ b/tests/lib.nix @@ -0,0 +1,47 @@ +{ pkgs ? (import {}) +, makeTest ? import +, ... +}: +{ + makeDiskoTest = { + disko-config, + extraTestScript, + extraConfig ? {} + }: + let + lib = pkgs.lib; + makeTest' = args: + makeTest args { + inherit pkgs; + inherit (pkgs) system; + }; + tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); + tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); + num-disks = builtins.length (builtins.filter (x: builtins.match "vd." x == []) (lib.attrNames disko-config.content)); + in makeTest' { + name = "disko"; + + nodes.machine = + { config, pkgs, modulesPath, ... }: + + { + imports = [ + (modulesPath + "/profiles/installation-device.nix") + (modulesPath + "/profiles/base.nix") + ]; + + # speed-up eval + documentation.enable = false; + + virtualisation.emptyDiskImages = builtins.genList (_: 512) num-disks; + } // extraConfig; + + testScript = '' + machine.succeed("echo 'secret' > /tmp/secret.key"); + machine.succeed("${tsp-create}"); + machine.succeed("${tsp-mount}"); + machine.succeed("${tsp-mount}"); # verify that the command is idempotent + ${extraTestScript} + ''; + }; +} diff --git a/tests/luks-lvm.nix b/tests/luks-lvm.nix index 3e2c5f2..305bff1 100644 --- a/tests/luks-lvm.nix +++ b/tests/luks-lvm.nix @@ -1,38 +1,9 @@ -{ makeTest ? import -, pkgs ? (import {}) +{ pkgs ? (import {}) +, makeDiskoTest ? (pkgs.callPackage ./lib.nix {}).makeDiskoTest }: -let - makeTest' = args: - makeTest args { - inherit pkgs; - inherit (pkgs) system; - }; +makeDiskoTest { disko-config = import ../example/luks-lvm.nix; - tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); - tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); -in makeTest' { - name = "disko"; - - nodes.machine = - { config, pkgs, modulesPath, ... }: - - { - imports = [ - (modulesPath + "/profiles/installation-device.nix") - (modulesPath + "/profiles/base.nix") - ]; - - # speed-up eval - documentation.enable = false; - - virtualisation.emptyDiskImages = [ 512 ]; - }; - - testScript = '' - machine.succeed("echo 'secret' > /tmp/secret.key"); - machine.succeed("${tsp-create}"); - machine.succeed("${tsp-mount}"); - machine.succeed("${tsp-mount}"); # verify that the command is idempotent + extraTestScript = '' machine.succeed("cryptsetup isLuks /dev/vdb2"); machine.succeed("grep -qs '/mnt/home' /proc/mounts"); ''; diff --git a/tests/lvm-raid.nix b/tests/lvm-raid.nix index 5165645..d7c4096 100644 --- a/tests/lvm-raid.nix +++ b/tests/lvm-raid.nix @@ -1,39 +1,12 @@ -{ makeTest ? import -, pkgs ? (import {}) +{ pkgs ? (import {}) +, makeDiskoTest ? (pkgs.callPackage ./lib.nix {}).makeDiskoTest }: -let - makeTest' = args: - makeTest args { - inherit pkgs; - inherit (pkgs) system; - }; +makeDiskoTest { disko-config = import ../example/lvm-raid.nix; - tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); - tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); -in makeTest' { - name = "disko"; - - nodes.machine = - { config, pkgs, modulesPath, ... }: - - { - imports = [ - (modulesPath + "/profiles/installation-device.nix") - (modulesPath + "/profiles/base.nix") - ]; - - # speed-up eval - documentation.enable = false; - - virtualisation.emptyDiskImages = [ 512 512 ]; - boot.kernelModules = [ "dm-raid" "dm-mirror" ]; - }; - - testScript = '' - machine.succeed("echo 'secret' > /tmp/secret.key"); - machine.succeed("${tsp-create}"); - machine.succeed("${tsp-mount}"); - machine.succeed("${tsp-mount}"); # verify that the command is idempotent + extraTestScript = '' machine.succeed("grep -qs '/mnt/home' /proc/mounts"); ''; + extraConfig = { + boot.kernelModules = [ "dm-raid" "dm-mirror" ]; + }; } diff --git a/tests/mdadm.nix b/tests/mdadm.nix index 37465e4..72f2faf 100644 --- a/tests/mdadm.nix +++ b/tests/mdadm.nix @@ -1,38 +1,9 @@ -{ makeTest ? import -, pkgs ? (import {}) +{ pkgs ? (import {}) +, makeDiskoTest ? (pkgs.callPackage ./lib.nix {}).makeDiskoTest }: -let - makeTest' = args: - makeTest args { - inherit pkgs; - inherit (pkgs) system; - }; +makeDiskoTest { disko-config = import ../example/mdadm.nix; - tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); - tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); -in makeTest' { - name = "disko"; - - nodes.machine = - { config, pkgs, modulesPath, ... }: - - { - imports = [ - (modulesPath + "/profiles/installation-device.nix") - (modulesPath + "/profiles/base.nix") - ]; - - # speed-up eval - documentation.enable = false; - - virtualisation.emptyDiskImages = [ 512 512 ]; - }; - - testScript = '' - machine.succeed("echo 'secret' > /tmp/secret.key"); - machine.succeed("${tsp-create}"); - machine.succeed("${tsp-mount}"); - machine.succeed("${tsp-mount}"); # verify that the command is idempotent + extraTestScript = '' machine.succeed("test -b /dev/md/raid1"); machine.succeed("grep -qs '/mnt/raid' /proc/mounts"); ''; diff --git a/tests/zfs.nix b/tests/zfs.nix index bc807bc..3c8216c 100644 --- a/tests/zfs.nix +++ b/tests/zfs.nix @@ -1,40 +1,10 @@ -{ makeTest ? import -, pkgs ? (import {}) +{ pkgs ? (import {}) +, makeDiskoTest ? (pkgs.callPackage ./lib.nix {}).makeDiskoTest }: -let - makeTest' = args: - makeTest args { - inherit pkgs; - inherit (pkgs) system; - }; +makeDiskoTest { disko-config = import ../example/zfs.nix; - tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); - tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); -in makeTest' { - name = "disko"; - - nodes.machine = - { config, pkgs, modulesPath, ... }: - - { - imports = [ - (modulesPath + "/profiles/installation-device.nix") - (modulesPath + "/profiles/base.nix") - ]; - - # speed-up eval - documentation.enable = false; - - virtualisation.emptyDiskImages = [ 512 512 ]; - }; - - testScript = '' - machine.succeed("echo 'secret' > /tmp/secret.key"); - machine.succeed("${tsp-create}"); - machine.succeed("${tsp-mount}"); - machine.succeed("${tsp-mount}"); # verify that the command is idempotent + extraTestScript = '' machine.succeed("test -b /dev/zvol/zroot/zfs_testvolume"); machine.succeed("grep -qs '/mnt/ext4onzfs' /proc/mounts"); ''; } - From 69f1337980756fe4ac001cadce7760e7b06c75da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 26 Aug 2022 08:41:58 +0200 Subject: [PATCH 09/13] fix inconsistent indentation with nixpkgs-fmt --- default.nix | 160 ++++++++++++++++++++----------------- tests/btrfs-subvolumes.nix | 4 +- tests/default.nix | 5 +- tests/lib.nix | 75 ++++++++--------- tests/luks-lvm.nix | 4 +- tests/lvm-raid.nix | 4 +- tests/mdadm.nix | 4 +- tests/zfs.nix | 4 +- 8 files changed, 138 insertions(+), 122 deletions(-) diff --git a/default.nix b/default.nix index d98e8d6..ac84345 100644 --- a/default.nix +++ b/default.nix @@ -4,12 +4,13 @@ with builtins; let - helper.find-device = device: let - environment = helper.device-id device; - in + helper.find-device = device: + let + environment = helper.device-id device; + in # DEVICE points already to /dev/disk, so we don't handle it via /dev/disk/by-path if hasPrefix "/dev/disk" device then - "${environment}='${device}'" + "${environment}='${device}'" else '' ${environment}=$(for x in $(find /dev/disk/{by-path,by-id}/); do dev=$x @@ -46,7 +47,7 @@ let }; config.devices = q: x: - foldl' recursiveUpdate {} (mapAttrsToList (name: config-f { device = "/dev/${name}"; }) x.content); + foldl' recursiveUpdate { } (mapAttrsToList (name: config-f { device = "/dev/${name}"; }) x.content); config.luks = q: x: { boot.initrd.luks.devices.${x.name}.device = q.device; @@ -56,15 +57,15 @@ let config-f { device = "/dev/${q.vgname}/${q.name}"; } x.content; config.lvm_vg = q: x: - foldl' recursiveUpdate {} (mapAttrsToList (name: config-f { inherit name; vgname = x.name; }) x.lvs); + foldl' recursiveUpdate { } (mapAttrsToList (name: config-f { inherit name; vgname = x.name; }) x.lvs); - config.noop = q: x: {}; + config.noop = q: x: { }; config.partition = q: x: config-f { device = q.device + toString q.index; } x.content; config.table = q: x: - foldl' recursiveUpdate {} (imap (index: config-f (q // { inherit index; })) x.partitions); + foldl' recursiveUpdate { } (imap (index: config-f (q // { inherit index; })) x.partitions); create-f = q: x: create.${x.type} q x; @@ -87,13 +88,15 @@ let ${q.device} ''; - create.devices = q: x: let - raid-devices = lib.filterAttrs (_: dev: dev.type == "mdadm" || dev.type == "zpool" || dev.type == "lvm_vg") x.content; - other-devices = lib.filterAttrs (_: dev: dev.type != "mdadm" && dev.type != "zpool" && dev.type != "lvm_vg") x.content; - in '' - ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; }) other-devices)} - ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; name = name; }) raid-devices)} - ''; + create.devices = q: x: + let + raid-devices = lib.filterAttrs (_: dev: dev.type == "mdadm" || dev.type == "zpool" || dev.type == "lvm_vg") x.content; + other-devices = lib.filterAttrs (_: dev: dev.type != "mdadm" && dev.type != "zpool" && dev.type != "lvm_vg") x.content; + in + '' + ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; }) other-devices)} + ${concatStrings (mapAttrsToList (name: create-f { device = "/dev/${name}"; name = name; }) raid-devices)} + ''; create.mdraid = q: x: '' RAIDDEVICES_N_${x.name}=$((''${RAIDDEVICES_N_${x.name}:-0}+1)) @@ -134,20 +137,22 @@ let create.noop = q: x: ""; - create.partition = q: x: let - env = helper.device-id q.device; - in '' - parted -s "''${${env}}" mkpart ${x.part-type} ${x.fs-type or ""} ${x.start} ${x.end} - # ensure /dev/disk/by-path/..-partN exists before continuing - udevadm trigger --subsystem-match=block; udevadm settle - ${optionalString (x.bootable or false) '' - parted -s "''${${env}}" set ${toString q.index} boot on - ''} - ${concatMapStringsSep "" (flag: '' - parted -s "''${${env}}" set ${toString q.index} ${flag} on - '') (x.flags or [])} - ${create-f { device = "\"\${${env}}\"-part" + toString q.index; } x.content} - ''; + create.partition = q: x: + let + env = helper.device-id q.device; + in + '' + parted -s "''${${env}}" mkpart ${x.part-type} ${x.fs-type or ""} ${x.start} ${x.end} + # ensure /dev/disk/by-path/..-partN exists before continuing + udevadm trigger --subsystem-match=block; udevadm settle + ${optionalString (x.bootable or false) '' + parted -s "''${${env}}" set ${toString q.index} boot on + ''} + ${concatMapStringsSep "" (flag: '' + parted -s "''${${env}}" set ${toString q.index} ${flag} on + '') (x.flags or [])} + ${create-f { device = "\"\${${env}}\"-part" + toString q.index; } x.content} + ''; create.table = q: x: '' ${helper.find-device q.device} @@ -185,34 +190,38 @@ let mount-f = q: x: mount.${x.type} q x; mount.filesystem = q: x: { - fs.${x.mountpoint} = '' - if ! findmnt ${q.device} "/mnt${x.mountpoint}" > /dev/null 2>&1; then - mount ${q.device} "/mnt${x.mountpoint}" -o X-mount.mkdir - fi - ''; - }; + fs.${x.mountpoint} = '' + if ! findmnt ${q.device} "/mnt${x.mountpoint}" > /dev/null 2>&1; then + mount ${q.device} "/mnt${x.mountpoint}" -o X-mount.mkdir + fi + ''; + }; mount.btrfs = mount.filesystem; - mount.devices = q: x: let - z = foldl' recursiveUpdate {} (mapAttrsToList (name: mount-f { device = "/dev/${name}"; inherit name; }) x.content); - # attrValues returns values sorted by name. This is important, because it - # ensures that "/" is processed before "/foo" etc. - in '' - ${optionalString (hasAttr "table" z) (concatStringsSep "\n" (attrValues z.table))} - ${optionalString (hasAttr "luks" z) (concatStringsSep "\n" (attrValues z.luks))} - ${optionalString (hasAttr "lvm" z) (concatStringsSep "\n" (attrValues z.lvm))} - ${optionalString (hasAttr "zpool" z) (concatStringsSep "\n" (attrValues z.zpool))} - ${optionalString (hasAttr "zfs" z) (concatStringsSep "\n" (attrValues z.zfs))} - ${optionalString (hasAttr "fs" z) (concatStringsSep "\n" (attrValues z.fs))} - ''; + mount.devices = q: x: + let + z = foldl' recursiveUpdate { } (mapAttrsToList (name: mount-f { device = "/dev/${name}"; inherit name; }) x.content); + # attrValues returns values sorted by name. This is important, because it + # ensures that "/" is processed before "/foo" etc. + in + '' + ${optionalString (hasAttr "table" z) (concatStringsSep "\n" (attrValues z.table))} + ${optionalString (hasAttr "luks" z) (concatStringsSep "\n" (attrValues z.luks))} + ${optionalString (hasAttr "lvm" z) (concatStringsSep "\n" (attrValues z.lvm))} + ${optionalString (hasAttr "zpool" z) (concatStringsSep "\n" (attrValues z.zpool))} + ${optionalString (hasAttr "zfs" z) (concatStringsSep "\n" (attrValues z.zfs))} + ${optionalString (hasAttr "fs" z) (concatStringsSep "\n" (attrValues z.fs))} + ''; mount.luks = q: x: ( recursiveUpdate - (mount-f { device = "/dev/mapper/${x.name}"; } x.content) - {luks.${q.device} = '' - cryptsetup status ${x.name} >/dev/null 2>/dev/null || cryptsetup luksOpen ${q.device} ${x.name} ${if builtins.hasAttr "keyfile" x then "--key-file " + x.keyfile else ""} - '';} + (mount-f { device = "/dev/mapper/${x.name}"; } x.content) + { + luks.${q.device} = '' + cryptsetup status ${x.name} >/dev/null 2>/dev/null || cryptsetup luksOpen ${q.device} ${x.name} ${if builtins.hasAttr "keyfile" x then "--key-file " + x.keyfile else ""} + ''; + } ); mount.lvm_lv = q: x: @@ -220,15 +229,17 @@ let mount.lvm_vg = q: x: ( recursiveUpdate - (foldl' recursiveUpdate {} (mapAttrsToList (name: mount-f { inherit name; vgname = q.name; }) x.lvs)) - {lvm.${q.device} = '' - vgchange -a y - '';} + (foldl' recursiveUpdate { } (mapAttrsToList (name: mount-f { inherit name; vgname = q.name; }) x.lvs)) + { + lvm.${q.device} = '' + vgchange -a y + ''; + } ); mount.lvm_pv = mount.noop; - mount.noop = q: x: {}; + mount.noop = q: x: { }; mount.mdadm = q: x: mount-f { device = "/dev/md/${q.name}"; } x.content; @@ -239,36 +250,39 @@ let mount.table = q: x: ( recursiveUpdate - (foldl' recursiveUpdate {} (imap (index: mount-f (q // { inherit index; device = helper.device-id q.device; })) x.partitions)) - {table.${q.device} = helper.find-device q.device;} + (foldl' recursiveUpdate { } (imap (index: mount-f (q // { inherit index; device = helper.device-id q.device; })) x.partitions)) + { table.${q.device} = helper.find-device q.device; } ); mount.zfs = mount.noop; mount.zpool = q: x: ( recursiveUpdate - (foldl' recursiveUpdate {} (map (mount-f (q // { pool = q.name; })) x.datasets)) - {zpool.${q.device} = '' - zpool list '${q.name}' >/dev/null 2>/dev/null || zpool import '${q.name}' - '';} + (foldl' recursiveUpdate { } (map (mount-f (q // { pool = q.name; })) x.datasets)) + { + zpool.${q.device} = '' + zpool list '${q.name}' >/dev/null 2>/dev/null || zpool import '${q.name}' + ''; + } ); mount.zfs_filesystem = q: x: { - zfs.${x.mountpoint} = '' - if ! findmnt '${q.pool}/${x.name}' /mnt${x.mountpoint} > /dev/null 2>&1; then - mount \ - ${lib.optionalString ((x.options.mountpoint or "") != "legacy") "-o zfsutil"} \ - -t zfs ${q.pool}/${x.name} /mnt${x.mountpoint} \ - -o X-mount.mkdir - fi - ''; - }; + zfs.${x.mountpoint} = '' + if ! findmnt '${q.pool}/${x.name}' /mnt${x.mountpoint} > /dev/null 2>&1; then + mount \ + ${lib.optionalString ((x.options.mountpoint or "") != "legacy") "-o zfsutil"} \ + -t zfs ${q.pool}/${x.name} /mnt${x.mountpoint} \ + -o X-mount.mkdir + fi + ''; + }; mount.zfs_volume = q: x: mount-f { device = "/dev/zvol/${q.pool}/${x.name}"; } x.content; -in { - config = config-f {}; +in +{ + config = config-f { }; create = cfg: '' set -efux ${create-f {} cfg} diff --git a/tests/btrfs-subvolumes.nix b/tests/btrfs-subvolumes.nix index d901b0e..3ff4386 100644 --- a/tests/btrfs-subvolumes.nix +++ b/tests/btrfs-subvolumes.nix @@ -1,5 +1,5 @@ -{ pkgs ? (import {}) -, makeDiskoTest ? (pkgs.callPackage ./lib.nix {}).makeDiskoTest +{ pkgs ? (import { }) +, makeDiskoTest ? (pkgs.callPackage ./lib.nix { }).makeDiskoTest }: makeDiskoTest { disko-config = import ../example/btrfs-subvolumes.nix; diff --git a/tests/default.nix b/tests/default.nix index 14b7a19..525f936 100644 --- a/tests/default.nix +++ b/tests/default.nix @@ -1,5 +1,5 @@ { makeTest ? import -, pkgs ? (import {}) +, pkgs ? (import { }) }@args: let lib = pkgs.lib; @@ -12,4 +12,5 @@ let ); allTests = lib.genAttrs (allTestFilenames) (test: import (./. + "/${test}.nix") { inherit makeDiskoTest; }); -in allTests +in +allTests diff --git a/tests/lib.nix b/tests/lib.nix index b98be69..2ec42e6 100644 --- a/tests/lib.nix +++ b/tests/lib.nix @@ -1,47 +1,48 @@ -{ pkgs ? (import {}) +{ pkgs ? (import { }) , makeTest ? import , ... }: { - makeDiskoTest = { - disko-config, - extraTestScript, - extraConfig ? {} - }: - let - lib = pkgs.lib; - makeTest' = args: - makeTest args { - inherit pkgs; - inherit (pkgs) system; - }; - tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. {}).create disko-config); - tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. {}).mount disko-config); - num-disks = builtins.length (builtins.filter (x: builtins.match "vd." x == []) (lib.attrNames disko-config.content)); - in makeTest' { - name = "disko"; + makeDiskoTest = + { disko-config + , extraTestScript + , extraConfig ? { } + }: + let + lib = pkgs.lib; + makeTest' = args: + makeTest args { + inherit pkgs; + inherit (pkgs) system; + }; + tsp-create = pkgs.writeScript "create" ((pkgs.callPackage ../. { }).create disko-config); + tsp-mount = pkgs.writeScript "mount" ((pkgs.callPackage ../. { }).mount disko-config); + num-disks = builtins.length (builtins.filter (x: builtins.match "vd." x == [ ]) (lib.attrNames disko-config.content)); + in + makeTest' { + name = "disko"; - nodes.machine = - { config, pkgs, modulesPath, ... }: + nodes.machine = + { config, pkgs, modulesPath, ... }: - { - imports = [ - (modulesPath + "/profiles/installation-device.nix") - (modulesPath + "/profiles/base.nix") - ]; + { + imports = [ + (modulesPath + "/profiles/installation-device.nix") + (modulesPath + "/profiles/base.nix") + ]; - # speed-up eval - documentation.enable = false; + # speed-up eval + documentation.enable = false; - virtualisation.emptyDiskImages = builtins.genList (_: 512) num-disks; - } // extraConfig; + virtualisation.emptyDiskImages = builtins.genList (_: 512) num-disks; + } // extraConfig; - testScript = '' - machine.succeed("echo 'secret' > /tmp/secret.key"); - machine.succeed("${tsp-create}"); - machine.succeed("${tsp-mount}"); - machine.succeed("${tsp-mount}"); # verify that the command is idempotent - ${extraTestScript} - ''; - }; + testScript = '' + machine.succeed("echo 'secret' > /tmp/secret.key"); + machine.succeed("${tsp-create}"); + machine.succeed("${tsp-mount}"); + machine.succeed("${tsp-mount}"); # verify that the command is idempotent + ${extraTestScript} + ''; + }; } diff --git a/tests/luks-lvm.nix b/tests/luks-lvm.nix index 305bff1..f578c6f 100644 --- a/tests/luks-lvm.nix +++ b/tests/luks-lvm.nix @@ -1,5 +1,5 @@ -{ pkgs ? (import {}) -, makeDiskoTest ? (pkgs.callPackage ./lib.nix {}).makeDiskoTest +{ pkgs ? (import { }) +, makeDiskoTest ? (pkgs.callPackage ./lib.nix { }).makeDiskoTest }: makeDiskoTest { disko-config = import ../example/luks-lvm.nix; diff --git a/tests/lvm-raid.nix b/tests/lvm-raid.nix index d7c4096..6c865c6 100644 --- a/tests/lvm-raid.nix +++ b/tests/lvm-raid.nix @@ -1,5 +1,5 @@ -{ pkgs ? (import {}) -, makeDiskoTest ? (pkgs.callPackage ./lib.nix {}).makeDiskoTest +{ pkgs ? (import { }) +, makeDiskoTest ? (pkgs.callPackage ./lib.nix { }).makeDiskoTest }: makeDiskoTest { disko-config = import ../example/lvm-raid.nix; diff --git a/tests/mdadm.nix b/tests/mdadm.nix index 72f2faf..c9a2162 100644 --- a/tests/mdadm.nix +++ b/tests/mdadm.nix @@ -1,5 +1,5 @@ -{ pkgs ? (import {}) -, makeDiskoTest ? (pkgs.callPackage ./lib.nix {}).makeDiskoTest +{ pkgs ? (import { }) +, makeDiskoTest ? (pkgs.callPackage ./lib.nix { }).makeDiskoTest }: makeDiskoTest { disko-config = import ../example/mdadm.nix; diff --git a/tests/zfs.nix b/tests/zfs.nix index 3c8216c..9e5e746 100644 --- a/tests/zfs.nix +++ b/tests/zfs.nix @@ -1,5 +1,5 @@ -{ pkgs ? (import {}) -, makeDiskoTest ? (pkgs.callPackage ./lib.nix {}).makeDiskoTest +{ pkgs ? (import { }) +, makeDiskoTest ? (pkgs.callPackage ./lib.nix { }).makeDiskoTest }: makeDiskoTest { disko-config = import ../example/zfs.nix; From 05a90d4227dd14ab08fa3982f320ec771e5626db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 26 Aug 2022 08:49:46 +0200 Subject: [PATCH 10/13] tests: use mountpoint program to check for mountpoints --- tests/luks-lvm.nix | 2 +- tests/lvm-raid.nix | 2 +- tests/mdadm.nix | 2 +- tests/zfs.nix | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/luks-lvm.nix b/tests/luks-lvm.nix index f578c6f..d59f3f4 100644 --- a/tests/luks-lvm.nix +++ b/tests/luks-lvm.nix @@ -5,6 +5,6 @@ makeDiskoTest { disko-config = import ../example/luks-lvm.nix; extraTestScript = '' machine.succeed("cryptsetup isLuks /dev/vdb2"); - machine.succeed("grep -qs '/mnt/home' /proc/mounts"); + machine.succeed("mountpoint /mnt/home"); ''; } diff --git a/tests/lvm-raid.nix b/tests/lvm-raid.nix index 6c865c6..102dcd7 100644 --- a/tests/lvm-raid.nix +++ b/tests/lvm-raid.nix @@ -4,7 +4,7 @@ makeDiskoTest { disko-config = import ../example/lvm-raid.nix; extraTestScript = '' - machine.succeed("grep -qs '/mnt/home' /proc/mounts"); + machine.succeed("mountpoint /mnt/home"); ''; extraConfig = { boot.kernelModules = [ "dm-raid" "dm-mirror" ]; diff --git a/tests/mdadm.nix b/tests/mdadm.nix index c9a2162..bd57a00 100644 --- a/tests/mdadm.nix +++ b/tests/mdadm.nix @@ -5,6 +5,6 @@ makeDiskoTest { disko-config = import ../example/mdadm.nix; extraTestScript = '' machine.succeed("test -b /dev/md/raid1"); - machine.succeed("grep -qs '/mnt/raid' /proc/mounts"); + machine.succeed("mountpoint /mnt/raid"); ''; } diff --git a/tests/zfs.nix b/tests/zfs.nix index 9e5e746..e0c8fab 100644 --- a/tests/zfs.nix +++ b/tests/zfs.nix @@ -5,6 +5,6 @@ makeDiskoTest { disko-config = import ../example/zfs.nix; extraTestScript = '' machine.succeed("test -b /dev/zvol/zroot/zfs_testvolume"); - machine.succeed("grep -qs '/mnt/ext4onzfs' /proc/mounts"); + machine.succeed("mountpoint /mnt/ext4onzfs"); ''; } From 57b7c5506a64e169370f291bffa75d2888027ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 26 Aug 2022 09:07:24 +0200 Subject: [PATCH 11/13] also test zfs filesystem options --- example/zfs.nix | 5 +++++ tests/zfs.nix | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/example/zfs.nix b/example/zfs.nix index e344289..2b56bd7 100644 --- a/example/zfs.nix +++ b/example/zfs.nix @@ -12,11 +12,16 @@ zroot = { type = "zpool"; mode = "mirror"; + rootFsOptions = { + compression = "lz4"; + "com.sun:auto-snapshot" = "false"; + }; datasets = [ { type = "zfs_filesystem"; name = "zfs_fs"; mountpoint = "/zfs_fs"; + options."com.sun:auto-snapshot" = "true"; } { type = "zfs_filesystem"; diff --git a/tests/zfs.nix b/tests/zfs.nix index e0c8fab..ebc6b04 100644 --- a/tests/zfs.nix +++ b/tests/zfs.nix @@ -5,6 +5,23 @@ makeDiskoTest { disko-config = import ../example/zfs.nix; extraTestScript = '' machine.succeed("test -b /dev/zvol/zroot/zfs_testvolume"); + + def assert_property(ds, property, expected_value): + out = machine.succeed(f"zfs get -H {property} {ds} -o value").rstrip() + assert ( + out == expected_value + ), f"Expected {property}={expected_value} on {ds}, got: {out}" + + assert_property("zroot", "compression", "lz4") + assert_property("zroot/zfs_fs", "compression", "lz4") + assert_property("zroot", "com.sun:auto-snapshot", "false") + assert_property("zroot/zfs_fs", "com.sun:auto-snapshot", "true") + assert_property("zroot/zfs_testvolume", "volsize", "10M") + + # FIXME: we cannot mount rootfs yet + #machine.succeed("mountpoint /mnt"); + machine.succeed("mountpoint /mnt/zfs_fs"); + machine.succeed("mountpoint /mnt/zfs_legacy_fs"); machine.succeed("mountpoint /mnt/ext4onzfs"); ''; } From 722dde361ca51864fd9b3b6b9a78875e436fbc24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Fri, 26 Aug 2022 10:28:07 +0200 Subject: [PATCH 12/13] zfs: support for root dataset mountpoint and mountpoint=none --- default.nix | 35 +++++++++++++++++++++++++++-------- example/zfs.nix | 7 +++++++ tests/zfs.nix | 4 ++-- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/default.nix b/default.nix index ac84345..1865d69 100644 --- a/default.nix +++ b/default.nix @@ -256,29 +256,48 @@ let mount.zfs = mount.noop; - mount.zpool = q: x: ( + mount.zpool = q: x: + let + datasets = [{ + inherit (q) name; + type = "zfs_filesystem"; + dataset = q.name; + mountpoint = x.mountpoint or "/${q.name}"; + options = q.rootFsOptions or { }; + }] ++ x.datasets; + in recursiveUpdate - (foldl' recursiveUpdate { } (map (mount-f (q // { pool = q.name; })) x.datasets)) + (foldl' recursiveUpdate { } + ( + (map + (x: mount-f + ({ + dataset = x.dataset or "${q.name}/${x.name}"; + mountpoint = x.mountpoint or "/${q.name}/${x.name}"; + } // q) + x) + datasets) + ) + ) { zpool.${q.device} = '' zpool list '${q.name}' >/dev/null 2>/dev/null || zpool import '${q.name}' ''; - } - ); + }; mount.zfs_filesystem = q: x: { - zfs.${x.mountpoint} = '' - if ! findmnt '${q.pool}/${x.name}' /mnt${x.mountpoint} > /dev/null 2>&1; then + zfs.${q.mountpoint} = lib.optionalString ((x.options.mountpoint or "") != "none") '' + if ! findmnt '${q.dataset}' /mnt${q.mountpoint} > /dev/null 2>&1; then mount \ ${lib.optionalString ((x.options.mountpoint or "") != "legacy") "-o zfsutil"} \ - -t zfs ${q.pool}/${x.name} /mnt${x.mountpoint} \ + -t zfs ${q.dataset} /mnt${q.mountpoint} \ -o X-mount.mkdir fi ''; }; mount.zfs_volume = q: x: - mount-f { device = "/dev/zvol/${q.pool}/${x.name}"; } x.content; + mount-f { device = "/dev/zvol/${q.dataset}"; } x.content; in { diff --git a/example/zfs.nix b/example/zfs.nix index 2b56bd7..60e4700 100644 --- a/example/zfs.nix +++ b/example/zfs.nix @@ -16,6 +16,8 @@ compression = "lz4"; "com.sun:auto-snapshot" = "false"; }; + mountpoint = "/"; + datasets = [ { type = "zfs_filesystem"; @@ -23,6 +25,11 @@ mountpoint = "/zfs_fs"; options."com.sun:auto-snapshot" = "true"; } + { + type = "zfs_filesystem"; + name = "zfs_unmounted_fs"; + options.mountpoint = "none"; + } { type = "zfs_filesystem"; name = "zfs_legacy_fs"; diff --git a/tests/zfs.nix b/tests/zfs.nix index ebc6b04..5940c4d 100644 --- a/tests/zfs.nix +++ b/tests/zfs.nix @@ -17,9 +17,9 @@ makeDiskoTest { assert_property("zroot", "com.sun:auto-snapshot", "false") assert_property("zroot/zfs_fs", "com.sun:auto-snapshot", "true") assert_property("zroot/zfs_testvolume", "volsize", "10M") + assert_property("zroot/zfs_unmounted_fs", "mountpoint", "none") - # FIXME: we cannot mount rootfs yet - #machine.succeed("mountpoint /mnt"); + machine.succeed("mountpoint /mnt"); machine.succeed("mountpoint /mnt/zfs_fs"); machine.succeed("mountpoint /mnt/zfs_legacy_fs"); machine.succeed("mountpoint /mnt/ext4onzfs"); From 9bb4aec9640cbc30e241c267158e506278862b5e Mon Sep 17 00:00:00 2001 From: lassulus Date: Fri, 26 Aug 2022 12:50:52 +0200 Subject: [PATCH 13/13] support zfs over legacy fs --- default.nix | 24 ++++++++++----------- example/zfs-over-legacy.nix | 42 +++++++++++++++++++++++++++++++++++++ tests/zfs-over-legacy.nix | 12 +++++++++++ 3 files changed, 65 insertions(+), 13 deletions(-) create mode 100644 example/zfs-over-legacy.nix create mode 100644 tests/zfs-over-legacy.nix diff --git a/default.nix b/default.nix index 1865d69..51b70f5 100644 --- a/default.nix +++ b/default.nix @@ -179,7 +179,7 @@ let create.zpool = q: x: '' zpool create ${q.name} \ - ${lib.optionalString (!isNull x.mode || x.mode != "stripe") x.mode} \ + ${lib.optionalString (!isNull (x.mode or null) && x.mode != "stripe") x.mode} \ ${lib.optionalString (isAttrs x.options or null) (concatStringsSep " " (mapAttrsToList (n: v: "-o ${n}=${v}") x.options))} \ ${lib.optionalString (isAttrs x.rootFsOptions or null) (concatStringsSep " " (mapAttrsToList (n: v: "-O ${n}=${v}") x.rootFsOptions))} \ ''${ZFSDEVICES_${q.name}} @@ -192,11 +192,20 @@ let mount.filesystem = q: x: { fs.${x.mountpoint} = '' if ! findmnt ${q.device} "/mnt${x.mountpoint}" > /dev/null 2>&1; then - mount ${q.device} "/mnt${x.mountpoint}" -o X-mount.mkdir + mount ${q.device} "/mnt${x.mountpoint}" \ + -o X-mount.mkdir \ + ${lib.optionalString (isList x.mountOptions or null) (concatStringsSep " " x.mountOptions)} fi ''; }; + mount.zfs_filesystem = q: x: + optionalAttrs ((x.options.mountpoint or "") != "none") + (mount.filesystem (q // { device = q.dataset; }) (x // { mountOptions = [ + (lib.optionalString ((x.options.mountpoint or "") != "legacy") "-o zfsutil") + "-t zfs" + ]; })); + mount.btrfs = mount.filesystem; mount.devices = q: x: @@ -285,17 +294,6 @@ let ''; }; - mount.zfs_filesystem = q: x: { - zfs.${q.mountpoint} = lib.optionalString ((x.options.mountpoint or "") != "none") '' - if ! findmnt '${q.dataset}' /mnt${q.mountpoint} > /dev/null 2>&1; then - mount \ - ${lib.optionalString ((x.options.mountpoint or "") != "legacy") "-o zfsutil"} \ - -t zfs ${q.dataset} /mnt${q.mountpoint} \ - -o X-mount.mkdir - fi - ''; - }; - mount.zfs_volume = q: x: mount-f { device = "/dev/zvol/${q.dataset}"; } x.content; diff --git a/example/zfs-over-legacy.nix b/example/zfs-over-legacy.nix new file mode 100644 index 0000000..8f5a8bc --- /dev/null +++ b/example/zfs-over-legacy.nix @@ -0,0 +1,42 @@ +{ + type = "devices"; + content = { + vdb = { + type = "table"; + format = "gpt"; + partitions = [ + { + type = "partition"; + # leave space for the grub aka BIOS boot + start = "0%"; + end = "100%"; + part-type = "primary"; + bootable = true; + content = { + type = "filesystem"; + format = "ext4"; + mountpoint = "/"; + }; + } + ]; + }; + vdc = { + type = "zfs"; + pool = "zroot"; + }; + zroot = { + type = "zpool"; + mountpoint = "/"; + + datasets = [ + { + type = "zfs_filesystem"; + name = "zfs_fs"; + mountpoint = "/zfs_fs"; + options."com.sun:auto-snapshot" = "true"; + } + ]; + }; + }; +} + diff --git a/tests/zfs-over-legacy.nix b/tests/zfs-over-legacy.nix new file mode 100644 index 0000000..13f86a9 --- /dev/null +++ b/tests/zfs-over-legacy.nix @@ -0,0 +1,12 @@ +{ pkgs ? (import { }) +, makeDiskoTest ? (pkgs.callPackage ./lib.nix { }).makeDiskoTest +}: +makeDiskoTest { + disko-config = import ../example/zfs-over-legacy.nix; + extraTestScript = '' + machine.succeed("test -e /mnt/zfs_fs"); + machine.succeed("mountpoint /mnt"); + machine.succeed("mountpoint /mnt/zfs_fs"); + ''; +} +