Compare commits
5 Commits
speed-up-v
...
revert-del
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
392211d65e | ||
|
|
dc6648520f | ||
|
|
0093836272 | ||
|
|
e026ada443 | ||
|
|
38bb2dfb56 |
37
clanServices/state-version/README.md
Normal file
37
clanServices/state-version/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
This service generates the `system.stateVersion` of the nixos installation
|
||||
automatically.
|
||||
|
||||
Possible values:
|
||||
[system.stateVersion](https://search.nixos.org/options?channel=unstable&show=system.stateVersion&from=0&size=50&sort=relevance&type=packages&query=stateVersion)
|
||||
|
||||
## Usage
|
||||
|
||||
The following configuration will set `stateVersion` for all machines:
|
||||
|
||||
```
|
||||
inventory.instances = {
|
||||
state-version = {
|
||||
module = {
|
||||
name = "state-version";
|
||||
input = "clan";
|
||||
};
|
||||
roles.default.tags.all = { };
|
||||
};
|
||||
```
|
||||
|
||||
## Migration
|
||||
|
||||
If you are already setting `system.stateVersion`, either let the automatic
|
||||
generation happen, or trigger the generation manually for the machine. The
|
||||
service will take the specified version, if one is already supplied through the
|
||||
config.
|
||||
|
||||
To manually generate the version for a specified machine run:
|
||||
|
||||
```
|
||||
clan vars generate [MACHINE]
|
||||
```
|
||||
|
||||
If the setting was already set, you can then remove `system.stateVersion` from
|
||||
your machine configuration. For new machines, just import the service as shown
|
||||
above.
|
||||
39
clanServices/state-version/default.nix
Normal file
39
clanServices/state-version/default.nix
Normal file
@@ -0,0 +1,39 @@
|
||||
{ ... }:
|
||||
{
|
||||
_class = "clan.service";
|
||||
manifest.name = "clan-core/state-version";
|
||||
manifest.description = "Automatically generate the state version of the nixos installation.";
|
||||
manifest.categories = [ "System" ];
|
||||
|
||||
roles.default = {
|
||||
|
||||
perInstance =
|
||||
{ ... }:
|
||||
{
|
||||
nixosModule =
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
var = config.clan.core.vars.generators.state-version.files.version or { };
|
||||
in
|
||||
{
|
||||
system.stateVersion = lib.mkDefault (lib.removeSuffix "\n" var.value);
|
||||
|
||||
clan.core.vars.generators.state-version = {
|
||||
files.version = {
|
||||
secret = false;
|
||||
value = lib.mkDefault config.system.nixos.release;
|
||||
};
|
||||
runtimeInputs = [ ];
|
||||
script = ''
|
||||
echo -n ${config.system.stateVersion} > "$out"/version
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
16
clanServices/state-version/flake-module.nix
Normal file
16
clanServices/state-version/flake-module.nix
Normal file
@@ -0,0 +1,16 @@
|
||||
{ lib, ... }:
|
||||
let
|
||||
module = lib.modules.importApply ./default.nix { };
|
||||
in
|
||||
{
|
||||
clan.modules.state-version = module;
|
||||
perSystem =
|
||||
{ ... }:
|
||||
{
|
||||
clan.nixosTests.state-version = {
|
||||
imports = [ ./tests/vm/default.nix ];
|
||||
|
||||
clan.modules."@clan/state-version" = module;
|
||||
};
|
||||
};
|
||||
}
|
||||
21
clanServices/state-version/tests/vm/default.nix
Normal file
21
clanServices/state-version/tests/vm/default.nix
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
name = "state-version";
|
||||
|
||||
clan = {
|
||||
directory = ./.;
|
||||
inventory = {
|
||||
machines.server = { };
|
||||
instances.default = {
|
||||
module.name = "@clan/state-version";
|
||||
module.input = "self";
|
||||
roles.default.machines."server" = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
nodes.server = { };
|
||||
|
||||
testScript = ''
|
||||
start_all()
|
||||
'';
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"publickey": "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg",
|
||||
"type": "age"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
25.11
|
||||
@@ -412,27 +412,6 @@ in
|
||||
```
|
||||
'';
|
||||
};
|
||||
options.services = mkOption {
|
||||
visible = false;
|
||||
type = attrsWith {
|
||||
placeholder = "serviceName";
|
||||
elemType = submoduleWith {
|
||||
modules = [
|
||||
{
|
||||
_module.args._ctx = _ctx ++ [
|
||||
config.manifest.name
|
||||
"roles"
|
||||
roleName
|
||||
"perInstance"
|
||||
"services"
|
||||
];
|
||||
}
|
||||
./service-module.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
@@ -537,25 +516,6 @@ in
|
||||
```
|
||||
'';
|
||||
};
|
||||
options.services = mkOption {
|
||||
visible = false;
|
||||
type = attrsWith {
|
||||
placeholder = "serviceName";
|
||||
elemType = submoduleWith {
|
||||
modules = [
|
||||
{
|
||||
_module.args._ctx = _ctx ++ [
|
||||
config.manifest.name
|
||||
"perMachine"
|
||||
"services"
|
||||
];
|
||||
}
|
||||
./service-module.nix
|
||||
];
|
||||
};
|
||||
};
|
||||
default = { };
|
||||
};
|
||||
})
|
||||
];
|
||||
};
|
||||
@@ -727,40 +687,18 @@ in
|
||||
instanceAcc: instanceName: instance:
|
||||
instanceAcc
|
||||
// {
|
||||
nixosModules =
|
||||
(
|
||||
(lib.mapAttrsToList (
|
||||
nestedServiceName: serviceModule:
|
||||
let
|
||||
unmatchedMachines = lib.attrNames (
|
||||
lib.removeAttrs serviceModule.result.final (lib.attrNames config.result.allMachines)
|
||||
);
|
||||
in
|
||||
if unmatchedMachines != [ ] then
|
||||
throw ''
|
||||
The following machines are not part of the parent service: ${builtins.toJSON unmatchedMachines}
|
||||
Either remove the machines, or include them into the parent via a role.
|
||||
(Added via roles.${roleName}.perInstance.services.${nestedServiceName})
|
||||
|
||||
${errorContext}
|
||||
''
|
||||
else
|
||||
serviceModule.result.final.${machineName}.nixosModule
|
||||
) instance.allMachines.${machineName}.services or { })
|
||||
|
||||
)
|
||||
++ (
|
||||
if instance.allMachines.${machineName}.nixosModule or { } != { } then
|
||||
instanceAcc.nixosModules
|
||||
++ [
|
||||
(lib.setDefaultModuleLocation
|
||||
"Via instances.${instanceName}.roles.${roleName}.machines.${machineName}"
|
||||
instance.allMachines.${machineName}.nixosModule
|
||||
)
|
||||
]
|
||||
else
|
||||
instanceAcc.nixosModules
|
||||
);
|
||||
nixosModules = (
|
||||
if instance.allMachines.${machineName}.nixosModule or { } != { } then
|
||||
instanceAcc.nixosModules
|
||||
++ [
|
||||
(lib.setDefaultModuleLocation
|
||||
"Via instances.${instanceName}.roles.${roleName}.machines.${machineName}"
|
||||
instance.allMachines.${machineName}.nixosModule
|
||||
)
|
||||
]
|
||||
else
|
||||
instanceAcc.nixosModules
|
||||
);
|
||||
}
|
||||
) roleAcc role.allInstances
|
||||
)
|
||||
@@ -773,38 +711,18 @@ in
|
||||
{
|
||||
inherit instanceResults machineResult;
|
||||
nixosModule = {
|
||||
imports =
|
||||
[
|
||||
# include service assertions:
|
||||
(
|
||||
let
|
||||
failedAssertions = (lib.filterAttrs (_: v: !v.assertion) config.result.assertions);
|
||||
in
|
||||
{
|
||||
assertions = lib.attrValues failedAssertions;
|
||||
}
|
||||
)
|
||||
(lib.setDefaultModuleLocation "Via ${config.manifest.name}.perMachine - machine='${machineName}';" machineResult.nixosModule)
|
||||
]
|
||||
++ (lib.mapAttrsToList (
|
||||
nestedServiceName: serviceModule:
|
||||
imports = [
|
||||
# include service assertions:
|
||||
(
|
||||
let
|
||||
unmatchedMachines = lib.attrNames (
|
||||
lib.removeAttrs serviceModule.result.final (lib.attrNames config.result.allMachines)
|
||||
);
|
||||
failedAssertions = (lib.filterAttrs (_: v: !v.assertion) config.result.assertions);
|
||||
in
|
||||
if unmatchedMachines != [ ] then
|
||||
throw ''
|
||||
The following machines are not part of the parent service: ${builtins.toJSON unmatchedMachines}
|
||||
Either remove the machines, or include them into the parent via a role.
|
||||
(Added via perMachine.services.${nestedServiceName})
|
||||
|
||||
${errorContext}
|
||||
''
|
||||
else
|
||||
serviceModule.result.final.${machineName}.nixosModule
|
||||
) machineResult.services)
|
||||
++ instanceResults.nixosModules;
|
||||
{
|
||||
assertions = lib.attrValues failedAssertions;
|
||||
}
|
||||
)
|
||||
(lib.setDefaultModuleLocation "Via ${config.manifest.name}.perMachine - machine='${machineName}';" machineResult.nixosModule)
|
||||
] ++ instanceResults.nixosModules;
|
||||
};
|
||||
}
|
||||
) config.result.allMachines;
|
||||
|
||||
@@ -288,5 +288,4 @@ in
|
||||
|
||||
per_machine_args = import ./per_machine_args.nix { inherit lib callInventoryAdapter; };
|
||||
per_instance_args = import ./per_instance_args.nix { inherit lib callInventoryAdapter; };
|
||||
nested = import ./nested_services { inherit lib clanLib; };
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
{ clanLib, lib, ... }:
|
||||
{
|
||||
test_simple = import ./simple.nix { inherit clanLib lib; };
|
||||
|
||||
test_multi_machine = import ./multi_machine.nix { inherit clanLib lib; };
|
||||
|
||||
test_multi_import_duplication = import ./multi_import_duplication.nix { inherit clanLib lib; };
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
{ clanLib, lib, ... }:
|
||||
let
|
||||
# Potentially imported many times
|
||||
# To add the ssh key
|
||||
example-admin = (
|
||||
{ lib, ... }:
|
||||
{
|
||||
manifest.name = "example-admin";
|
||||
|
||||
roles.client.interface = {
|
||||
options.keys = lib.mkOption { };
|
||||
};
|
||||
|
||||
roles.client.perInstance =
|
||||
{ settings, ... }:
|
||||
{
|
||||
nixosModule = {
|
||||
inherit (settings) keys;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
consumer-A =
|
||||
{ ... }:
|
||||
{
|
||||
manifest.name = "consumer-A";
|
||||
|
||||
instances.foo = {
|
||||
roles.server.machines."jon" = { };
|
||||
};
|
||||
instances.bar = {
|
||||
roles.server.machines."jon" = { };
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
perInstance =
|
||||
{ machine, instanceName, ... }:
|
||||
{
|
||||
services."example-admin" = {
|
||||
imports = [
|
||||
example-admin
|
||||
];
|
||||
instances."${instanceName}" = {
|
||||
roles.client.machines.${machine.name} = {
|
||||
settings.keys = [ "pubkey-1" ];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
consumer-B =
|
||||
{ ... }:
|
||||
{
|
||||
manifest.name = "consumer-A";
|
||||
|
||||
instances.foo = {
|
||||
roles.server.machines."jon" = { };
|
||||
};
|
||||
instances.bar = {
|
||||
roles.server.machines."jon" = { };
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
perInstance =
|
||||
{ machine, instanceName, ... }:
|
||||
{
|
||||
services."example-admin" = {
|
||||
imports = [
|
||||
example-admin
|
||||
];
|
||||
instances."${instanceName}" = {
|
||||
roles.client.machines.${machine.name} = {
|
||||
settings.keys = [
|
||||
"pubkey-1"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
eval = clanLib.evalService {
|
||||
modules = [
|
||||
(consumer-A)
|
||||
];
|
||||
prefix = [ ];
|
||||
};
|
||||
eval2 = clanLib.evalService {
|
||||
modules = [
|
||||
(consumer-B)
|
||||
];
|
||||
prefix = [ ];
|
||||
};
|
||||
|
||||
evalNixos = lib.evalModules {
|
||||
modules = [
|
||||
{
|
||||
options.assertions = lib.mkOption { };
|
||||
# This is suboptimal
|
||||
options.keys = lib.mkOption { };
|
||||
}
|
||||
eval.config.result.final.jon.nixosModule
|
||||
eval2.config.result.final.jon.nixosModule
|
||||
];
|
||||
};
|
||||
in
|
||||
{
|
||||
# Check that the nixos system has the settings from the nested module, as well as those from the "perMachine" and "perInstance"
|
||||
inherit eval;
|
||||
expr = evalNixos.config;
|
||||
expected = {
|
||||
assertions = [ ];
|
||||
# TODO: Some deduplication mechanism is nice
|
||||
# Could add types.set or do 'apply = unique', or something else ?
|
||||
keys = [
|
||||
"pubkey-1"
|
||||
"pubkey-1"
|
||||
"pubkey-1"
|
||||
"pubkey-1"
|
||||
];
|
||||
};
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
{ clanLib, lib, ... }:
|
||||
let
|
||||
service-B = (
|
||||
{ lib, ... }:
|
||||
{
|
||||
manifest.name = "service-B";
|
||||
|
||||
roles.client.interface = {
|
||||
options.user = lib.mkOption { };
|
||||
options.host = lib.mkOption { };
|
||||
};
|
||||
roles.client.perInstance =
|
||||
{ settings, instanceName, ... }:
|
||||
{
|
||||
nixosModule = {
|
||||
units.${instanceName} = {
|
||||
script = settings.user + "@" + settings.host;
|
||||
};
|
||||
};
|
||||
};
|
||||
perMachine =
|
||||
{ ... }:
|
||||
{
|
||||
nixosModule = {
|
||||
ssh.enable = true;
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
service-A =
|
||||
{ ... }:
|
||||
{
|
||||
manifest.name = "service-A";
|
||||
|
||||
instances.foo = {
|
||||
roles.server.machines."jon" = { };
|
||||
roles.server.machines."sara" = { };
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
perInstance =
|
||||
{ machine, instanceName, ... }:
|
||||
{
|
||||
services."B" = {
|
||||
imports = [
|
||||
service-B
|
||||
];
|
||||
instances."A-${instanceName}-B" = {
|
||||
roles.client.machines.${machine.name} = {
|
||||
settings.user = "johnny";
|
||||
settings.host = machine.name;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
eval = clanLib.evalService {
|
||||
modules = [
|
||||
(service-A)
|
||||
];
|
||||
prefix = [ ];
|
||||
};
|
||||
|
||||
evalNixos = lib.mapAttrs (
|
||||
_n: v:
|
||||
(lib.evalModules {
|
||||
modules = [
|
||||
{
|
||||
options.assertions = lib.mkOption { };
|
||||
options.units = lib.mkOption { };
|
||||
options.ssh = lib.mkOption { };
|
||||
}
|
||||
v.nixosModule
|
||||
];
|
||||
}).config
|
||||
) eval.config.result.final;
|
||||
in
|
||||
{
|
||||
# Check that the nixos system has the settings from the nested module, as well as those from the "perMachine" and "perInstance"
|
||||
inherit eval;
|
||||
expr = evalNixos;
|
||||
expected = {
|
||||
jon = {
|
||||
assertions = [ ];
|
||||
ssh = {
|
||||
enable = true;
|
||||
};
|
||||
units = {
|
||||
A-foo-B = {
|
||||
script = "johnny@jon";
|
||||
};
|
||||
};
|
||||
};
|
||||
sara = {
|
||||
assertions = [ ];
|
||||
ssh = {
|
||||
enable = true;
|
||||
};
|
||||
units = {
|
||||
A-foo-B = {
|
||||
script = "johnny@sara";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
/*
|
||||
service-B :: Service
|
||||
exports a nixosModule which set "address" and "hostname"
|
||||
Note: How we use null together with mkIf to create optional values.
|
||||
This is a method, to create mergable modules
|
||||
|
||||
service-A :: Service
|
||||
|
||||
service-A.roles.server.perInstance.services."B"
|
||||
imports service-B
|
||||
configures a client with hostname = "johnny"
|
||||
|
||||
service-A.perMachine.services."B"
|
||||
imports service-B
|
||||
configures a client with address = "root"
|
||||
*/
|
||||
{ clanLib, lib, ... }:
|
||||
let
|
||||
service-B = (
|
||||
{ lib, ... }:
|
||||
{
|
||||
manifest.name = "service-B";
|
||||
|
||||
roles.client.interface = {
|
||||
options.hostname = lib.mkOption { default = null; };
|
||||
options.address = lib.mkOption { default = null; };
|
||||
};
|
||||
roles.client.perInstance =
|
||||
{ settings, ... }:
|
||||
{
|
||||
nixosModule = {
|
||||
imports = [
|
||||
# Only export the value that is actually set.
|
||||
(lib.mkIf (settings.hostname != null) {
|
||||
hostname = settings.hostname;
|
||||
})
|
||||
(lib.mkIf (settings.address != null) {
|
||||
address = settings.address;
|
||||
})
|
||||
];
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
service-A =
|
||||
{ ... }:
|
||||
{
|
||||
manifest.name = "service-A";
|
||||
|
||||
instances.foo = {
|
||||
roles.server.machines."jon" = { };
|
||||
};
|
||||
instances.bar = {
|
||||
roles.server.machines."jon" = { };
|
||||
};
|
||||
|
||||
roles.server = {
|
||||
perInstance =
|
||||
{ machine, instanceName, ... }:
|
||||
{
|
||||
services."B" = {
|
||||
imports = [
|
||||
service-B
|
||||
];
|
||||
instances."B-for-A" = {
|
||||
roles.client.machines.${machine.name} = {
|
||||
settings.hostname = instanceName + "+johnny";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
perMachine =
|
||||
{ machine, ... }:
|
||||
{
|
||||
services."B" = {
|
||||
imports = [
|
||||
service-B
|
||||
];
|
||||
instances."B-for-A" = {
|
||||
roles.client.machines.${machine.name} = {
|
||||
settings.address = "root";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
eval = clanLib.evalService {
|
||||
modules = [
|
||||
(service-A)
|
||||
];
|
||||
prefix = [ ];
|
||||
};
|
||||
|
||||
evalNixos = lib.evalModules {
|
||||
modules = [
|
||||
{
|
||||
options.assertions = lib.mkOption { };
|
||||
options.hostname = lib.mkOption { type = lib.types.separatedString " "; };
|
||||
options.address = lib.mkOption { type = lib.types.str; };
|
||||
}
|
||||
eval.config.result.final."jon".nixosModule
|
||||
];
|
||||
};
|
||||
in
|
||||
{
|
||||
# Check that the nixos system has the settings from the nested module, as well as those from the "perMachine" and "perInstance"
|
||||
inherit eval;
|
||||
expr = evalNixos.config;
|
||||
expected = {
|
||||
address = "root";
|
||||
assertions = [ ];
|
||||
# Concatenates hostnames from both instances
|
||||
hostname = "bar+johnny foo+johnny";
|
||||
};
|
||||
}
|
||||
@@ -11,8 +11,7 @@ in
|
||||
enable = lib.mkEnableOption "automatic state-version generation.
|
||||
|
||||
The option will take the specified version, if one is already supplied through
|
||||
the config or generate one if not.
|
||||
";
|
||||
the config or generate one if not";
|
||||
};
|
||||
|
||||
config = lib.mkIf (config.clan.core.settings.state-version.enable) {
|
||||
|
||||
Reference in New Issue
Block a user