Skip to content

Commit

Permalink
nixos/storage: Make devspec an attrset internally
Browse files Browse the repository at this point in the history
So far we passed the device specification as-is to nixpart, but from
within the module system it's quite tricky to validate or look up such a
string, because we need to parse it every time we need to do a look up
an a configuration value in "storage.*".

Now a device specification is an attribute set consisting of a `name'
and a `type' attribute. We also have a new applyTypeContainer attribute
we need to pass to mkDeviceSpecOption so that we can properly convert
things such as "listOf devspecType" into a list of valid internal
representations of device specifications.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
  • Loading branch information
aszlig committed Jan 6, 2017
1 parent 17d464b commit 8e861d2
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 5 deletions.
7 changes: 7 additions & 0 deletions nixos/modules/tasks/storage/default.nix
Expand Up @@ -75,6 +75,7 @@ let
orderableOptions = deviceSpec: {
options.before = storageLib.mkDeviceSpecOption {
typeContainer = types.listOf;
applyTypeContainer = map;
validDeviceTypes = [ deviceSpec.name ];
default = [];
description = ''
Expand All @@ -85,6 +86,7 @@ let

options.after = storageLib.mkDeviceSpecOption {
typeContainer = types.listOf;
applyTypeContainer = map;
validDeviceTypes = [ deviceSpec.name ];
default = [];
description = ''
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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 = ''
Expand All @@ -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 = ''
Expand Down
26 changes: 21 additions & 5 deletions nixos/modules/tasks/storage/lib.nix
Expand Up @@ -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.
*
Expand All @@ -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";
Expand All @@ -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
<literal>&lt;type&gt;.&lt;name&gt;</literal> where <literal>type</literal>
is ${oneOf (attrs.validDeviceTypes or [])} and <literal>name</literal> is
the name in <option>storage.sometype.name</option>.
'';
} // removeAttrs attrs [ "validDeviceTypes" "typeContainer" "description" ]);
} // removeAttrs attrs [
"validDeviceTypes" "typeContainer" "applyTypeContainer" "description"
]);

types = {
size = lib.mkOptionType {
Expand Down

0 comments on commit 8e861d2

Please sign in to comment.