WIP: dont merge
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
# tests for the nixos options to jsonschema converter
|
||||
# run these tests via `nix-unit ./test.nix`
|
||||
{
|
||||
lib ? (import <nixpkgs> { }).lib,
|
||||
lib ? import /home/johannes/git/nixpkgs/lib,
|
||||
# lib ? (import <nixpkgs> { }).lib,
|
||||
slib ? (import ./. { inherit lib; }),
|
||||
}:
|
||||
let
|
||||
@@ -67,17 +68,24 @@ in
|
||||
};
|
||||
};
|
||||
};
|
||||
test_no_default = {
|
||||
expr = stableView (
|
||||
slib.getPrios {
|
||||
options =
|
||||
(eval [
|
||||
test_no_default =
|
||||
let
|
||||
|
||||
configuration = (
|
||||
eval [
|
||||
{
|
||||
options.foo.bar = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
};
|
||||
}
|
||||
]).options;
|
||||
]
|
||||
);
|
||||
in
|
||||
{
|
||||
inherit configuration;
|
||||
expr = stableView (
|
||||
slib.getPrios {
|
||||
options = configuration.options;
|
||||
}
|
||||
);
|
||||
expected = {
|
||||
|
||||
53
pkgs/clan-cli/clan_lib/persist/modules.nix
Normal file
53
pkgs/clan-cli/clan_lib/persist/modules.nix
Normal file
@@ -0,0 +1,53 @@
|
||||
let
|
||||
lib = import /home/johannes/git/nixpkgs/lib;
|
||||
|
||||
clanLib = import ../../../../lib { inherit lib; };
|
||||
|
||||
inherit (lib) evalModules mkOption types;
|
||||
|
||||
eval = evalModules {
|
||||
modules = [
|
||||
{
|
||||
options.foos = mkOption {
|
||||
type = types.attrsOf (
|
||||
types.submodule {
|
||||
options.bar = mkOption { };
|
||||
}
|
||||
);
|
||||
};
|
||||
# config.foos = lib.mkForce { this.bar = 42; };
|
||||
config.instances.a = { };
|
||||
# config.foo = lib.mkForce {
|
||||
# bar = 42;
|
||||
# };
|
||||
}
|
||||
{
|
||||
_file = "inventory.json";
|
||||
# instances.a = { setting = };
|
||||
}
|
||||
|
||||
# {
|
||||
# options.foo = mkOption {
|
||||
# type = types.attrsOf (types.attrsOf (types.submoduleWith { modules = [
|
||||
# {
|
||||
# options.bar = mkOption {};
|
||||
# }
|
||||
# ]; }));
|
||||
# default = { bar = { }; };
|
||||
# };
|
||||
# }
|
||||
# {
|
||||
# _file = "static.nix";
|
||||
# foo.static.thing = { bar = 1; }; # <- Can: Op.Modify
|
||||
# }
|
||||
# {
|
||||
# _file = "inventory.json";
|
||||
# foo.managed.thing = { bar = 1; }; # <- Can: Op.Delete, Op.Modify
|
||||
# #
|
||||
# }
|
||||
];
|
||||
};
|
||||
in
|
||||
{
|
||||
inherit clanLib eval;
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
from enum import Enum
|
||||
from typing import Any, TypedDict
|
||||
|
||||
from clan_lib.errors import ClanError
|
||||
from clan_lib.persist.path_utils import PathTuple, path_to_string
|
||||
from clan_lib.persist.path_utils import (
|
||||
PathTuple,
|
||||
path_to_string,
|
||||
)
|
||||
|
||||
WRITABLE_PRIORITY_THRESHOLD = 100 # Values below this are not writeable
|
||||
|
||||
@@ -189,3 +193,69 @@ def compute_write_map(
|
||||
|
||||
"""
|
||||
return _determine_writeability_recursive(priorities, all_values, persisted)
|
||||
|
||||
|
||||
class RawAttributes(TypedDict):
|
||||
headType: str
|
||||
nullable: bool
|
||||
prio: int
|
||||
total: bool
|
||||
files: list[str]
|
||||
|
||||
|
||||
class OpType(Enum):
|
||||
MODIFY = "modify"
|
||||
DELETE = "delete"
|
||||
|
||||
|
||||
def transform_attribute_properties(
|
||||
introspection: dict[str, Any],
|
||||
all_values: dict[str, Any],
|
||||
persisted: dict[str, Any],
|
||||
# Passthrough for recursion
|
||||
curr_path: PathTuple = (),
|
||||
parent_attributes: RawAttributes | None = None,
|
||||
) -> dict[PathTuple, set[OpType]]:
|
||||
"""Transform attribute properties to ensure correct types and defaults."""
|
||||
results: dict[PathTuple, set[OpType]] = {}
|
||||
|
||||
for key, key_meta in introspection.items():
|
||||
if key in {"__this", "__list"}:
|
||||
continue
|
||||
|
||||
path = (*curr_path, key)
|
||||
results[path] = set()
|
||||
|
||||
local_attributes: RawAttributes = key_meta.get("__this")
|
||||
|
||||
key_priority = local_attributes["prio"] or None
|
||||
|
||||
effective_priority = key_priority or (
|
||||
parent_attributes["prio"] if parent_attributes else None
|
||||
)
|
||||
if effective_priority is None:
|
||||
msg = f"Priority for path '{path_to_string(path)}' is not defined and no parent to inherit from. Cannot determine effective priority."
|
||||
raise ClanError(msg)
|
||||
|
||||
if isinstance(key_meta, dict):
|
||||
subattrs = transform_attribute_properties(
|
||||
key_meta,
|
||||
all_values.get(key, {}),
|
||||
persisted.get(key, {}),
|
||||
curr_path=path,
|
||||
parent_attributes=local_attributes,
|
||||
)
|
||||
results.update(dict(subattrs.items()))
|
||||
|
||||
return results
|
||||
|
||||
# Only defined in inventory.json -> We might be able to delete it, because we defined it.
|
||||
# But we could also have some option default somewhere else, so we cannot be sure.
|
||||
# if all(f.endswith("inventory.json") for f in raw_attributes["files"]):
|
||||
# operations.add(OpType.DELETE)
|
||||
|
||||
# if (
|
||||
# raw_attributes["prio"] >= WRITABLE_PRIORITY_THRESHOLD
|
||||
# or ".json" in raw_attributes["files"]
|
||||
# ):
|
||||
# operations.add(OpType.MODIFY)
|
||||
|
||||
@@ -5,11 +5,110 @@ import pytest
|
||||
|
||||
from clan_lib.flake.flake import Flake
|
||||
from clan_lib.persist.inventory_store import InventoryStore
|
||||
from clan_lib.persist.write_rules import compute_write_map
|
||||
from clan_lib.persist.write_rules import (
|
||||
compute_write_map,
|
||||
transform_attribute_properties,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from clan_lib.nix_models.clan import Clan
|
||||
|
||||
# foos.this = lib.mkForce { bar = 42; };
|
||||
# ->
|
||||
# {
|
||||
# foos = {
|
||||
# __this = {
|
||||
# files = [
|
||||
# "inventory.json"
|
||||
# "<unknown-file>"
|
||||
# ];
|
||||
# headType = "attrsOf";
|
||||
# nullable = false;
|
||||
# prio = 100;
|
||||
# total = false;
|
||||
# };
|
||||
# this = {
|
||||
# __this = {
|
||||
# files = [ "<unknown-file>" ];
|
||||
# headType = "submodule";
|
||||
# nullable = false;
|
||||
# prio = 50;
|
||||
# total = true;
|
||||
# };
|
||||
# bar = {
|
||||
# __this = {
|
||||
# files = [ "<unknown-file>" ];
|
||||
# headType = "unspecified";
|
||||
# nullable = false;
|
||||
# prio = 100;
|
||||
# total = false;
|
||||
# };
|
||||
# };
|
||||
# };
|
||||
# };
|
||||
# }
|
||||
|
||||
|
||||
def test_write_new() -> None:
|
||||
all_data: dict = {"foo": {"bar": 42}}
|
||||
persisted_data: dict = {}
|
||||
introspection: dict = {
|
||||
"foo": {
|
||||
"__this": {
|
||||
"files": ["/dir/file.nix"],
|
||||
"headType": "unspecified",
|
||||
"nullable": False,
|
||||
"prio": 100, # <- default prio
|
||||
"total": False,
|
||||
},
|
||||
"bar": {
|
||||
"__this": {
|
||||
"files": ["/dir/file.nix"],
|
||||
"headType": "int",
|
||||
"nullable": False,
|
||||
"prio": 100, # <- default prio
|
||||
"total": False,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
res = transform_attribute_properties(introspection, all_data, persisted_data)
|
||||
|
||||
breakpoint()
|
||||
|
||||
# No operations allowed, because mkForce
|
||||
# We cannot modify this value in ANY possible way.
|
||||
# inventory.json definitions and children definition are filtered out by the module system
|
||||
# assert attributes == {"operations": set(), "path": ["foo", "bar"]}
|
||||
|
||||
# normal_prio_attrs: RawAttributes = {
|
||||
# "files": ["/dir/file.nix"],
|
||||
# "headType": "attrsOf",
|
||||
# "nullable": False,
|
||||
# "prio": 100, # <- default prio
|
||||
# "total": False,
|
||||
# }
|
||||
|
||||
# attributes = transform_attribute_properties(("foo", "bar"), normal_prio_attrs)
|
||||
|
||||
# # We can modify this value, because its a normal prio
|
||||
# # This means keys can be added/removed/changed respecting their individual local constraints
|
||||
# assert attributes == {"operations": { OpType.MODIFY }, "path": ["foo", "bar"]}
|
||||
|
||||
# default_prio_attrs: RawAttributes = {
|
||||
# "files": ["/dir/file.nix"],
|
||||
# "headType": "attrsOf",
|
||||
# "nullable": False,
|
||||
# "prio": 100, # <- default prio
|
||||
# "total": False,
|
||||
# }
|
||||
# attributes = transform_attribute_properties(("foo", "bar"), default_prio_attrs)
|
||||
|
||||
# # We can modify this value, because its a normal prio
|
||||
# # This means keys can be added/removed/changed respecting their individual local constraints
|
||||
# assert attributes == {"operations": { OpType.MODIFY, OpType.DELETE }, "path": ["foo", "bar"]}
|
||||
|
||||
|
||||
# Integration test
|
||||
@pytest.mark.with_core
|
||||
|
||||
Reference in New Issue
Block a user