From 968fe4b64ee4acc23bd6e6bc36cba0be6fa2ff40 Mon Sep 17 00:00:00 2001 From: DavHau Date: Tue, 8 Apr 2025 20:23:57 +0700 Subject: [PATCH] inventory tests: add vars support - Add support for leading vars - Extend test to test for vars support - Improve update-vars.py to take test name as argument --- .../dummy-module/shared.nix | 25 +++++++- checks/lib/test-inventory.nix | 14 +++++ pkgs/scripts/update-vars.py | 61 ++++++++++++++++--- 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/checks/dummy-inventory-test/dummy-module/shared.nix b/checks/dummy-inventory-test/dummy-module/shared.nix index e7d6612ac..11cf466b9 100644 --- a/checks/dummy-inventory-test/dummy-module/shared.nix +++ b/checks/dummy-inventory-test/dummy-module/shared.nix @@ -1,4 +1,4 @@ -{ pkgs, ... }: +{ config, ... }: { systemd.services.dummy-service = { enable = true; @@ -6,8 +6,29 @@ wantedBy = [ "multi-user.target" ]; serviceConfig = { Type = "oneshot"; - ExecStart = "${pkgs.coreutils}/bin/true"; RemainAfterExit = true; }; + script = '' + generated_password_path="${config.clan.core.vars.generators.dummy-generator.files.generated-password.path}" + if [ ! -f "$generated_password_path" ]; then + echo "Generated password file not found: $generated_password_path" + exit 1 + fi + host_id_path="${config.clan.core.vars.generators.dummy-generator.files.host-id.path}" + if [ ! -e "$host_id_path" ]; then + echo "Host ID file not found: $host_id_path" + exit 1 + fi + ''; + }; + + # TODO: add and prompt and make it work in the test framework + clan.core.vars.generators.dummy-generator = { + files.host-id.secret = false; + files.generated-password.secret = true; + script = '' + echo $RANDOM > $out/host-id + echo $RANDOM > $out/generated-password + ''; }; } diff --git a/checks/lib/test-inventory.nix b/checks/lib/test-inventory.nix index e5adf46c2..94d7f2675 100644 --- a/checks/lib/test-inventory.nix +++ b/checks/lib/test-inventory.nix @@ -47,6 +47,20 @@ in documentation.enable = lib.mkDefault false; nix.settings.min-free = 0; system.stateVersion = config.system.nixos.release; + boot.initrd.systemd.enable = false; + + # setup for sops + sops.age.keyFile = "/run/age-key.txt"; + system.activationScripts = + { + setupSecrets.deps = [ "age-key" ]; + age-key.text = '' + echo AGE-SECRET-KEY-1PL0M9CWRCG3PZ9DXRTTLMCVD57U6JDFE8K7DNVQ35F4JENZ6G3MQ0RQLRV > /run/age-key.txt + ''; + } + // lib.optionalAttrs (lib.filterAttrs (_: v: v.neededForUsers) config.sops.secrets != { }) { + setupSecretsForUsers.deps = [ "age-key" ]; + }; } ); diff --git a/pkgs/scripts/update-vars.py b/pkgs/scripts/update-vars.py index c7f28cc50..40c51910e 100755 --- a/pkgs/scripts/update-vars.py +++ b/pkgs/scripts/update-vars.py @@ -1,16 +1,17 @@ #! /usr/bin/env python3 +import argparse import json import os import subprocess from pathlib import Path +from tempfile import NamedTemporaryFile from typing import Any from clan_cli.flake import Flake from clan_cli.machines.machines import Machine from clan_cli.nix import nix_build, nix_config, nix_eval from clan_cli.vars.generate import generate_vars -from clan_cli.vars.keygen import keygen if _project_root := os.environ.get("PRJ_ROOT"): clan_core_dir = Path(_project_root) @@ -18,7 +19,10 @@ else: msg = "PRJ_ROOT not set. Enter the dev environment first" raise Exception(msg) # noqa TRY002 -test_name = "dummy-inventory-test" +sops_priv_key = ( + "AGE-SECRET-KEY-1PL0M9CWRCG3PZ9DXRTTLMCVD57U6JDFE8K7DNVQ35F4JENZ6G3MQ0RQLRV" +) +sops_pub_key = "age1qm0p4vf9jvcnn43s6l4prk8zn6cx0ep9gzvevxecv729xz540v8qa742eg" def _test_dir(test_name: str) -> Path: @@ -47,13 +51,17 @@ class TestMachine(Machine): clan-core#checks...nodes.. """ + def __init__(self, name: str, flake: Flake, test_name: str) -> None: + super().__init__(name, flake) + self.test_name = test_name + @property def deployment(self) -> dict: if getattr(self, "_deployment", None): return self._deployment cmd = nix_build( [ - f"{clan_core_dir}#checks.{nix_config()['system']}.{test_name}.nodes.{self.name}.system.clan.deployment.file" + f"{clan_core_dir}#checks.{nix_config()['system']}.{self.test_name}.nodes.{self.name}.system.clan.deployment.file" ] ) out = subprocess.run(cmd, check=True, text=True, stdout=subprocess.PIPE) @@ -78,7 +86,7 @@ class TestMachine(Machine): # return self.nix("eval", attr, nix_options) cmd = nix_eval( [ - f"{clan_core_dir}#checks.{nix_config()['system']}.{test_name}.nodes.{self.name}.{attr}" + f"{clan_core_dir}#checks.{nix_config()['system']}.{self.test_name}.nodes.{self.name}.{attr}" ] ) out = subprocess.run(cmd, check=True, text=True, stdout=subprocess.PIPE) @@ -100,7 +108,7 @@ class TestMachine(Machine): cmd = nix_build( [ - f"{clan_core_dir}#checks.{nix_config()['system']}.{test_name}.nodes.{self.name}.{attr}" + f"{clan_core_dir}#checks.{nix_config()['system']}.{self.test_name}.nodes.{self.name}.{attr}" ] ) out = subprocess.run(cmd, check=True, text=True, stdout=subprocess.PIPE) @@ -113,15 +121,48 @@ class TestMachine(Machine): return +def parse_args() -> argparse.Namespace: + import argparse + + parser = argparse.ArgumentParser( + description="Update the vars of an inventory test", + ) + parser.add_argument( + "test_name", + type=str, + help="The name of the test to update", + ) + return parser.parse_args() + + if __name__ == "__main__": os.environ["CLAN_NO_COMMIT"] = "1" - test_dir = _test_dir(test_name) + args = parse_args() + test_dir = _test_dir(args.test_name) subprocess.run(["rm", "-rf", f"{test_dir}/vars", f"{test_dir}/sops"]) flake = Flake(str(test_dir)) flake._path = test_dir # noqa SLF001 flake._is_local = True # noqa SLF001 - machines = [TestMachine(name, flake) for name in machine_names(test_name)] + machines = [ + TestMachine(name, flake, args.test_name) + for name in machine_names(args.test_name) + ] user = "admin" - if not Path(flake.path / "sops" / "users" / user / "key.json").exists(): - keygen(user, flake, False) - generate_vars(list(machines)) + admin_key_path = Path(flake.path / "sops" / "users" / user / "key.json") + admin_key_path.parent.mkdir(parents=True, exist_ok=True) + admin_key_path.write_text( + json.dumps( + { + "publickey": sops_pub_key, + "type": "age", + } + ) + ) + with NamedTemporaryFile("w") as f: + f.write("# created: 2023-07-17T10:51:45+02:00\n") + f.write(f"# public key: {sops_pub_key}\n") + f.write(sops_priv_key) + f.seek(0) + print(Path(f.name).read_text()) + os.environ["SOPS_AGE_KEY_FILE"] = f.name + generate_vars(list(machines))