Merge pull request 'clanModules: remove unused code' (#4785) from clean-dead-code into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/4785
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -39,7 +39,6 @@ select
|
||||
# Generated files
|
||||
pkgs/clan-app/ui/api/API.json
|
||||
pkgs/clan-app/ui/api/API.ts
|
||||
pkgs/clan-app/ui/api/Inventory.ts
|
||||
pkgs/clan-app/ui/api/modules_schemas.json
|
||||
pkgs/clan-app/ui/api/schema.json
|
||||
pkgs/clan-app/ui/.fonts
|
||||
|
||||
@@ -139,33 +139,6 @@ in
|
||||
nixosTests
|
||||
// flakeOutputs
|
||||
// {
|
||||
# TODO: Automatically provide this check to downstream users to check their modules
|
||||
clan-modules-json-compatible =
|
||||
let
|
||||
allSchemas = lib.mapAttrs (
|
||||
_n: m:
|
||||
let
|
||||
schema =
|
||||
(self.clanLib.evalService {
|
||||
modules = [ m ];
|
||||
prefix = [
|
||||
"checks"
|
||||
system
|
||||
];
|
||||
}).config.result.api.schema;
|
||||
in
|
||||
schema
|
||||
) self.clan.modules;
|
||||
in
|
||||
pkgs.runCommand "combined-result"
|
||||
{
|
||||
schemaFile = builtins.toFile "schemas.json" (builtins.toJSON allSchemas);
|
||||
}
|
||||
''
|
||||
mkdir -p $out
|
||||
cat $schemaFile > $out/allSchemas.json
|
||||
'';
|
||||
|
||||
clan-core-for-checks = pkgs.runCommand "clan-core-for-checks" { } ''
|
||||
cp -r ${privateInputs.clan-core-for-checks} $out
|
||||
chmod -R +w $out
|
||||
|
||||
@@ -18,26 +18,7 @@
|
||||
inherit (self) clanModules;
|
||||
clan-core = self;
|
||||
inherit pkgs;
|
||||
evalClanModules = self.clanLib.evalClan.evalClanModules;
|
||||
modulesRolesOptions = self.clanLib.evalClan.evalClanModulesWithRoles {
|
||||
allModules = self.clanModules;
|
||||
inherit pkgs;
|
||||
clan-core = self;
|
||||
};
|
||||
};
|
||||
|
||||
# Frontmatter for clanModules
|
||||
clanModulesFrontmatter =
|
||||
let
|
||||
docs = pkgs.nixosOptionsDoc {
|
||||
options = self.clanLib.modules.frontmatterOptions;
|
||||
transformOptions = self.clanLib.docs.stripStorePathsFromDeclarations;
|
||||
};
|
||||
in
|
||||
docs.optionsJSON;
|
||||
|
||||
# Options available when imported via ` inventory.${moduleName}....${rolesName} `
|
||||
clanModulesViaRoles = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModulesViaRoles);
|
||||
|
||||
# clan service options
|
||||
clanModulesViaService = pkgs.writeText "info.json" (builtins.toJSON jsonDocs.clanModulesViaService);
|
||||
@@ -88,12 +69,10 @@
|
||||
}
|
||||
}
|
||||
export CLAN_CORE_DOCS=${jsonDocs.clanCore}/share/doc/nixos/options.json
|
||||
|
||||
# A file that contains the links to all clanModule docs
|
||||
export CLAN_MODULES_VIA_ROLES=${clanModulesViaRoles}
|
||||
export CLAN_MODULES_VIA_SERVICE=${clanModulesViaService}
|
||||
export CLAN_SERVICE_INTERFACE=${self'.legacyPackages.clan-service-module-interface}/share/doc/nixos/options.json
|
||||
# Frontmatter format for clanModules
|
||||
export CLAN_MODULES_FRONTMATTER_DOCS=${clanModulesFrontmatter}/share/doc/nixos/options.json
|
||||
|
||||
export BUILD_CLAN_PATH=${buildClanOptions}/share/doc/nixos/options.json
|
||||
|
||||
@@ -107,7 +86,6 @@
|
||||
legacyPackages = {
|
||||
inherit
|
||||
jsonDocs
|
||||
clanModulesViaRoles
|
||||
clanModulesViaService
|
||||
;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
{
|
||||
modulesRolesOptions,
|
||||
nixosOptionsDoc,
|
||||
evalClanModules,
|
||||
lib,
|
||||
pkgs,
|
||||
clan-core,
|
||||
@@ -10,21 +8,36 @@
|
||||
let
|
||||
inherit (clan-core.clanLib.docs) stripStorePathsFromDeclarations;
|
||||
transformOptions = stripStorePathsFromDeclarations;
|
||||
|
||||
nixosConfigurationWithClan =
|
||||
let
|
||||
evaled = lib.evalModules {
|
||||
class = "nixos";
|
||||
modules = [
|
||||
# Basemodule
|
||||
(
|
||||
{ config, ... }:
|
||||
{
|
||||
imports = (import (pkgs.path + "/nixos/modules/module-list.nix"));
|
||||
nixpkgs.pkgs = pkgs;
|
||||
clan.core.name = "dummy";
|
||||
system.stateVersion = config.system.nixos.release;
|
||||
# Set this to work around a bug where `clan.core.settings.machine.name`
|
||||
# is forced due to `networking.interfaces` being forced
|
||||
# somewhere in the nixpkgs options
|
||||
facter.detected.dhcp.enable = lib.mkForce false;
|
||||
}
|
||||
)
|
||||
{
|
||||
clan.core.settings.directory = clan-core;
|
||||
}
|
||||
clan-core.nixosModules.clanCore
|
||||
];
|
||||
};
|
||||
in
|
||||
evaled;
|
||||
in
|
||||
{
|
||||
|
||||
clanModulesViaRoles = lib.mapAttrs (
|
||||
_moduleName: rolesOptions:
|
||||
lib.mapAttrs (
|
||||
_roleName: options:
|
||||
(nixosOptionsDoc {
|
||||
inherit options;
|
||||
warningsAreErrors = true;
|
||||
inherit transformOptions;
|
||||
}).optionsJSON
|
||||
) rolesOptions
|
||||
) modulesRolesOptions;
|
||||
|
||||
# Test with:
|
||||
# nix build .\#legacyPackages.x86_64-linux.clanModulesViaService
|
||||
clanModulesViaService = lib.mapAttrs (
|
||||
@@ -38,7 +51,6 @@ in
|
||||
{
|
||||
roles = lib.mapAttrs (
|
||||
_roleName: role:
|
||||
|
||||
(nixosOptionsDoc {
|
||||
transformOptions =
|
||||
opt:
|
||||
@@ -54,20 +66,13 @@ in
|
||||
warningsAreErrors = true;
|
||||
}).optionsJSON
|
||||
) evaluatedService.config.roles;
|
||||
|
||||
manifest = evaluatedService.config.manifest;
|
||||
|
||||
}
|
||||
) clan-core.clan.modules;
|
||||
|
||||
clanCore =
|
||||
(nixosOptionsDoc {
|
||||
options =
|
||||
((evalClanModules {
|
||||
modules = [ ];
|
||||
inherit pkgs clan-core;
|
||||
}).options
|
||||
).clan.core or { };
|
||||
options = nixosConfigurationWithClan.options.clan.core;
|
||||
warningsAreErrors = true;
|
||||
inherit transformOptions;
|
||||
}).optionsJSON;
|
||||
|
||||
@@ -33,22 +33,13 @@ from clan_lib.errors import ClanError
|
||||
from clan_lib.services.modules import (
|
||||
CategoryInfo,
|
||||
Frontmatter,
|
||||
extract_frontmatter,
|
||||
get_roles,
|
||||
)
|
||||
|
||||
# Get environment variables
|
||||
CLAN_CORE_PATH = Path(os.environ["CLAN_CORE_PATH"])
|
||||
CLAN_CORE_DOCS = Path(os.environ["CLAN_CORE_DOCS"])
|
||||
CLAN_MODULES_FRONTMATTER_DOCS = os.environ.get("CLAN_MODULES_FRONTMATTER_DOCS")
|
||||
BUILD_CLAN_PATH = os.environ.get("BUILD_CLAN_PATH")
|
||||
|
||||
## Clan modules ##
|
||||
# Some modules can be imported via nix natively
|
||||
CLAN_MODULES_VIA_NIX = os.environ.get("CLAN_MODULES_VIA_NIX")
|
||||
# Some modules can be imported via inventory
|
||||
CLAN_MODULES_VIA_ROLES = os.environ.get("CLAN_MODULES_VIA_ROLES")
|
||||
|
||||
# Options how to author clan.modules
|
||||
# perInstance, perMachine, ...
|
||||
CLAN_SERVICE_INTERFACE = os.environ.get("CLAN_SERVICE_INTERFACE")
|
||||
@@ -190,23 +181,6 @@ def module_header(module_name: str, has_inventory_feature: bool = False) -> str:
|
||||
return f"# {module_name}{indicator}\n\n"
|
||||
|
||||
|
||||
def module_nix_usage(module_name: str) -> str:
|
||||
return f"""## Usage via Nix
|
||||
|
||||
**This module can be also imported directly in your nixos configuration. Although it is recommended to use the [inventory](../../concepts/inventory.md) interface if available.**
|
||||
|
||||
Some modules are considered 'low-level' or 'expert modules' and are not available via the inventory interface.
|
||||
|
||||
```nix
|
||||
{{config, lib, inputs, ...}}: {{
|
||||
imports = [ inputs.clan-core.clanModules.{module_name} ];
|
||||
# ...
|
||||
}}
|
||||
```
|
||||
|
||||
"""
|
||||
|
||||
|
||||
clan_core_descr = """
|
||||
`clan.core` is always present in a clan machine
|
||||
|
||||
@@ -223,68 +197,6 @@ The following options are available for this module.
|
||||
"""
|
||||
|
||||
|
||||
def produce_clan_modules_frontmatter_docs() -> None:
|
||||
if not CLAN_MODULES_FRONTMATTER_DOCS:
|
||||
msg = f"Environment variables are not set correctly: $CLAN_CORE_DOCS={CLAN_CORE_DOCS}"
|
||||
raise ClanError(msg)
|
||||
|
||||
if not OUT:
|
||||
msg = f"Environment variables are not set correctly: $out={OUT}"
|
||||
raise ClanError(msg)
|
||||
|
||||
with Path(CLAN_MODULES_FRONTMATTER_DOCS).open() as f:
|
||||
options: dict[str, dict[str, Any]] = json.load(f)
|
||||
|
||||
# header
|
||||
output = """# Frontmatter
|
||||
|
||||
Every clan module has a `frontmatter` section within its readme. It provides
|
||||
machine readable metadata about the module.
|
||||
|
||||
!!! example
|
||||
|
||||
The used format is `TOML`
|
||||
|
||||
The content is separated by `---` and the frontmatter must be placed at the very top of the `README.md` file.
|
||||
|
||||
```toml
|
||||
---
|
||||
description = "A description of the module"
|
||||
categories = ["category1", "category2"]
|
||||
|
||||
[constraints]
|
||||
roles.client.max = 10
|
||||
roles.server.min = 1
|
||||
---
|
||||
# Readme content
|
||||
...
|
||||
```
|
||||
|
||||
"""
|
||||
|
||||
output += """## Overview
|
||||
|
||||
This provides an overview of the available attributes of the `frontmatter`
|
||||
within the `README.md` of a clan module.
|
||||
|
||||
"""
|
||||
# for option_name, info in options.items():
|
||||
# if option_name == "_module.args":
|
||||
# continue
|
||||
# output += render_option(option_name, info)
|
||||
root = options_to_tree(options, debug=True)
|
||||
for option in root.suboptions:
|
||||
output += options_docs_from_tree(option, init_level=2)
|
||||
|
||||
outfile = Path(OUT) / "clanModules/frontmatter/index.md"
|
||||
outfile.parent.mkdir(
|
||||
parents=True,
|
||||
exist_ok=True,
|
||||
)
|
||||
with outfile.open("w") as of:
|
||||
of.write(output)
|
||||
|
||||
|
||||
def produce_clan_core_docs() -> None:
|
||||
if not CLAN_CORE_DOCS:
|
||||
msg = f"Environment variables are not set correctly: $CLAN_CORE_DOCS={CLAN_CORE_DOCS}"
|
||||
@@ -505,154 +417,6 @@ Learn how to use `clanServices` in practice in the [Using clanServices guide](..
|
||||
of.write(output)
|
||||
|
||||
|
||||
def produce_clan_modules_docs() -> None:
|
||||
if not CLAN_MODULES_VIA_NIX:
|
||||
msg = f"Environment variables are not set correctly: $CLAN_MODULES_VIA_NIX={CLAN_MODULES_VIA_NIX}"
|
||||
raise ClanError(msg)
|
||||
|
||||
if not CLAN_MODULES_VIA_ROLES:
|
||||
msg = f"Environment variables are not set correctly: $CLAN_MODULES_VIA_ROLES={CLAN_MODULES_VIA_ROLES}"
|
||||
raise ClanError(msg)
|
||||
|
||||
if not CLAN_CORE_PATH:
|
||||
msg = f"Environment variables are not set correctly: $CLAN_CORE_PATH={CLAN_CORE_PATH}"
|
||||
raise ClanError(msg)
|
||||
|
||||
if not OUT:
|
||||
msg = f"Environment variables are not set correctly: $out={OUT}"
|
||||
raise ClanError(msg)
|
||||
|
||||
modules_index = "# Modules Overview\n\n"
|
||||
modules_index += clan_modules_descr
|
||||
modules_index += "## Overview\n\n"
|
||||
modules_index += '<div class="grid cards" markdown>\n\n'
|
||||
|
||||
with Path(CLAN_MODULES_VIA_ROLES).open() as f2:
|
||||
role_links: dict[str, dict[str, str]] = json.load(f2)
|
||||
|
||||
with Path(CLAN_MODULES_VIA_NIX).open() as f:
|
||||
links: dict[str, str] = json.load(f)
|
||||
|
||||
for module_name, options_file in links.items():
|
||||
print(f"Rendering ClanModule: {module_name}")
|
||||
readme_file = CLAN_CORE_PATH / "clanModules" / module_name / "README.md"
|
||||
with readme_file.open() as f:
|
||||
readme = f.read()
|
||||
frontmatter: Frontmatter
|
||||
frontmatter, readme_content = extract_frontmatter(readme, str(readme_file))
|
||||
|
||||
# skip if experimental feature enabled
|
||||
if "experimental" in frontmatter.features:
|
||||
print(f"Skipping {module_name}: Experimental feature")
|
||||
continue
|
||||
|
||||
modules_index += build_option_card(module_name, frontmatter)
|
||||
|
||||
##### Print module documentation #####
|
||||
|
||||
# 1. Header
|
||||
output = module_header(module_name, "inventory" in frontmatter.features)
|
||||
|
||||
# 2. Description from README.md
|
||||
if frontmatter.description:
|
||||
output += f"*{frontmatter.description}*\n\n"
|
||||
|
||||
# 2. Deprecation note if the module is deprecated
|
||||
if "deprecated" in frontmatter.features:
|
||||
output += f"""
|
||||
!!! Warning "Deprecated"
|
||||
The `{module_name}` module is deprecated.*
|
||||
|
||||
Use 'clanServices/{module_name}' or a similar successor instead
|
||||
"""
|
||||
else:
|
||||
output += f"""
|
||||
!!! Warning "Will be deprecated"
|
||||
The `{module_name}` module might eventually be migrated to 'clanServices'*
|
||||
|
||||
See: [clanServices](../../guides/clanServices.md)
|
||||
"""
|
||||
|
||||
# 3. Categories from README.md
|
||||
output += "## Categories\n\n"
|
||||
output += render_categories(frontmatter.categories, frontmatter.categories_info)
|
||||
output += "\n---\n\n"
|
||||
|
||||
# 3. README.md content
|
||||
output += f"{readme_content}\n"
|
||||
|
||||
# 4. Usage
|
||||
##### Print usage via Inventory #####
|
||||
|
||||
# get_roles(str) -> list[str] | None
|
||||
# if not isinstance(options_file, str):
|
||||
roles = get_roles(CLAN_CORE_PATH / "clanModules" / module_name)
|
||||
if roles:
|
||||
# Render inventory usage
|
||||
output += """## Usage via Inventory\n\n"""
|
||||
output += render_roles(roles, module_name)
|
||||
for role in roles:
|
||||
role_options_file = role_links[module_name][role]
|
||||
# Abort if the options file is not found
|
||||
if not isinstance(role_options_file, str):
|
||||
print(
|
||||
f"Error: module: {module_name} in role: {role} - options file not found, Got {role_options_file}"
|
||||
)
|
||||
exit(1)
|
||||
|
||||
no_options = f"""### Options of `{role}` role
|
||||
|
||||
**The `{module_name}` `{role}` doesnt offer / require any options to be set.**
|
||||
"""
|
||||
|
||||
heading = f"""### Options of `{role}` role
|
||||
|
||||
The following options are available when using the `{role}` role.
|
||||
"""
|
||||
output += print_options(
|
||||
role_options_file,
|
||||
heading,
|
||||
no_options,
|
||||
replace_prefix=f"clan.{module_name}",
|
||||
)
|
||||
else:
|
||||
# No roles means no inventory usage
|
||||
output += """## Usage via Inventory
|
||||
|
||||
**This module cannot be used via the inventory interface.**
|
||||
"""
|
||||
|
||||
##### Print usage via Nix / nixos #####
|
||||
if not isinstance(options_file, str):
|
||||
print(
|
||||
f"Skipping {module_name}: Cannot be used via import clanModules.{module_name}"
|
||||
)
|
||||
output += """## Usage via Nix
|
||||
|
||||
**This module cannot be imported directly in your nixos configuration.**
|
||||
|
||||
"""
|
||||
else:
|
||||
output += module_nix_usage(module_name)
|
||||
no_options = "** This module doesnt require any options to be set.**"
|
||||
output += print_options(options_file, options_head, no_options)
|
||||
|
||||
outfile = Path(OUT) / f"clanModules/{module_name}.md"
|
||||
outfile.parent.mkdir(
|
||||
parents=True,
|
||||
exist_ok=True,
|
||||
)
|
||||
with outfile.open("w") as of:
|
||||
of.write(output)
|
||||
|
||||
modules_index += "</div>"
|
||||
modules_index += "\n"
|
||||
modules_outfile = Path(OUT) / "clanModules/index.md"
|
||||
|
||||
with modules_outfile.open("w") as of:
|
||||
of.write(modules_index)
|
||||
|
||||
|
||||
def build_option_card(module_name: str, frontmatter: Frontmatter) -> str:
|
||||
"""
|
||||
Build the overview index card for each reference target option.
|
||||
@@ -863,8 +627,4 @@ if __name__ == "__main__": #
|
||||
produce_clan_core_docs()
|
||||
|
||||
produce_clan_service_author_docs()
|
||||
|
||||
# produce_clan_modules_docs()
|
||||
produce_clan_service_docs()
|
||||
|
||||
# produce_clan_modules_frontmatter_docs()
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
clan = {
|
||||
meta.name = "clan-core";
|
||||
inventory = {
|
||||
services = { };
|
||||
machines = {
|
||||
"test-darwin-machine" = {
|
||||
machineClass = "darwin";
|
||||
|
||||
@@ -33,7 +33,6 @@ lib.fix (
|
||||
evalService = clanLib.callLib ./modules/inventory/distributed-service/evalService.nix { };
|
||||
# ------------------------------------
|
||||
# ClanLib functions
|
||||
evalClan = clanLib.callLib ./modules/inventory/eval-clan-modules { };
|
||||
inventory = clanLib.callLib ./modules/inventory { };
|
||||
modules = clanLib.callLib ./modules/inventory/frontmatter { };
|
||||
test = clanLib.callLib ./test { };
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
{
|
||||
lib,
|
||||
clanLib,
|
||||
}:
|
||||
let
|
||||
baseModule =
|
||||
{ pkgs }:
|
||||
# Module
|
||||
{ config, ... }:
|
||||
{
|
||||
imports = (import (pkgs.path + "/nixos/modules/module-list.nix"));
|
||||
nixpkgs.pkgs = pkgs;
|
||||
clan.core.name = "dummy";
|
||||
system.stateVersion = config.system.nixos.release;
|
||||
# Set this to work around a bug where `clan.core.settings.machine.name`
|
||||
# is forced due to `networking.interfaces` being forced
|
||||
# somewhere in the nixpkgs options
|
||||
facter.detected.dhcp.enable = lib.mkForce false;
|
||||
};
|
||||
|
||||
# This function takes a list of module names and evaluates them
|
||||
# [ module ] -> { config, options, ... }
|
||||
evalClanModulesLegacy =
|
||||
{
|
||||
modules,
|
||||
pkgs,
|
||||
clan-core,
|
||||
}:
|
||||
let
|
||||
evaled = lib.evalModules {
|
||||
class = "nixos";
|
||||
modules = [
|
||||
(baseModule { inherit pkgs; })
|
||||
{
|
||||
clan.core.settings.directory = clan-core;
|
||||
}
|
||||
clan-core.nixosModules.clanCore
|
||||
]
|
||||
++ modules;
|
||||
};
|
||||
in
|
||||
# lib.warn ''
|
||||
# doesn't respect role specific interfaces.
|
||||
|
||||
# The following {module}/default.nix file trying to be imported.
|
||||
|
||||
# Modules: ${builtins.toJSON modulenames}
|
||||
|
||||
# This might result in incomplete or incorrect interfaces.
|
||||
|
||||
# FIX: Use evalClanModuleWithRole instead.
|
||||
# ''
|
||||
evaled;
|
||||
|
||||
/*
|
||||
This function takes a list of module names and evaluates them
|
||||
Returns a set of interfaces as described below:
|
||||
|
||||
Fn :: { ${moduleName} = Module; } -> {
|
||||
${moduleName} :: {
|
||||
${roleName}: JSONSchema
|
||||
}
|
||||
}
|
||||
*/
|
||||
evalClanModulesWithRoles =
|
||||
{
|
||||
allModules,
|
||||
clan-core,
|
||||
pkgs,
|
||||
}:
|
||||
let
|
||||
res = builtins.mapAttrs (
|
||||
moduleName: module:
|
||||
let
|
||||
frontmatter = clanLib.modules.getFrontmatter allModules.${moduleName} moduleName;
|
||||
roles =
|
||||
if builtins.elem "inventory" frontmatter.features or [ ] then
|
||||
assert lib.isPath module;
|
||||
clan-core.clanLib.modules.getRoles "Documentation: inventory.modules" allModules moduleName
|
||||
else
|
||||
[ ];
|
||||
in
|
||||
lib.listToAttrs (
|
||||
lib.map (role: {
|
||||
name = role;
|
||||
value =
|
||||
(lib.evalModules {
|
||||
class = "nixos";
|
||||
modules = [
|
||||
(baseModule { inherit pkgs; })
|
||||
clan-core.nixosModules.clanCore
|
||||
{
|
||||
clan.core.settings.directory = clan-core;
|
||||
}
|
||||
# Role interface
|
||||
(module + "/roles/${role}.nix")
|
||||
];
|
||||
}).options.clan.${moduleName} or { };
|
||||
}) roles
|
||||
)
|
||||
) allModules;
|
||||
in
|
||||
res;
|
||||
in
|
||||
{
|
||||
evalClanModules = evalClanModulesLegacy;
|
||||
inherit evalClanModulesWithRoles;
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
{
|
||||
self,
|
||||
inputs,
|
||||
options,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inputOverrides = self.clanLib.flake-inputs.getOverrides inputs;
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./distributed-service/flake-module.nix
|
||||
@@ -15,16 +11,13 @@ in
|
||||
{
|
||||
pkgs,
|
||||
lib,
|
||||
config,
|
||||
system,
|
||||
self',
|
||||
...
|
||||
}:
|
||||
{
|
||||
devShells.inventory-schema = pkgs.mkShell {
|
||||
name = "clan-inventory-schema";
|
||||
inputsFrom = with config.checks; [
|
||||
eval-lib-inventory
|
||||
inputsFrom = [
|
||||
self'.devShells.default
|
||||
];
|
||||
};
|
||||
@@ -51,41 +44,5 @@ in
|
||||
warningsAreErrors = true;
|
||||
transformOptions = self.clanLib.docs.stripStorePathsFromDeclarations;
|
||||
}).optionsJSON;
|
||||
|
||||
# Run: nix-unit --extra-experimental-features flakes --flake .#legacyPackages.x86_64-linux.evalTests
|
||||
legacyPackages.evalTests-inventory = import ./tests {
|
||||
inherit lib;
|
||||
clan-core = self;
|
||||
inherit (self) clanLib;
|
||||
inherit (self.inputs) nix-darwin;
|
||||
};
|
||||
|
||||
checks = {
|
||||
eval-lib-inventory = pkgs.runCommand "tests" { nativeBuildInputs = [ pkgs.nix-unit ]; } ''
|
||||
export HOME="$(realpath .)"
|
||||
export NIX_ABORT_ON_WARN=1
|
||||
nix-unit --eval-store "$HOME" \
|
||||
--extra-experimental-features flakes \
|
||||
--show-trace \
|
||||
${inputOverrides} \
|
||||
--flake ${
|
||||
lib.fileset.toSource {
|
||||
root = ../../..;
|
||||
fileset = lib.fileset.unions [
|
||||
../../../flake.nix
|
||||
../../../flake.lock
|
||||
(lib.fileset.fileFilter (file: file.name == "flake-module.nix") ../../..)
|
||||
../../../flakeModules
|
||||
../../../lib
|
||||
../../../nixosModules/clanCore
|
||||
../../../machines
|
||||
../../../inventory.json
|
||||
];
|
||||
}
|
||||
}#legacyPackages.${system}.evalTests-inventory
|
||||
|
||||
touch $out
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,51 +3,6 @@ let
|
||||
# Trim the .nix extension from a filename
|
||||
trimExtension = name: builtins.substring 0 (builtins.stringLength name - 4) name;
|
||||
|
||||
jsonWithoutHeader = clanLib.jsonschema {
|
||||
includeDefaults = true;
|
||||
header = { };
|
||||
};
|
||||
|
||||
getModulesSchema =
|
||||
{
|
||||
modules,
|
||||
clan-core,
|
||||
pkgs,
|
||||
}:
|
||||
lib.mapAttrs
|
||||
(
|
||||
_moduleName: rolesOptions:
|
||||
lib.mapAttrs (_roleName: options: jsonWithoutHeader.parseOptions options { }) rolesOptions
|
||||
)
|
||||
(
|
||||
clanLib.evalClan.evalClanModulesWithRoles {
|
||||
allModules = modules;
|
||||
inherit pkgs clan-core;
|
||||
}
|
||||
);
|
||||
|
||||
evalFrontmatter =
|
||||
{
|
||||
moduleName,
|
||||
instanceName,
|
||||
resolvedRoles,
|
||||
allModules,
|
||||
}:
|
||||
lib.evalModules {
|
||||
modules = [
|
||||
(getFrontmatter allModules.${moduleName} moduleName)
|
||||
./interface.nix
|
||||
{
|
||||
constraints.imports = [
|
||||
(lib.modules.importApply ../constraints {
|
||||
inherit moduleName resolvedRoles instanceName;
|
||||
allRoles = getRoles "inventory.modules" allModules moduleName;
|
||||
})
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
# For Documentation purposes only
|
||||
frontmatterOptions =
|
||||
(lib.evalModules {
|
||||
@@ -119,17 +74,12 @@ let
|
||||
builtins.readDir (checkedPath)
|
||||
)
|
||||
);
|
||||
|
||||
checkConstraints = args: (evalFrontmatter args).config.constraints.assertions;
|
||||
getFrontmatter = _modulepath: _modulename: "clanModules are removed!";
|
||||
in
|
||||
{
|
||||
inherit
|
||||
frontmatterOptions
|
||||
getModulesSchema
|
||||
getFrontmatter
|
||||
|
||||
checkConstraints
|
||||
getRoles
|
||||
;
|
||||
}
|
||||
|
||||
@@ -1,29 +1,14 @@
|
||||
{
|
||||
self,
|
||||
self',
|
||||
lib,
|
||||
pkgs,
|
||||
flakeOptions,
|
||||
...
|
||||
}:
|
||||
let
|
||||
|
||||
modulesSchema = self.clanLib.modules.getModulesSchema {
|
||||
modules = self.clanModules;
|
||||
inherit pkgs;
|
||||
clan-core = self;
|
||||
};
|
||||
|
||||
jsonLib = self.clanLib.jsonschema { inherit includeDefaults; };
|
||||
includeDefaults = true;
|
||||
|
||||
frontMatterSchema = jsonLib.parseOptions self.clanLib.modules.frontmatterOptions { };
|
||||
|
||||
inventorySchema = jsonLib.parseModule ({
|
||||
imports = [ ../../inventoryClass/interface.nix ];
|
||||
_module.args = { inherit (self) clanLib; };
|
||||
});
|
||||
|
||||
opts = (flakeOptions.flake.type.getSubOptions [ "flake" ]);
|
||||
clanOpts = opts.clan.type.getSubOptions [ "clan" ];
|
||||
include = [
|
||||
@@ -38,13 +23,6 @@ let
|
||||
];
|
||||
clanSchema = jsonLib.parseOptions (lib.filterAttrs (n: _v: lib.elem n include) clanOpts) { };
|
||||
|
||||
renderSchema = pkgs.writers.writePython3Bin "render-schema" {
|
||||
flakeIgnore = [
|
||||
"F401"
|
||||
"E501"
|
||||
];
|
||||
} ./render_schema.py;
|
||||
|
||||
clan-schema-abstract = pkgs.stdenv.mkDerivation {
|
||||
name = "clan-schema-files";
|
||||
buildInputs = [ pkgs.cue ];
|
||||
@@ -63,29 +41,7 @@ in
|
||||
{
|
||||
inherit
|
||||
flakeOptions
|
||||
frontMatterSchema
|
||||
clanSchema
|
||||
inventorySchema
|
||||
modulesSchema
|
||||
renderSchema
|
||||
clan-schema-abstract
|
||||
;
|
||||
|
||||
# Inventory schema, with the modules schema added per role
|
||||
inventory =
|
||||
pkgs.runCommand "rendered"
|
||||
{
|
||||
buildInputs = [
|
||||
pkgs.python3
|
||||
self'.packages.clan-cli
|
||||
];
|
||||
}
|
||||
''
|
||||
export INVENTORY_SCHEMA_PATH=${builtins.toFile "inventory-schema.json" (builtins.toJSON inventorySchema)}
|
||||
export MODULES_SCHEMA_PATH=${builtins.toFile "modules-schema.json" (builtins.toJSON modulesSchema)}
|
||||
|
||||
mkdir $out
|
||||
# The python script will place the schemas in the output directory
|
||||
exec python3 ${renderSchema}/bin/render-schema
|
||||
'';
|
||||
}
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
"""
|
||||
Python script to join the abstract inventory schema, with the concrete clan modules
|
||||
Inventory has slots which are 'Any' type.
|
||||
We dont want to evaluate the clanModules interface in nix, when evaluating the inventory
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from clan_lib.errors import ClanError
|
||||
|
||||
# Get environment variables
|
||||
INVENTORY_SCHEMA_PATH = Path(os.environ["INVENTORY_SCHEMA_PATH"])
|
||||
|
||||
# { [moduleName] :: { [roleName] :: SCHEMA }}
|
||||
MODULES_SCHEMA_PATH = Path(os.environ["MODULES_SCHEMA_PATH"])
|
||||
|
||||
OUT = os.environ.get("out")
|
||||
|
||||
if not INVENTORY_SCHEMA_PATH:
|
||||
msg = f"Environment variables are not set correctly: INVENTORY_SCHEMA_PATH={INVENTORY_SCHEMA_PATH}."
|
||||
raise ClanError(msg)
|
||||
|
||||
if not MODULES_SCHEMA_PATH:
|
||||
msg = f"Environment variables are not set correctly: MODULES_SCHEMA_PATH={MODULES_SCHEMA_PATH}."
|
||||
raise ClanError(msg)
|
||||
|
||||
if not OUT:
|
||||
msg = f"Environment variables are not set correctly: OUT={OUT}."
|
||||
raise ClanError(msg)
|
||||
|
||||
|
||||
def service_roles_to_schema(
|
||||
schema: dict[str, Any],
|
||||
service_name: str,
|
||||
roles: list[str],
|
||||
roles_schemas: dict[str, dict[str, Any]],
|
||||
# Original service properties: {'config': Schema, 'machines': Schema, 'meta': Schema, 'extraModules': Schema, ...?}
|
||||
orig: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Add roles to the service schema
|
||||
"""
|
||||
# collect all the roles for the service, to form a type union
|
||||
all_roles_schema: list[dict[str, Any]] = []
|
||||
for role_name, role_schema in roles_schemas.items():
|
||||
role_schema["title"] = f"{module_name}-config-role-{role_name}"
|
||||
all_roles_schema.append(role_schema)
|
||||
|
||||
role_schema = {}
|
||||
for role in roles:
|
||||
role_schema[role] = {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"properties": {
|
||||
**orig["roles"]["additionalProperties"]["properties"],
|
||||
"config": {
|
||||
**roles_schemas.get(role, {}),
|
||||
"title": f"{service_name}-config-role-{role}",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
machines_schema = {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
**orig["machines"]["additionalProperties"]["properties"],
|
||||
"config": {
|
||||
"title": f"{service_name}-config",
|
||||
"oneOf": all_roles_schema,
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
services["properties"][service_name] = {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"properties": {
|
||||
# Original inventory schema
|
||||
**orig,
|
||||
# Inject the roles schemas
|
||||
"roles": {
|
||||
"title": f"{service_name}-roles",
|
||||
"type": "object",
|
||||
"properties": role_schema,
|
||||
"additionalProperties": False,
|
||||
},
|
||||
"machines": machines_schema,
|
||||
"config": {
|
||||
"title": f"{service_name}-config",
|
||||
"oneOf": all_roles_schema,
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"additionalProperties": False,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return schema
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Joining inventory schema with modules schema")
|
||||
print(f"Inventory schema path: {INVENTORY_SCHEMA_PATH}")
|
||||
print(f"Modules schema path: {MODULES_SCHEMA_PATH}")
|
||||
|
||||
modules_schema = {}
|
||||
with Path.open(MODULES_SCHEMA_PATH) as f:
|
||||
modules_schema = json.load(f)
|
||||
|
||||
inventory_schema = {}
|
||||
with Path.open(INVENTORY_SCHEMA_PATH) as f:
|
||||
inventory_schema = json.load(f)
|
||||
|
||||
services = inventory_schema["properties"]["services"]
|
||||
original_service_props = services["additionalProperties"]["additionalProperties"][
|
||||
"properties"
|
||||
].copy()
|
||||
# Init the outer services schema
|
||||
# Properties (service names) will be filled in the next step
|
||||
services = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
# Service names
|
||||
},
|
||||
"additionalProperties": False,
|
||||
}
|
||||
|
||||
for module_name, roles_schemas in modules_schema.items():
|
||||
# Add the roles schemas to the service schema
|
||||
roles = list(roles_schemas.keys())
|
||||
if roles:
|
||||
services = service_roles_to_schema(
|
||||
services,
|
||||
module_name,
|
||||
roles,
|
||||
roles_schemas,
|
||||
original_service_props,
|
||||
)
|
||||
|
||||
inventory_schema["properties"]["services"] = services
|
||||
|
||||
outpath = Path(OUT)
|
||||
with (outpath / "schema.json").open("w") as f:
|
||||
json.dump(inventory_schema, f, indent=2)
|
||||
|
||||
with (outpath / "modules_schemas.json").open("w") as f:
|
||||
json.dump(modules_schema, f, indent=2)
|
||||
@@ -1,90 +0,0 @@
|
||||
{
|
||||
clan-core,
|
||||
nix-darwin,
|
||||
lib,
|
||||
clanLib,
|
||||
}:
|
||||
let
|
||||
# TODO: Unify these tests with clan tests
|
||||
clan =
|
||||
m:
|
||||
lib.evalModules {
|
||||
specialArgs = { inherit clan-core nix-darwin clanLib; };
|
||||
modules = [
|
||||
clan-core.modules.clan.default
|
||||
{
|
||||
self = { };
|
||||
}
|
||||
m
|
||||
];
|
||||
};
|
||||
in
|
||||
{
|
||||
test_inventory_a =
|
||||
let
|
||||
eval = clan {
|
||||
inventory = {
|
||||
machines = {
|
||||
A = { };
|
||||
};
|
||||
services = {
|
||||
legacyModule = { };
|
||||
};
|
||||
modules = {
|
||||
legacyModule = ./legacyModule;
|
||||
};
|
||||
};
|
||||
directory = ./.;
|
||||
};
|
||||
in
|
||||
{
|
||||
inherit eval;
|
||||
expr = {
|
||||
legacyModule = lib.filterAttrs (
|
||||
name: _: name == "isClanModule"
|
||||
) eval.config.clanInternals.inventoryClass.machines.A.compiledServices.legacyModule;
|
||||
};
|
||||
expected = {
|
||||
legacyModule = {
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
test_inventory_empty =
|
||||
let
|
||||
eval = clan {
|
||||
inventory = { };
|
||||
directory = ./.;
|
||||
};
|
||||
in
|
||||
{
|
||||
# Empty inventory should return an empty module
|
||||
expr = eval.config.clanInternals.inventoryClass.machines;
|
||||
expected = { };
|
||||
};
|
||||
|
||||
test_inventory_module_doesnt_exist =
|
||||
let
|
||||
eval = clan {
|
||||
directory = ./.;
|
||||
inventory = {
|
||||
services = {
|
||||
fanatasy.instance_1 = {
|
||||
roles.default.machines = [ "machine_1" ];
|
||||
};
|
||||
};
|
||||
machines = {
|
||||
"machine_1" = { };
|
||||
};
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
inherit eval;
|
||||
expr = eval.config.clanInternals.inventoryClass.machines.machine_1.machineImports;
|
||||
expectedError = {
|
||||
type = "ThrownError";
|
||||
msg = "ClanModule not found*";
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
features = [ "inventory" ]
|
||||
---
|
||||
Description
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
lib,
|
||||
clan-core,
|
||||
...
|
||||
}:
|
||||
{
|
||||
# Just some random stuff
|
||||
options.test = lib.mapAttrs clan-core;
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
# Integrity validation of the inventory
|
||||
{ config, lib, ... }:
|
||||
{
|
||||
# Assertion must be of type
|
||||
# { assertion :: bool, message :: string, severity :: "error" | "warning" }
|
||||
imports = [
|
||||
# Check that each machine used in a service is defined in the top-level machines
|
||||
{
|
||||
assertions = lib.foldlAttrs (
|
||||
ass1: serviceName: c:
|
||||
ass1
|
||||
++ lib.foldlAttrs (
|
||||
ass2: instanceName: instanceConfig:
|
||||
let
|
||||
topLevelMachines = lib.attrNames config.machines;
|
||||
# All machines must be defined in the top-level machines
|
||||
assertions = lib.foldlAttrs (
|
||||
assertions: roleName: role:
|
||||
assertions
|
||||
++ builtins.filter (a: !a.assertion) (
|
||||
builtins.map (m: {
|
||||
assertion = builtins.elem m topLevelMachines;
|
||||
message = ''
|
||||
Machine '${m}' is not defined in the inventory. This might still work, if the machine is defined via nix.
|
||||
|
||||
Defined in service: '${serviceName}' instance: '${instanceName}' role: '${roleName}'.
|
||||
|
||||
Inventory machines:
|
||||
${builtins.concatStringsSep "\n" (map (n: "'${n}'") topLevelMachines)}
|
||||
'';
|
||||
severity = "warning";
|
||||
}) role.machines
|
||||
)
|
||||
) [ ] instanceConfig.roles;
|
||||
in
|
||||
ass2 ++ assertions
|
||||
) [ ] c
|
||||
) [ ] config.services;
|
||||
}
|
||||
# Check that each tag used in a role is defined in at least one machines tags
|
||||
{
|
||||
assertions = lib.foldlAttrs (
|
||||
ass1: serviceName: c:
|
||||
ass1
|
||||
++ lib.foldlAttrs (
|
||||
ass2: instanceName: instanceConfig:
|
||||
let
|
||||
allTags = lib.foldlAttrs (
|
||||
tags: _machineName: machine:
|
||||
tags ++ machine.tags
|
||||
) [ ] config.machines;
|
||||
# All machines must be defined in the top-level machines
|
||||
assertions = lib.foldlAttrs (
|
||||
assertions: roleName: role:
|
||||
assertions
|
||||
++ builtins.filter (a: !a.assertion) (
|
||||
builtins.map (m: {
|
||||
assertion = builtins.elem m allTags;
|
||||
message = ''
|
||||
Tag '${m}' is not defined in the inventory.
|
||||
|
||||
Defined in service: '${serviceName}' instance: '${instanceName}' role: '${roleName}'.
|
||||
|
||||
Available tags:
|
||||
${builtins.concatStringsSep "\n" (map (n: "'${n}'") allTags)}
|
||||
'';
|
||||
severity = "error";
|
||||
}) role.tags
|
||||
)
|
||||
) [ ] instanceConfig.roles;
|
||||
in
|
||||
ass2 ++ assertions
|
||||
) [ ] c
|
||||
) [ ] config.services;
|
||||
}
|
||||
];
|
||||
|
||||
}
|
||||
@@ -1,268 +1,5 @@
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
clanLib,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (config) inventory directory;
|
||||
resolveTags =
|
||||
# Inventory, { machines :: [string], tags :: [string] }
|
||||
{
|
||||
serviceName,
|
||||
instanceName,
|
||||
roleName,
|
||||
inventory,
|
||||
members,
|
||||
}:
|
||||
{
|
||||
machines =
|
||||
members.machines or [ ]
|
||||
++ (builtins.foldl' (
|
||||
acc: tag:
|
||||
let
|
||||
# For error printing
|
||||
availableTags = lib.foldlAttrs (
|
||||
acc: _: v:
|
||||
v.tags or [ ] ++ acc
|
||||
) [ ] (inventory.machines);
|
||||
|
||||
tagMembers = builtins.attrNames (
|
||||
lib.filterAttrs (_n: v: builtins.elem tag v.tags or [ ]) inventory.machines
|
||||
);
|
||||
in
|
||||
if tagMembers == [ ] then
|
||||
lib.warn ''
|
||||
inventory.services.${serviceName}.${instanceName}: - ${roleName} tags: no machine with tag '${tag}' found.
|
||||
Available tags: ${builtins.toJSON (lib.unique availableTags)}
|
||||
'' [ ]
|
||||
else
|
||||
acc ++ tagMembers
|
||||
) [ ] members.tags or [ ]);
|
||||
};
|
||||
|
||||
checkService =
|
||||
modulepath: serviceName:
|
||||
builtins.elem "inventory" (clanLib.modules.getFrontmatter modulepath serviceName).features or [ ];
|
||||
|
||||
compileMachine =
|
||||
{ machineConfig }:
|
||||
{
|
||||
machineImports = [
|
||||
(lib.optionalAttrs (machineConfig.deploy.targetHost or null != null) {
|
||||
config.clan.core.networking.targetHost = lib.mkForce machineConfig.deploy.targetHost;
|
||||
})
|
||||
(lib.optionalAttrs (machineConfig.deploy.buildHost or null != null) {
|
||||
config.clan.core.networking.buildHost = lib.mkForce machineConfig.deploy.buildHost;
|
||||
})
|
||||
];
|
||||
assertions = { };
|
||||
};
|
||||
|
||||
resolveImports =
|
||||
{
|
||||
supportedRoles,
|
||||
resolvedRolesPerInstance,
|
||||
serviceConfigs,
|
||||
serviceName,
|
||||
machineName,
|
||||
getRoleFile,
|
||||
}:
|
||||
(lib.foldlAttrs (
|
||||
# : [ Modules ] -> String -> ServiceConfig -> [ Modules ]
|
||||
acc2: instanceName: serviceConfig:
|
||||
let
|
||||
resolvedRoles = resolvedRolesPerInstance.${instanceName};
|
||||
|
||||
isInService = builtins.any (members: builtins.elem machineName members.machines) (
|
||||
builtins.attrValues resolvedRoles
|
||||
);
|
||||
|
||||
# all roles where the machine is present
|
||||
machineRoles = builtins.attrNames (
|
||||
lib.filterAttrs (_role: roleConfig: builtins.elem machineName roleConfig.machines) resolvedRoles
|
||||
);
|
||||
|
||||
machineServiceConfig = (serviceConfig.machines.${machineName} or { }).config or { };
|
||||
globalConfig = serviceConfig.config or { };
|
||||
|
||||
globalExtraModules = serviceConfig.extraModules or [ ];
|
||||
machineExtraModules = serviceConfig.machines.${machineName}.extraModules or [ ];
|
||||
roleServiceExtraModules = builtins.foldl' (
|
||||
acc: role: acc ++ serviceConfig.roles.${role}.extraModules or [ ]
|
||||
) [ ] machineRoles;
|
||||
|
||||
# TODO: maybe optimize this don't lookup the role in inverse roles. Imports are not lazy
|
||||
roleModules = builtins.map (
|
||||
role:
|
||||
if builtins.elem role supportedRoles && inventory.modules ? ${serviceName} then
|
||||
getRoleFile role
|
||||
else
|
||||
throw "Module ${serviceName} doesn't have role: '${role}'. Role: ${
|
||||
inventory.modules.${serviceName}
|
||||
}/roles/${role}.nix not found."
|
||||
) machineRoles;
|
||||
|
||||
roleServiceConfigs = builtins.filter (m: m != { }) (
|
||||
builtins.map (role: serviceConfig.roles.${role}.config or { }) machineRoles
|
||||
);
|
||||
|
||||
extraModules = map (s: if builtins.typeOf s == "string" then "${directory}/${s}" else s) (
|
||||
globalExtraModules ++ machineExtraModules ++ roleServiceExtraModules
|
||||
);
|
||||
|
||||
features =
|
||||
(clanLib.modules.getFrontmatter inventory.modules.${serviceName} serviceName).features or [ ];
|
||||
deprecationWarning = lib.optionalAttrs (builtins.elem "deprecated" features) {
|
||||
warnings = [
|
||||
''
|
||||
The '${serviceName}' module has been migrated from `inventory.services` to `inventory.instances`
|
||||
See https://docs.clan.lol/guides/clanServices/ for usage.
|
||||
''
|
||||
];
|
||||
};
|
||||
in
|
||||
if !(serviceConfig.enabled or true) then
|
||||
acc2
|
||||
else if isInService then
|
||||
acc2
|
||||
++ [
|
||||
deprecationWarning
|
||||
{
|
||||
imports = roleModules ++ extraModules;
|
||||
clan.inventory.services.${serviceName}.${instanceName} = {
|
||||
roles = resolvedRoles;
|
||||
# TODO: Add inverseRoles to the service config if needed
|
||||
# inherit inverseRoles;
|
||||
};
|
||||
}
|
||||
(lib.optionalAttrs (globalConfig != { } || machineServiceConfig != { } || roleServiceConfigs != [ ])
|
||||
{
|
||||
clan.${serviceName} = lib.mkMerge (
|
||||
[
|
||||
globalConfig
|
||||
machineServiceConfig
|
||||
]
|
||||
++ roleServiceConfigs
|
||||
);
|
||||
}
|
||||
)
|
||||
]
|
||||
else
|
||||
acc2
|
||||
) [ ] (serviceConfigs));
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./interface.nix
|
||||
];
|
||||
config = {
|
||||
machines = builtins.mapAttrs (
|
||||
machineName: machineConfig: m:
|
||||
let
|
||||
compiledServices = lib.mapAttrs (
|
||||
_: serviceConfigs:
|
||||
(
|
||||
{ config, ... }:
|
||||
let
|
||||
serviceName = config.serviceName;
|
||||
|
||||
getRoleFile = role: builtins.seq role inventory.modules.${serviceName} + "/roles/${role}.nix";
|
||||
in
|
||||
{
|
||||
_file = "inventory/builder.nix";
|
||||
_module.args = {
|
||||
inherit
|
||||
resolveTags
|
||||
inventory
|
||||
clanLib
|
||||
machineName
|
||||
serviceConfigs
|
||||
;
|
||||
};
|
||||
imports = [
|
||||
./roles.nix
|
||||
];
|
||||
|
||||
machineImports = resolveImports {
|
||||
supportedRoles = config.supportedRoles;
|
||||
resolvedRolesPerInstance = config.resolvedRolesPerInstance;
|
||||
inherit
|
||||
serviceConfigs
|
||||
serviceName
|
||||
machineName
|
||||
getRoleFile
|
||||
;
|
||||
};
|
||||
|
||||
# Assertions
|
||||
assertions = {
|
||||
"checkservice.${serviceName}" = {
|
||||
assertion = checkService inventory.modules.${serviceName} serviceName;
|
||||
message = ''
|
||||
Service ${serviceName} cannot be used in inventory. It does not declare the 'inventory' feature.
|
||||
|
||||
To allow it add the following to the beginning of the README.md of the module:
|
||||
|
||||
---
|
||||
...
|
||||
|
||||
features = [ "inventory" ]
|
||||
---
|
||||
|
||||
Also make sure to test the module with the 'inventory' feature enabled.
|
||||
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
) (config.inventory.services or { });
|
||||
|
||||
compiledMachine = compileMachine {
|
||||
inherit
|
||||
machineConfig
|
||||
;
|
||||
};
|
||||
|
||||
machineImports = (
|
||||
compiledMachine.machineImports
|
||||
++ builtins.foldl' (
|
||||
acc: service:
|
||||
let
|
||||
failedAssertions = (lib.filterAttrs (_: v: !v.assertion) service.assertions);
|
||||
failedAssertionsImports =
|
||||
if failedAssertions != { } then
|
||||
[
|
||||
{
|
||||
clan.inventory.assertions = failedAssertions;
|
||||
}
|
||||
]
|
||||
else
|
||||
[
|
||||
{
|
||||
clan.inventory.assertions = {
|
||||
"alive.assertion.inventory" = {
|
||||
assertion = true;
|
||||
message = ''
|
||||
No failed assertions found for machine ${machineName}. This will never be displayed.
|
||||
It is here for testing purposes.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
in
|
||||
acc
|
||||
++ service.machineImports
|
||||
# Import failed assertions
|
||||
++ failedAssertionsImports
|
||||
) [ ] (builtins.attrValues m.config.compiledServices)
|
||||
);
|
||||
in
|
||||
{
|
||||
inherit machineImports compiledServices compiledMachine;
|
||||
}
|
||||
) (inventory.machines or { });
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,76 +16,13 @@ in
|
||||
type = types.raw;
|
||||
};
|
||||
machines = mkOption {
|
||||
type = types.attrsOf (
|
||||
submodule (
|
||||
{ name, ... }:
|
||||
let
|
||||
machineName = name;
|
||||
in
|
||||
{
|
||||
type = types.attrsOf (submodule ({
|
||||
options = {
|
||||
compiledMachine = mkOption {
|
||||
type = types.raw;
|
||||
};
|
||||
compiledServices = mkOption {
|
||||
# type = types.attrsOf;
|
||||
type = types.attrsOf (
|
||||
types.submoduleWith {
|
||||
modules = [
|
||||
(
|
||||
{ name, ... }:
|
||||
let
|
||||
serviceName = name;
|
||||
in
|
||||
{
|
||||
options = {
|
||||
machineName = mkOption {
|
||||
default = machineName;
|
||||
readOnly = true;
|
||||
};
|
||||
serviceName = mkOption {
|
||||
default = serviceName;
|
||||
readOnly = true;
|
||||
};
|
||||
# Outputs
|
||||
machineImports = mkOption {
|
||||
type = types.listOf types.raw;
|
||||
};
|
||||
supportedRoles = mkOption {
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
matchedRoles = mkOption {
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
machinesRoles = mkOption {
|
||||
type = types.attrsOf (types.listOf types.str);
|
||||
};
|
||||
resolvedRolesPerInstance = mkOption {
|
||||
type = types.attrsOf (
|
||||
types.attrsOf (submodule {
|
||||
options.machines = mkOption {
|
||||
type = types.listOf types.str;
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
assertions = mkOption {
|
||||
type = types.attrsOf types.raw;
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
];
|
||||
}
|
||||
);
|
||||
};
|
||||
machineImports = mkOption {
|
||||
type = types.listOf types.raw;
|
||||
};
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
}));
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
{
|
||||
lib,
|
||||
config,
|
||||
resolveTags,
|
||||
inventory,
|
||||
clanLib,
|
||||
machineName,
|
||||
serviceConfigs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
serviceName = config.serviceName;
|
||||
in
|
||||
{
|
||||
# Roles resolution
|
||||
# : List String
|
||||
supportedRoles = clanLib.modules.getRoles "inventory.modules" inventory.modules serviceName;
|
||||
matchedRoles = builtins.attrNames (
|
||||
lib.filterAttrs (_: ms: builtins.elem machineName ms) config.machinesRoles
|
||||
);
|
||||
resolvedRolesPerInstance = lib.mapAttrs (
|
||||
instanceName: instanceConfig:
|
||||
let
|
||||
resolvedRoles = lib.genAttrs config.supportedRoles (
|
||||
roleName:
|
||||
resolveTags {
|
||||
members = instanceConfig.roles.${roleName} or { };
|
||||
inherit
|
||||
instanceName
|
||||
serviceName
|
||||
roleName
|
||||
inventory
|
||||
;
|
||||
}
|
||||
);
|
||||
usedRoles = builtins.attrNames instanceConfig.roles;
|
||||
unmatchedRoles = builtins.filter (role: !builtins.elem role config.supportedRoles) usedRoles;
|
||||
in
|
||||
if unmatchedRoles != [ ] then
|
||||
throw ''
|
||||
Roles ${builtins.toJSON unmatchedRoles} are not defined in the service ${serviceName}.
|
||||
Instance: '${instanceName}'
|
||||
Please use one of available roles: ${builtins.toJSON config.supportedRoles}
|
||||
''
|
||||
else
|
||||
resolvedRoles
|
||||
) serviceConfigs;
|
||||
|
||||
machinesRoles = builtins.zipAttrsWith (
|
||||
_n: vs:
|
||||
let
|
||||
flat = builtins.foldl' (acc: s: acc ++ s.machines) [ ] vs;
|
||||
in
|
||||
lib.unique flat
|
||||
) (builtins.attrValues config.resolvedRolesPerInstance);
|
||||
|
||||
assertions = lib.concatMapAttrs (
|
||||
instanceName: resolvedRoles:
|
||||
clanLib.modules.checkConstraints {
|
||||
moduleName = serviceName;
|
||||
allModules = inventory.modules;
|
||||
inherit resolvedRoles instanceName;
|
||||
}
|
||||
) config.resolvedRolesPerInstance;
|
||||
}
|
||||
@@ -31,70 +31,13 @@ let
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
moduleConfig = lib.mkOption {
|
||||
default = { };
|
||||
# TODO: use types.deferredModule
|
||||
# clan.borgbackup MUST be defined as submodule
|
||||
type = types.attrsOf types.anything;
|
||||
description = ''
|
||||
Configuration of the specific clanModule.
|
||||
|
||||
!!! Note
|
||||
Configuration is passed to the nixos configuration scoped to the module.
|
||||
|
||||
```nix
|
||||
clan.<serviceName> = { ... # Config }
|
||||
```
|
||||
'';
|
||||
};
|
||||
|
||||
extraModulesOption = lib.mkOption {
|
||||
description = ''
|
||||
List of additionally imported `.nix` expressions.
|
||||
|
||||
Supported types:
|
||||
|
||||
- **Strings**: Interpreted relative to the 'directory' passed to `lib.clan`.
|
||||
- **Paths**: should be relative to the current file.
|
||||
- **Any**: Nix expression must be serializable to JSON.
|
||||
|
||||
!!! Note
|
||||
**The import only happens if the machine is part of the service or role.**
|
||||
|
||||
Other types are passed through to the nixos configuration.
|
||||
|
||||
???+ Example
|
||||
To import the `special.nix` file
|
||||
|
||||
```
|
||||
. Clan Directory
|
||||
├── flake.nix
|
||||
...
|
||||
└── modules
|
||||
├── special.nix
|
||||
└── ...
|
||||
```
|
||||
|
||||
```nix
|
||||
{
|
||||
extraModules = [ "modules/special.nix" ];
|
||||
}
|
||||
```
|
||||
'';
|
||||
apply = value: if lib.isString value then value else builtins.seq (builtins.toJSON value) value;
|
||||
default = [ ];
|
||||
type = types.listOf (
|
||||
types.oneOf [
|
||||
types.str
|
||||
types.anything
|
||||
]
|
||||
);
|
||||
};
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
./assertions.nix
|
||||
(lib.mkRemovedOptionModule [ "services" ] ''
|
||||
The `inventory.services` option has been removed. Use `inventory.instances` instead.
|
||||
See: https://docs.clan.lol/concepts/inventory/#services
|
||||
'')
|
||||
];
|
||||
options = {
|
||||
# Internal things
|
||||
@@ -415,160 +358,5 @@ in
|
||||
);
|
||||
default = { };
|
||||
};
|
||||
|
||||
services = lib.mkOption {
|
||||
# TODO: deprecate these options
|
||||
# services are deprecated in favor of `instances`
|
||||
# visible = false;
|
||||
description = ''
|
||||
Services of the inventory.
|
||||
|
||||
- The first `<name>` is the moduleName. It must be a valid clanModule name.
|
||||
- The second `<name>` is an arbitrary instance name.
|
||||
|
||||
???+ Example
|
||||
```nix
|
||||
# ClanModule name. See the module documentation for the available modules.
|
||||
# ↓ ↓ Instance name, can be anything, some services might use it as a unique identifier.
|
||||
services.borgbackup."instance_1" = {
|
||||
roles.client.machines = ["machineA"];
|
||||
};
|
||||
```
|
||||
|
||||
!!! Note
|
||||
Services MUST be added to machines via `roles` exclusively.
|
||||
See [`roles.<rolename>.machines`](#inventory.services.roles.machines) or [`roles.<rolename>.tags`](#inventory.services.roles.tags) for more information.
|
||||
'';
|
||||
default = { };
|
||||
type = types.attrsOf (
|
||||
types.attrsOf (
|
||||
types.submodule (
|
||||
# instance name
|
||||
{ name, ... }:
|
||||
{
|
||||
options.enabled = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Enable or disable the complete service.
|
||||
|
||||
If the service is disabled, it will not be added to any machine.
|
||||
|
||||
!!! Note
|
||||
This flag is primarily used to temporarily disable a service.
|
||||
I.e. A 'backup service' without any 'server' might be incomplete and would cause failure if enabled.
|
||||
'';
|
||||
};
|
||||
options.meta = metaOptionsWith name;
|
||||
options.extraModules = extraModulesOption;
|
||||
options.config = moduleConfig // {
|
||||
description = ''
|
||||
Configuration of the specific clanModule.
|
||||
|
||||
!!! Note
|
||||
Configuration is passed to the nixos configuration scoped to the module.
|
||||
|
||||
```nix
|
||||
clan.<serviceName> = { ... # Config }
|
||||
```
|
||||
|
||||
???+ Example
|
||||
|
||||
For `services.borgbackup` the config is the passed to the machine with the prefix of `clan.borgbackup`.
|
||||
This means all config values are mapped to the `borgbackup` clanModule exclusively (`config.clan.borgbackup`).
|
||||
|
||||
```nix
|
||||
{
|
||||
services.borgbackup."instance_1".config = {
|
||||
destinations = [ ... ];
|
||||
# See the 'borgbackup' module docs for all options
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
!!! Note
|
||||
The module author is responsible for supporting multiple instance configurations in different roles.
|
||||
See each clanModule's documentation for more information.
|
||||
'';
|
||||
};
|
||||
options.machines = lib.mkOption {
|
||||
description = ''
|
||||
Attribute set of machines specific config for the service.
|
||||
|
||||
Will be merged with other service configs, such as the role config and the global config.
|
||||
For machine specific overrides use `mkForce` or other higher priority methods.
|
||||
|
||||
???+ Example
|
||||
|
||||
```{.nix hl_lines="4-7"}
|
||||
services.borgbackup."instance_1" = {
|
||||
roles.client.machines = ["machineA"];
|
||||
|
||||
machines.machineA.config = {
|
||||
# Additional specific config for the machine
|
||||
# This is merged with all other config places
|
||||
};
|
||||
};
|
||||
```
|
||||
'';
|
||||
default = { };
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
options.extraModules = extraModulesOption;
|
||||
options.config = moduleConfig // {
|
||||
description = ''
|
||||
Additional configuration of the specific machine.
|
||||
|
||||
See how [`service.<name>.<name>.config`](#inventory.services.config) works in general for further information.
|
||||
'';
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
options.roles = lib.mkOption {
|
||||
default = { };
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
options.machines = lib.mkOption {
|
||||
default = [ ];
|
||||
type = types.listOf types.str;
|
||||
example = [ "machineA" ];
|
||||
description = ''
|
||||
List of machines which are part of the role.
|
||||
|
||||
The machines are referenced by their `attributeName` in the `inventory.machines` attribute set.
|
||||
|
||||
Memberships are declared here to determine which machines are part of the service.
|
||||
|
||||
Alternatively, `tags` can be used to determine the membership, more dynamically.
|
||||
'';
|
||||
};
|
||||
options.tags = lib.mkOption {
|
||||
default = [ ];
|
||||
apply = lib.unique;
|
||||
type = types.listOf types.str;
|
||||
description = ''
|
||||
List of tags which are used to determine the membership of the role.
|
||||
|
||||
The tags are matched against the `inventory.machines.<machineName>.tags` attribute set.
|
||||
If a machine has at least one tag of the role, it is part of the role.
|
||||
'';
|
||||
};
|
||||
options.config = moduleConfig // {
|
||||
description = ''
|
||||
Additional configuration of the specific role.
|
||||
|
||||
See how [`service.<name>.<name>.config`](#inventory.services.config) works in general for further information.
|
||||
'';
|
||||
};
|
||||
options.extraModules = extraModulesOption;
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
default =
|
||||
builtins.removeAttrs (clanLib.introspection.getPrios { options = config.inventory.options; })
|
||||
# tags are freeformType which is not supported yet.
|
||||
[ "tags" ];
|
||||
# services is removed and throws an error if accessed.
|
||||
[
|
||||
"tags"
|
||||
"services"
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
import { API } from "@/api/API";
|
||||
import { Schema as Inventory } from "@/api/Inventory";
|
||||
|
||||
export type OperationNames = keyof API;
|
||||
type Services = NonNullable<Inventory["services"]>;
|
||||
type ServiceNames = keyof Services;
|
||||
|
||||
export type OperationArgs<T extends OperationNames> = API[T]["arguments"];
|
||||
export type OperationResponse<T extends OperationNames> = API[T]["return"];
|
||||
|
||||
export type ClanServiceInstance<T extends ServiceNames> = NonNullable<
|
||||
Services[T]
|
||||
>[string];
|
||||
|
||||
export type SuccessQuery<T extends OperationNames> = Extract<
|
||||
OperationResponse<T>,
|
||||
{ status: "success" }
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
from clan_lib.nix_models.clan import Inventory
|
||||
from clan_lib.nix_models.clan import InventoryMachine as Machine
|
||||
from clan_lib.nix_models.clan import InventoryMeta as Meta
|
||||
from clan_lib.nix_models.clan import InventoryService as Service
|
||||
|
||||
|
||||
def test_make_meta_minimal() -> None:
|
||||
# Name is required
|
||||
res = Meta(
|
||||
{
|
||||
"name": "foo",
|
||||
}
|
||||
)
|
||||
|
||||
assert res == {"name": "foo"}
|
||||
|
||||
|
||||
def test_make_inventory_minimal() -> None:
|
||||
# Meta is required
|
||||
res = Inventory(
|
||||
{
|
||||
"meta": Meta(
|
||||
{
|
||||
"name": "foo",
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
assert res == {"meta": {"name": "foo"}}
|
||||
|
||||
|
||||
def test_make_machine_minimal() -> None:
|
||||
# Empty is valid
|
||||
res = Machine({})
|
||||
|
||||
assert res == {}
|
||||
|
||||
|
||||
def test_make_service_minimal() -> None:
|
||||
# Empty is valid
|
||||
res = Service({})
|
||||
|
||||
assert res == {}
|
||||
@@ -53,7 +53,6 @@ def test_inventory_deserialize_variants(
|
||||
# Check that all keys are present
|
||||
assert "meta" in inventory
|
||||
assert "machines" in inventory
|
||||
assert "services" in inventory
|
||||
# assert "tags" in inventory
|
||||
# assert "modules" in inventory
|
||||
assert "instances" in inventory
|
||||
|
||||
@@ -97,18 +97,10 @@ class InventoryMeta(TypedDict):
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class InventoryService(TypedDict):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
InventoryInstancesType = dict[str, InventoryInstance]
|
||||
InventoryMachinesType = dict[str, InventoryMachine]
|
||||
InventoryMetaType = InventoryMeta
|
||||
InventoryModulesType = dict[str, dict[str, Any] | list[Any] | bool | float | int | str | None]
|
||||
InventoryServicesType = dict[str, InventoryService]
|
||||
InventoryTagsType = dict[str, list[str]]
|
||||
|
||||
class Inventory(TypedDict):
|
||||
@@ -116,7 +108,6 @@ class Inventory(TypedDict):
|
||||
machines: NotRequired[InventoryMachinesType]
|
||||
meta: NotRequired[InventoryMetaType]
|
||||
modules: NotRequired[InventoryModulesType]
|
||||
services: NotRequired[InventoryServicesType]
|
||||
tags: NotRequired[InventoryTagsType]
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ from clan_lib.nix_models.clan import (
|
||||
InventoryInstancesType,
|
||||
InventoryMachinesType,
|
||||
InventoryMetaType,
|
||||
InventoryServicesType,
|
||||
InventoryTagsType,
|
||||
)
|
||||
|
||||
@@ -106,7 +105,6 @@ class InventorySnapshot(TypedDict):
|
||||
machines: NotRequired[InventoryMachinesType]
|
||||
instances: NotRequired[InventoryInstancesType]
|
||||
meta: NotRequired[InventoryMetaType]
|
||||
services: NotRequired[InventoryServicesType]
|
||||
tags: NotRequired[InventoryTagsType]
|
||||
|
||||
|
||||
@@ -163,7 +161,8 @@ class InventoryStore:
|
||||
return sanitized
|
||||
|
||||
def get_readonly_raw(self) -> Inventory:
|
||||
return self._flake.select("clanInternals.inventoryClass.inventory")
|
||||
attrs = "{" + ",".join(self._keys) + "}"
|
||||
return self._flake.select(f"clanInternals.inventoryClass.inventory.{attrs}")
|
||||
|
||||
def _get_persisted(self) -> InventorySnapshot:
|
||||
"""
|
||||
|
||||
@@ -101,7 +101,7 @@ def test_simple_read_write(setup_test_files: Path) -> None:
|
||||
store = InventoryStore(
|
||||
flake=MockFlake(nix_file),
|
||||
inventory_file_name=json_file.name,
|
||||
_keys=[], # disable toplevel filtering
|
||||
_keys=["foo", "protected"],
|
||||
)
|
||||
store._flake.invalidate_cache()
|
||||
data: dict = store.read() # type: ignore
|
||||
@@ -149,7 +149,7 @@ def test_simple_deferred(setup_test_files: Path) -> None:
|
||||
inventory_file_name=json_file.name,
|
||||
# Needed to allow auto-transforming deferred modules
|
||||
_allowed_path_transforms=["foo.*"],
|
||||
_keys=[], # disable toplevel filtering
|
||||
_keys=["foo"], # disable toplevel filtering
|
||||
)
|
||||
|
||||
data = store.read()
|
||||
@@ -230,7 +230,7 @@ def test_manipulate_list(setup_test_files: Path) -> None:
|
||||
store = InventoryStore(
|
||||
flake=MockFlake(nix_file),
|
||||
inventory_file_name=json_file.name,
|
||||
_keys=[], # disable toplevel filtering
|
||||
_keys=["empty", "predefined"],
|
||||
)
|
||||
|
||||
data = store.read()
|
||||
@@ -275,7 +275,7 @@ def test_static_list_items(setup_test_files: Path) -> None:
|
||||
store = InventoryStore(
|
||||
flake=MockFlake(nix_file),
|
||||
inventory_file_name=json_file.name,
|
||||
_keys=[], # disable toplevel filtering
|
||||
_keys=["empty", "predefined"],
|
||||
)
|
||||
|
||||
data = store.read()
|
||||
|
||||
@@ -5,7 +5,7 @@ import shutil
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, cast
|
||||
from typing import cast
|
||||
|
||||
import clan_cli.clan.create
|
||||
import pytest
|
||||
@@ -26,7 +26,6 @@ from clan_lib.nix import nix_command
|
||||
from clan_lib.nix_models.clan import (
|
||||
InventoryInstancesType,
|
||||
InventoryMachine,
|
||||
InventoryServicesType,
|
||||
Unknown,
|
||||
)
|
||||
from clan_lib.nix_models.clan import InventoryMachineDeploy as MachineDeploy
|
||||
@@ -41,7 +40,6 @@ log = logging.getLogger(__name__)
|
||||
|
||||
@dataclass
|
||||
class InventoryWrapper:
|
||||
services: InventoryServicesType
|
||||
instances: InventoryInstancesType
|
||||
|
||||
|
||||
@@ -65,8 +63,6 @@ def create_base_inventory(ssh_keys_pairs: list[SSHKeyPair]) -> InventoryWrapper:
|
||||
ssh_keys.append(InvSSHKeyEntry(f"user_{num}", ssh_key.public.read_text()))
|
||||
|
||||
"""Create the base inventory structure."""
|
||||
legacy_services: dict[str, Any] = {}
|
||||
|
||||
instances = InventoryInstancesType(
|
||||
{
|
||||
"admin-inst": {
|
||||
@@ -88,7 +84,7 @@ def create_base_inventory(ssh_keys_pairs: list[SSHKeyPair]) -> InventoryWrapper:
|
||||
}
|
||||
)
|
||||
|
||||
return InventoryWrapper(services=legacy_services, instances=instances)
|
||||
return InventoryWrapper(instances=instances)
|
||||
|
||||
|
||||
# TODO: We need a way to calculate the narHash of the current clan-core
|
||||
@@ -212,7 +208,6 @@ def test_clan_create_api(
|
||||
== "clan-core/admin"
|
||||
)
|
||||
|
||||
set_value_by_path(inventory, "services", inventory_conf.services)
|
||||
set_value_by_path(inventory, "instances", inventory_conf.instances)
|
||||
store.write(
|
||||
inventory,
|
||||
|
||||
@@ -100,12 +100,6 @@
|
||||
# It treats it not as the type of an empty object, but as non-nullish.
|
||||
# Should be fixed in json2ts: https://github.com/bcherny/json-schema-to-typescript/issues/557
|
||||
sed -i -e 's/{}/Record<string, never>/g' $out/API.ts
|
||||
|
||||
# Retrieve python API Typescript types
|
||||
# delete the reserved tags from typechecking because the conversion library doesn't support them
|
||||
jq 'del(.properties.tags.properties)' ${self'.legacyPackages.schemas.inventory}/schema.json > schema.json
|
||||
json2ts --input schema.json > $out/Inventory.ts
|
||||
cp ${self'.legacyPackages.schemas.inventory}/* $out
|
||||
'';
|
||||
};
|
||||
clan-lib-openapi = pkgs.stdenv.mkDerivation {
|
||||
|
||||
Reference in New Issue
Block a user