instances: create_service_instance init
This commit is contained in:
133
pkgs/clan-cli/clan_lib/services/instances.py
Normal file
133
pkgs/clan-cli/clan_lib/services/instances.py
Normal file
@@ -0,0 +1,133 @@
|
||||
from typing import TypedDict
|
||||
|
||||
from clan_lib.api import API
|
||||
from clan_lib.errors import ClanError
|
||||
from clan_lib.flake.flake import Flake
|
||||
from clan_lib.nix_models.clan import (
|
||||
InventoryInstanceModule,
|
||||
InventoryInstanceRolesType,
|
||||
InventoryInstancesType,
|
||||
InventoryMachinesType,
|
||||
)
|
||||
from clan_lib.persist.inventory_store import InventoryStore
|
||||
from clan_lib.persist.util import set_value_by_path
|
||||
from clan_lib.services.modules import list_service_modules
|
||||
|
||||
# TODO: move imports out of cli/__init__.py causing import cycles
|
||||
# from clan_lib.machines.actions import list_machines
|
||||
|
||||
|
||||
@API.register
|
||||
def list_service_instances(flake: Flake) -> InventoryInstancesType:
|
||||
"""
|
||||
Returns all currently present service instances including their full configuration
|
||||
"""
|
||||
|
||||
inventory_store = InventoryStore(flake)
|
||||
inventory = inventory_store.read()
|
||||
instances = inventory.get("instances", {})
|
||||
return instances
|
||||
|
||||
|
||||
def collect_tags(machines: InventoryMachinesType) -> set[str]:
|
||||
res = set()
|
||||
for _, machine in machines.items():
|
||||
res |= set(machine.get("tags", []))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
# Removed 'module' ref - Needs to be passed seperately
|
||||
class InstanceConfig(TypedDict):
|
||||
roles: InventoryInstanceRolesType
|
||||
|
||||
|
||||
@API.register
|
||||
def create_service_instance(
|
||||
flake: Flake,
|
||||
module_ref: InventoryInstanceModule,
|
||||
instance_name: str,
|
||||
instance_config: InstanceConfig,
|
||||
) -> None:
|
||||
# TODO: Should take a flake
|
||||
avilable_modules = list_service_modules(flake)
|
||||
|
||||
input_ref = module_ref.get("input", None)
|
||||
if input_ref is None:
|
||||
msg = "Setting module_ref.input is currently required"
|
||||
raise ClanError(msg)
|
||||
|
||||
module_set = avilable_modules.get("modules", {}).get(input_ref)
|
||||
|
||||
if module_set is None:
|
||||
msg = f"module set for input '{input_ref}' not found"
|
||||
msg += f"\nAvilable input_refs: {avilable_modules.get('modules', {}).keys()}"
|
||||
raise ClanError(msg)
|
||||
|
||||
module_name = module_ref.get("name")
|
||||
assert module_name
|
||||
module = module_set.get(module_name)
|
||||
if module is None:
|
||||
msg = f"module with name '{module_name}' not found"
|
||||
raise ClanError(msg)
|
||||
|
||||
inventory_store = InventoryStore(flake)
|
||||
inventory = inventory_store.read()
|
||||
|
||||
instances = inventory.get("instances", {})
|
||||
if instance_name in instances:
|
||||
msg = f"service instance '{instance_name}' already exists."
|
||||
raise ClanError(msg)
|
||||
|
||||
target_roles = instance_config.get("roles")
|
||||
if not target_roles:
|
||||
msg = "Creating a service instance requires adding roles"
|
||||
raise ClanError(msg)
|
||||
|
||||
available_roles = set(module.get("roles", {}).keys())
|
||||
|
||||
unavailable_roles = list(filter(lambda r: r not in available_roles, target_roles))
|
||||
if unavailable_roles:
|
||||
msg = f"Unknown roles: {unavailable_roles}. Use one of {available_roles}"
|
||||
raise ClanError(msg)
|
||||
|
||||
role_configs = instance_config.get("roles")
|
||||
if not role_configs:
|
||||
return
|
||||
|
||||
## Validate machine references
|
||||
all_machines = inventory.get("machines", {})
|
||||
available_machine_refs = set(all_machines.keys())
|
||||
available_tag_refs = collect_tags(all_machines)
|
||||
|
||||
for role_name, role_members in role_configs.items():
|
||||
machine_refs = role_members.get("machines")
|
||||
msg = f"Role: '{role_name}' - "
|
||||
if machine_refs:
|
||||
unavailable_machines = list(
|
||||
filter(lambda m: m not in available_machine_refs, machine_refs)
|
||||
)
|
||||
if unavailable_machines:
|
||||
msg += f"Unknown machine reference: {unavailable_machines}. Use one of {available_machine_refs}"
|
||||
raise ClanError(msg)
|
||||
|
||||
tag_refs = role_members.get("tags")
|
||||
if tag_refs:
|
||||
unavailable_tags = list(
|
||||
filter(lambda m: m not in available_tag_refs, tag_refs)
|
||||
)
|
||||
|
||||
if unavailable_tags:
|
||||
msg += (
|
||||
f"Unknown tags: {unavailable_tags}. Use one of {available_tag_refs}"
|
||||
)
|
||||
raise ClanError(msg)
|
||||
|
||||
# TODO:
|
||||
# Validate instance_config roles settings against role schema
|
||||
|
||||
set_value_by_path(inventory, f"instances.{instance_name}", instance_config)
|
||||
set_value_by_path(inventory, f"instances.{instance_name}.module", module_ref)
|
||||
inventory_store.write(
|
||||
inventory, message=f"services: instance '{instance_name}' init"
|
||||
)
|
||||
Reference in New Issue
Block a user