API/list_service_instances: add module metadata (#5023)

@hsjobeki

Co-authored-by: Johannes Kirschbauer <hsjobeki@gmail.com>
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5023
This commit is contained in:
DavHau
2025-08-29 13:14:19 +00:00
parent d0134d131e
commit a364b5ebf3
4 changed files with 73 additions and 10 deletions

View File

@@ -78,8 +78,9 @@ const SelectService = () => {
instances: Object.entries(serviceInstancesQuery.data)
.filter(
([name, i]) =>
i.module?.name === m.module.name &&
(!i.module?.input || i.module?.input === m.module.input),
i.module.module.name === m.module.name &&
(!i.module.module.input ||
i.module.module.input === m.module.input),
)
.map(([name, _]) => name),
})),

View File

@@ -162,6 +162,10 @@ def list_service_modules(flake: Flake) -> list[Module]:
"""Show information about a module"""
modules = flake.select("clanInternals.inventoryClass.modulesPerSource")
if "clan-core" not in modules:
msg = "Cannot find 'clan-core' input in the flake. Make sure your clan-core input is named 'clan-core'"
raise ClanError(msg)
res: list[Module] = []
for input_name, module_set in modules.items():
for module_name, module_info in module_set.items():
@@ -331,11 +335,34 @@ def create_service_instance(
)
class InventoryInstanceInfo(TypedDict):
module: Module
roles: InventoryInstanceRolesType
@API.register
def list_service_instances(
flake: Flake,
) -> dict[str, InventoryInstance]:
"""Show information about a module"""
def list_service_instances(flake: Flake) -> dict[str, InventoryInstanceInfo]:
"""Returns all currently present service instances including their full configuration"""
inventory_store = InventoryStore(flake)
inventory = inventory_store.read()
return inventory.get("instances", {})
service_modules = {
(mod["module"]["name"], mod["module"].get("input", "clan-core")): mod
for mod in list_service_modules(flake)
}
instances = inventory.get("instances", {})
res: dict[str, InventoryInstanceInfo] = {}
for instance_name, instance in instances.items():
module_key = (
instance.get("module", {})["name"],
instance.get("module", {}).get("input")
or "clan-core", # Replace None (or falsey) with "clan-core"
)
module = service_modules.get(module_key)
if module is None:
msg = f"Module '{module_key}' for instance '{instance_name}' not found"
raise ClanError(msg)
res[instance_name] = InventoryInstanceInfo(
module=module,
roles=instance.get("roles", {}),
)
return res

View File

@@ -0,0 +1,35 @@
from collections.abc import Callable
import pytest
from clan_cli.tests.fixtures_flakes import nested_dict
from clan_lib.flake.flake import Flake
from clan_lib.services.modules import list_service_instances
@pytest.mark.with_core
def test_list_service_instances(
clan_flake: Callable[..., Flake],
) -> None:
config = nested_dict()
config["inventory"]["machines"]["alice"] = {}
config["inventory"]["machines"]["bob"] = {}
# implicit module selection (defaults to clan-core/admin)
config["inventory"]["instances"]["admin"]["roles"]["default"]["tags"]["all"] = {}
# explicit module selection
config["inventory"]["instances"]["my-sshd"]["module"]["input"] = "clan-core"
config["inventory"]["instances"]["my-sshd"]["module"]["name"] = "sshd"
# input = null
config["inventory"]["instances"]["my-sshd-2"]["module"]["input"] = None
config["inventory"]["instances"]["my-sshd-2"]["module"]["name"] = "sshd"
# external input
flake = clan_flake(config)
instances = list_service_instances(flake)
assert list(instances.keys()) == ["admin", "my-sshd", "my-sshd-2"]
assert instances["admin"]["module"]["module"].get("input") == "clan-core"
assert instances["admin"]["module"]["module"].get("name") == "admin"
assert instances["my-sshd"]["module"]["module"].get("input") == "clan-core"
assert instances["my-sshd"]["module"]["module"].get("name") == "sshd"
assert instances["my-sshd-2"]["module"]["module"].get("input") == "clan-core"
assert instances["my-sshd-2"]["module"]["module"].get("name") == "sshd"

View File

@@ -1,11 +1,11 @@
{
inputs = {
clan.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
nixpkgs.follows = "clan/nixpkgs";
clan-core.url = "https://git.clan.lol/clan/clan-core/archive/main.tar.gz";
nixpkgs.follows = "clan-core/nixpkgs";
flake-parts.url = "github:hercules-ci/flake-parts";
flake-parts.inputs.nixpkgs-lib.follows = "clan/nixpkgs";
flake-parts.inputs.nixpkgs-lib.follows = "clan-core/nixpkgs";
};
outputs =