Merge pull request 'tests: improve performance, keep flake.lock between tests' (#2428) from DavHau/clan-core:DavHau-nix-eval into main

This commit is contained in:
clan-bot
2024-11-18 10:08:43 +00:00
5 changed files with 224 additions and 320 deletions

View File

@@ -4,9 +4,10 @@ import os
import shutil
import subprocess as sp
import tempfile
from collections.abc import Iterator
from collections import defaultdict
from collections.abc import Callable, Iterator
from pathlib import Path
from typing import NamedTuple
from typing import Any, NamedTuple
import pytest
from clan_cli.dirs import nixpkgs_source
@@ -16,6 +17,14 @@ from root import CLAN_CORE
log = logging.getLogger(__name__)
# allows defining nested dictionary in a single line
def def_value() -> defaultdict:
return defaultdict(def_value)
nested_dict: Callable[[], dict[str, Any]] = lambda: defaultdict(def_value)
# Substitutes strings in a file.
# This can be used on the flake.nix or default.nix of a machine
def substitute(
@@ -59,92 +68,112 @@ def set_machine_settings(
config_path.write_text(json.dumps(machine_settings, indent=2))
def init_git(monkeypatch: pytest.MonkeyPatch, flake: Path) -> None:
def set_git_credentials(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("GIT_AUTHOR_NAME", "clan-tool")
monkeypatch.setenv("GIT_AUTHOR_EMAIL", "clan@example.com")
monkeypatch.setenv("GIT_COMMITTER_NAME", "clan-tool")
monkeypatch.setenv("GIT_COMMITTER_EMAIL", "clan@example.com")
def init_git(monkeypatch: pytest.MonkeyPatch, flake: Path) -> None:
set_git_credentials(monkeypatch)
sp.run(["git", "init", "-b", "main"], cwd=flake, check=True)
# TODO: Find out why test_vms_api.py fails in nix build
# but works in pytest when this bottom line is commented out
sp.run(
["git", "config", "--global", "init.defaultBranch", "main"],
cwd=flake,
check=True,
)
sp.run(["git", "init"], cwd=flake, check=True)
sp.run(["git", "add", "."], cwd=flake, check=True)
sp.run(["git", "commit", "-a", "-m", "Initial commit"], cwd=flake, check=True)
def generate_flake(
class ClanFlake:
"""
This class holds all attributes for generating a clan flake.
For example, inventory and machine configs can be set via self.inventory and self.machines["my_machine"] = {...}.
Whenever a flake's configuration is changed, it needs to be re-generated by calling refresh().
This class can also be used for managing templates.
Once initialized, all its settings including all generated files, if any, can be copied using the copy() method.
This avoids expensive re-computation, like for example creating the flake.lock over and over again.
"""
def __init__(
self,
temporary_home: Path,
flake_template: Path,
monkeypatch: pytest.MonkeyPatch,
substitutions: dict[str, str] | None = None,
# define the machines directly including their config
machine_configs: dict[str, dict] | None = None,
inventory: dict[str, dict] | None = None,
clan_modules: list[str] | None = None,
) -> FlakeForTest:
"""
Creates a clan flake with the given name.
Machines are fully generated from the machine_configs.
Example:
machine_configs = dict(
my_machine=dict(
clan=dict(
core=dict(
backups=dict(
...
)
)
)
)
)
"""
if machine_configs is None:
machine_configs = {}
if inventory is None:
inventory = {}
if clan_modules is None:
clan_modules = []
substitutions = {
suppress_tmp_home_warning: bool = False,
) -> None:
self._flake_template = flake_template
self.inventory = nested_dict()
self.machines = nested_dict()
self.substitutions: dict[str, str] = {
"git+https://git.clan.lol/clan/clan-core": "path://" + str(CLAN_CORE),
"https://git.clan.lol/clan/clan-core/archive/main.tar.gz": "path://"
+ str(CLAN_CORE),
}
flake = temporary_home / "flake"
shutil.copytree(flake_template, flake)
sp.run(["chmod", "+w", "-R", str(flake)], check=True)
self.clan_modules: list[str] = []
self.temporary_home = temporary_home
self.path = temporary_home / "flake"
if not suppress_tmp_home_warning:
if "/tmp" not in str(os.environ.get("HOME")):
log.warning(
f"!! $HOME does not point to a temp directory!! HOME={os.environ['HOME']}"
)
# initialize inventory
if inventory:
# check if inventory valid
inventory_path = flake / "inventory.json"
inventory_path.write_text(json.dumps(inventory, indent=2))
def copy(
self,
temporary_home: Path,
monkeypatch: pytest.MonkeyPatch,
) -> "ClanFlake":
# copy the files to the new location
shutil.copytree(self.path, temporary_home / "flake")
set_git_credentials(monkeypatch)
return ClanFlake(
temporary_home=temporary_home,
flake_template=self._flake_template,
)
# substitute `substitutions` in all files of the template
for file in flake.rglob("*"):
def substitute(self) -> None:
for file in self.path.rglob("*"):
if ".git" in file.parts:
continue
if file.is_file():
print(f"Final Content of {file}:")
buf = ""
with file.open() as f:
for line in f:
for key, value in substitutions.items():
for key, value in self.substitutions.items():
line = line.replace(key, value)
buf += line
file.write_text(buf)
# generate machines from machineConfigs
for machine_name, machine_config in machine_configs.items():
configuration_nix = flake / "machines" / machine_name / "configuration.nix"
configuration_nix.parent.mkdir(parents=True, exist_ok=True)
imports = "\n".join(
[f"clan-core.clanModules.{module}" for module in clan_modules]
def init_from_template(self) -> None:
shutil.copytree(self._flake_template, self.path)
sp.run(["chmod", "+w", "-R", str(self.path)], check=True)
self.substitute()
if not (self.path / ".git").exists():
sp.run(
["nix", "flake", "lock"],
cwd=self.path,
check=True,
)
with pytest.MonkeyPatch.context() as mp:
init_git(mp, self.path)
def refresh(self) -> None:
if not self.path.exists():
self.init_from_template()
self.substitute()
if self.inventory:
inventory_path = self.path / "inventory.json"
inventory_path.write_text(json.dumps(self.inventory, indent=2))
imports = "\n".join(
[f"clan-core.clanModules.{module}" for module in self.clan_modules]
)
for machine_name, machine_config in self.machines.items():
configuration_nix = (
self.path / "machines" / machine_name / "configuration.nix"
)
configuration_nix.parent.mkdir(parents=True, exist_ok=True)
configuration_nix.write_text(f"""
{{clan-core, ...}}:
{{
@@ -154,23 +183,37 @@ def generate_flake(
];
}}
""")
set_machine_settings(flake, machine_name, machine_config)
if "/tmp" not in str(os.environ.get("HOME")):
log.warning(
f"!! $HOME does not point to a temp directory!! HOME={os.environ['HOME']}"
)
# TODO: Find out why test_vms_api.py fails in nix build
# but works in pytest when this bottom line is commented out
set_machine_settings(self.path, machine_name, machine_config)
sp.run(["git", "add", "."], cwd=self.path, check=True)
sp.run(
["git", "config", "--global", "init.defaultBranch", "main"],
cwd=flake,
["git", "commit", "-a", "-m", "Update by flake generator"],
cwd=self.path,
check=True,
)
init_git(monkeypatch, flake)
return FlakeForTest(flake)
@pytest.fixture(scope="session")
def minimal_flake_template() -> Iterator[ClanFlake]:
with (
tempfile.TemporaryDirectory(prefix="flake-") as home,
pytest.MonkeyPatch.context() as mp,
):
mp.setenv("HOME", home)
flake = ClanFlake(
temporary_home=Path(home),
flake_template=CLAN_CORE / "templates" / "minimal",
)
flake.init_from_template()
yield flake
@pytest.fixture
def flake(
temporary_home: Path,
minimal_flake_template: ClanFlake,
monkeypatch: pytest.MonkeyPatch,
) -> ClanFlake:
return minimal_flake_template.copy(temporary_home, monkeypatch)
def create_flake(
@@ -274,21 +317,3 @@ def test_flake_with_core(
clan_core_flake=CLAN_CORE,
monkeypatch=monkeypatch,
)
@pytest.fixture
def test_local_democlan(
monkeypatch: pytest.MonkeyPatch, temporary_home: Path
) -> FlakeForTest:
democlan = os.getenv(key="DEMOCLAN_ROOT")
if democlan is None:
msg = (
"DEMOCLAN_ROOT not set. This test requires the democlan flake to be present"
)
raise FixtureError(msg)
democlan_p = Path(democlan).resolve()
if not democlan_p.is_dir():
msg = f"DEMOCLAN_ROOT ({democlan_p}) is not a directory. This test requires the democlan directory to be present"
raise FixtureError(msg)
return FlakeForTest(democlan_p)

View File

@@ -8,22 +8,25 @@ from clan_cli.machines.facts import machine_get_fact
from clan_cli.machines.machines import Machine
from clan_cli.nix import nix_shell
from clan_cli.ssh import HostGroup
from fixtures_flakes import generate_flake
from fixtures_flakes import ClanFlake
from helpers import cli
from helpers.nixos_config import nested_dict
from helpers.validator import is_valid_ssh_key
from root import CLAN_CORE
@pytest.mark.impure
def test_upload_secret(
monkeypatch: pytest.MonkeyPatch,
flake: ClanFlake,
temporary_home: Path,
host_group: HostGroup,
) -> None:
config = nested_dict()
flake.clan_modules = [
"root-password",
"user-password",
"sshd",
]
config = flake.machines["vm1"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
# clan.core.networking.zerotier.controller.enable = true;
config["clan"]["core"]["networking"]["zerotier"]["controller"]["enable"] = True
host = host_group.hosts[0]
addr = f"{host.user}@{host.host}:{host.port}?StrictHostKeyChecking=no&UserKnownHostsFile=/dev/null&IdentityFile={host.key}"
@@ -38,17 +41,7 @@ def test_upload_secret(
)
facts["secretUploadDirectory"]["priority"] = 50
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"vm1": config},
clan_modules=[
"root-password",
"user-password",
"sshd",
],
)
flake.refresh()
monkeypatch.chdir(flake.path)
gnupghome = temporary_home / "gpg"
gnupghome.mkdir(mode=0o700)

View File

@@ -16,10 +16,8 @@ from clan_cli.vars.list import stringify_all_vars
from clan_cli.vars.public_modules import in_repo
from clan_cli.vars.secret_modules import password_store, sops
from clan_cli.vars.set import set_var
from fixtures_flakes import generate_flake, set_machine_settings
from fixtures_flakes import ClanFlake
from helpers import cli
from helpers.nixos_config import nested_dict
from root import CLAN_CORE
from stdout import CaptureOutput
@@ -80,19 +78,14 @@ def test_required_generators() -> None:
@pytest.mark.impure
def test_generate_public_var(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_value"]["secret"] = False
my_generator["script"] = "echo hello > $out/my_value"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
machine = Machine(name="my_machine", flake=FlakeId(str(flake.path)))
assert not check_vars(machine)
@@ -120,20 +113,15 @@ def test_generate_public_var(
@pytest.mark.impure
def test_generate_secret_var_sops(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
sops_setup: SopsSetup,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_secret"]["secret"] = True
my_generator["script"] = "echo hello > $out/my_secret"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
machine = Machine(name="my_machine", flake=FlakeId(str(flake.path)))
@@ -163,21 +151,16 @@ def test_generate_secret_var_sops(
@pytest.mark.impure
def test_generate_secret_var_sops_with_default_group(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
sops_setup: SopsSetup,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
config["clan"]["core"]["sops"]["defaultGroups"] = ["my_group"]
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_secret"]["secret"] = True
my_generator["script"] = "echo hello > $out/my_secret"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
cli.run(["secrets", "groups", "add-user", "my_group", sops_setup.user])
@@ -221,10 +204,10 @@ def test_generate_secret_var_sops_with_default_group(
@pytest.mark.impure
def test_generated_shared_secret_sops(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
sops_setup: SopsSetup,
) -> None:
m1_config = nested_dict()
m1_config = flake.machines["machine1"]
m1_config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
shared_generator = m1_config["clan"]["core"]["vars"]["generators"][
"my_shared_generator"
@@ -232,17 +215,12 @@ def test_generated_shared_secret_sops(
shared_generator["share"] = True
shared_generator["files"]["my_shared_secret"]["secret"] = True
shared_generator["script"] = "echo hello > $out/my_shared_secret"
m2_config = nested_dict()
m2_config = flake.machines["machine2"]
m2_config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
m2_config["clan"]["core"]["vars"]["generators"]["my_shared_generator"] = (
shared_generator.copy()
)
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"machine1": m1_config, "machine2": m2_config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
machine1 = Machine(name="machine1", flake=FlakeId(str(flake.path)))
@@ -267,10 +245,10 @@ def test_generated_shared_secret_sops(
@pytest.mark.impure
def test_generate_secret_var_password_store(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
test_root: Path,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
config["clan"]["core"]["vars"]["settings"]["secretStore"] = "password-store"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
@@ -282,20 +260,15 @@ def test_generate_secret_var_password_store(
my_shared_generator["share"] = True
my_shared_generator["files"]["my_shared_secret"]["secret"] = True
my_shared_generator["script"] = "echo hello > $out/my_shared_secret"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
gnupghome = temporary_home / "gpg"
gnupghome = flake.path / "gpg"
shutil.copytree(test_root / "data" / "gnupg-home", gnupghome)
monkeypatch.setenv("GNUPGHOME", str(gnupghome))
password_store_dir = temporary_home / "pass"
password_store_dir = flake.path / "pass"
shutil.copytree(test_root / "data" / "password-store", password_store_dir)
monkeypatch.setenv("PASSWORD_STORE_DIR", str(temporary_home / "pass"))
monkeypatch.setenv("PASSWORD_STORE_DIR", str(flake.path / "pass"))
machine = Machine(name="my_machine", flake=FlakeId(str(flake.path)))
assert not check_vars(machine)
@@ -316,10 +289,10 @@ def test_generate_secret_var_password_store(
@pytest.mark.impure
def test_generate_secret_for_multiple_machines(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
sops_setup: SopsSetup,
) -> None:
machine1_config = nested_dict()
machine1_config = flake.machines["machine1"]
machine1_generator = machine1_config["clan"]["core"]["vars"]["generators"][
"my_generator"
]
@@ -328,7 +301,7 @@ def test_generate_secret_for_multiple_machines(
machine1_generator["script"] = (
"echo machine1 > $out/my_secret && echo machine1 > $out/my_value"
)
machine2_config = nested_dict()
machine2_config = flake.machines["machine2"]
machine2_generator = machine2_config["clan"]["core"]["vars"]["generators"][
"my_generator"
]
@@ -337,12 +310,7 @@ def test_generate_secret_for_multiple_machines(
machine2_generator["script"] = (
"echo machine2 > $out/my_secret && echo machine2 > $out/my_value"
)
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"machine1": machine1_config, "machine2": machine2_config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
cli.run(["vars", "generate", "--flake", str(flake.path)])
@@ -373,9 +341,9 @@ def test_generate_secret_for_multiple_machines(
@pytest.mark.impure
def test_dependant_generators(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
parent_gen = config["clan"]["core"]["vars"]["generators"]["parent_generator"]
parent_gen["files"]["my_value"]["secret"] = False
parent_gen["script"] = "echo hello > $out/my_value"
@@ -383,12 +351,7 @@ def test_dependant_generators(
child_gen["files"]["my_value"]["secret"] = False
child_gen["dependencies"] = ["parent_generator"]
child_gen["script"] = "cat $in/parent_generator/my_value > $out/my_value"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"])
in_repo_store = in_repo.FactStore(
@@ -412,23 +375,18 @@ def test_dependant_generators(
)
def test_prompt(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
prompt_type: str,
input_value: str,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_value"]["secret"] = False
my_generator["prompts"]["prompt1"]["description"] = "dream2nix"
my_generator["prompts"]["prompt1"]["createFile"] = False
my_generator["prompts"]["prompt1"]["type"] = prompt_type
my_generator["script"] = "cat $prompts/prompt1 > $out/my_value"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
monkeypatch.setattr("sys.stdin", StringIO(input_value))
cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"])
@@ -442,10 +400,10 @@ def test_prompt(
@pytest.mark.impure
def test_share_flag(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
sops_setup: SopsSetup,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
shared_generator = config["clan"]["core"]["vars"]["generators"]["shared_generator"]
shared_generator["share"] = True
@@ -463,12 +421,7 @@ def test_share_flag(
unshared_generator["script"] = (
"echo hello > $out/my_secret && echo hello > $out/my_value"
)
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
machine = Machine(name="my_machine", flake=FlakeId(str(flake.path)))
@@ -504,10 +457,10 @@ def test_share_flag(
@pytest.mark.impure
def test_depending_on_shared_secret_succeeds(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
sops_setup: SopsSetup,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
shared_generator = config["clan"]["core"]["vars"]["generators"]["shared_generator"]
shared_generator["share"] = True
shared_generator["files"]["my_secret"]["secret"] = True
@@ -521,12 +474,7 @@ def test_depending_on_shared_secret_succeeds(
dependent_generator["script"] = (
"cat $in/shared_generator/my_secret > $out/my_secret"
)
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"])
@@ -535,22 +483,17 @@ def test_depending_on_shared_secret_succeeds(
@pytest.mark.impure
def test_prompt_create_file(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
sops_setup: SopsSetup,
) -> None:
"""
Test that the createFile flag in the prompt configuration works as expected
"""
config = nested_dict()
config = flake.machines["my_machine"]
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["prompts"]["prompt1"]["createFile"] = True
my_generator["prompts"]["prompt2"]["createFile"] = False
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
monkeypatch.setattr("sys.stdin", StringIO("input1\ninput2\n"))
@@ -566,20 +509,15 @@ def test_prompt_create_file(
@pytest.mark.impure
def test_api_get_prompts(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
) -> None:
from clan_cli.vars.list import get_prompts
config = nested_dict()
config = flake.machines["my_machine"]
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["prompts"]["prompt1"]["type"] = "line"
my_generator["files"]["prompt1"]["secret"] = False
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
monkeypatch.setattr("sys.stdin", StringIO("input1"))
cli.run(["vars", "generate", "--flake", str(flake.path), "my_machine"])
@@ -594,21 +532,16 @@ def test_api_get_prompts(
@pytest.mark.impure
def test_api_set_prompts(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
) -> None:
from clan_cli.vars._types import GeneratorUpdate
from clan_cli.vars.list import set_prompts
config = nested_dict()
config = flake.machines["my_machine"]
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["prompts"]["prompt1"]["type"] = "line"
my_generator["files"]["prompt1"]["secret"] = False
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
machine = Machine(name="my_machine", flake=FlakeId(str(flake.path)))
set_prompts(
@@ -638,10 +571,10 @@ def test_api_set_prompts(
@pytest.mark.impure
def test_commit_message(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
sops_setup: SopsSetup,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_value"]["secret"] = False
my_generator["script"] = "echo hello > $out/my_value"
@@ -650,12 +583,7 @@ def test_commit_message(
]
my_secret_generator["files"]["my_secret"]["secret"] = True
my_secret_generator["script"] = "echo hello > $out/my_secret"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
cli.run(
@@ -700,9 +628,9 @@ def test_commit_message(
@pytest.mark.impure
def test_default_value(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_value"]["secret"] = False
@@ -710,12 +638,7 @@ def test_default_value(
my_generator["files"]["my_value"]["value"]["priority"] = 1000 # mkDefault
my_generator["files"]["my_value"]["value"]["content"] = "foo"
my_generator["script"] = "echo -n hello > $out/my_value"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
# ensure evaluating the default value works without generating the value
value_eval = run(
@@ -742,11 +665,11 @@ def test_default_value(
@pytest.mark.impure
def test_stdout_of_generate(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
capture_output: CaptureOutput,
sops_setup: SopsSetup,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_value"]["secret"] = False
@@ -756,12 +679,7 @@ def test_stdout_of_generate(
]
my_secret_generator["files"]["my_secret"]["secret"] = True
my_secret_generator["script"] = "echo -n hello > $out/my_secret"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
from clan_cli.vars.generate import generate_vars_for_machine
@@ -828,10 +746,10 @@ def test_stdout_of_generate(
@pytest.mark.impure
def test_migration_skip(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
sops_setup: SopsSetup,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_service = config["clan"]["core"]["facts"]["services"]["my_service"]
my_service["secret"]["my_value"] = {}
@@ -841,12 +759,7 @@ def test_migration_skip(
my_generator["files"]["my_value"]["secret"] = False
my_generator["migrateFact"] = "my_service"
my_generator["script"] = "echo -n world > $out/my_value"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
cli.run(["facts", "generate", "--flake", str(flake.path), "my_machine"])
@@ -861,10 +774,10 @@ def test_migration_skip(
@pytest.mark.impure
def test_migration(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
sops_setup: SopsSetup,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_service = config["clan"]["core"]["facts"]["services"]["my_service"]
my_service["public"]["my_value"] = {}
@@ -873,12 +786,7 @@ def test_migration(
my_generator["files"]["my_value"]["secret"] = False
my_generator["migrateFact"] = "my_service"
my_generator["script"] = "echo -n world > $out/my_value"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
cli.run(["facts", "generate", "--flake", str(flake.path), "my_machine"])
@@ -893,10 +801,10 @@ def test_migration(
@pytest.mark.impure
def test_fails_when_files_are_left_from_other_backend(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
sops_setup: SopsSetup,
) -> None:
config = nested_dict()
config = flake.machines["my_machine"]
config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
my_secret_generator = config["clan"]["core"]["vars"]["generators"][
"my_secret_generator"
@@ -908,12 +816,7 @@ def test_fails_when_files_are_left_from_other_backend(
]
my_value_generator["files"]["my_value"]["secret"] = False
my_value_generator["script"] = "echo hello > $out/my_value"
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"my_machine": config},
)
flake.refresh()
monkeypatch.chdir(flake.path)
sops_setup.init()
for generator in ["my_secret_generator", "my_value_generator"]:
@@ -925,7 +828,7 @@ def test_fails_when_files_are_left_from_other_backend(
)
my_secret_generator["files"]["my_secret"]["secret"] = False
my_value_generator["files"]["my_value"]["secret"] = True
set_machine_settings(flake.path, "my_machine", config)
flake.refresh()
monkeypatch.chdir(flake.path)
for generator in ["my_secret_generator", "my_value_generator"]:
with pytest.raises(ClanError):

View File

@@ -1,6 +1,5 @@
import json
from contextlib import ExitStack
from pathlib import Path
import pytest
from age_keys import SopsSetup
@@ -9,22 +8,19 @@ from clan_cli.clan_uri import FlakeId
from clan_cli.machines.machines import Machine
from clan_cli.nix import nix_eval, run
from clan_cli.vms.run import inspect_vm, spawn_vm
from fixtures_flakes import generate_flake
from fixtures_flakes import ClanFlake
from helpers import cli
from helpers.nixos_config import nested_dict
from nix_config import ConfigItem
from root import CLAN_CORE
@pytest.mark.impure
def test_vm_deployment(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
nix_config: dict[str, ConfigItem],
sops_setup: SopsSetup,
) -> None:
# machine 1
machine1_config = nested_dict()
machine1_config = flake.machines["m1_machine"]
machine1_config["nixpkgs"]["hostPlatform"] = nix_config["system"].value
machine1_config["clan"]["virtualisation"]["graphics"] = False
machine1_config["services"]["getty"]["autologinUser"] = "root"
@@ -50,7 +46,7 @@ def test_vm_deployment(
echo hello > $out/no_deploy_secret
"""
# machine 2
machine2_config = nested_dict()
machine2_config = flake.machines["m2_machine"]
machine2_config["nixpkgs"]["hostPlatform"] = nix_config["system"].value
machine2_config["clan"]["virtualisation"]["graphics"] = False
machine2_config["services"]["getty"]["autologinUser"] = "root"
@@ -63,12 +59,7 @@ def test_vm_deployment(
m1_shared_generator.copy()
)
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs={"m1_machine": machine1_config, "m2_machine": machine2_config},
)
flake.refresh()
sops_setup.init(flake.path)
cli.run(["vars", "generate", "--flake", str(flake.path)])

View File

@@ -5,10 +5,8 @@ import pytest
from clan_cli.clan_uri import FlakeId
from clan_cli.machines.machines import Machine
from clan_cli.vms.run import inspect_vm, spawn_vm
from fixtures_flakes import FlakeForTest, generate_flake
from fixtures_flakes import ClanFlake, FlakeForTest
from helpers import cli
from helpers.nixos_config import nested_dict
from root import CLAN_CORE
from stdout import CaptureOutput
if TYPE_CHECKING:
@@ -59,33 +57,27 @@ def test_run(
@pytest.mark.skipif(no_kvm, reason="Requires KVM")
@pytest.mark.impure
def test_vm_persistence(
monkeypatch: pytest.MonkeyPatch,
temporary_home: Path,
flake: ClanFlake,
) -> None:
# set up a clan flake with some systemd services to test persistence
config = nested_dict()
config = flake.machines["my_machine"]
# logrotate-checkconf doesn't work in VM because /nix/store is owned by nobody
config["my_machine"]["systemd"]["services"]["logrotate-checkconf"]["enable"] = False
config["my_machine"]["services"]["getty"]["autologinUser"] = "root"
config["my_machine"]["clan"]["virtualisation"] = {"graphics": False}
config["my_machine"]["clan"]["core"]["networking"] = {"targetHost": "client"}
config["my_machine"]["clan"]["core"]["state"]["my_state"]["folders"] = [
config["systemd"]["services"]["logrotate-checkconf"]["enable"] = False
config["services"]["getty"]["autologinUser"] = "root"
config["clan"]["virtualisation"] = {"graphics": False}
config["clan"]["core"]["networking"] = {"targetHost": "client"}
config["clan"]["core"]["state"]["my_state"]["folders"] = [
# to be owned by root
"/var/my-state",
# to be owned by user 'test'
"/var/user-state",
]
config["my_machine"]["users"]["users"] = {
config["users"]["users"] = {
"test": {"initialPassword": "test", "isSystemUser": True, "group": "users"},
"root": {"initialPassword": "root"},
}
flake = generate_flake(
temporary_home,
flake_template=CLAN_CORE / "templates" / "minimal",
monkeypatch=monkeypatch,
machine_configs=config,
)
flake.refresh()
vm_config = inspect_vm(machine=Machine("my_machine", FlakeId(str(flake.path))))