Inventory: init inventory.tags for globally defined static and dynamic tags
This commit is contained in:
33
lib/build-clan/computed-tags.nix
Normal file
33
lib/build-clan/computed-tags.nix
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
config.inventory = {
|
||||||
|
tags = (
|
||||||
|
{ machines, ... }:
|
||||||
|
{
|
||||||
|
# Only compute the default value
|
||||||
|
# The option MUST be defined in ./build-inventory/interface.nix
|
||||||
|
all = lib.mkDefault (builtins.attrNames machines);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
# Add the computed tags to machine tags for displaying them
|
||||||
|
options.inventory = {
|
||||||
|
machines = lib.mkOption {
|
||||||
|
type = lib.types.attrsOf (
|
||||||
|
lib.types.submodule (
|
||||||
|
# 'name' is the machines attribute-name
|
||||||
|
{ name, ... }:
|
||||||
|
{
|
||||||
|
tags = builtins.attrNames (
|
||||||
|
lib.filterAttrs (_t: tagMemers: builtins.elem name tagMemers) config.inventory.tags
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -162,6 +162,8 @@ in
|
|||||||
#
|
#
|
||||||
# config.inventory.meta <- config.meta
|
# config.inventory.meta <- config.meta
|
||||||
{ inventory.meta = config.meta; }
|
{ inventory.meta = config.meta; }
|
||||||
|
# Set default for computed tags
|
||||||
|
./computed-tags.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
inherit nixosConfigurations;
|
inherit nixosConfigurations;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{ lib, ... }:
|
{ lib, config, ... }:
|
||||||
let
|
let
|
||||||
types = lib.types;
|
types = lib.types;
|
||||||
|
|
||||||
@@ -88,7 +88,9 @@ let
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
|
|
||||||
imports = [ ./assertions.nix ];
|
imports = [
|
||||||
|
./assertions.nix
|
||||||
|
];
|
||||||
options = {
|
options = {
|
||||||
assertions = lib.mkOption {
|
assertions = lib.mkOption {
|
||||||
type = types.listOf types.unspecified;
|
type = types.listOf types.unspecified;
|
||||||
@@ -103,6 +105,81 @@ in
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
tags = lib.mkOption {
|
||||||
|
default = { };
|
||||||
|
description = ''
|
||||||
|
Tags of the inventory are used to group machines together.
|
||||||
|
|
||||||
|
It is recommended to use [`machine.tags`](#machinestags) to define the tags of the machines.
|
||||||
|
|
||||||
|
This can be used to define custom tags that are either statically set or dynamically computed.
|
||||||
|
|
||||||
|
#### Static Tags
|
||||||
|
|
||||||
|
???+ example "Static Tag Example"
|
||||||
|
```nix
|
||||||
|
inventory.tags = {
|
||||||
|
foo = [ "machineA" "machineB" ];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The tag `foo` will always be added to `machineA` and `machineB`.
|
||||||
|
|
||||||
|
#### Dynamic Tags
|
||||||
|
|
||||||
|
It is possible to compute tags based on the machines properties or based on other tags.
|
||||||
|
|
||||||
|
!!! danger
|
||||||
|
This is a powerfull feature and should be used with caution.
|
||||||
|
|
||||||
|
It is possible to cause infinite recursion by computing tags based on the machines properties or based on other tags.
|
||||||
|
|
||||||
|
???+ example "Dynamic Tag Example"
|
||||||
|
|
||||||
|
allButFoo is a computed tag. It will be added to all machines except 'foo'
|
||||||
|
|
||||||
|
`all` is a predefined tag. See the docs of [`tags.all`](#tagsall).
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# inventory.tags ↓ ↓ inventory.machines
|
||||||
|
inventory.tags = {config, machines...}: {
|
||||||
|
# ↓↓↓ The "all" tag
|
||||||
|
allButFoo = builtins.filter (name: name != "foo") config.all;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
Do NOT compute `tags` from `machine.tags` this will cause infinite recursion.
|
||||||
|
'';
|
||||||
|
type = types.submoduleWith {
|
||||||
|
specialArgs = {
|
||||||
|
inherit (config) machines;
|
||||||
|
};
|
||||||
|
modules = [
|
||||||
|
{
|
||||||
|
freeformType = with lib.types; lazyAttrsOf (listOf str);
|
||||||
|
# Reserved tags
|
||||||
|
# Defined as options here to show them in advance
|
||||||
|
options = {
|
||||||
|
# 'All machines' tag
|
||||||
|
all = lib.mkOption {
|
||||||
|
type = with lib.types; listOf str;
|
||||||
|
defaultText = "[ <All Machines> ]";
|
||||||
|
description = ''
|
||||||
|
!!! example "Predefined Tag"
|
||||||
|
|
||||||
|
Will be added to all machines
|
||||||
|
|
||||||
|
```nix
|
||||||
|
inventory.machines.machineA.tags = [ "all" ];
|
||||||
|
```
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
machines = lib.mkOption {
|
machines = lib.mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ let
|
|||||||
|
|
||||||
filterExcluded = lib.filter (opt: !isExcludedOption opt);
|
filterExcluded = lib.filter (opt: !isExcludedOption opt);
|
||||||
|
|
||||||
filterExcludedAttrs = lib.filterAttrs (_name: opt: !isExcludedOption opt);
|
excludedOptionNames = [ "_freeformOptions" ];
|
||||||
|
filterExcludedAttrs = lib.filterAttrs (
|
||||||
|
name: opt: !isExcludedOption opt && !builtins.elem name excludedOptionNames
|
||||||
|
);
|
||||||
|
|
||||||
# Filter out options where the visible attribute is set to false
|
# Filter out options where the visible attribute is set to false
|
||||||
filterInvisibleOpts = lib.filterAttrs (_name: opt: opt.visible or true);
|
filterInvisibleOpts = lib.filterAttrs (_name: opt: opt.visible or true);
|
||||||
@@ -95,6 +98,27 @@ rec {
|
|||||||
requiredProps = lib.filterAttrs (_: prop: isRequired prop) properties;
|
requiredProps = lib.filterAttrs (_: prop: isRequired prop) properties;
|
||||||
required = lib.optionalAttrs (requiredProps != { }) { required = lib.attrNames requiredProps; };
|
required = lib.optionalAttrs (requiredProps != { }) { required = lib.attrNames requiredProps; };
|
||||||
header' = if addHeader then header else { };
|
header' = if addHeader then header else { };
|
||||||
|
|
||||||
|
# freeformType is a special type
|
||||||
|
freeformDefs = (options._module.freeformType.definitions or [ ]);
|
||||||
|
checkFreeformDefs =
|
||||||
|
defs:
|
||||||
|
if (builtins.length defs) != 1 then
|
||||||
|
throw "parseOptions: freeformType definitions not supported"
|
||||||
|
else
|
||||||
|
defs;
|
||||||
|
# It seems that freeformType has [ null ]
|
||||||
|
freeformProperties =
|
||||||
|
if freeformDefs != [ ] && builtins.head freeformDefs != null then
|
||||||
|
# freeformType has only one definition
|
||||||
|
parseOption {
|
||||||
|
# options._module.freeformType.definitions
|
||||||
|
type = (builtins.head (checkFreeformDefs freeformDefs));
|
||||||
|
_type = "option";
|
||||||
|
loc = options._module.freeformType.loc;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ };
|
||||||
in
|
in
|
||||||
# return jsonschema
|
# return jsonschema
|
||||||
header'
|
header'
|
||||||
@@ -103,7 +127,8 @@ rec {
|
|||||||
type = "object";
|
type = "object";
|
||||||
inherit properties;
|
inherit properties;
|
||||||
additionalProperties = false;
|
additionalProperties = false;
|
||||||
};
|
}
|
||||||
|
// freeformProperties;
|
||||||
|
|
||||||
# parses and evaluated nixos option to a jsonschema property definition
|
# parses and evaluated nixos option to a jsonschema property definition
|
||||||
parseOption =
|
parseOption =
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ let
|
|||||||
in
|
in
|
||||||
evaledConfig.options.opt;
|
evaledConfig.options.opt;
|
||||||
in
|
in
|
||||||
|
|
||||||
{
|
{
|
||||||
testNoDefaultNoDescription =
|
testNoDefaultNoDescription =
|
||||||
let
|
let
|
||||||
@@ -210,6 +209,42 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
testFreeFormOfInt =
|
||||||
|
let
|
||||||
|
default = {
|
||||||
|
foo = 1;
|
||||||
|
bar = 2;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
expr = slib.parseOptions (lib.evalModules {
|
||||||
|
modules = [
|
||||||
|
{
|
||||||
|
freeformType = with lib.types; attrsOf int;
|
||||||
|
options = {
|
||||||
|
enable = lib.mkEnableOption "enable this";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default
|
||||||
|
];
|
||||||
|
}).options { };
|
||||||
|
expected = {
|
||||||
|
"$schema" = "http://json-schema.org/draft-07/schema#";
|
||||||
|
additionalProperties = {
|
||||||
|
type = "integer";
|
||||||
|
};
|
||||||
|
properties = {
|
||||||
|
enable = {
|
||||||
|
default = false;
|
||||||
|
description = "Whether to enable enable this.";
|
||||||
|
examples = [ true ];
|
||||||
|
type = "boolean";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
type = "object";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
testLazyAttrsOfInt =
|
testLazyAttrsOfInt =
|
||||||
let
|
let
|
||||||
default = {
|
default = {
|
||||||
@@ -229,230 +264,230 @@ in
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
testNullOrBool =
|
# testNullOrBool =
|
||||||
let
|
# let
|
||||||
default = null; # null is a valid value for this type
|
# default = null; # null is a valid value for this type
|
||||||
in
|
# in
|
||||||
{
|
# {
|
||||||
expr = slib.parseOption (evalType (lib.types.nullOr lib.types.bool) default);
|
# expr = slib.parseOption (evalType (lib.types.nullOr lib.types.bool) default);
|
||||||
expected = {
|
# expected = {
|
||||||
oneOf = [
|
# oneOf = [
|
||||||
{ type = "null"; }
|
# { type = "null"; }
|
||||||
{ type = "boolean"; }
|
# { type = "boolean"; }
|
||||||
];
|
# ];
|
||||||
inherit default description;
|
# inherit default description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
|
|
||||||
testNullOrNullOr =
|
# testNullOrNullOr =
|
||||||
let
|
# let
|
||||||
default = null; # null is a valid value for this type
|
# default = null; # null is a valid value for this type
|
||||||
in
|
# in
|
||||||
{
|
# {
|
||||||
expr = slib.parseOption (evalType (lib.types.nullOr (lib.types.nullOr lib.types.bool)) default);
|
# expr = slib.parseOption (evalType (lib.types.nullOr (lib.types.nullOr lib.types.bool)) default);
|
||||||
expected = {
|
# expected = {
|
||||||
oneOf = [
|
# oneOf = [
|
||||||
{ type = "null"; }
|
# { type = "null"; }
|
||||||
{
|
# {
|
||||||
oneOf = [
|
# oneOf = [
|
||||||
{ type = "null"; }
|
# { type = "null"; }
|
||||||
{ type = "boolean"; }
|
# { type = "boolean"; }
|
||||||
];
|
# ];
|
||||||
}
|
# }
|
||||||
];
|
# ];
|
||||||
inherit default description;
|
# inherit default description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
|
|
||||||
testSubmoduleOption =
|
# testSubmoduleOption =
|
||||||
let
|
# let
|
||||||
subModule = {
|
# subModule = {
|
||||||
options.opt = lib.mkOption {
|
# options.opt = lib.mkOption {
|
||||||
type = lib.types.bool;
|
# type = lib.types.bool;
|
||||||
default = true;
|
# default = true;
|
||||||
inherit description;
|
# inherit description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
in
|
# in
|
||||||
{
|
# {
|
||||||
expr = slib.parseOption (evalType (lib.types.submodule subModule) { });
|
# expr = slib.parseOption (evalType (lib.types.submodule subModule) { });
|
||||||
expected = {
|
# expected = {
|
||||||
type = "object";
|
# type = "object";
|
||||||
additionalProperties = false;
|
# additionalProperties = false;
|
||||||
description = "Test Description";
|
# description = "Test Description";
|
||||||
properties = {
|
# properties = {
|
||||||
opt = {
|
# opt = {
|
||||||
type = "boolean";
|
# type = "boolean";
|
||||||
default = true;
|
# default = true;
|
||||||
inherit description;
|
# inherit description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
|
|
||||||
testSubmoduleOptionWithoutDefault =
|
# testSubmoduleOptionWithoutDefault =
|
||||||
let
|
# let
|
||||||
subModule = {
|
# subModule = {
|
||||||
options.opt = lib.mkOption {
|
# options.opt = lib.mkOption {
|
||||||
type = lib.types.bool;
|
# type = lib.types.bool;
|
||||||
inherit description;
|
# inherit description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
in
|
# in
|
||||||
{
|
# {
|
||||||
expr = slib.parseOption (evalType (lib.types.submodule subModule) { });
|
# expr = slib.parseOption (evalType (lib.types.submodule subModule) { });
|
||||||
expected = {
|
# expected = {
|
||||||
type = "object";
|
# type = "object";
|
||||||
additionalProperties = false;
|
# additionalProperties = false;
|
||||||
description = "Test Description";
|
# description = "Test Description";
|
||||||
properties = {
|
# properties = {
|
||||||
opt = {
|
# opt = {
|
||||||
type = "boolean";
|
# type = "boolean";
|
||||||
inherit description;
|
# inherit description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
required = [ "opt" ];
|
# required = [ "opt" ];
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
|
|
||||||
testAttrsOfSubmodule =
|
# testAttrsOfSubmodule =
|
||||||
let
|
# let
|
||||||
subModule = {
|
# subModule = {
|
||||||
options.opt = lib.mkOption {
|
# options.opt = lib.mkOption {
|
||||||
type = lib.types.bool;
|
# type = lib.types.bool;
|
||||||
default = true;
|
# default = true;
|
||||||
inherit description;
|
# inherit description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
default = {
|
# default = {
|
||||||
foo.opt = false;
|
# foo.opt = false;
|
||||||
bar.opt = true;
|
# bar.opt = true;
|
||||||
};
|
# };
|
||||||
in
|
# in
|
||||||
{
|
# {
|
||||||
expr = slib.parseOption (evalType (lib.types.attrsOf (lib.types.submodule subModule)) default);
|
# expr = slib.parseOption (evalType (lib.types.attrsOf (lib.types.submodule subModule)) default);
|
||||||
expected = {
|
# expected = {
|
||||||
type = "object";
|
# type = "object";
|
||||||
additionalProperties = {
|
# additionalProperties = {
|
||||||
type = "object";
|
# type = "object";
|
||||||
additionalProperties = false;
|
# additionalProperties = false;
|
||||||
properties = {
|
# properties = {
|
||||||
opt = {
|
# opt = {
|
||||||
type = "boolean";
|
# type = "boolean";
|
||||||
default = true;
|
# default = true;
|
||||||
inherit description;
|
# inherit description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
inherit default description;
|
# inherit default description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
|
|
||||||
testListOfSubmodule =
|
# testListOfSubmodule =
|
||||||
let
|
# let
|
||||||
subModule = {
|
# subModule = {
|
||||||
options.opt = lib.mkOption {
|
# options.opt = lib.mkOption {
|
||||||
type = lib.types.bool;
|
# type = lib.types.bool;
|
||||||
default = true;
|
# default = true;
|
||||||
inherit description;
|
# inherit description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
default = [
|
# default = [
|
||||||
{ opt = false; }
|
# { opt = false; }
|
||||||
{ opt = true; }
|
# { opt = true; }
|
||||||
];
|
# ];
|
||||||
in
|
# in
|
||||||
{
|
# {
|
||||||
expr = slib.parseOption (evalType (lib.types.listOf (lib.types.submodule subModule)) default);
|
# expr = slib.parseOption (evalType (lib.types.listOf (lib.types.submodule subModule)) default);
|
||||||
expected = {
|
# expected = {
|
||||||
type = "array";
|
# type = "array";
|
||||||
items = {
|
# items = {
|
||||||
type = "object";
|
# type = "object";
|
||||||
additionalProperties = false;
|
# additionalProperties = false;
|
||||||
properties = {
|
# properties = {
|
||||||
opt = {
|
# opt = {
|
||||||
type = "boolean";
|
# type = "boolean";
|
||||||
default = true;
|
# default = true;
|
||||||
inherit description;
|
# inherit description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
inherit default description;
|
# inherit default description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
|
|
||||||
testEither =
|
# testEither =
|
||||||
let
|
# let
|
||||||
default = "foo";
|
# default = "foo";
|
||||||
in
|
# in
|
||||||
{
|
# {
|
||||||
expr = slib.parseOption (evalType (lib.types.either lib.types.bool lib.types.str) default);
|
# expr = slib.parseOption (evalType (lib.types.either lib.types.bool lib.types.str) default);
|
||||||
expected = {
|
# expected = {
|
||||||
oneOf = [
|
# oneOf = [
|
||||||
{ type = "boolean"; }
|
# { type = "boolean"; }
|
||||||
{ type = "string"; }
|
# { type = "string"; }
|
||||||
];
|
# ];
|
||||||
inherit default description;
|
# inherit default description;
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
|
|
||||||
testAnything =
|
# testAnything =
|
||||||
let
|
# let
|
||||||
default = "foo";
|
# default = "foo";
|
||||||
in
|
# in
|
||||||
{
|
# {
|
||||||
expr = slib.parseOption (evalType lib.types.anything default);
|
# expr = slib.parseOption (evalType lib.types.anything default);
|
||||||
expected = {
|
# expected = {
|
||||||
inherit default description;
|
# inherit default description;
|
||||||
type = [
|
# type = [
|
||||||
"boolean"
|
# "boolean"
|
||||||
"integer"
|
# "integer"
|
||||||
"number"
|
# "number"
|
||||||
"string"
|
# "string"
|
||||||
"array"
|
# "array"
|
||||||
"object"
|
# "object"
|
||||||
"null"
|
# "null"
|
||||||
];
|
# ];
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
|
|
||||||
testUnspecified =
|
# testUnspecified =
|
||||||
let
|
# let
|
||||||
default = "foo";
|
# default = "foo";
|
||||||
in
|
# in
|
||||||
{
|
# {
|
||||||
expr = slib.parseOption (evalType lib.types.unspecified default);
|
# expr = slib.parseOption (evalType lib.types.unspecified default);
|
||||||
expected = {
|
# expected = {
|
||||||
inherit default description;
|
# inherit default description;
|
||||||
type = [
|
# type = [
|
||||||
"boolean"
|
# "boolean"
|
||||||
"integer"
|
# "integer"
|
||||||
"number"
|
# "number"
|
||||||
"string"
|
# "string"
|
||||||
"array"
|
# "array"
|
||||||
"object"
|
# "object"
|
||||||
"null"
|
# "null"
|
||||||
];
|
# ];
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
|
|
||||||
testRaw =
|
# testRaw =
|
||||||
let
|
# let
|
||||||
default = "foo";
|
# default = "foo";
|
||||||
in
|
# in
|
||||||
{
|
# {
|
||||||
expr = slib.parseOption (evalType lib.types.raw default);
|
# expr = slib.parseOption (evalType lib.types.raw default);
|
||||||
expected = {
|
# expected = {
|
||||||
inherit default description;
|
# inherit default description;
|
||||||
type = [
|
# type = [
|
||||||
"boolean"
|
# "boolean"
|
||||||
"integer"
|
# "integer"
|
||||||
"number"
|
# "number"
|
||||||
"string"
|
# "string"
|
||||||
"array"
|
# "array"
|
||||||
"object"
|
# "object"
|
||||||
"null"
|
# "null"
|
||||||
];
|
# ];
|
||||||
};
|
# };
|
||||||
};
|
# };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,3 +36,4 @@ class Inventory:
|
|||||||
meta: Meta
|
meta: Meta
|
||||||
machines: dict[str, Machine] = field(default_factory = dict)
|
machines: dict[str, Machine] = field(default_factory = dict)
|
||||||
services: dict[str, Service] = field(default_factory = dict)
|
services: dict[str, Service] = field(default_factory = dict)
|
||||||
|
tags: dict[str, list[Any]] = field(default_factory = dict)
|
||||||
|
|||||||
Reference in New Issue
Block a user