feat(InventoryStore): return a restricted view of the inventory
This commit is contained in:
@@ -2,9 +2,8 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
from clan_lib.api import API
|
from clan_lib.api import API
|
||||||
from clan_lib.flake import Flake
|
from clan_lib.flake import Flake
|
||||||
from clan_lib.nix_models.clan import Inventory
|
|
||||||
from clan_lib.nix_models.clan import InventoryMeta as Meta
|
from clan_lib.nix_models.clan import InventoryMeta as Meta
|
||||||
from clan_lib.persist.inventory_store import InventoryStore
|
from clan_lib.persist.inventory_store import InventorySnapshot, InventoryStore
|
||||||
from clan_lib.persist.util import set_value_by_path
|
from clan_lib.persist.util import set_value_by_path
|
||||||
|
|
||||||
|
|
||||||
@@ -15,7 +14,7 @@ class UpdateOptions:
|
|||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def update_clan_meta(options: UpdateOptions) -> Inventory:
|
def update_clan_meta(options: UpdateOptions) -> InventorySnapshot:
|
||||||
inventory_store = InventoryStore(options.flake)
|
inventory_store = InventoryStore(options.flake)
|
||||||
inventory = inventory_store.read()
|
inventory = inventory_store.read()
|
||||||
set_value_by_path(inventory, "meta", options.meta)
|
set_value_by_path(inventory, "meta", options.meta)
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ Interacting with 'clan_lib.inventory' is NOT recommended and will be removed
|
|||||||
|
|
||||||
from clan_lib.api import API
|
from clan_lib.api import API
|
||||||
from clan_lib.flake import Flake
|
from clan_lib.flake import Flake
|
||||||
from clan_lib.nix_models.clan import Inventory
|
from clan_lib.persist.inventory_store import InventorySnapshot, InventoryStore
|
||||||
from clan_lib.persist.inventory_store import InventoryStore
|
|
||||||
|
|
||||||
|
|
||||||
@API.register
|
@API.register
|
||||||
def get_inventory(flake: Flake) -> Inventory:
|
def get_inventory(flake: Flake) -> InventorySnapshot:
|
||||||
inventory_store = InventoryStore(flake)
|
inventory_store = InventoryStore(flake)
|
||||||
inventory = inventory_store.read()
|
inventory = inventory_store.read()
|
||||||
return inventory
|
return inventory
|
||||||
|
|||||||
111
pkgs/clan-cli/clan_lib/nix_models/inventory.py
Normal file
111
pkgs/clan-cli/clan_lib/nix_models/inventory.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# DO NOT EDIT THIS FILE MANUALLY. IT IS GENERATED.
|
||||||
|
# This file was generated by running `pkgs/clan-cli/clan_lib.inventory/update.sh`
|
||||||
|
#
|
||||||
|
# ruff: noqa: N815
|
||||||
|
# ruff: noqa: N806
|
||||||
|
# ruff: noqa: F401
|
||||||
|
# fmt: off
|
||||||
|
from typing import Any, Literal, NotRequired, TypedDict
|
||||||
|
|
||||||
|
|
||||||
|
# Mimic "unknown".
|
||||||
|
# 'Any' is unsafe because it allows any operations
|
||||||
|
# This forces the user to use type-narrowing or casting in the code
|
||||||
|
class Unknown:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
InstanceModuleNameType = str
|
||||||
|
InstanceModuleInputType = str
|
||||||
|
|
||||||
|
class InstanceModule(TypedDict):
|
||||||
|
name: str
|
||||||
|
input: NotRequired[InstanceModuleInputType]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
InstanceRoleMachineSettingsType = Unknown
|
||||||
|
|
||||||
|
class InstanceRoleMachine(TypedDict):
|
||||||
|
settings: NotRequired[InstanceRoleMachineSettingsType]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class InstanceRoleTag(TypedDict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
InstanceRoleMachinesType = dict[str, InstanceRoleMachine]
|
||||||
|
InstanceRoleSettingsType = Unknown
|
||||||
|
InstanceRoleTagsType = dict[str, InstanceRoleTag]
|
||||||
|
|
||||||
|
class InstanceRole(TypedDict):
|
||||||
|
machines: NotRequired[InstanceRoleMachinesType]
|
||||||
|
settings: NotRequired[InstanceRoleSettingsType]
|
||||||
|
tags: NotRequired[InstanceRoleTagsType]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
InstanceModuleType = InstanceModule
|
||||||
|
InstanceRolesType = dict[str, InstanceRole]
|
||||||
|
|
||||||
|
class Instance(TypedDict):
|
||||||
|
module: NotRequired[InstanceModuleType]
|
||||||
|
roles: NotRequired[InstanceRolesType]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MachineDeployTargethostType = str
|
||||||
|
|
||||||
|
class MachineDeploy(TypedDict):
|
||||||
|
targetHost: NotRequired[MachineDeployTargethostType]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MachineDeployType = MachineDeploy
|
||||||
|
MachineDescriptionType = str
|
||||||
|
MachineIconType = str
|
||||||
|
MachineMachineclassType = Literal["nixos", "darwin"]
|
||||||
|
MachineNameType = str
|
||||||
|
MachineTagsType = list[str]
|
||||||
|
|
||||||
|
class Machine(TypedDict):
|
||||||
|
deploy: NotRequired[MachineDeployType]
|
||||||
|
description: NotRequired[MachineDescriptionType]
|
||||||
|
icon: NotRequired[MachineIconType]
|
||||||
|
machineClass: NotRequired[MachineMachineclassType]
|
||||||
|
name: NotRequired[MachineNameType]
|
||||||
|
tags: NotRequired[MachineTagsType]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MetaNameType = str
|
||||||
|
MetaDescriptionType = str
|
||||||
|
MetaIconType = str
|
||||||
|
|
||||||
|
class Meta(TypedDict):
|
||||||
|
name: str
|
||||||
|
description: NotRequired[MetaDescriptionType]
|
||||||
|
icon: NotRequired[MetaIconType]
|
||||||
|
|
||||||
|
Service = dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
InventoryInstancesType = dict[str, Instance]
|
||||||
|
InventoryMachinesType = dict[str, Machine]
|
||||||
|
InventoryMetaType = Meta
|
||||||
|
InventoryModulesType = dict[str, Any]
|
||||||
|
InventoryServicesType = dict[str, Service]
|
||||||
|
InventoryTagsType = dict[str, Any]
|
||||||
|
|
||||||
|
class Inventory(TypedDict):
|
||||||
|
instances: NotRequired[InventoryInstancesType]
|
||||||
|
machines: NotRequired[InventoryMachinesType]
|
||||||
|
meta: NotRequired[InventoryMetaType]
|
||||||
|
modules: NotRequired[InventoryModulesType]
|
||||||
|
services: NotRequired[InventoryServicesType]
|
||||||
|
tags: NotRequired[InventoryTagsType]
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
import json
|
import json
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Protocol
|
from typing import Any, NotRequired, Protocol, TypedDict
|
||||||
|
|
||||||
from clan_lib.errors import ClanError
|
from clan_lib.errors import ClanError
|
||||||
from clan_lib.git import commit_file
|
from clan_lib.git import commit_file
|
||||||
from clan_lib.nix_models.clan import Inventory
|
from clan_lib.nix_models.clan import (
|
||||||
|
Inventory,
|
||||||
|
InventoryInstancesType,
|
||||||
|
InventoryMachinesType,
|
||||||
|
InventoryMetaType,
|
||||||
|
InventoryServicesType,
|
||||||
|
)
|
||||||
|
|
||||||
from .util import (
|
from .util import (
|
||||||
calc_patches,
|
calc_patches,
|
||||||
@@ -75,8 +81,8 @@ def sanitize(data: Any, whitelist_paths: list[str], current_path: list[str]) ->
|
|||||||
@dataclass
|
@dataclass
|
||||||
class WriteInfo:
|
class WriteInfo:
|
||||||
writeables: dict[str, set[str]]
|
writeables: dict[str, set[str]]
|
||||||
data_eval: Inventory
|
data_eval: "InventorySnapshot"
|
||||||
data_disk: Inventory
|
data_disk: "InventorySnapshot"
|
||||||
|
|
||||||
|
|
||||||
class FlakeInterface(Protocol):
|
class FlakeInterface(Protocol):
|
||||||
@@ -92,6 +98,19 @@ class FlakeInterface(Protocol):
|
|||||||
def path(self) -> Path: ...
|
def path(self) -> Path: ...
|
||||||
|
|
||||||
|
|
||||||
|
class InventorySnapshot(TypedDict):
|
||||||
|
"""
|
||||||
|
Restricted view of an Inventory.
|
||||||
|
|
||||||
|
It contains only the keys that are convertible to python types and can be serialized to JSON.
|
||||||
|
"""
|
||||||
|
|
||||||
|
machines: NotRequired[InventoryMachinesType]
|
||||||
|
instances: NotRequired[InventoryInstancesType]
|
||||||
|
meta: NotRequired[InventoryMetaType]
|
||||||
|
services: NotRequired[InventoryServicesType]
|
||||||
|
|
||||||
|
|
||||||
class InventoryStore:
|
class InventoryStore:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -117,10 +136,11 @@ class InventoryStore:
|
|||||||
self._allowed_path_transforms = _allowed_path_transforms
|
self._allowed_path_transforms = _allowed_path_transforms
|
||||||
|
|
||||||
if _keys is None:
|
if _keys is None:
|
||||||
_keys = ["machines", "instances", "meta", "services"]
|
_keys = list(InventorySnapshot.__annotations__.keys())
|
||||||
|
|
||||||
self._keys = _keys
|
self._keys = _keys
|
||||||
|
|
||||||
def _load_merged_inventory(self) -> Inventory:
|
def _load_merged_inventory(self) -> InventorySnapshot:
|
||||||
"""
|
"""
|
||||||
Loads the evaluated inventory.
|
Loads the evaluated inventory.
|
||||||
After all merge operations with eventual nix code in buildClan.
|
After all merge operations with eventual nix code in buildClan.
|
||||||
@@ -140,7 +160,7 @@ class InventoryStore:
|
|||||||
|
|
||||||
return sanitized
|
return sanitized
|
||||||
|
|
||||||
def _get_persisted(self) -> Inventory:
|
def _get_persisted(self) -> InventorySnapshot:
|
||||||
"""
|
"""
|
||||||
Load the inventory FILE from the flake directory
|
Load the inventory FILE from the flake directory
|
||||||
If no file is found, returns an empty dictionary
|
If no file is found, returns an empty dictionary
|
||||||
@@ -189,8 +209,8 @@ class InventoryStore:
|
|||||||
"""
|
"""
|
||||||
current_priority = self._get_inventory_current_priority()
|
current_priority = self._get_inventory_current_priority()
|
||||||
|
|
||||||
data_eval: Inventory = self._load_merged_inventory()
|
data_eval: InventorySnapshot = self._load_merged_inventory()
|
||||||
data_disk: Inventory = self._get_persisted()
|
data_disk: InventorySnapshot = self._get_persisted()
|
||||||
|
|
||||||
writeables = determine_writeability(
|
writeables = determine_writeability(
|
||||||
current_priority, dict(data_eval), dict(data_disk)
|
current_priority, dict(data_eval), dict(data_disk)
|
||||||
@@ -198,7 +218,7 @@ class InventoryStore:
|
|||||||
|
|
||||||
return WriteInfo(writeables, data_eval, data_disk)
|
return WriteInfo(writeables, data_eval, data_disk)
|
||||||
|
|
||||||
def read(self) -> Inventory:
|
def read(self) -> InventorySnapshot:
|
||||||
"""
|
"""
|
||||||
Accessor to the merged inventory
|
Accessor to the merged inventory
|
||||||
|
|
||||||
@@ -226,7 +246,9 @@ class InventoryStore:
|
|||||||
commit_message=f"Delete inventory keys {delete_set}",
|
commit_message=f"Delete inventory keys {delete_set}",
|
||||||
)
|
)
|
||||||
|
|
||||||
def write(self, update: Inventory, message: str, commit: bool = True) -> None:
|
def write(
|
||||||
|
self, update: InventorySnapshot, message: str, commit: bool = True
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Write the inventory to the flake directory
|
Write the inventory to the flake directory
|
||||||
and commit it to git with the given message
|
and commit it to git with the given message
|
||||||
|
|||||||
Reference in New Issue
Block a user