From fe6fd41a4d132e73836ee5567440fc0d0d0f778f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 22 Apr 2025 14:36:13 +0200 Subject: [PATCH 1/6] zerotier: migrate from facts to vars --- clanModules/data-mesher/shared.nix | 4 +- clanModules/static-hosts/default.nix | 4 +- .../syncthing-static-peers/default.nix | 9 +- clanModules/zerotier-static-peers/default.nix | 21 ++--- clanModules/zerotier/roles/controller.nix | 4 +- clanModules/zerotier/shared.nix | 17 ++-- docs/site/getting-started/mesh-vpn.md | 2 +- nixosModules/clanCore/zerotier/default.nix | 71 ++++++++------- .../clan_cli/tests/test_secrets_generate.py | 89 ++++++++++++------- .../tests/test_secrets_password_store.py | 35 +++++--- templates/clan/flake-parts/flake.nix | 2 +- .../new-clan/machines/sara/configuration.nix | 2 +- 12 files changed, 148 insertions(+), 112 deletions(-) diff --git a/clanModules/data-mesher/shared.nix b/clanModules/data-mesher/shared.nix index bfeea2989..54db475e9 100644 --- a/clanModules/data-mesher/shared.nix +++ b/clanModules/data-mesher/shared.nix @@ -13,10 +13,10 @@ let defaultBootstrapNodes = builtins.foldl' ( urls: name: if - builtins.pathExists "${config.clan.core.settings.directory}/machines/${name}/facts/zerotier-ip" + builtins.pathExists "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value" then let - ip = builtins.readFile "${config.clan.core.settings.directory}/machines/${name}/facts/zerotier-ip"; + ip = builtins.readFile "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value"; in urls ++ "${ip}:${cfg.network.port}" else diff --git a/clanModules/static-hosts/default.nix b/clanModules/static-hosts/default.nix index 951d4fd54..e214d38d9 100644 --- a/clanModules/static-hosts/default.nix +++ b/clanModules/static-hosts/default.nix @@ -26,8 +26,8 @@ config.networking.hosts = let dir = config.clan.core.settings.directory; - machineDir = dir + "/machines/"; - zerotierIpMachinePath = machines: machineDir + machines + "/facts/zerotier-ip"; + machineDir = "${dir}/vars/per-machine"; + zerotierIpMachinePath = machine: "${machineDir}/${machine}/zerotier/zerotier-ip/value"; machinesFileSet = builtins.readDir machineDir; machines = lib.mapAttrsToList (name: _: name) machinesFileSet; networkIpsUnchecked = builtins.map ( diff --git a/clanModules/syncthing-static-peers/default.nix b/clanModules/syncthing-static-peers/default.nix index 6b853d6af..6a85b3900 100644 --- a/clanModules/syncthing-static-peers/default.nix +++ b/clanModules/syncthing-static-peers/default.nix @@ -6,10 +6,9 @@ }: let dir = config.clan.core.settings.directory; - machineDir = dir + "/machines/"; - machineVarDir = dir + "/vars/per-machine/"; - syncthingPublicKeyPath = machines: machineVarDir + machines + "/syncthing/id/value"; - machinesFileSet = builtins.readDir machineDir; + machineVarDir = "${dir}/vars/per-machine/"; + syncthingPublicKeyPath = machine: "${machineVarDir}/${machine}/syncthing/id/value"; + machinesFileSet = builtins.readDir machineVarDir; machines = lib.mapAttrsToList (name: _: name) machinesFileSet; syncthingPublicKeysUnchecked = builtins.map ( machine: @@ -19,7 +18,7 @@ let if builtins.pathExists fullPath then machine else null ) machines; syncthingPublicKeyMachines = lib.filter (machine: machine != null) syncthingPublicKeysUnchecked; - zerotierIpMachinePath = machines: machineDir + machines + "/facts/zerotier-ip"; + zerotierIpMachinePath = machine: "${machineVarDir}/${machine}/zerotier/zerotier-ip/value"; networkIpsUnchecked = builtins.map ( machine: let diff --git a/clanModules/zerotier-static-peers/default.nix b/clanModules/zerotier-static-peers/default.nix index d9105dcf9..3000dc501 100644 --- a/clanModules/zerotier-static-peers/default.nix +++ b/clanModules/zerotier-static-peers/default.nix @@ -6,15 +6,16 @@ }: let dir = config.clan.core.settings.directory; - machineDir = dir + "/machines/"; + machineDir = "${dir}/vars/per-machine"; + # TODO: This should use the inventory + # However we are probably going to replace this with the network module. machinesFileSet = builtins.readDir machineDir; machines = lib.mapAttrsToList (name: _: name) machinesFileSet; - zerotierNetworkIdPath = machines: machineDir + machines + "/facts/zerotier-network-id"; networkIdsUnchecked = builtins.map ( machine: let - fullPath = zerotierNetworkIdPath machine; + fullPath = "${machineDir}/vars/per-machine/${machine}/zerotier/zerotier-network-id/value"; in if builtins.pathExists fullPath then builtins.readFile fullPath else null ) machines; @@ -45,13 +46,9 @@ in config.systemd.services.zerotier-static-peers-autoaccept = let - zerotierIpMachinePath = machines: machineDir + machines + "/facts/zerotier-ip"; + zerotierIpFor = machine: "${machineDir}/vars/per-machine/${machine}/zerotier/zerotier-ip/value"; networkIpsUnchecked = builtins.map ( - machine: - let - fullPath = zerotierIpMachinePath machine; - in - if builtins.pathExists fullPath then machine else null + machine: if builtins.pathExists (zerotierIpFor machine) then machine else null ) machines; networkIps = lib.filter (machine: machine != null) networkIpsUnchecked; machinesWithIp = lib.filterAttrs (name: _: (lib.elem name networkIps)) machinesFileSet; @@ -60,11 +57,7 @@ in ) machinesWithIp; hosts = lib.mapAttrsToList (host: _: host) ( lib.mapAttrs' ( - machine: _: - let - fullPath = zerotierIpMachinePath machine; - in - lib.nameValuePair (builtins.readFile fullPath) [ machine ] + machine: _: lib.nameValuePair (builtins.readFile (zerotierIpFor machine)) [ machine ] ) filteredMachines ); allHostIPs = config.clan.zerotier-static-peers.networkIps ++ hosts; diff --git a/clanModules/zerotier/roles/controller.nix b/clanModules/zerotier/roles/controller.nix index 2396789e0..c060c0f1d 100644 --- a/clanModules/zerotier/roles/controller.nix +++ b/clanModules/zerotier/roles/controller.nix @@ -23,11 +23,11 @@ in networkIps = builtins.foldl' ( ips: name: if - builtins.pathExists "${config.clan.core.settings.directory}/machines/${name}/facts/zerotier-ip" + builtins.pathExists "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value" then ips ++ [ - (builtins.readFile "${config.clan.core.settings.directory}/machines/${name}/facts/zerotier-ip") + (builtins.readFile "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value") ] else ips diff --git a/clanModules/zerotier/shared.nix b/clanModules/zerotier/shared.nix index 2556222ee..f1504b8f5 100644 --- a/clanModules/zerotier/shared.nix +++ b/clanModules/zerotier/shared.nix @@ -10,17 +10,24 @@ let zeroTierInstance = config.clan.inventory.services.zerotier.${instanceName}; roles = zeroTierInstance.roles; controllerMachine = builtins.head roles.controller.machines; - networkIdPath = "${config.clan.core.settings.directory}/machines/${controllerMachine}/facts/zerotier-network-id"; - networkId = if builtins.pathExists networkIdPath then builtins.readFile networkIdPath else null; + networkIdPath = "${config.clan.core.settings.directory}/vars/per-machine/${controllerMachine}/zerotier/zerotier-network-id/value"; + networkId = + if builtins.pathExists networkIdPath then + builtins.readFile networkIdPath + else + builtins.throw '' + No zerotier network id found for ${controllerMachine}. + Please run `clan vars generate ${controllerMachine}` first. + ''; moons = roles.moon.machines; moonIps = builtins.foldl' ( ips: name: if - builtins.pathExists "${config.clan.core.settings.directory}/machines/${name}/facts/zerotier-ip" + builtins.pathExists "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value" then ips ++ [ - (builtins.readFile "${config.clan.core.settings.directory}/machines/${name}/facts/zerotier-ip") + (builtins.readFile "${config.clan.core.settings.directory}/vars/per-machine/${name}/zerotier/zerotier-ip/value") ] else ips @@ -62,7 +69,7 @@ in clan.core.networking.zerotier.networkId = networkId; clan.core.networking.zerotier.name = instanceName; - # TODO: in future we want to have the node id of our moons in our facts + # TODO: in future we want to have the node id of our moons in our vars systemd.services.zerotierone.serviceConfig.ExecStartPost = lib.mkIf (moonIps != [ ]) ( lib.mkAfter [ "+${pkgs.writeScript "orbit-moons-by-ip" '' diff --git a/docs/site/getting-started/mesh-vpn.md b/docs/site/getting-started/mesh-vpn.md index 9c095702a..9d680f415 100644 --- a/docs/site/getting-started/mesh-vpn.md +++ b/docs/site/getting-started/mesh-vpn.md @@ -86,7 +86,7 @@ This guide shows you how to configure `zerotier` either through `NixOS Options` configuration, substituting `` with the controller machine name: ```nix { config, ... }: { - clan.core.networking.zerotier.networkId = builtins.readFile (config.clan.core.settings.directory + "/machines//facts/zerotier-network-id"); + clan.core.networking.zerotier.networkId = builtins.readFile ../../vars/per-machine//zerotier/zerotier-network-id/value; } ``` 1. **Update the New Machine**: Execute: diff --git a/nixosModules/clanCore/zerotier/default.nix b/nixosModules/clanCore/zerotier/default.nix index 25797db0c..8bd9b9ebf 100644 --- a/nixosModules/clanCore/zerotier/default.nix +++ b/nixosModules/clanCore/zerotier/default.nix @@ -2,15 +2,11 @@ config, lib, pkgs, + options, ... }: let cfg = config.clan.core.networking.zerotier; - facts = config.clan.core.facts.services.zerotier.public or { }; - genMoonScript = pkgs.runCommand "genmoon" { nativeBuildInputs = [ pkgs.python3 ]; } '' - install -Dm755 ${./genmoon.py} $out/bin/genmoon - patchShebangs $out/bin/genmoon - ''; in { options.clan.core.networking.zerotier = { @@ -89,15 +85,14 @@ in }; }; config = lib.mkMerge [ - ({ + { # Override license so that we can build zerotierone without # having to re-import nixpkgs. services.zerotierone.package = lib.mkDefault (pkgs.callPackage ../../../pkgs/zerotierone { }); - }) - (lib.mkIf ((facts.zerotier-ip.value or null) != null) { - environment.etc."zerotier/ip".text = facts.zerotier-ip.value; - }) + } (lib.mkIf (cfg.networkId != null) { + environment.etc."zerotier/ip".text = + config.clan.core.vars.generators.zerotier.files.zerotier-ip.value; systemd.network.networks."09-zerotier" = { matchConfig.Name = "zt*"; @@ -112,12 +107,12 @@ in "+${pkgs.writeShellScript "init-zerotier" '' # compare hashes of the current identity secret and the one in the config hash1=$(sha256sum /var/lib/zerotier-one/identity.secret | cut -d ' ' -f 1) - hash2=$(sha256sum ${config.clan.core.facts.services.zerotier.secret.zerotier-identity-secret.path} | cut -d ' ' -f 1) + hash2=$(sha256sum ${config.clan.core.vars.generators.zerotier.files.zerotier-identity-secret.path} | cut -d ' ' -f 1) if [[ "$hash1" != "$hash2" ]]; then echo "Identity secret has changed, backing up old identity to /var/lib/zerotier-one/identity.secret.bac" cp /var/lib/zerotier-one/identity.secret /var/lib/zerotier-one/identity.secret.bac cp /var/lib/zerotier-one/identity.public /var/lib/zerotier-one/identity.public.bac - cp ${config.clan.core.facts.services.zerotier.secret.zerotier-identity-secret.path} /var/lib/zerotier-one/identity.secret + cp ${config.clan.core.vars.generators.zerotier.files.zerotier-identity-secret.path} /var/lib/zerotier-one/identity.secret zerotier-idtool getpublic /var/lib/zerotier-one/identity.secret > /var/lib/zerotier-one/identity.public fi @@ -129,7 +124,12 @@ in if [[ ! -f /var/lib/zerotier-one/moon.json ]]; then zerotier-idtool initmoon /var/lib/zerotier-one/identity.public > /var/lib/zerotier-one/moon.json fi - ${genMoonScript}/bin/genmoon /var/lib/zerotier-one/moon.json ${builtins.toFile "moon.json" (builtins.toJSON cfg.moon.stableEndpoints)} /var/lib/zerotier-one/moons.d + ${ + pkgs.runCommand "genmoon" { nativeBuildInputs = [ pkgs.python3 ]; } '' + install -Dm755 ${./genmoon.py} $out/bin/genmoon + patchShebangs $out/bin/genmoon + '' + }/bin/genmoon /var/lib/zerotier-one/moon.json ${builtins.toFile "moon.json" (builtins.toJSON cfg.moon.stableEndpoints)} /var/lib/zerotier-one/moons.d ''} # cleanup old networks @@ -181,22 +181,26 @@ in services.zerotierone.localConf.settings.tcpFallbackRelay = "65.21.12.51/4443"; }) (lib.mkIf cfg.controller.enable { + environment.etc."zerotier/ip".text = + config.clan.core.vars.generators.zerotier.files.zerotier-ip.value; + # only the controller needs to have the key in the repo, the other clients can be dynamic # we generate the zerotier code manually for the controller, since it's part of the bootstrap command - clan.core.facts.services.zerotier = { - public.zerotier-ip = { }; - public.zerotier-network-id = { }; - secret.zerotier-identity-secret = { }; - generator.path = [ + clan.core.vars.generators.zerotier = { + migrateFact = "zerotier"; + files.zerotier-ip.secret = false; + files.zerotier-network-id.secret = false; + files.zerotier-identity-secret = { }; + runtimeInputs = [ config.services.zerotierone.package pkgs.python3 ]; - generator.script = '' + script = '' source ${(pkgs.callPackage ../../../pkgs/minifakeroot { })}/share/minifakeroot/rc python3 ${./generate.py} --mode network \ - --ip "$facts/zerotier-ip" \ - --identity-secret "$secrets/zerotier-identity-secret" \ - --network-id "$facts/zerotier-network-id" + --ip "$out/zerotier-ip" \ + --identity-secret "$out/zerotier-identity-secret" \ + --network-id "$out/zerotier-network-id" ''; }; clan.core.state.zerotier.folders = [ "/var/lib/zerotier-one" ]; @@ -204,23 +208,25 @@ in environment.systemPackages = [ config.clan.core.clanPkgs.zerotier-members ]; }) (lib.mkIf (!cfg.controller.enable && cfg.networkId != null) { - clan.core.facts.services.zerotier = { - public.zerotier-ip = { }; - secret.zerotier-identity-secret = { }; - generator.path = [ + clan.core.vars.generators.zerotier = { + migrateFact = "zerotier"; + files.zerotier-ip.secret = false; + files.zerotier-identity-secret = { }; + runtimeInputs = [ config.services.zerotierone.package pkgs.python3 ]; - generator.script = '' + script = '' python3 ${./generate.py} --mode identity \ - --ip "$facts/zerotier-ip" \ - --identity-secret "$secrets/zerotier-identity-secret" \ + --ip "$out/zerotier-ip" \ + --identity-secret "$out/zerotier-identity-secret" \ --network-id ${cfg.networkId} ''; }; }) - (lib.mkIf (cfg.controller.enable && (facts.zerotier-network-id.value or null) != null) { - clan.core.networking.zerotier.networkId = facts.zerotier-network-id.value; + (lib.mkIf (cfg.controller.enable && config.clan.core.vars.generators ? zerotier) { + clan.core.networking.zerotier.networkId = + config.clan.core.vars.generators.zerotier.files.zerotier-network-id.value; clan.core.networking.zerotier.settings = { authTokens = [ null ]; authorizationEndpoint = ""; @@ -259,7 +265,8 @@ in zt = false; }; }; - environment.etc."zerotier/network-id".text = facts.zerotier-network-id.value; + environment.etc."zerotier/network-id".text = + config.clan.core.vars.generators.zerotier.files.zerotier-network-id.value; systemd.services.zerotierone.serviceConfig.ExecStartPost = [ "+${pkgs.writeShellScript "whitelist-controller" '' ${config.clan.core.clanPkgs.zerotier-members}/bin/zerotier-members allow ${ diff --git a/pkgs/clan-cli/clan_cli/tests/test_secrets_generate.py b/pkgs/clan-cli/clan_cli/tests/test_secrets_generate.py index a49aa2d71..bdf383b79 100644 --- a/pkgs/clan-cli/clan_cli/tests/test_secrets_generate.py +++ b/pkgs/clan-cli/clan_cli/tests/test_secrets_generate.py @@ -4,7 +4,6 @@ from typing import TYPE_CHECKING import pytest from clan_cli.facts.secret_modules.sops import SecretStore from clan_cli.flake import Flake -from clan_cli.machines.facts import machine_get_fact from clan_cli.machines.machines import Machine from clan_cli.secrets.folders import sops_secrets_folder from clan_cli.tests.fixtures_flakes import FlakeForTest @@ -45,66 +44,90 @@ def test_generate_secret( "user1", ] ) - cmd = ["facts", "generate", "--flake", str(test_flake_with_core.path), "vm1"] + cmd = [ + "vars", + "generate", + "--flake", + str(test_flake_with_core.path), + "vm1", + "--generator", + "zerotier", + ] cli.run(cmd) + store1 = SecretStore( Machine(name="vm1", flake=Flake(str(test_flake_with_core.path))) ) assert store1.exists("", "age.key") - assert store1.exists("", "zerotier-identity-secret") - network_id = machine_get_fact( - test_flake_with_core.path, "vm1", "zerotier-network-id" - ) + network_id = ( + test_flake_with_core.path + / "vars" + / "per-machine" + / "vm1" + / "zerotier" + / "zerotier-network-id" + / "value" + ).read_text() assert len(network_id) == 16 secrets_folder = sops_secrets_folder(test_flake_with_core.path) age_key = secrets_folder / "vm1-age.key" / "secret" - identity_secret = secrets_folder / "vm1-zerotier-identity-secret" / "secret" + identity_secret = ( + test_flake_with_core.path + / "vars" + / "per-machine" + / "vm1" + / "zerotier" + / "zerotier-identity-secret" + / "secret" + ) age_key_mtime = age_key.lstat().st_mtime_ns secret1_mtime = identity_secret.lstat().st_mtime_ns # Assert that the age key is valid age_secret = store1.get("", "age.key").decode() - assert age_secret.isprintable() assert is_valid_age_key(age_secret) # test idempotency for vm1 and also generate for vm2 - cli.run(["facts", "generate", "--flake", str(test_flake_with_core.path)]) + cli.run( + [ + "vars", + "generate", + "--flake", + str(test_flake_with_core.path), + "--generator", + "zerotier", + ] + ) assert age_key.lstat().st_mtime_ns == age_key_mtime assert identity_secret.lstat().st_mtime_ns == secret1_mtime - assert ( - secrets_folder / "vm1-zerotier-identity-secret" / "machines" / "vm1" - ).exists() - store2 = SecretStore( Machine(name="vm2", flake=Flake(str(test_flake_with_core.path))) ) - # clan vars generate - # TODO: Test vars - # varsStore = VarsSecretStore( - # machine=Machine(name="vm2", flake=FlakeId(str(test_flake_with_core.path))) - # ) - # generators = get_generators(str(test_flake_with_core.path), "vm2") - # generator = next((gen for gen in generators if gen.name == "root-password"), None) - - # if not generator: - # raise Exception("Generator not found") - - # password_update = GeneratorUpdate( - # generator=generator.name, prompt_values={"password": "1234"} - # ) - # set_prompts(str(test_flake_with_core.path), "vm2", [password_update]) - # assert varsStore.exists(generator, "root-password") - assert store2.exists("", "age.key") - assert store2.exists("", "zerotier-identity-secret") + ( + test_flake_with_core.path + / "vars" + / "per-machine" + / "vm2" + / "zerotier" + / "zerotier-identity-secret" + / "secret" + ).exists() - ip = machine_get_fact(test_flake_with_core.path, "vm1", "zerotier-ip") + ip = ( + test_flake_with_core.path + / "vars" + / "per-machine" + / "vm2" + / "zerotier" + / "zerotier-ip" + / "value" + ).read_text() assert ipaddress.IPv6Address(ip).is_private # Assert that the age key is valid age_secret = store2.get("", "age.key").decode() - assert age_secret.isprintable() assert is_valid_age_key(age_secret) diff --git a/pkgs/clan-cli/clan_cli/tests/test_secrets_password_store.py b/pkgs/clan-cli/clan_cli/tests/test_secrets_password_store.py index 92c8b6443..24c3130f3 100644 --- a/pkgs/clan-cli/clan_cli/tests/test_secrets_password_store.py +++ b/pkgs/clan-cli/clan_cli/tests/test_secrets_password_store.py @@ -2,10 +2,6 @@ import subprocess from pathlib import Path import pytest -from clan_cli.facts.secret_modules.password_store import SecretStore -from clan_cli.flake import Flake -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.host import Host from clan_cli.tests.fixtures_flakes import ClanFlake @@ -32,6 +28,8 @@ def test_upload_secret( config["clan"]["core"]["networking"]["targetHost"] = addr config["clan"]["user-password"]["user"] = "alice" config["clan"]["user-password"]["prompt"] = False + vars_config = config["clan"]["core"]["vars"] + vars_config["settings"]["secretStore"] = "password-store" facts = config["clan"]["core"]["facts"] facts["secretStore"] = "password-store" facts["secretUploadDirectory"]["_type"] = "override" @@ -62,23 +60,32 @@ def test_upload_secret( check=True, ) subprocess.run(nix_shell(["pass"], ["pass", "init", "test@local"]), check=True) - cli.run(["facts", "generate", "vm1", "--flake", str(flake.path)]) + cli.run(["vars", "generate", "vm1", "--flake", str(flake.path), "--generator", "zerotier"]) - store = SecretStore(Machine(name="vm1", flake=Flake(str(flake.path)))) - - network_id = machine_get_fact(flake.path, "vm1", "zerotier-network-id") + network_id = ( + flake.path + / "vars" + / "per-machine" + / "vm1" + / "zerotier" + / "zerotier-network-id" + / "value" + ).read_text() assert len(network_id) == 16 identity_secret = ( - temporary_home / "pass" / "machines" / "vm1" / "zerotier-identity-secret.gpg" + temporary_home + / "pass" + / "clan-vars" + / "per-machine" + / "vm1" + / "zerotier" + / "zerotier-identity-secret.gpg" ) secret1_mtime = identity_secret.lstat().st_mtime_ns # test idempotency - cli.run(["facts", "generate", "vm1"]) + cli.run(["vars", "generate", "vm1", "--generator", "zerotier"]) assert identity_secret.lstat().st_mtime_ns == secret1_mtime - cli.run(["facts", "upload", "vm1"]) + cli.run(["vars", "upload", "vm1"]) zerotier_identity_secret = flake.path / "secrets" / "zerotier-identity-secret" assert zerotier_identity_secret.exists() - assert store.exists("", "zerotier-identity-secret") - - assert store.exists("", "zerotier-identity-secret") diff --git a/templates/clan/flake-parts/flake.nix b/templates/clan/flake-parts/flake.nix index e141aba1f..60c7982ad 100644 --- a/templates/clan/flake-parts/flake.nix +++ b/templates/clan/flake-parts/flake.nix @@ -100,7 +100,7 @@ This will allow sara to share the VPN overlay network with jon The networkId is generated by the first deployment of jon */ - # clan.core.networking.zerotier.networkId = builtins.readFile ../jon/facts/zerotier-network-id; + # clan.core.networking.zerotier.networkId = builtins.readFile ../../vars/per-machine/jon/zerotier/zerotier-network-id/value; }; }; }; diff --git a/templates/clan/new-clan/machines/sara/configuration.nix b/templates/clan/new-clan/machines/sara/configuration.nix index b68885d4e..66da728a1 100644 --- a/templates/clan/new-clan/machines/sara/configuration.nix +++ b/templates/clan/new-clan/machines/sara/configuration.nix @@ -30,5 +30,5 @@ This will allow sara to share the VPN overlay network with jon The networkId is generated by the first deployment of jon */ - # clan.core.networking.zerotier.networkId = builtins.readFile ../jon/facts/zerotier-network-id; + # clan.core.networking.zerotier.networkId = builtins.readFile ../../vars/per-machine/jon/zerotier/zerotier-network-id/value; } From 7392570859d69e42a0e651f18c44503bf7a58636 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 22 Apr 2025 16:53:31 +0200 Subject: [PATCH 2/6] use machine.{secrets,public}_{vars,fact}_store everywhere --- pkgs/clan-cli/clan_cli/facts/check.py | 10 ++---- pkgs/clan-cli/clan_cli/facts/generate.py | 10 ++---- pkgs/clan-cli/clan_cli/facts/list.py | 17 +--------- pkgs/clan-cli/clan_cli/facts/upload.py | 8 ++--- pkgs/clan-cli/clan_cli/flash/flash.py | 10 ++---- pkgs/clan-cli/clan_cli/machines/delete.py | 10 ++---- pkgs/clan-cli/clan_cli/machines/machines.py | 36 +++------------------ pkgs/clan-cli/clan_cli/vars/check.py | 21 +++++------- pkgs/clan-cli/clan_cli/vars/fix.py | 11 ++----- pkgs/clan-cli/clan_cli/vars/list.py | 24 ++++---------- pkgs/clan-cli/clan_cli/vars/upload.py | 9 +++--- pkgs/clan-cli/clan_cli/vms/run.py | 10 ++---- 12 files changed, 39 insertions(+), 137 deletions(-) diff --git a/pkgs/clan-cli/clan_cli/facts/check.py b/pkgs/clan-cli/clan_cli/facts/check.py index e769a74ea..fccf3b92f 100644 --- a/pkgs/clan-cli/clan_cli/facts/check.py +++ b/pkgs/clan-cli/clan_cli/facts/check.py @@ -1,5 +1,4 @@ import argparse -import importlib import logging from clan_cli.completions import add_dynamic_completer, complete_machines @@ -9,11 +8,6 @@ log = logging.getLogger(__name__) def check_secrets(machine: Machine, service: None | str = None) -> bool: - secret_facts_module = importlib.import_module(machine.secret_facts_module) - secret_facts_store = secret_facts_module.SecretStore(machine=machine) - public_facts_module = importlib.import_module(machine.public_facts_module) - public_facts_store = public_facts_module.FactStore(machine=machine) - missing_secret_facts = [] missing_public_facts = [] services = [service] if service else list(machine.facts_data.keys()) @@ -23,14 +17,14 @@ def check_secrets(machine: Machine, service: None | str = None) -> bool: secret_name = secret_fact else: secret_name = secret_fact["name"] - if not secret_facts_store.exists(service, secret_name): + if not machine.secret_facts_store.exists(service, secret_name): machine.info( f"Secret fact '{secret_fact}' for service '{service}' is missing." ) missing_secret_facts.append((service, secret_name)) for public_fact in machine.facts_data[service]["public"]: - if not public_facts_store.exists(service, public_fact): + if not machine.public_facts_store.exists(service, public_fact): machine.info( f"Public fact '{public_fact}' for service '{service}' is missing." ) diff --git a/pkgs/clan-cli/clan_cli/facts/generate.py b/pkgs/clan-cli/clan_cli/facts/generate.py index 090e578fa..3d0dabb1d 100644 --- a/pkgs/clan-cli/clan_cli/facts/generate.py +++ b/pkgs/clan-cli/clan_cli/facts/generate.py @@ -1,5 +1,4 @@ import argparse -import importlib import logging import os import sys @@ -161,11 +160,6 @@ def _generate_facts_for_machine( ) -> bool: local_temp = tmpdir / machine.name local_temp.mkdir() - secret_facts_module = importlib.import_module(machine.secret_facts_module) - secret_facts_store = secret_facts_module.SecretStore(machine=machine) - - public_facts_module = importlib.import_module(machine.public_facts_module) - public_facts_store = public_facts_module.FactStore(machine=machine) machine_updated = False @@ -184,8 +178,8 @@ def _generate_facts_for_machine( machine=machine, service=service, regenerate=regenerate, - secret_facts_store=secret_facts_store, - public_facts_store=public_facts_store, + secret_facts_store=machine.secret_facts_store, + public_facts_store=machine.public_facts_store, tmpdir=local_temp, prompt=prompt, ) diff --git a/pkgs/clan-cli/clan_cli/facts/list.py b/pkgs/clan-cli/clan_cli/facts/list.py index 07eae7a02..c89d5f17a 100644 --- a/pkgs/clan-cli/clan_cli/facts/list.py +++ b/pkgs/clan-cli/clan_cli/facts/list.py @@ -1,8 +1,6 @@ import argparse -import importlib import json import logging -from typing import Any from clan_cli.completions import add_dynamic_completer, complete_machines from clan_cli.machines.machines import Machine @@ -10,24 +8,11 @@ from clan_cli.machines.machines import Machine log = logging.getLogger(__name__) -# TODO get also secret facts -def get_all_facts(machine: Machine) -> dict: - public_facts_store = get_public_facts_store(machine) - - return public_facts_store.get_all() - - -def get_public_facts_store(machine: Machine) -> Any: - public_facts_module = importlib.import_module(machine.public_facts_module) - public_facts_store = public_facts_module.FactStore(machine=machine) - return public_facts_store - - def get_command(args: argparse.Namespace) -> None: machine = Machine(name=args.machine, flake=args.flake) # the raw_facts are bytestrings making them not json serializable - raw_facts = get_all_facts(machine) + raw_facts = machine.public_facts_store.get_all() facts = {} for key in raw_facts["TODO"]: facts[key] = raw_facts["TODO"][key].decode("utf8") diff --git a/pkgs/clan-cli/clan_cli/facts/upload.py b/pkgs/clan-cli/clan_cli/facts/upload.py index 054714d8e..63f347c73 100644 --- a/pkgs/clan-cli/clan_cli/facts/upload.py +++ b/pkgs/clan-cli/clan_cli/facts/upload.py @@ -1,5 +1,4 @@ import argparse -import importlib import logging from pathlib import Path from tempfile import TemporaryDirectory @@ -12,16 +11,13 @@ log = logging.getLogger(__name__) def upload_secrets(machine: Machine) -> None: - secret_facts_module = importlib.import_module(machine.secret_facts_module) - secret_facts_store = secret_facts_module.SecretStore(machine=machine) - - if not secret_facts_store.needs_upload(): + if not machine.secret_facts_store.needs_upload(): machine.info("Secrets already uploaded") return with TemporaryDirectory(prefix="facts-upload-") as _tempdir: local_secret_dir = Path(_tempdir).resolve() - secret_facts_store.upload(local_secret_dir) + machine.secret_facts_store.upload(local_secret_dir) remote_secret_dir = Path(machine.secrets_upload_directory) upload(machine.target_host, local_secret_dir, remote_secret_dir) diff --git a/pkgs/clan-cli/clan_cli/flash/flash.py b/pkgs/clan-cli/clan_cli/flash/flash.py index 0c43435ef..709083ad6 100644 --- a/pkgs/clan-cli/clan_cli/flash/flash.py +++ b/pkgs/clan-cli/clan_cli/flash/flash.py @@ -1,4 +1,3 @@ -import importlib import json import logging import os @@ -11,10 +10,10 @@ from clan_cli.api import API from clan_cli.cmd import Log, RunOpts, cmd_with_root, run from clan_cli.errors import ClanError from clan_cli.facts.generate import generate_facts -from clan_cli.facts.secret_modules import SecretStoreBase from clan_cli.machines.machines import Machine from clan_cli.nix import nix_shell from clan_cli.vars.generate import generate_vars +from clan_cli.vars.upload import upload_secret_vars from .automount import pause_automounting from .list import list_possible_keymaps, list_possible_languages @@ -96,10 +95,6 @@ def flash_machine( msg = f"Partitioning time secrets are not supported with `clan flash write`: clan.core.vars.generators.{generator.name}.files.{file.name}" raise ClanError(msg) - secret_facts_module = importlib.import_module(machine.secret_facts_module) - secret_facts_store: SecretStoreBase = secret_facts_module.SecretStore( - machine=machine - ) with TemporaryDirectory(prefix="disko-install-") as _tmpdir: tmpdir = Path(_tmpdir) upload_dir = machine.secrets_upload_directory @@ -110,7 +105,8 @@ def flash_machine( local_dir = tmpdir / upload_dir local_dir.mkdir(parents=True) - secret_facts_store.upload(local_dir) + machine.secret_facts_store.upload(local_dir) + upload_secret_vars(machine, local_dir) disko_install = [] if os.geteuid() != 0: diff --git a/pkgs/clan-cli/clan_cli/machines/delete.py b/pkgs/clan-cli/clan_cli/machines/delete.py index 1d973dd14..efef4d970 100644 --- a/pkgs/clan-cli/clan_cli/machines/delete.py +++ b/pkgs/clan-cli/clan_cli/machines/delete.py @@ -14,12 +14,6 @@ from clan_cli.secrets.machines import remove_machine as secrets_machine_remove from clan_cli.secrets.secrets import ( list_secrets, ) -from clan_cli.vars.list import ( - public_store as vars_public_store, -) -from clan_cli.vars.list import ( - secret_store as vars_secret_store, -) from .machines import Machine @@ -55,8 +49,8 @@ def delete_machine(flake: Flake, name: str) -> None: shutil.rmtree(secret_path) machine = Machine(name, flake) - changed_paths.extend(vars_public_store(machine).delete_store()) - changed_paths.extend(vars_secret_store(machine).delete_store()) + changed_paths.extend(machine.public_vars_store.delete_store()) + changed_paths.extend(machine.secret_vars_store.delete_store()) # Remove the machine's key, and update secrets & vars that referenced it: if secrets_has_machine(flake.path, name): secrets_machine_remove(flake.path, name) diff --git a/pkgs/clan-cli/clan_cli/machines/machines.py b/pkgs/clan-cli/clan_cli/machines/machines.py index 5a6e31d67..942102f4c 100644 --- a/pkgs/clan-cli/clan_cli/machines/machines.py +++ b/pkgs/clan-cli/clan_cli/machines/machines.py @@ -5,7 +5,7 @@ import re from dataclasses import dataclass, field from functools import cached_property from pathlib import Path -from typing import TYPE_CHECKING, Any, Literal +from typing import TYPE_CHECKING, Any from clan_cli.cmd import Log, RunOpts, run_no_stdout from clan_cli.errors import ClanCmdError, ClanError @@ -94,50 +94,24 @@ class Machine: ) return val - @property - def secret_facts_module( - self, - ) -> Literal[ - "clan_cli.facts.secret_modules.sops", - "clan_cli.facts.secret_modules.vm", - "clan_cli.facts.secret_modules.password_store", - ]: - return self.deployment["facts"]["secretModule"] - - @property - def public_facts_module( - self, - ) -> Literal[ - "clan_cli.facts.public_modules.in_repo", "clan_cli.facts.public_modules.vm" - ]: - return self.deployment["facts"]["publicModule"] - @cached_property def secret_facts_store(self) -> facts_secret_modules.SecretStoreBase: - module = importlib.import_module(self.secret_facts_module) + module = importlib.import_module(self.deployment["facts"]["secretModule"]) return module.SecretStore(machine=self) @cached_property def public_facts_store(self) -> facts_public_modules.FactStoreBase: - module = importlib.import_module(self.public_facts_module) + module = importlib.import_module(self.deployment["facts"]["publicModule"]) return module.FactStore(machine=self) - @property - def secret_vars_module(self) -> str: - return self.deployment["vars"]["secretModule"] - - @property - def public_vars_module(self) -> str: - return self.deployment["vars"]["publicModule"] - @cached_property def secret_vars_store(self) -> StoreBase: - module = importlib.import_module(self.secret_vars_module) + module = importlib.import_module(self.deployment["vars"]["secretModule"]) return module.SecretStore(machine=self) @cached_property def public_vars_store(self) -> StoreBase: - module = importlib.import_module(self.public_vars_module) + module = importlib.import_module(self.deployment["vars"]["publicModule"]) return module.FactStore(machine=self) @property diff --git a/pkgs/clan-cli/clan_cli/vars/check.py b/pkgs/clan-cli/clan_cli/vars/check.py index b74f1ff91..4db7bd42b 100644 --- a/pkgs/clan-cli/clan_cli/vars/check.py +++ b/pkgs/clan-cli/clan_cli/vars/check.py @@ -1,11 +1,9 @@ import argparse -import importlib import logging from clan_cli.completions import add_dynamic_completer, complete_machines from clan_cli.errors import ClanError from clan_cli.machines.machines import Machine -from clan_cli.vars._types import StoreBase log = logging.getLogger(__name__) @@ -30,11 +28,6 @@ class VarStatus: def vars_status(machine: Machine, generator_name: None | str = None) -> VarStatus: - secret_vars_module = importlib.import_module(machine.secret_vars_module) - secret_vars_store: StoreBase = secret_vars_module.SecretStore(machine=machine) - public_vars_module = importlib.import_module(machine.public_vars_module) - public_vars_store: StoreBase = public_vars_module.FactStore(machine=machine) - missing_secret_vars = [] missing_public_vars = [] # signals if a var needs to be updated (eg. needs re-encryption due to new users added) @@ -55,17 +48,19 @@ def vars_status(machine: Machine, generator_name: None | str = None) -> VarStatu for generator in generators: generator.machine(machine) for file in generator.files: - file.store(secret_vars_store if file.secret else public_vars_store) + file.store( + machine.secret_vars_store if file.secret else machine.public_vars_store + ) file.generator(generator) if file.secret: - if not secret_vars_store.exists(generator, file.name): + if not machine.secret_vars_store.exists(generator, file.name): machine.info( f"Secret var '{file.name}' for service '{generator.name}' in machine {machine.name} is missing." ) missing_secret_vars.append(file) else: - msg = secret_vars_store.health_check( + msg = machine.secret_vars_store.health_check( generator=generator, file_name=file.name, ) @@ -75,15 +70,15 @@ def vars_status(machine: Machine, generator_name: None | str = None) -> VarStatu ) unfixed_secret_vars.append(file) - elif not public_vars_store.exists(generator, file.name): + elif not machine.public_vars_store.exists(generator, file.name): machine.info( f"Public var '{file.name}' for service '{generator.name}' in machine {machine.name} is missing." ) missing_public_vars.append(file) # check if invalidation hash is up to date if not ( - secret_vars_store.hash_is_valid(generator) - and public_vars_store.hash_is_valid(generator) + machine.secret_vars_store.hash_is_valid(generator) + and machine.public_vars_store.hash_is_valid(generator) ): invalid_generators.append(generator.name) machine.info( diff --git a/pkgs/clan-cli/clan_cli/vars/fix.py b/pkgs/clan-cli/clan_cli/vars/fix.py index db84f8945..a0264454f 100644 --- a/pkgs/clan-cli/clan_cli/vars/fix.py +++ b/pkgs/clan-cli/clan_cli/vars/fix.py @@ -1,21 +1,14 @@ import argparse -import importlib import logging from clan_cli.completions import add_dynamic_completer, complete_machines from clan_cli.errors import ClanError from clan_cli.machines.machines import Machine -from clan_cli.vars._types import StoreBase log = logging.getLogger(__name__) def fix_vars(machine: Machine, generator_name: None | str = None) -> None: - secret_vars_module = importlib.import_module(machine.secret_vars_module) - secret_vars_store: StoreBase = secret_vars_module.SecretStore(machine=machine) - public_vars_module = importlib.import_module(machine.public_vars_module) - public_vars_store: StoreBase = public_vars_module.FactStore(machine=machine) - generators = machine.vars_generators if generator_name: for generator in generators: @@ -29,8 +22,8 @@ def fix_vars(machine: Machine, generator_name: None | str = None) -> None: raise ClanError(err_msg) for generator in generators: - public_vars_store.fix(generator=generator) - secret_vars_store.fix(generator=generator) + machine.public_vars_store.fix(generator=generator) + machine.secret_vars_store.fix(generator=generator) def fix_command(args: argparse.Namespace) -> None: diff --git a/pkgs/clan-cli/clan_cli/vars/list.py b/pkgs/clan-cli/clan_cli/vars/list.py index 669a3cdab..496c05a20 100644 --- a/pkgs/clan-cli/clan_cli/vars/list.py +++ b/pkgs/clan-cli/clan_cli/vars/list.py @@ -1,5 +1,4 @@ import argparse -import importlib import logging from clan_cli.api import API @@ -7,7 +6,6 @@ from clan_cli.completions import add_dynamic_completer, complete_machines from clan_cli.errors import ClanError from clan_cli.flake import Flake from clan_cli.machines.machines import Machine -from clan_cli.vars._types import StoreBase from ._types import GeneratorUpdate from .generate import Generator, Prompt, Var, execute_generator @@ -15,21 +13,11 @@ from .generate import Generator, Prompt, Var, execute_generator log = logging.getLogger(__name__) -def public_store(machine: Machine) -> StoreBase: - public_vars_module = importlib.import_module(machine.public_vars_module) - return public_vars_module.FactStore(machine=machine) - - -def secret_store(machine: Machine) -> StoreBase: - secret_vars_module = importlib.import_module(machine.secret_vars_module) - return secret_vars_module.SecretStore(machine=machine) - - @API.register def get_vars(base_dir: str, machine_name: str) -> list[Var]: machine = Machine(name=machine_name, flake=Flake(base_dir)) - pub_store = public_store(machine) - sec_store = secret_store(machine) + pub_store = machine.public_vars_store + sec_store = machine.secret_vars_store all_vars = [] for generator in machine.vars_generators: for var in generator.files: @@ -50,10 +38,10 @@ def _get_previous_value( if not prompt.persist: return None - pub_store = public_store(machine) + pub_store = machine.public_vars_store if pub_store.exists(generator, prompt.name): return pub_store.get(generator, prompt.name).decode() - sec_store = secret_store(machine) + sec_store = machine.secret_vars_store if sec_store.exists(generator, prompt.name): return sec_store.get(generator, prompt.name).decode() return None @@ -87,8 +75,8 @@ def set_prompts( execute_generator( machine, generator, - secret_vars_store=secret_store(machine), - public_vars_store=public_store(machine), + secret_vars_store=machine.secret_vars_store, + public_vars_store=machine.public_vars_store, prompt_values=update.prompt_values, ) diff --git a/pkgs/clan-cli/clan_cli/vars/upload.py b/pkgs/clan-cli/clan_cli/vars/upload.py index 605216dce..02fc504e4 100644 --- a/pkgs/clan-cli/clan_cli/vars/upload.py +++ b/pkgs/clan-cli/clan_cli/vars/upload.py @@ -1,5 +1,4 @@ import argparse -import importlib import logging from pathlib import Path @@ -10,12 +9,12 @@ log = logging.getLogger(__name__) def upload_secret_vars(machine: Machine, directory: Path | None = None) -> None: - secret_store_module = importlib.import_module(machine.secret_vars_module) - secret_store = secret_store_module.SecretStore(machine=machine) if directory: - secret_store.populate_dir(directory, phases=["activation", "users", "services"]) + machine.secret_vars_store.populate_dir( + directory, phases=["activation", "users", "services"] + ) else: - secret_store.upload(phases=["activation", "users", "services"]) + machine.secret_vars_store.upload(phases=["activation", "users", "services"]) def upload_command(args: argparse.Namespace) -> None: diff --git a/pkgs/clan-cli/clan_cli/vms/run.py b/pkgs/clan-cli/clan_cli/vms/run.py index 80301d81f..f004b5b05 100644 --- a/pkgs/clan-cli/clan_cli/vms/run.py +++ b/pkgs/clan-cli/clan_cli/vms/run.py @@ -1,5 +1,4 @@ import argparse -import importlib import json import logging import os @@ -55,9 +54,7 @@ def build_vm( nix_options = [] secrets_dir = get_secrets(machine, tmpdir) - public_facts_module = importlib.import_module(machine.public_facts_module) - public_facts_store = public_facts_module.FactStore(machine=machine) - public_facts = public_facts_store.get_all() + public_facts = machine.public_facts_store.get_all() nixos_config_file = machine.build_nix( "config.system.clan.vm.create", @@ -81,12 +78,9 @@ def get_secrets( secrets_dir = tmpdir / "secrets" secrets_dir.mkdir(parents=True, exist_ok=True) - secret_facts_module = importlib.import_module(machine.secret_facts_module) - secret_facts_store = secret_facts_module.SecretStore(machine=machine) - generate_facts([machine]) - secret_facts_store.upload(secrets_dir) + machine.secret_facts_store.upload(secrets_dir) return secrets_dir From 847e3ac4abba9af97e22b6d7361bb5e08e68d9e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 22 Apr 2025 16:54:42 +0200 Subject: [PATCH 3/6] drop unused machine_get_fact --- pkgs/clan-cli/clan_cli/machines/facts.py | 11 ----------- pkgs/clan-cli/clan_cli/tests/test_modules.py | 1 - 2 files changed, 12 deletions(-) delete mode 100644 pkgs/clan-cli/clan_cli/machines/facts.py diff --git a/pkgs/clan-cli/clan_cli/machines/facts.py b/pkgs/clan-cli/clan_cli/machines/facts.py deleted file mode 100644 index be9e81782..000000000 --- a/pkgs/clan-cli/clan_cli/machines/facts.py +++ /dev/null @@ -1,11 +0,0 @@ -from pathlib import Path - -from clan_cli.dirs import specific_machine_dir - - -def machine_has_fact(flake_dir: Path, machine: str, fact: str) -> bool: - return (specific_machine_dir(flake_dir, machine) / "facts" / fact).exists() - - -def machine_get_fact(flake_dir: Path, machine: str, fact: str) -> str: - return (specific_machine_dir(flake_dir, machine) / "facts" / fact).read_text() diff --git a/pkgs/clan-cli/clan_cli/tests/test_modules.py b/pkgs/clan-cli/clan_cli/tests/test_modules.py index a1261d276..17c1090b1 100644 --- a/pkgs/clan-cli/clan_cli/tests/test_modules.py +++ b/pkgs/clan-cli/clan_cli/tests/test_modules.py @@ -18,7 +18,6 @@ from clan_cli.tests.fixtures_flakes import FlakeForTest if TYPE_CHECKING: from .age_keys import KeyPair -# from clan_cli.vars.var import machine_get_fact from clan_cli.machines.machines import Machine as MachineMachine from clan_cli.tests.helpers import cli From ffaa30d89490cf8d9c69f359a9efb6ce7d519977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 22 Apr 2025 16:55:12 +0200 Subject: [PATCH 4/6] vms/run: upload vars --- pkgs/clan-cli/clan_cli/vms/run.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/clan-cli/clan_cli/vms/run.py b/pkgs/clan-cli/clan_cli/vms/run.py index f004b5b05..9ed661059 100644 --- a/pkgs/clan-cli/clan_cli/vms/run.py +++ b/pkgs/clan-cli/clan_cli/vms/run.py @@ -21,6 +21,8 @@ from clan_cli.machines.machines import Machine from clan_cli.nix import nix_shell from clan_cli.qemu.qga import QgaSession from clan_cli.qemu.qmp import QEMUMonitorProtocol +from clan_cli.vars.generate import generate_vars +from clan_cli.vars.upload import upload_secret_vars from .inspect import VmConfig, inspect_vm from .qemu import qemu_command @@ -79,8 +81,10 @@ def get_secrets( secrets_dir.mkdir(parents=True, exist_ok=True) generate_facts([machine]) + generate_vars([machine]) machine.secret_facts_store.upload(secrets_dir) + upload_secret_vars(machine, secrets_dir) return secrets_dir From c2030eb3ba86672c368930d8ac73dde6bed2f21e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 22 Apr 2025 17:47:22 +0200 Subject: [PATCH 5/6] drop test_secrets_password_store we are phasing out facts and the only fact we had left for testing, got now dropped. We still have a sops facts test, which we might also drop soon. --- .../tests/test_secrets_password_store.py | 91 ------------------- 1 file changed, 91 deletions(-) delete mode 100644 pkgs/clan-cli/clan_cli/tests/test_secrets_password_store.py diff --git a/pkgs/clan-cli/clan_cli/tests/test_secrets_password_store.py b/pkgs/clan-cli/clan_cli/tests/test_secrets_password_store.py deleted file mode 100644 index 24c3130f3..000000000 --- a/pkgs/clan-cli/clan_cli/tests/test_secrets_password_store.py +++ /dev/null @@ -1,91 +0,0 @@ -import subprocess -from pathlib import Path - -import pytest -from clan_cli.nix import nix_shell -from clan_cli.ssh.host import Host -from clan_cli.tests.fixtures_flakes import ClanFlake -from clan_cli.tests.helpers import cli - - -@pytest.mark.impure -def test_upload_secret( - monkeypatch: pytest.MonkeyPatch, - flake: ClanFlake, - temporary_home: Path, - hosts: list[Host], -) -> None: - flake.clan_modules = [ - "root-password", - "user-password", - "sshd", - ] - config = flake.machines["vm1"] - config["nixpkgs"]["hostPlatform"] = "x86_64-linux" - config["clan"]["core"]["networking"]["zerotier"]["controller"]["enable"] = True - host = hosts[0] - addr = f"{host.user}@{host.host}:{host.port}?StrictHostKeyChecking=no&UserKnownHostsFile=/dev/null&IdentityFile={host.key}" - config["clan"]["core"]["networking"]["targetHost"] = addr - config["clan"]["user-password"]["user"] = "alice" - config["clan"]["user-password"]["prompt"] = False - vars_config = config["clan"]["core"]["vars"] - vars_config["settings"]["secretStore"] = "password-store" - facts = config["clan"]["core"]["facts"] - facts["secretStore"] = "password-store" - facts["secretUploadDirectory"]["_type"] = "override" - facts["secretUploadDirectory"]["content"] = str( - temporary_home / "flake" / "secrets" - ) - facts["secretUploadDirectory"]["priority"] = 50 - - flake.refresh() - monkeypatch.chdir(flake.path) - gnupghome = temporary_home / "gpg" - gnupghome.mkdir(mode=0o700) - monkeypatch.setenv("GNUPGHOME", str(gnupghome)) - monkeypatch.setenv("PASSWORD_STORE_DIR", str(temporary_home / "pass")) - gpg_key_spec = temporary_home / "gpg_key_spec" - gpg_key_spec.write_text( - """ - Key-Type: 1 - Key-Length: 1024 - Name-Real: Root Superuser - Name-Email: test@local - Expire-Date: 0 - %no-protection - """ - ) - subprocess.run( - nix_shell(["gnupg"], ["gpg", "--batch", "--gen-key", str(gpg_key_spec)]), - check=True, - ) - subprocess.run(nix_shell(["pass"], ["pass", "init", "test@local"]), check=True) - cli.run(["vars", "generate", "vm1", "--flake", str(flake.path), "--generator", "zerotier"]) - - network_id = ( - flake.path - / "vars" - / "per-machine" - / "vm1" - / "zerotier" - / "zerotier-network-id" - / "value" - ).read_text() - assert len(network_id) == 16 - identity_secret = ( - temporary_home - / "pass" - / "clan-vars" - / "per-machine" - / "vm1" - / "zerotier" - / "zerotier-identity-secret.gpg" - ) - secret1_mtime = identity_secret.lstat().st_mtime_ns - - # test idempotency - cli.run(["vars", "generate", "vm1", "--generator", "zerotier"]) - assert identity_secret.lstat().st_mtime_ns == secret1_mtime - cli.run(["vars", "upload", "vm1"]) - zerotier_identity_secret = flake.path / "secrets" / "zerotier-identity-secret" - assert zerotier_identity_secret.exists() From 043077269acf1ecd0c61ff1f28c24427059c5515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= Date: Tue, 22 Apr 2025 18:03:50 +0200 Subject: [PATCH 6/6] drop overrides of nix version --- devShell.nix | 3 +-- pkgs/clan-cli/default.nix | 6 ++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/devShell.nix b/devShell.nix index 4f4075cf9..dd81b8282 100644 --- a/devShell.nix +++ b/devShell.nix @@ -29,8 +29,7 @@ select-shell pkgs.nix-unit pkgs.tea - # Better error messages than nix 2.18 - pkgs.nixVersions.latest + pkgs.nix self'.packages.tea-create-pr self'.packages.merge-after-ci self'.packages.pending-reviews diff --git a/pkgs/clan-cli/default.nix b/pkgs/clan-cli/default.nix index 0d80f1bdc..9b6ae9cce 100644 --- a/pkgs/clan-cli/default.nix +++ b/pkgs/clan-cli/default.nix @@ -63,10 +63,8 @@ let nixpkgs' = runCommand "nixpkgs" { - nativeBuildInputs = [ - # old nix version doesn't support --flake flag - (if lib.versionAtLeast nix.version "2.24" then nix else nixVersions.latest) - ]; + # Not all versions have `nix flake update --flake` option + nativeBuildInputs = [ nixVersions.stable ]; } '' mkdir $out