Merge pull request 'chore(lib/select): move into subfolder with a test file' (#3175) from hsjobeki/clan-core:lib-cleanup into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/3175
This commit is contained in:
@@ -39,4 +39,45 @@
|
||||
acc ++ tagMembers
|
||||
) [ ] members.tags or [ ]);
|
||||
};
|
||||
/**
|
||||
Checks whether a module has a specific class
|
||||
|
||||
# Arguments
|
||||
- `module` The module to check.
|
||||
|
||||
# Returns
|
||||
- `string` | null: The specified class, or null if the class is not set
|
||||
|
||||
# Throws
|
||||
- If the module is not a valid module
|
||||
- If the module has a type that is not supported
|
||||
*/
|
||||
getModuleClass =
|
||||
module:
|
||||
let
|
||||
loadModuleForClassCheck =
|
||||
m:
|
||||
# Logic path adapted from nixpkgs/lib/modules.nix
|
||||
if lib.isFunction m then
|
||||
let
|
||||
args = lib.functionArgs m;
|
||||
in
|
||||
m args
|
||||
else if lib.isAttrs m then
|
||||
# module doesn't have a _type attribute
|
||||
if m._type or "module" == "module" then
|
||||
m
|
||||
# module has a _type set but it is not "module"
|
||||
else if m._type == "if" || m._type == "override" then
|
||||
throw "Module modifiers are not supported yet. Got: ${m._type}"
|
||||
else
|
||||
throw "Unsupported module type ${lib.typeOf m}"
|
||||
else if lib.isList m then
|
||||
throw "Invalid or unsupported module type ${lib.typeOf m}"
|
||||
else
|
||||
import m;
|
||||
|
||||
loaded = loadModuleForClassCheck module;
|
||||
in
|
||||
if loaded ? _class then loaded._class else null;
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ let
|
||||
resolvedModule =
|
||||
resolvedModuleSet.${instance.module.name}
|
||||
or (throw "flake doesn't provide clan-module with name ${instance.module.name}");
|
||||
moduleClass = clanLib.inventory.getModuleClass resolvedModule;
|
||||
|
||||
# Every instance includes machines via roles
|
||||
# :: { client :: ... }
|
||||
@@ -86,13 +87,13 @@ let
|
||||
machineName:
|
||||
let
|
||||
machineSettings = instance.roles.${roleName}.machines.${machineName}.settings or { };
|
||||
# TODO: tag settings
|
||||
# Wait for this feature until option introspection for 'settings' is done.
|
||||
# This might get too complex to handle otherwise.
|
||||
# settingsViaTags = lib.filterAttrs (
|
||||
# tagName: _: machineHasTag machineName tagName
|
||||
# ) instance.roles.${roleName}.tags;
|
||||
in
|
||||
# TODO: tag settings
|
||||
# Wait for this feature until option introspection for 'settings' is done.
|
||||
# This might get too complex to handle otherwise.
|
||||
# settingsViaTags = lib.filterAttrs (
|
||||
# tagName: _: machineHasTag machineName tagName
|
||||
# ) instance.roles.${roleName}.tags;
|
||||
{
|
||||
# TODO: Do we want to wrap settings with
|
||||
# setDefaultModuleLocation "inventory.instances.${instanceName}.roles.${roleName}.tags.${tagName}";
|
||||
@@ -112,20 +113,29 @@ let
|
||||
in
|
||||
{
|
||||
inherit (instance) module;
|
||||
inherit resolvedModule instanceRoles;
|
||||
inherit resolvedModule instanceRoles moduleClass;
|
||||
}
|
||||
) inventory.instances;
|
||||
|
||||
# TODO: Eagerly check the _class of the resolved module
|
||||
importedModulesEvaluated = lib.mapAttrs (
|
||||
_module_ident: instances:
|
||||
let
|
||||
matchedClass = "clan.service";
|
||||
instance = (builtins.head instances).instance;
|
||||
classCheckedModule =
|
||||
if instance.moduleClass == matchedClass then
|
||||
instance.resolvedModule
|
||||
else
|
||||
(throw ''Module '${instance.module.name}' is not a valid '${matchedClass}' module. Got module with class:${builtins.toJSON instance.moduleClass}'');
|
||||
in
|
||||
(lib.evalModules {
|
||||
class = "clan.service";
|
||||
class = matchedClass;
|
||||
modules =
|
||||
[
|
||||
./service-module.nix
|
||||
# Import the resolved module
|
||||
(builtins.head instances).instance.resolvedModule
|
||||
classCheckedModule
|
||||
]
|
||||
# Include all the instances that correlate to the resolved module
|
||||
++ (builtins.map (v: {
|
||||
|
||||
@@ -317,20 +317,24 @@ in
|
||||
*/
|
||||
v: instanceName: machineName:
|
||||
(lib.evalModules {
|
||||
specialArgs = {
|
||||
inherit instanceName;
|
||||
machine = {
|
||||
name = machineName;
|
||||
specialArgs =
|
||||
let
|
||||
roles = applySettings instanceName config.instances.${instanceName};
|
||||
in
|
||||
{
|
||||
inherit instanceName roles;
|
||||
machine = {
|
||||
name = machineName;
|
||||
roles = lib.attrNames (lib.filterAttrs (_n: v: v.machines ? ${machineName}) roles);
|
||||
};
|
||||
settings = (
|
||||
makeExtensibleConfig evalMachineSettings {
|
||||
inherit roleName instanceName machineName;
|
||||
settings =
|
||||
config.instances.${instanceName}.roles.${roleName}.machines.${machineName}.settings or { };
|
||||
}
|
||||
);
|
||||
};
|
||||
settings = (
|
||||
makeExtensibleConfig evalMachineSettings {
|
||||
inherit roleName instanceName machineName;
|
||||
settings =
|
||||
config.instances.${instanceName}.roles.${roleName}.machines.${machineName}.settings or { };
|
||||
}
|
||||
);
|
||||
};
|
||||
modules = [ v ];
|
||||
}).config;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,15 @@ let
|
||||
}).config;
|
||||
|
||||
flakeInputsFixture = {
|
||||
# Example upstream module
|
||||
upstream.clan.modules = {
|
||||
uzzi = {
|
||||
_class = "clan.service";
|
||||
manifest = {
|
||||
name = "uzzi-from-upstream";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
callInventoryAdapter =
|
||||
@@ -32,6 +41,7 @@ let
|
||||
};
|
||||
in
|
||||
{
|
||||
resolve_module_spec = import ./import_module_spec.nix { inherit lib callInventoryAdapter; };
|
||||
test_simple =
|
||||
let
|
||||
res = callInventoryAdapter {
|
||||
@@ -57,6 +67,7 @@ in
|
||||
# We might change the attribute name in the future
|
||||
expr = res.importedModulesEvaluated ? "self-simple-module";
|
||||
expected = true;
|
||||
inherit res;
|
||||
};
|
||||
|
||||
# A module can be imported multiple times
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
{ callInventoryAdapter, ... }:
|
||||
let
|
||||
# Authored module
|
||||
# A minimal module looks like this
|
||||
# It isn't exactly doing anything but it's a valid module that produces an output
|
||||
modules."A" = {
|
||||
_class = "clan.service";
|
||||
manifest = {
|
||||
name = "network";
|
||||
};
|
||||
};
|
||||
|
||||
modules."B" =
|
||||
{ ... }:
|
||||
{
|
||||
options.stuff = "legacy-clan-service";
|
||||
};
|
||||
|
||||
machines = {
|
||||
jon = { };
|
||||
sara = { };
|
||||
};
|
||||
|
||||
resolve =
|
||||
spec:
|
||||
callInventoryAdapter {
|
||||
inherit modules machines;
|
||||
instances."instance_foo" = {
|
||||
module = spec;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
test_import_local_module_by_name = {
|
||||
expr = (resolve { name = "A"; }).importedModuleWithInstances.instance_foo.resolvedModule;
|
||||
expected = {
|
||||
_class = "clan.service";
|
||||
manifest = {
|
||||
name = "network";
|
||||
};
|
||||
};
|
||||
};
|
||||
test_import_remote_module_by_name = {
|
||||
expr =
|
||||
(resolve {
|
||||
name = "uzzi";
|
||||
input = "upstream";
|
||||
}).importedModuleWithInstances.instance_foo.resolvedModule;
|
||||
expected = {
|
||||
_class = "clan.service";
|
||||
manifest = {
|
||||
name = "uzzi-from-upstream";
|
||||
};
|
||||
};
|
||||
};
|
||||
# Currently this should fail
|
||||
# TODO: Can we implement a default wrapper to make migration easy?
|
||||
test_import_local_legacy_module = {
|
||||
expr = (resolve { name = "B"; }).allMachines;
|
||||
expectedError = {
|
||||
type = "ThrownError";
|
||||
msg = "Module 'B' is not a valid 'clan.service' module.*";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -10,6 +10,7 @@ let
|
||||
};
|
||||
# Define two roles with unmergeable interfaces
|
||||
# Both define some 'timeout' but with completely different types.
|
||||
roles.controller = { };
|
||||
roles.peer.interface =
|
||||
{ lib, ... }:
|
||||
{
|
||||
@@ -23,6 +24,7 @@ let
|
||||
instanceName,
|
||||
settings,
|
||||
machine,
|
||||
roles,
|
||||
...
|
||||
}:
|
||||
let
|
||||
@@ -35,7 +37,12 @@ let
|
||||
in
|
||||
{
|
||||
nixosModule = {
|
||||
inherit instanceName settings machine;
|
||||
inherit
|
||||
instanceName
|
||||
settings
|
||||
machine
|
||||
roles
|
||||
;
|
||||
|
||||
# We are double vendoring the settings
|
||||
# To test that we can do it indefinitely
|
||||
@@ -64,6 +71,7 @@ let
|
||||
roles.peer = {
|
||||
settings.timeout = "foo-peer";
|
||||
};
|
||||
roles.controller.machines.jon = { };
|
||||
};
|
||||
instances."instance_bar" = {
|
||||
module = {
|
||||
@@ -73,6 +81,8 @@ let
|
||||
settings.timeout = "bar-peer-jon";
|
||||
};
|
||||
};
|
||||
# TODO: move this into a seperate test.
|
||||
# Seperate out the check that this module is never imported
|
||||
# import the module "B" (undefined)
|
||||
# All machines have this instance
|
||||
instances."instance_zaza" = {
|
||||
@@ -108,17 +118,9 @@ in
|
||||
# roles = peer
|
||||
# machines = jon
|
||||
settings = filterInternals res.importedModulesEvaluated.self-A.config.result.allRoles.peer.allInstances.instance_foo.allMachines.jon.nixosModule.settings;
|
||||
machine = mapInternalsRecursive res.importedModulesEvaluated.self-A.config.result.allRoles.peer.allInstances.instance_foo.allMachines.jon.nixosModule.machine;
|
||||
|
||||
# hasRoleSettings =
|
||||
# res.importedModulesEvaluated.self-A.config.result.allMachines.jon.nixosModule.instance_foo.roles.peer ? settings;
|
||||
|
||||
# # settings are specific.
|
||||
# # Below we access:
|
||||
# # instance = instance_foo
|
||||
# # roles = peer
|
||||
# # machines = *
|
||||
# specificRoleSettings = filterInternals res.importedModulesEvaluated.self-A.config.result.allMachines.jon.nixosModule.instance_foo.roles.peer.settings;
|
||||
machine =
|
||||
res.importedModulesEvaluated.self-A.config.result.allRoles.peer.allInstances.instance_foo.allMachines.jon.nixosModule.machine;
|
||||
roles = mapInternalsRecursive res.importedModulesEvaluated.self-A.config.result.allRoles.peer.allInstances.instance_foo.allMachines.jon.nixosModule.roles;
|
||||
};
|
||||
expected = {
|
||||
instanceName = "instance_foo";
|
||||
@@ -127,21 +129,37 @@ in
|
||||
};
|
||||
machine = {
|
||||
name = "jon";
|
||||
roles = {
|
||||
peer = {
|
||||
machines = {
|
||||
jon = {
|
||||
settings = {
|
||||
__functor = "__functor";
|
||||
timeout = "foo-peer-jon";
|
||||
};
|
||||
roles = [
|
||||
"controller"
|
||||
"peer"
|
||||
];
|
||||
};
|
||||
roles = {
|
||||
controller = {
|
||||
machines = {
|
||||
jon = {
|
||||
settings = {
|
||||
__functor = "__functor";
|
||||
};
|
||||
};
|
||||
settings = {
|
||||
__functor = "__functor";
|
||||
timeout = "foo-peer";
|
||||
};
|
||||
settings = {
|
||||
__functor = "__functor";
|
||||
};
|
||||
};
|
||||
peer = {
|
||||
machines = {
|
||||
jon = {
|
||||
settings = {
|
||||
__functor = "__functor";
|
||||
timeout = "foo-peer-jon";
|
||||
};
|
||||
};
|
||||
};
|
||||
settings = {
|
||||
__functor = "__functor";
|
||||
timeout = "foo-peer";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -26,9 +26,11 @@ let
|
||||
};
|
||||
|
||||
perMachine =
|
||||
{ instances, ... }:
|
||||
{ instances, machine, ... }:
|
||||
{
|
||||
nixosModule = instances;
|
||||
nixosModule = {
|
||||
inherit instances machine;
|
||||
};
|
||||
};
|
||||
};
|
||||
machines = {
|
||||
@@ -71,9 +73,10 @@ in
|
||||
|
||||
# settings should evaluate
|
||||
test_per_machine_receives_instance_settings = {
|
||||
inherit res;
|
||||
expr = {
|
||||
hasMachineSettings =
|
||||
res.importedModulesEvaluated.self-A.config.result.allMachines.jon.nixosModule.instance_foo.roles.peer.machines.jon
|
||||
res.importedModulesEvaluated.self-A.config.result.allMachines.jon.nixosModule.instances.instance_foo.roles.peer.machines.jon
|
||||
? settings;
|
||||
|
||||
# settings are specific.
|
||||
@@ -81,10 +84,10 @@ in
|
||||
# instance = instance_foo
|
||||
# roles = peer
|
||||
# machines = jon
|
||||
specificMachineSettings = filterInternals res.importedModulesEvaluated.self-A.config.result.allMachines.jon.nixosModule.instance_foo.roles.peer.machines.jon.settings;
|
||||
specificMachineSettings = filterInternals res.importedModulesEvaluated.self-A.config.result.allMachines.jon.nixosModule.instances.instance_foo.roles.peer.machines.jon.settings;
|
||||
|
||||
hasRoleSettings =
|
||||
res.importedModulesEvaluated.self-A.config.result.allMachines.jon.nixosModule.instance_foo.roles.peer
|
||||
res.importedModulesEvaluated.self-A.config.result.allMachines.jon.nixosModule.instances.instance_foo.roles.peer
|
||||
? settings;
|
||||
|
||||
# settings are specific.
|
||||
@@ -92,7 +95,7 @@ in
|
||||
# instance = instance_foo
|
||||
# roles = peer
|
||||
# machines = *
|
||||
specificRoleSettings = filterInternals res.importedModulesEvaluated.self-A.config.result.allMachines.jon.nixosModule.instance_foo.roles.peer.settings;
|
||||
specificRoleSettings = filterInternals res.importedModulesEvaluated.self-A.config.result.allMachines.jon.nixosModule.instances.instance_foo.roles.peer.settings;
|
||||
};
|
||||
expected = {
|
||||
hasMachineSettings = true;
|
||||
|
||||
Reference in New Issue
Block a user