From 62f939e21365e03023a37ab2c924a2eab86270e2 Mon Sep 17 00:00:00 2001 From: lassulus Date: Wed, 7 Jun 2023 13:42:39 +0200 Subject: [PATCH 1/5] types: pass parent node to all subTypes --- lib/default.nix | 29 +++++++++++++++++++---------- lib/types/btrfs.nix | 6 +++++- lib/types/disk.nix | 2 +- lib/types/filesystem.nix | 6 +++++- lib/types/luks.nix | 8 ++++++-- lib/types/lvm_pv.nix | 6 +++++- lib/types/lvm_vg.nix | 2 +- lib/types/mdadm.nix | 2 +- lib/types/mdraid.nix | 6 +++++- lib/types/swap.nix | 6 +++++- lib/types/table.nix | 8 ++++++-- lib/types/zfs.nix | 6 +++++- lib/types/zfs_fs.nix | 6 +++++- lib/types/zfs_volume.nix | 8 ++++++-- lib/types/zpool.nix | 5 ++++- 15 files changed, 79 insertions(+), 27 deletions(-) diff --git a/lib/default.nix b/lib/default.nix index be96594..79cba7a 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -7,31 +7,40 @@ let diskoLib = { # like lib.types.oneOf but instead of a list takes an attrset # uses the field "type" to find the correct type in the attrset - subType = typeAttr: lib.mkOptionType rec { + subType = { types, extraArgs ? { parent = { type = "rootNode"; name = "root"; }; } }: lib.mkOptionType rec { name = "subType"; - description = "one of ${concatStringsSep "," (attrNames typeAttr)}"; - check = x: if x ? type then typeAttr.${x.type}.check x else throw "No type option set in:\n${generators.toPretty {} x}"; - merge = loc: foldl' (_res: def: typeAttr.${def.value.type}.merge loc [ def ]) { }; - nestedTypes = typeAttr; + description = "one of ${concatStringsSep "," (attrNames types)}"; + check = x: if x ? type then types.${x.type}.check x else throw "No type option set in:\n${generators.toPretty {} x}"; + merge = loc: foldl' (res: def: types.${def.value.type}.merge loc [ + # we add a dummy root parent node to render documentation + (lib.recursiveUpdate { value._module.args = extraArgs; } def) + ]) { }; + nestedTypes = types; }; # option for valid contents of partitions (basically like devices, but without tables) - partitionType = lib.mkOption { - type = lib.types.nullOr (diskoLib.subType { inherit (diskoLib.types) btrfs filesystem zfs mdraid luks lvm_pv swap; }); + partitionType = extraArgs: lib.mkOption { + type = lib.types.nullOr (diskoLib.subType { + types = { inherit (diskoLib.types) btrfs filesystem zfs mdraid luks lvm_pv swap; }; + inherit extraArgs; + }); default = null; description = "The type of partition"; }; # option for valid contents of devices - deviceType = lib.mkOption { - type = lib.types.nullOr (diskoLib.subType { inherit (diskoLib.types) table btrfs filesystem zfs mdraid luks lvm_pv swap; }); + deviceType = extraArgs: lib.mkOption { + type = lib.types.nullOr (diskoLib.subType { + types = { inherit (diskoLib.types) table table_gpt btrfs filesystem zfs mdraid luks lvm_pv swap; }; + inherit extraArgs; + }); default = null; description = "The type of device"; }; /* deepMergeMap takes a function and a list of attrsets and deep merges them - deepMergeMap :: -> (AttrSet -> AttrSet ) -> [ AttrSet ] -> Attrset + deepMergeMap :: (AttrSet -> AttrSet ) -> [ AttrSet ] -> Attrset Example: deepMergeMap (x: x.t = "test") [ { x = { y = 1; z = 3; }; } { x = { bla = 234; }; } ] diff --git a/lib/types/btrfs.nix b/lib/types/btrfs.nix index 88f207a..18718f9 100644 --- a/lib/types/btrfs.nix +++ b/lib/types/btrfs.nix @@ -1,4 +1,4 @@ -{ config, options, diskoLib, lib, rootMountPoint, ... }: +{ config, options, diskoLib, lib, rootMountPoint, parent, ... }: { options = { type = lib.mkOption { @@ -55,6 +55,10 @@ default = null; description = "A path to mount the BTRFS filesystem to."; }; + _parent = lib.mkOption { + internal = true; + default = parent; + }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/disk.nix b/lib/types/disk.nix index 06499ac..1f30788 100644 --- a/lib/types/disk.nix +++ b/lib/types/disk.nix @@ -16,7 +16,7 @@ type = diskoLib.optionTypes.absolute-pathname; # TODO check if subpath of /dev ? - No! eg: /.swapfile description = "Device path"; }; - content = diskoLib.deviceType; + content = diskoLib.deviceType { parent = config; }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/filesystem.nix b/lib/types/filesystem.nix index c604938..1e15e51 100644 --- a/lib/types/filesystem.nix +++ b/lib/types/filesystem.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, rootMountPoint, ... }: +{ config, options, lib, diskoLib, rootMountPoint, parent, ... }: { options = { type = lib.mkOption { @@ -25,6 +25,10 @@ type = lib.types.str; description = "Format of the filesystem"; }; + _parent = lib.mkOption { + internal = true; + default = parent; + }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/luks.nix b/lib/types/luks.nix index bd8a5fc..55fd994 100644 --- a/lib/types/luks.nix +++ b/lib/types/luks.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, ... }: +{ config, options, lib, diskoLib, parent, ... }: { options = { type = lib.mkOption { @@ -33,7 +33,11 @@ description = "Extra arguments to pass to `cryptsetup luksOpen` when opening"; example = [ "--allow-discards" ]; }; - content = diskoLib.deviceType; + content = diskoLib.deviceType { parent = config; }; + _parent = lib.mkOption { + internal = true; + default = parent; + }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/lvm_pv.nix b/lib/types/lvm_pv.nix index bc988dc..98c193a 100644 --- a/lib/types/lvm_pv.nix +++ b/lib/types/lvm_pv.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, ... }: +{ config, options, lib, diskoLib, parent, ... }: { options = { type = lib.mkOption { @@ -10,6 +10,10 @@ type = lib.types.str; description = "Volume group"; }; + _parent = lib.mkOption { + internal = true; + default = parent; + }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/lvm_vg.nix b/lib/types/lvm_vg.nix index 1a6d3e5..53e3d31 100644 --- a/lib/types/lvm_vg.nix +++ b/lib/types/lvm_vg.nix @@ -33,7 +33,7 @@ default = [ ]; description = "Extra arguments"; }; - content = diskoLib.partitionType; + content = diskoLib.partitionType { parent = config; }; }; })); default = { }; diff --git a/lib/types/mdadm.nix b/lib/types/mdadm.nix index 5d1f043..f2edd6d 100644 --- a/lib/types/mdadm.nix +++ b/lib/types/mdadm.nix @@ -22,7 +22,7 @@ default = "default"; description = "Metadata"; }; - content = diskoLib.deviceType; + content = diskoLib.deviceType { parent = config; }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/mdraid.nix b/lib/types/mdraid.nix index 1c7a0d0..79d097c 100644 --- a/lib/types/mdraid.nix +++ b/lib/types/mdraid.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, ... }: +{ config, options, lib, diskoLib, parent, ... }: { options = { type = lib.mkOption { @@ -11,6 +11,10 @@ type = lib.types.str; description = "Name"; }; + _parent = lib.mkOption { + internal = true; + default = parent; + }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/swap.nix b/lib/types/swap.nix index e886453..496be46 100644 --- a/lib/types/swap.nix +++ b/lib/types/swap.nix @@ -1,4 +1,4 @@ -{ diskoLib, config, options, lib, ... }: +{ diskoLib, config, options, lib, parent, ... }: { options = { type = lib.mkOption { @@ -11,6 +11,10 @@ default = false; description = "Whether to randomly encrypt the swap"; }; + _parent = lib.mkOption { + internal = true; + default = parent; + }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/table.nix b/lib/types/table.nix index 0009990..9353507 100644 --- a/lib/types/table.nix +++ b/lib/types/table.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, ... }: +{ config, options, lib, diskoLib, parent, ... }: { options = { type = lib.mkOption { @@ -48,12 +48,16 @@ default = false; description = "Whether to make the partition bootable"; }; - content = diskoLib.partitionType; + content = diskoLib.partitionType { parent = config; }; }; })); default = [ ]; description = "List of partitions to add to the partition table"; }; + _parent = lib.mkOption { + internal = true; + default = parent; + }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/zfs.nix b/lib/types/zfs.nix index 257afa9..7e7589a 100644 --- a/lib/types/zfs.nix +++ b/lib/types/zfs.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, ... }: +{ config, options, lib, diskoLib, parent, ... }: { options = { type = lib.mkOption { @@ -10,6 +10,10 @@ type = lib.types.str; description = "Name of the ZFS pool"; }; + _parent = lib.mkOption { + internal = true; + default = parent; + }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/zfs_fs.nix b/lib/types/zfs_fs.nix index 0c5e96b..c3e7ce5 100644 --- a/lib/types/zfs_fs.nix +++ b/lib/types/zfs_fs.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, rootMountPoint, ... }: +{ config, options, lib, diskoLib, rootMountPoint, parent, ... }: { options = { name = lib.mkOption { @@ -29,6 +29,10 @@ description = "Path to mount the dataset to"; }; + _parent = lib.mkOption { + internal = true; + default = parent; + }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/zfs_volume.nix b/lib/types/zfs_volume.nix index f0cd849..bd231f9 100644 --- a/lib/types/zfs_volume.nix +++ b/lib/types/zfs_volume.nix @@ -1,4 +1,4 @@ -{ config, options, lib, diskoLib, ... }: +{ config, options, lib, diskoLib, parent, ... }: { options = { name = lib.mkOption { @@ -30,8 +30,12 @@ description = "Size of the dataset"; }; - content = diskoLib.partitionType; + content = diskoLib.partitionType { parent = config; }; + _parent = lib.mkOption { + internal = true; + default = parent; + }; _meta = lib.mkOption { internal = true; readOnly = true; diff --git a/lib/types/zpool.nix b/lib/types/zpool.nix index 2733e8f..e53fea7 100644 --- a/lib/types/zpool.nix +++ b/lib/types/zpool.nix @@ -49,7 +49,10 @@ ''; }; datasets = lib.mkOption { - type = lib.types.attrsOf (diskoLib.subType { inherit (diskoLib.types) zfs_fs zfs_volume; }); + type = lib.types.attrsOf (diskoLib.subType { + types = { inherit (diskoLib.types) zfs_fs zfs_volume; }; + extraArgs.parent = config; + }); description = "List of datasets to define"; }; _meta = lib.mkOption { From 6e01bdc086a92cfd6286e3c35d43626a5a6b2cd3 Mon Sep 17 00:00:00 2001 From: lassulus Date: Wed, 7 Jun 2023 13:52:57 +0200 Subject: [PATCH 2/5] tests: use module mode by default, export installed-system --- tests/lib.nix | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/lib.nix b/tests/lib.nix index f4cd232..5a961c3 100644 --- a/tests/lib.nix +++ b/tests/lib.nix @@ -14,7 +14,7 @@ , efi ? true , enableOCR ? false , postDisko ? "" - , testMode ? "direct" # can be one of direct module cli + , testMode ? "module" # can be one of direct module cli , testBoot ? true # if we actually want to test booting or just create/mount }: let @@ -65,10 +65,12 @@ efiInstallAsRemovable = efi; }; }; - installedTopLevel = (eval-config { + installed-system-eval = eval-config { modules = [ installed-system ]; inherit (pkgs) system; - }).config.system.build.toplevel; + }; + + installedTopLevel = installed-system-eval.config.system.build.toplevel; in makeTest' { name = "disko-${name}"; @@ -111,6 +113,9 @@ }; virtualisation.emptyDiskImages = builtins.genList (_: 4096) num-disks; + + # useful for debugging via repl + system.build.systemToInstall = installed-system-eval; }; testScript = { nodes, ... }: '' From c230584db3de155a6e594834c650aacd8991be6a Mon Sep 17 00:00:00 2001 From: lassulus Date: Wed, 7 Jun 2023 13:53:13 +0200 Subject: [PATCH 3/5] types: init table_gpt --- lib/default.nix | 2 +- lib/types/gpt.nix | 120 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 lib/types/gpt.nix diff --git a/lib/default.nix b/lib/default.nix index 79cba7a..9f9ae6d 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -31,7 +31,7 @@ let # option for valid contents of devices deviceType = extraArgs: lib.mkOption { type = lib.types.nullOr (diskoLib.subType { - types = { inherit (diskoLib.types) table table_gpt btrfs filesystem zfs mdraid luks lvm_pv swap; }; + types = { inherit (diskoLib.types) table gpt btrfs filesystem zfs mdraid luks lvm_pv swap; }; inherit extraArgs; }); default = null; diff --git a/lib/types/gpt.nix b/lib/types/gpt.nix new file mode 100644 index 0000000..00ddbb9 --- /dev/null +++ b/lib/types/gpt.nix @@ -0,0 +1,120 @@ +{ config, options, lib, diskoLib, parent, ... }@args: +{ + options = { + type = lib.mkOption { + type = lib.types.enum [ "gpt" ]; + internal = true; + description = "Partition table"; + }; + partitions = lib.mkOption { + type = lib.types.attrsOf (lib.types.submodule ({ name, ... }@partition: { + options = { + type = lib.mkOption { + type = lib.types.strMatching "[A-Fa-f0-9]{4}"; + default = "8300"; + description = "Filesystem type to use, run sgdisk -L to see what is available"; + }; + priority = lib.mkOption { + type = lib.types.int; + default = 1000; + description = "Priority of the partition, higher priority partitions are created first"; + }; + name = lib.mkOption { + type = lib.types.str; + description = "Name of the partition"; + default = name; + }; + label = lib.mkOption { + type = lib.types.str; + default = "${config._parent.type}-${config._parent.name}-${partition.name}"; + }; + start = lib.mkOption { + type = lib.types.str; + default = "0"; + description = "Start of the partition, in sgdisk format, use 0 for next available range"; + }; + end = lib.mkOption { + type = lib.types.str; + description = '' + End of the partition, in sgdisk format. + Use + for relative sizes from the partitons start + or - for relative sizes from the disks end + ''; + }; + content = diskoLib.partitionType { parent = config; }; + }; + })); + default = [ ]; + description = "Attrs of partitions to add to the partition table"; + }; + _parent = lib.mkOption { + internal = true; + default = parent; + }; + _meta = lib.mkOption { + internal = true; + readOnly = true; + type = lib.types.functionTo diskoLib.jsonType; + default = dev: + lib.foldr lib.recursiveUpdate { } (lib.imap + (index: partition: + lib.optionalAttrs (partition.content != null) (partition.content._meta dev) + ) + (lib.attrValues config.partitions)); + description = "Metadata"; + }; + _create = diskoLib.mkCreateOption { + inherit config options; + default = { dev }: '' + ${lib.concatStrings (lib.imap (index: partition: '' + sgdisk \ + --new=${toString index}:${partition.start}:${partition.end} \ + --change-name=${toString index}:${partition.label} \ + --typecode=${toString index}:${partition.type} \ + ${dev} + # ensure /dev/disk/by-path/..-partN exists before continuing + udevadm trigger --subsystem-match=block; udevadm settle + ${lib.optionalString (partition.content != null) (partition.content._create { dev = "/dev/disk/by-partlabel/${partition.label}"; })} + '') (lib.attrValues config.partitions))} + ''; + }; + _mount = diskoLib.mkMountOption { + inherit config options; + default = { dev }: + let + partMounts = lib.foldr lib.recursiveUpdate { } (lib.imap + (index: partition: + lib.optionalAttrs (partition.content != null) (partition.content._mount { dev = "/dev/disk/by-partlabel/${partition.label}"; }) + ) + (lib.attrValues config.partitions)); + in + { + dev = partMounts.dev or ""; + fs = partMounts.fs or { }; + }; + }; + _config = lib.mkOption { + internal = true; + readOnly = true; + default = dev: + lib.imap + (index: partition: + lib.optional (partition.content != null) (partition.content._config "/dev/disk/by-partlabel/${partition.label}") + ) + (lib.attrValues config.partitions); + description = "NixOS configuration"; + }; + _pkgs = lib.mkOption { + internal = true; + readOnly = true; + type = lib.types.functionTo (lib.types.listOf lib.types.package); + default = pkgs: + [ pkgs.gptfdisk pkgs.systemdMinimal ] ++ lib.flatten (map + (partition: + lib.optional (partition.content != null) (partition.content._pkgs pkgs) + ) + (lib.attrValues config.partitions)); + description = "Packages"; + }; + }; +} From aca7f1866758db7a08cdd6a59e7f1d6d2e60cfe5 Mon Sep 17 00:00:00 2001 From: lassulus Date: Wed, 7 Jun 2023 13:54:53 +0200 Subject: [PATCH 4/5] example simple-efi: use table_gpt --- example/simple-efi.nix | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/example/simple-efi.nix b/example/simple-efi.nix index 459fdec..1d12941 100644 --- a/example/simple-efi.nix +++ b/example/simple-efi.nix @@ -5,33 +5,27 @@ device = builtins.elemAt disks 0; type = "disk"; content = { - type = "table"; - format = "gpt"; - partitions = [ - { - name = "ESP"; - start = "1MiB"; - end = "100MiB"; - bootable = true; + type = "table_gpt"; + partitions = { + ESP = { + type = "EF00"; + start = "1M"; + end = "+100M"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; }; - } - { - name = "root"; - start = "100MiB"; - end = "100%"; - part-type = "primary"; - bootable = true; + }; + root = { + end = "-0"; content = { type = "filesystem"; format = "ext4"; mountpoint = "/"; }; - } - ]; + }; + }; }; }; }; From 93632ac0ae08e22a45b86ee5b0781381f8a2ae29 Mon Sep 17 00:00:00 2001 From: lassulus Date: Wed, 7 Jun 2023 14:31:21 +0200 Subject: [PATCH 5/5] tests: use diskoScript output --- example/simple-efi.nix | 2 +- tests/lib.nix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/simple-efi.nix b/example/simple-efi.nix index 1d12941..ecb3384 100644 --- a/example/simple-efi.nix +++ b/example/simple-efi.nix @@ -5,7 +5,7 @@ device = builtins.elemAt disks 0; type = "disk"; content = { - type = "table_gpt"; + type = "gpt"; partitions = { ESP = { type = "EF00"; diff --git a/tests/lib.nix b/tests/lib.nix index 5a961c3..98d43ce 100644 --- a/tests/lib.nix +++ b/tests/lib.nix @@ -145,7 +145,7 @@ 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.disko}") # verify that we can destroy and recreate again + 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