Merge pull request #256 from nix-community/table_gpt

introduce new table_gpt type
This commit is contained in:
Jörg Thalheim 2023-06-16 20:18:48 +01:00 committed by GitHub
commit 72e0068edd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 219 additions and 48 deletions

View file

@ -5,33 +5,27 @@
device = builtins.elemAt disks 0; device = builtins.elemAt disks 0;
type = "disk"; type = "disk";
content = { content = {
type = "table"; type = "gpt";
format = "gpt"; partitions = {
partitions = [ ESP = {
{ type = "EF00";
name = "ESP"; start = "1M";
start = "1MiB"; end = "+100M";
end = "100MiB";
bootable = true;
content = { content = {
type = "filesystem"; type = "filesystem";
format = "vfat"; format = "vfat";
mountpoint = "/boot"; mountpoint = "/boot";
}; };
} };
{ root = {
name = "root"; end = "-0";
start = "100MiB";
end = "100%";
part-type = "primary";
bootable = true;
content = { content = {
type = "filesystem"; type = "filesystem";
format = "ext4"; format = "ext4";
mountpoint = "/"; mountpoint = "/";
}; };
} };
]; };
}; };
}; };
}; };

View file

@ -7,31 +7,40 @@ let
diskoLib = { diskoLib = {
# 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
subType = typeAttr: lib.mkOptionType rec { subType = { types, extraArgs ? { parent = { type = "rootNode"; name = "root"; }; } }: lib.mkOptionType rec {
name = "subType"; name = "subType";
description = "one of ${concatStringsSep "," (attrNames typeAttr)}"; description = "one of ${concatStringsSep "," (attrNames types)}";
check = x: if x ? type then typeAttr.${x.type}.check x else throw "No type option set in:\n${generators.toPretty {} x}"; 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: typeAttr.${def.value.type}.merge loc [ def ]) { }; merge = loc: foldl' (res: def: types.${def.value.type}.merge loc [
nestedTypes = typeAttr; # 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) # option for valid contents of partitions (basically like devices, but without tables)
partitionType = lib.mkOption { partitionType = extraArgs: lib.mkOption {
type = lib.types.nullOr (diskoLib.subType { inherit (diskoLib.types) btrfs filesystem zfs mdraid luks lvm_pv swap; }); type = lib.types.nullOr (diskoLib.subType {
types = { inherit (diskoLib.types) btrfs filesystem zfs mdraid luks lvm_pv swap; };
inherit extraArgs;
});
default = null; default = null;
description = "The type of partition"; description = "The type of partition";
}; };
# option for valid contents of devices # option for valid contents of devices
deviceType = lib.mkOption { deviceType = extraArgs: lib.mkOption {
type = lib.types.nullOr (diskoLib.subType { inherit (diskoLib.types) table btrfs filesystem zfs mdraid luks lvm_pv swap; }); type = lib.types.nullOr (diskoLib.subType {
types = { inherit (diskoLib.types) table gpt btrfs filesystem zfs mdraid luks lvm_pv swap; };
inherit extraArgs;
});
default = null; default = null;
description = "The type of device"; description = "The type of device";
}; };
/* deepMergeMap takes a function and a list of attrsets and deep merges them /* deepMergeMap takes a function and a list of attrsets and deep merges them
deepMergeMap :: -> (AttrSet -> AttrSet ) -> [ AttrSet ] -> Attrset deepMergeMap :: (AttrSet -> AttrSet ) -> [ AttrSet ] -> Attrset
Example: Example:
deepMergeMap (x: x.t = "test") [ { x = { y = 1; z = 3; }; } { x = { bla = 234; }; } ] deepMergeMap (x: x.t = "test") [ { x = { y = 1; z = 3; }; } { x = { bla = 234; }; } ]

View file

@ -1,4 +1,4 @@
{ config, options, diskoLib, lib, rootMountPoint, ... }: { config, options, diskoLib, lib, rootMountPoint, parent, ... }:
{ {
options = { options = {
type = lib.mkOption { type = lib.mkOption {
@ -55,6 +55,10 @@
default = null; default = null;
description = "A path to mount the BTRFS filesystem to."; description = "A path to mount the BTRFS filesystem to.";
}; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

View file

@ -16,7 +16,7 @@
type = diskoLib.optionTypes.absolute-pathname; # TODO check if subpath of /dev ? - No! eg: /.swapfile type = diskoLib.optionTypes.absolute-pathname; # TODO check if subpath of /dev ? - No! eg: /.swapfile
description = "Device path"; description = "Device path";
}; };
content = diskoLib.deviceType; content = diskoLib.deviceType { parent = config; };
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

View file

@ -1,4 +1,4 @@
{ config, options, lib, diskoLib, rootMountPoint, ... }: { config, options, lib, diskoLib, rootMountPoint, parent, ... }:
{ {
options = { options = {
type = lib.mkOption { type = lib.mkOption {
@ -25,6 +25,10 @@
type = lib.types.str; type = lib.types.str;
description = "Format of the filesystem"; description = "Format of the filesystem";
}; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

120
lib/types/gpt.nix Normal file
View file

@ -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";
};
};
}

View file

@ -1,4 +1,4 @@
{ config, options, lib, diskoLib, ... }: { config, options, lib, diskoLib, parent, ... }:
{ {
options = { options = {
type = lib.mkOption { type = lib.mkOption {
@ -33,7 +33,11 @@
description = "Extra arguments to pass to `cryptsetup luksOpen` when opening"; description = "Extra arguments to pass to `cryptsetup luksOpen` when opening";
example = [ "--allow-discards" ]; example = [ "--allow-discards" ];
}; };
content = diskoLib.deviceType; content = diskoLib.deviceType { parent = config; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

View file

@ -1,4 +1,4 @@
{ config, options, lib, diskoLib, ... }: { config, options, lib, diskoLib, parent, ... }:
{ {
options = { options = {
type = lib.mkOption { type = lib.mkOption {
@ -10,6 +10,10 @@
type = lib.types.str; type = lib.types.str;
description = "Volume group"; description = "Volume group";
}; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

View file

@ -33,7 +33,7 @@
default = [ ]; default = [ ];
description = "Extra arguments"; description = "Extra arguments";
}; };
content = diskoLib.partitionType; content = diskoLib.partitionType { parent = config; };
}; };
})); }));
default = { }; default = { };

View file

@ -22,7 +22,7 @@
default = "default"; default = "default";
description = "Metadata"; description = "Metadata";
}; };
content = diskoLib.deviceType; content = diskoLib.deviceType { parent = config; };
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

View file

@ -1,4 +1,4 @@
{ config, options, lib, diskoLib, ... }: { config, options, lib, diskoLib, parent, ... }:
{ {
options = { options = {
type = lib.mkOption { type = lib.mkOption {
@ -11,6 +11,10 @@
type = lib.types.str; type = lib.types.str;
description = "Name"; description = "Name";
}; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

View file

@ -1,4 +1,4 @@
{ diskoLib, config, options, lib, ... }: { diskoLib, config, options, lib, parent, ... }:
{ {
options = { options = {
type = lib.mkOption { type = lib.mkOption {
@ -11,6 +11,10 @@
default = false; default = false;
description = "Whether to randomly encrypt the swap"; description = "Whether to randomly encrypt the swap";
}; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

View file

@ -1,4 +1,4 @@
{ config, options, lib, diskoLib, ... }: { config, options, lib, diskoLib, parent, ... }:
{ {
options = { options = {
type = lib.mkOption { type = lib.mkOption {
@ -48,12 +48,16 @@
default = false; default = false;
description = "Whether to make the partition bootable"; description = "Whether to make the partition bootable";
}; };
content = diskoLib.partitionType; content = diskoLib.partitionType { parent = config; };
}; };
})); }));
default = [ ]; default = [ ];
description = "List of partitions to add to the partition table"; description = "List of partitions to add to the partition table";
}; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

View file

@ -1,4 +1,4 @@
{ config, options, lib, diskoLib, ... }: { config, options, lib, diskoLib, parent, ... }:
{ {
options = { options = {
type = lib.mkOption { type = lib.mkOption {
@ -10,6 +10,10 @@
type = lib.types.str; type = lib.types.str;
description = "Name of the ZFS pool"; description = "Name of the ZFS pool";
}; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

View file

@ -1,4 +1,4 @@
{ config, options, lib, diskoLib, rootMountPoint, ... }: { config, options, lib, diskoLib, rootMountPoint, parent, ... }:
{ {
options = { options = {
name = lib.mkOption { name = lib.mkOption {
@ -29,6 +29,10 @@
description = "Path to mount the dataset to"; description = "Path to mount the dataset to";
}; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

View file

@ -1,4 +1,4 @@
{ config, options, lib, diskoLib, ... }: { config, options, lib, diskoLib, parent, ... }:
{ {
options = { options = {
name = lib.mkOption { name = lib.mkOption {
@ -30,8 +30,12 @@
description = "Size of the dataset"; description = "Size of the dataset";
}; };
content = diskoLib.partitionType; content = diskoLib.partitionType { parent = config; };
_parent = lib.mkOption {
internal = true;
default = parent;
};
_meta = lib.mkOption { _meta = lib.mkOption {
internal = true; internal = true;
readOnly = true; readOnly = true;

View file

@ -49,7 +49,10 @@
''; '';
}; };
datasets = lib.mkOption { 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"; description = "List of datasets to define";
}; };
_meta = lib.mkOption { _meta = lib.mkOption {

View file

@ -14,7 +14,7 @@
, efi ? true , efi ? true
, enableOCR ? false , enableOCR ? false
, postDisko ? "" , 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 , testBoot ? true # if we actually want to test booting or just create/mount
}: }:
let let
@ -65,10 +65,12 @@
efiInstallAsRemovable = efi; efiInstallAsRemovable = efi;
}; };
}; };
installedTopLevel = (eval-config { installed-system-eval = eval-config {
modules = [ installed-system ]; modules = [ installed-system ];
inherit (pkgs) system; inherit (pkgs) system;
}).config.system.build.toplevel; };
installedTopLevel = installed-system-eval.config.system.build.toplevel;
in in
makeTest' { makeTest' {
name = "disko-${name}"; name = "disko-${name}";
@ -111,6 +113,9 @@
}; };
virtualisation.emptyDiskImages = builtins.genList (_: 4096) num-disks; virtualisation.emptyDiskImages = builtins.genList (_: 4096) num-disks;
# useful for debugging via repl
system.build.systemToInstall = installed-system-eval;
}; };
testScript = { nodes, ... }: '' testScript = { nodes, ... }: ''
@ -140,7 +145,7 @@
machine.succeed("${nodes.machine.system.build.formatScript}") machine.succeed("${nodes.machine.system.build.formatScript}")
machine.succeed("${nodes.machine.system.build.mountScript}") 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.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") '' ${lib.optionalString (testMode == "cli") ''
# TODO use the disko cli here # TODO use the disko cli here