vars: raise error when shared generators differ between machines
When generators are shared between machines, their definition has to be the same across all machines. If not, it might lead to unexpected problems, as the architecture assumes that all definitions are the same. fixes https://git.clan.lol/clan/clan-core/issues/5253
This commit is contained in:
@@ -1335,6 +1335,46 @@ def test_cache_misses_for_vars_operations(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.with_core
|
||||
def test_shared_generator_conflicting_definition_raises_error(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
flake_with_sops: ClanFlake,
|
||||
) -> None:
|
||||
"""Test that vars generation raises an error when two machines have different
|
||||
definitions for the same shared generator.
|
||||
"""
|
||||
flake = flake_with_sops
|
||||
|
||||
# Create machine1 with a shared generator
|
||||
machine1_config = flake.machines["machine1"] = create_test_machine_config()
|
||||
shared_gen1 = machine1_config["clan"]["core"]["vars"]["generators"][
|
||||
"shared_generator"
|
||||
]
|
||||
shared_gen1["share"] = True
|
||||
shared_gen1["files"]["file1"]["secret"] = False
|
||||
shared_gen1["script"] = 'echo "test" > "$out"/file1'
|
||||
|
||||
# Create machine2 with the same shared generator but different files
|
||||
machine2_config = flake.machines["machine2"] = create_test_machine_config()
|
||||
shared_gen2 = machine2_config["clan"]["core"]["vars"]["generators"][
|
||||
"shared_generator"
|
||||
]
|
||||
shared_gen2["share"] = True
|
||||
shared_gen2["files"]["file2"]["secret"] = False # Different file name
|
||||
shared_gen2["script"] = 'echo "test" > "$out"/file2'
|
||||
|
||||
flake.refresh()
|
||||
monkeypatch.chdir(flake.path)
|
||||
|
||||
# Attempting to generate vars for both machines should raise an error
|
||||
# because they have conflicting definitions for the same shared generator
|
||||
with pytest.raises(
|
||||
ClanError,
|
||||
match=".*differ.*",
|
||||
):
|
||||
cli.run(["vars", "generate", "--flake", str(flake.path)])
|
||||
|
||||
|
||||
@pytest.mark.with_core
|
||||
def test_dynamic_invalidation(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
|
||||
@@ -144,6 +144,9 @@ class Generator:
|
||||
flake.precache(cls.get_machine_selectors(machine_names))
|
||||
|
||||
generators = []
|
||||
shared_generators_raw: dict[
|
||||
str, tuple[str, dict, dict]
|
||||
] = {} # name -> (machine_name, gen_data, files_data)
|
||||
|
||||
for machine_name in machine_names:
|
||||
# Get all generator metadata in one select (safe fields only)
|
||||
@@ -165,6 +168,38 @@ class Generator:
|
||||
sec_store = machine.secret_vars_store
|
||||
|
||||
for gen_name, gen_data in generators_data.items():
|
||||
# Check for conflicts in shared generator definitions using raw data
|
||||
if gen_data["share"]:
|
||||
if gen_name in shared_generators_raw:
|
||||
prev_machine, prev_gen_data, prev_files_data = (
|
||||
shared_generators_raw[gen_name]
|
||||
)
|
||||
# Compare raw data
|
||||
prev_gen_files = prev_files_data.get(gen_name, {})
|
||||
curr_gen_files = files_data.get(gen_name, {})
|
||||
# Build list of differences with details
|
||||
differences = []
|
||||
if prev_gen_files != curr_gen_files:
|
||||
differences.append("files")
|
||||
if prev_gen_data.get("prompts") != gen_data.get("prompts"):
|
||||
differences.append("prompts")
|
||||
if prev_gen_data.get("dependencies") != gen_data.get(
|
||||
"dependencies"
|
||||
):
|
||||
differences.append("dependencies")
|
||||
if prev_gen_data.get("validationHash") != gen_data.get(
|
||||
"validationHash"
|
||||
):
|
||||
differences.append("validation_hash")
|
||||
if differences:
|
||||
msg = f"Machines {prev_machine} and {machine_name} have different definitions for shared generator '{gen_name}' (differ in: {', '.join(differences)})"
|
||||
raise ClanError(msg)
|
||||
else:
|
||||
shared_generators_raw[gen_name] = (
|
||||
machine_name,
|
||||
gen_data,
|
||||
files_data,
|
||||
)
|
||||
# Build files from the files_data
|
||||
files = []
|
||||
gen_files = files_data.get(gen_name, {})
|
||||
@@ -216,6 +251,7 @@ class Generator:
|
||||
_public_store=pub_store,
|
||||
_secret_store=sec_store,
|
||||
)
|
||||
|
||||
generators.append(generator)
|
||||
|
||||
# TODO: This should be done in a non-mutable way.
|
||||
|
||||
Reference in New Issue
Block a user