diff --git a/nixos/modules/tasks/storage/default.nix b/nixos/modules/tasks/storage/default.nix index 6c971bc35995ca..39464dc200173d 100644 --- a/nixos/modules/tasks/storage/default.nix +++ b/nixos/modules/tasks/storage/default.nix @@ -75,6 +75,7 @@ let orderableOptions = deviceSpec: { options.before = storageLib.mkDeviceSpecOption { typeContainer = types.listOf; + applyTypeContainer = map; validDeviceTypes = [ deviceSpec.name ]; default = []; description = '' @@ -85,6 +86,7 @@ let options.after = storageLib.mkDeviceSpecOption { typeContainer = types.listOf; + applyTypeContainer = map; validDeviceTypes = [ deviceSpec.name ]; default = []; description = '' @@ -114,6 +116,7 @@ let devices = storageLib.mkDeviceSpecOption { typeContainer = types.listOf; + applyTypeContainer = map; validDeviceTypes = containerTypes; description = '' List of devices that will be part of this array. @@ -124,6 +127,7 @@ let volgroupOptions.options = { devices = storageLib.mkDeviceSpecOption { typeContainer = types.listOf; + applyTypeContainer = map; validDeviceTypes = containerTypes; description = '' List of devices that will be part of this volume group. @@ -143,6 +147,7 @@ let btrfsOptions.options = { devices = storageLib.mkDeviceSpecOption { typeContainer = types.listOf; + applyTypeContainer = map; validDeviceTypes = containerTypes; description = '' List of devices that will be part of this BTRFS volume. @@ -258,6 +263,7 @@ in options.storage = storageLib.mkDeviceSpecOption { validDeviceTypes = containerTypes ++ [ "btrfs" ]; typeContainer = types.nullOr; + applyTypeContainer = fun: val: if val == null then null else fun val; default = null; example = "partition.root"; description = '' @@ -279,6 +285,7 @@ in options.storage = storageLib.mkDeviceSpecOption { validDeviceTypes = containerTypes; typeContainer = types.nullOr; + applyTypeContainer = fun: val: if val == null then null else fun val; default = null; example = "partition.swap"; description = '' diff --git a/nixos/modules/tasks/storage/lib.nix b/nixos/modules/tasks/storage/lib.nix index 9d8d1f521aa3ea..e3c3f4a6a35ba0 100644 --- a/nixos/modules/tasks/storage/lib.nix +++ b/nixos/modules/tasks/storage/lib.nix @@ -83,6 +83,17 @@ let strAssertions = lib.concatStringsSep "\n" (assertions); in if assertions == [] then true else builtins.trace strAssertions false; + /* Decode a device specification string like "partition.foo" into an attribute + * set consisting of the attributes `type' ("partition" here) and `name' + * ("foo" here). + */ + decodeSpec = spec: let + typeAndName = builtins.match "([a-z]+)\\.([a-zA-Z0-9_-]+)" spec; + in if typeAndName == null then null else { + type = lib.head typeAndName; + name = lib.last typeAndName; + }; + /* Validate the device specification and return true if it's valid or false if * it's not. * @@ -99,14 +110,13 @@ let invalidNameMsg = "Device `${type}.${name}' does not exist in `config.storage.*'."; syntaxError = builtins.trace syntaxErrorMsg false; - typeAndName = builtins.match "([a-z]+)\\.([a-zA-Z0-9_-]+)" spec; - type = lib.head typeAndName; - name = lib.last typeAndName; + decoded = decodeSpec spec; + inherit (decoded) type name; assertName = if (cfg.${type} or {}) ? ${name} then true else builtins.trace invalidNameMsg false; assertType = if lib.elem type validTypes then assertName else builtins.trace invalidTypeMsg false; - in if typeAndName == null then syntaxError else assertType; + in if decoded == null then syntaxError else assertType; deviceSpecType = validTypes: lib.mkOptionType { name = "deviceSpec"; @@ -127,13 +137,19 @@ in { # wrap the deviceSpecType in any other container type in lib.types. typeContainer = attrs.typeContainer or lib.id; in typeContainer (deviceSpecType validDeviceTypes); + # `applyTypeContainer' is a function that's used to unpack the individual + # deviceSpecType from the typeContainer. So for example if typeContainer is + # `listOf', the applyTypeContainer function is "map". + apply = (attrs.applyTypeContainer or lib.id) decodeSpec; description = attrs.description + '' The device specification has to be in the form <type>.<name> where type is ${oneOf (attrs.validDeviceTypes or [])} and name is the name in . ''; - } // removeAttrs attrs [ "validDeviceTypes" "typeContainer" "description" ]); + } // removeAttrs attrs [ + "validDeviceTypes" "typeContainer" "applyTypeContainer" "description" + ]); types = { size = lib.mkOptionType {