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:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)])
|
||||
|
||||
@@ -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))))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user