fixes https://git.clan.lol/clan/clan-core/issues/3791 This fixes multiple issues we had when re-generating shared vars. Problem 1: shared vars are re-generated for each individual machine instead of just once (see #3791) Problem 2: When a shared var was re-generated for one machine, dependent vars on other machines did not get re-generated, leading to broken state Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5001
140 lines
4.3 KiB
Python
140 lines
4.3 KiB
Python
import logging
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING
|
|
|
|
from clan_lib.errors import ClanError
|
|
from clan_lib.git import commit_files
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
if TYPE_CHECKING:
|
|
from clan_cli.vars.generator import Generator
|
|
from clan_lib.machines.machines import Machine
|
|
|
|
|
|
def _migration_file_exists(
|
|
machine: "Machine",
|
|
generator: "Generator",
|
|
fact_name: str,
|
|
) -> bool:
|
|
for file in generator.files:
|
|
if file.name == fact_name:
|
|
break
|
|
else:
|
|
msg = f"Could not find file {fact_name} in generator {generator.name}"
|
|
raise ClanError(msg)
|
|
|
|
is_secret = file.secret
|
|
if is_secret:
|
|
if machine.secret_facts_store.exists(generator.name, fact_name):
|
|
return True
|
|
machine.debug(
|
|
f"Cannot migrate fact {fact_name} for service {generator.name}, as it does not exist in the secret fact store",
|
|
)
|
|
if not is_secret:
|
|
if machine.public_facts_store.exists(generator.name, fact_name):
|
|
return True
|
|
machine.debug(
|
|
f"Cannot migrate fact {fact_name} for service {generator.name}, as it does not exist in the public fact store",
|
|
)
|
|
return False
|
|
|
|
|
|
def _migrate_file(
|
|
machine: "Machine",
|
|
generator: "Generator",
|
|
var_name: str,
|
|
service_name: str,
|
|
fact_name: str,
|
|
) -> list[Path]:
|
|
for file in generator.files:
|
|
if file.name == var_name:
|
|
break
|
|
else:
|
|
msg = f"Could not find file {fact_name} in generator {generator.name}"
|
|
raise ClanError(msg)
|
|
|
|
paths = []
|
|
|
|
if file.secret:
|
|
old_value = machine.secret_facts_store.get(service_name, fact_name)
|
|
paths_list = machine.secret_vars_store.set(
|
|
generator, file, old_value, machine.name, is_migration=True
|
|
)
|
|
paths.extend(paths_list)
|
|
else:
|
|
old_value = machine.public_facts_store.get(service_name, fact_name)
|
|
paths_list = machine.public_vars_store.set(
|
|
generator, file, old_value, machine.name, is_migration=True
|
|
)
|
|
paths.extend(paths_list)
|
|
|
|
return paths
|
|
|
|
|
|
def migrate_files(
|
|
machine: "Machine",
|
|
generator: "Generator",
|
|
) -> None:
|
|
not_found = []
|
|
files_to_commit = []
|
|
for file in generator.files:
|
|
if _migration_file_exists(machine, generator, file.name):
|
|
if generator.migrate_fact is None:
|
|
msg = f"Generator {generator.name} has no migrate_fact defined"
|
|
raise ClanError(msg)
|
|
files_to_commit += _migrate_file(
|
|
machine,
|
|
generator,
|
|
file.name,
|
|
generator.migrate_fact,
|
|
file.name,
|
|
)
|
|
else:
|
|
not_found.append(file.name)
|
|
if len(not_found) > 0:
|
|
msg = f"Could not migrate the following files for generator {generator.name}, as no fact or secret exists with the same name: {not_found}"
|
|
raise ClanError(msg)
|
|
commit_files(
|
|
files_to_commit,
|
|
machine.flake_dir,
|
|
f"migrated facts to vars for generator {generator.name} for machine {machine.name}",
|
|
)
|
|
|
|
|
|
def check_can_migrate(
|
|
machine: "Machine",
|
|
generator: "Generator",
|
|
) -> bool:
|
|
service_name = generator.migrate_fact
|
|
if not service_name:
|
|
return False
|
|
# ensure that none of the generated vars already exist in the store
|
|
all_files_missing = True
|
|
all_files_present = True
|
|
for file in generator.files:
|
|
if file.secret:
|
|
if machine.secret_vars_store.exists(generator, file.name):
|
|
all_files_missing = False
|
|
else:
|
|
all_files_present = False
|
|
elif machine.public_vars_store.exists(generator, file.name):
|
|
all_files_missing = False
|
|
else:
|
|
all_files_present = False
|
|
|
|
if not all_files_present and not all_files_missing:
|
|
msg = f"Cannot migrate facts for generator {generator.name} as some files already exist in the store"
|
|
raise ClanError(msg)
|
|
if all_files_present:
|
|
# all files already migrated, no need to run migration again
|
|
return False
|
|
|
|
# ensure that all files can be migrated (exists in the corresponding fact store)
|
|
return bool(
|
|
all(
|
|
_migration_file_exists(machine, generator, file.name)
|
|
for file in generator.files
|
|
),
|
|
)
|