Merge pull request 'inventoryStore: align class names and methods' (#5551) from fix-deletions into main

Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5551
This commit is contained in:
hsjobeki
2025-10-16 11:09:29 +00:00
9 changed files with 41 additions and 39 deletions

View File

@@ -7,7 +7,7 @@ from clan_lib.machines.actions import FieldSchema
from clan_lib.nix_models.clan import InventoryMeta
from clan_lib.persist.introspection import retrieve_typed_field_names
from clan_lib.persist.inventory_store import InventoryStore
from clan_lib.persist.write_rules import is_writeable_key
from clan_lib.persist.write_rules import is_readonly_key
log = logging.getLogger(__name__)
@@ -51,13 +51,13 @@ def get_clan_details_schema(flake: Flake) -> dict[str, FieldSchema]:
"""
inventory_store = InventoryStore(flake)
write_info = inventory_store.get_write_map()
attribute_props = inventory_store.get_attribute_props()
field_names = retrieve_typed_field_names(InventoryMeta)
return {
field: {
"readonly": not is_writeable_key(f"meta.{field}", write_info),
"readonly": is_readonly_key(f"meta.{field}", attribute_props),
# TODO: Provide a meaningful reason
"reason": None,
"readonly_members": [],

View File

@@ -18,7 +18,7 @@ from clan_lib.persist.path_utils import (
list_difference,
set_value_by_path,
)
from clan_lib.persist.write_rules import is_writeable_key
from clan_lib.persist.write_rules import is_readonly_key
@dataclass
@@ -170,7 +170,7 @@ def get_machine_fields_schema(machine: Machine) -> dict[str, FieldSchema]:
"""
inventory_store = InventoryStore(machine.flake)
write_info = inventory_store.get_write_map()
attribute_props = inventory_store.get_attribute_props()
field_names = retrieve_typed_field_names(InventoryMachine)
@@ -195,9 +195,9 @@ def get_machine_fields_schema(machine: Machine) -> dict[str, FieldSchema]:
"readonly": (
True
if field in protected_fields
else not is_writeable_key(
else is_readonly_key(
f"machines.{machine.name}.{field}",
write_info,
attribute_props,
)
),
# TODO: Provide a meaningful reason

View File

@@ -249,13 +249,15 @@ def test_get_machine_writeability(clan_flake: Callable[..., Flake]) -> None:
persisted = inventory_store._get_persisted()
assert get_value_by_path(persisted, "machines.jon.tags", []) == new_tags
write_info = get_machine_fields_schema(Machine("jon", flake))
attribute_props = get_machine_fields_schema(Machine("jon", flake))
# {'tags': {'writable': True, 'reason': None}, 'machineClass': {'writable': False, 'reason': None}, 'name': {'writable': False, 'reason': None}, 'description': {'writable': True, 'reason': None}, 'deploy.buildHost': {'writable': True, 'reason': None}, 'icon': {'writable': True, 'reason': None}, 'deploy.targetHost': {'writable': True, 'reason': None}}
writeable_fields = {
field for field, info in write_info.items() if not info["readonly"]
field for field, info in attribute_props.items() if not info["readonly"]
}
read_only_fields = {
field for field, info in attribute_props.items() if info["readonly"]
}
read_only_fields = {field for field, info in write_info.items() if info["readonly"]}
assert writeable_fields == {
"tags",
@@ -267,7 +269,7 @@ def test_get_machine_writeability(clan_flake: Callable[..., Flake]) -> None:
}
assert read_only_fields == {"machineClass", "name"}
assert write_info["tags"]["readonly_members"] == ["nix1", "all", "nixos"]
assert attribute_props["tags"]["readonly_members"] == ["nix1", "all", "nixos"]
@pytest.mark.with_core

View File

@@ -77,8 +77,8 @@ def sanitize(data: Any, whitelist_paths: list[str], current_path: list[str]) ->
@dataclass
class WriteInfo:
writeables: AttributeMap
class PersistenceInfo:
attribute_props: AttributeMap
data_eval: "InventorySnapshot"
data_disk: "InventorySnapshot"
@@ -184,7 +184,7 @@ class InventoryStore:
return inventory
def _get_inventory_current_priority(self) -> dict:
def _get_introspection(self) -> dict:
"""Returns the current priority of the inventory values
machines = {
@@ -203,33 +203,33 @@ class InventoryStore:
"""
return self._flake.select("clanInternals.inventoryClass.introspection")
def _write_map(self) -> WriteInfo:
def _get_persistence_info(self) -> PersistenceInfo:
"""Get the paths of the writeable keys in the inventory
Load the inventory and determine the writeable keys
Performs 2 nix evaluations to get the current priority and the inventory
"""
current_priority = self._get_inventory_current_priority()
current_priority = self._get_introspection()
data_eval: InventorySnapshot = self._load_merged_inventory()
data_disk: InventorySnapshot = self._get_persisted()
write_map = compute_attribute_persistence(
attribute_props = compute_attribute_persistence(
current_priority,
dict(data_eval),
dict(data_disk),
inventory_file_name=self.inventory_file.name,
)
return WriteInfo(write_map, data_eval, data_disk)
return PersistenceInfo(attribute_props, data_eval, data_disk)
def get_write_map(self) -> Any:
def get_attribute_props(self) -> Any:
"""Get the writeability of the inventory
:return: A dictionary with the writeability of all paths
"""
write_info = self._write_map()
return write_info.writeables
persistence_info = self._get_persistence_info()
return persistence_info.attribute_props
def read(self) -> InventorySnapshot:
"""Accessor to the merged inventory
@@ -265,15 +265,15 @@ class InventoryStore:
"""Write the inventory to the flake directory
and commit it to git with the given message
"""
write_info = self._write_map()
persistence_info = self._get_persistence_info()
patchset, delete_set = calc_patches(
dict(write_info.data_disk),
dict(persistence_info.data_disk),
dict(update),
dict(write_info.data_eval),
write_info.writeables,
dict(persistence_info.data_eval),
persistence_info.attribute_props,
)
persisted = dict(write_info.data_disk)
persisted = dict(persistence_info.data_disk)
for patch_path, data in patchset.items():
set_value_by_path_tuple(persisted, patch_path, data)

View File

@@ -153,7 +153,7 @@ def test_simple_deferred(setup_test_files: Path) -> None:
_keys={"foo"}, # disable toplevel filtering
)
attribute_props = store._write_map().writeables
attribute_props = store._get_persistence_info().attribute_props
assert attribute_props == {
("foo",): {PersistenceAttribute.WRITE},
("foo", "a"): {PersistenceAttribute.WRITE, PersistenceAttribute.DELETE},

View File

@@ -12,9 +12,9 @@ from clan_lib.persist.path_utils import (
from clan_lib.persist.validate import (
validate_list_uniqueness,
validate_no_static_deletion,
validate_not_readonly,
validate_patch_conflicts,
validate_type_compatibility,
validate_writeability,
)
from clan_lib.persist.write_rules import AttributeMap, PersistenceAttribute
@@ -131,7 +131,7 @@ Please report this issue at https://git.clan.lol/clan/clan-core/issues
continue
# Validate the change is allowed
validate_writeability(path, attribute_props)
validate_not_readonly(path, attribute_props)
validate_type_compatibility(path, old_value, new_value)
validate_list_uniqueness(path, new_value)

View File

@@ -69,7 +69,7 @@ def test_update_add_empty_dict() -> None:
data_disk: dict = {}
writeables = compute_attribute_persistence(prios, data_eval, data_disk)
attribute_props = compute_attribute_persistence(prios, data_eval, data_disk)
update = deepcopy(data_eval)
@@ -79,7 +79,7 @@ def test_update_add_empty_dict() -> None:
data_disk,
update,
all_values=data_eval,
attribute_props=writeables,
attribute_props=attribute_props,
)
assert patchset == {("foo", "mimi"): {}} # this is what gets persisted

View File

@@ -7,7 +7,7 @@ from clan_lib.persist.path_utils import (
path_starts_with,
path_to_string,
)
from clan_lib.persist.write_rules import AttributeMap, is_writeable_path
from clan_lib.persist.write_rules import AttributeMap, is_readonly_path
def validate_no_static_deletion(
@@ -20,9 +20,9 @@ def validate_no_static_deletion(
raise ClanError(msg)
def validate_writeability(path: PathTuple, writeables: AttributeMap) -> None:
def validate_not_readonly(path: PathTuple, writeables: AttributeMap) -> None:
"""Validate that a path is writeable."""
if not is_writeable_path(path, writeables):
if is_readonly_path(path, writeables):
msg = f"Path '{path_to_string(path)}' is readonly. - It seems its value is statically defined in nix."
raise ClanError(msg)

View File

@@ -16,7 +16,7 @@ class PersistenceAttribute(Enum):
type AttributeMap = dict[PathTuple, set[PersistenceAttribute]]
def is_writeable_path(
def is_readonly_path(
key: PathTuple,
attributes: AttributeMap,
) -> bool:
@@ -30,9 +30,9 @@ def is_writeable_path(
current_path = remaining
if current_path in attributes:
if PersistenceAttribute.WRITE in attributes[current_path]:
return True
if PersistenceAttribute.READONLY in attributes[current_path]:
return False
if PersistenceAttribute.READONLY in attributes[current_path]:
return True
# Check the parent path
remaining = remaining[:-1]
@@ -40,7 +40,7 @@ def is_writeable_path(
raise ClanError(msg)
def is_writeable_key(
def is_readonly_key(
key: str,
attributes: AttributeMap,
) -> bool:
@@ -51,7 +51,7 @@ def is_writeable_key(
In case of ambiguity use is_writeable_path with tuple keys.
"""
items = key.split(".")
return is_writeable_path(tuple(items), attributes)
return is_readonly_path(tuple(items), attributes)
def get_priority(value: Any) -> int | None: