From eebd9d0b4ad892702f433ba5181f6604b3f2f726 Mon Sep 17 00:00:00 2001 From: lassulus Date: Sat, 10 Feb 2024 11:47:09 +0100 Subject: [PATCH] allow passing of extra_config into machines --- checks/backups/flake-module.nix | 2 +- checks/installation/flake-module.nix | 2 +- flake.nix | 3 + lib/build-clan/default.nix | 12 +- pkgs/clan-cli/clan_cli/clan_modules.py | 33 ----- pkgs/clan-cli/clan_cli/machines/machines.py | 139 +++++++++++++++----- 6 files changed, 123 insertions(+), 68 deletions(-) delete mode 100644 pkgs/clan-cli/clan_cli/clan_modules.py diff --git a/checks/backups/flake-module.nix b/checks/backups/flake-module.nix index f57d7a67..c5eb638f 100644 --- a/checks/backups/flake-module.nix +++ b/checks/backups/flake-module.nix @@ -15,7 +15,7 @@ let in { flake.nixosConfigurations = { inherit (clan.nixosConfigurations) test_backup_client; }; - flake.clanInternals.machines = clan.clanInternals.machines; + flake.clanInternals = clan.clanInternals; flake.nixosModules = { test_backup_server = { ... }: { imports = [ diff --git a/checks/installation/flake-module.nix b/checks/installation/flake-module.nix index 0769c67f..0768ad4f 100644 --- a/checks/installation/flake-module.nix +++ b/checks/installation/flake-module.nix @@ -13,7 +13,7 @@ let in { flake.nixosConfigurations = { inherit (clan.nixosConfigurations) test_install_machine; }; - flake.clanInternals.machines = clan.clanInternals.machines; + flake.clanInternals = clan.clanInternals; flake.nixosModules = { test_install_machine = { lib, modulesPath, ... }: { imports = [ diff --git a/flake.nix b/flake.nix index b2f71aac..41b87450 100644 --- a/flake.nix +++ b/flake.nix @@ -49,6 +49,9 @@ machines = lib.mkOption { type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); }; + machinesFunc = lib.mkOption { + type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); + }; }; }; }; diff --git a/lib/build-clan/default.nix b/lib/build-clan/default.nix index 37110c0d..39e84705 100644 --- a/lib/build-clan/default.nix +++ b/lib/build-clan/default.nix @@ -30,13 +30,14 @@ let (machineSettings.clanImports or [ ]); # TODO: remove default system once we have a hardware-config mechanism - nixosConfiguration = { system ? "x86_64-linux", name, pkgs ? null }: nixpkgs.lib.nixosSystem { + nixosConfiguration = { system ? "x86_64-linux", name, pkgs ? null, extraConfig ? { } }: nixpkgs.lib.nixosSystem { modules = let settings = machineSettings name; in (machineImports settings) ++ [ + (nixpkgs.lib.mkOverride 51 extraConfig) settings clan-core.nixosModules.clanCore (machines.${name} or { }) @@ -77,7 +78,13 @@ let configsPerSystem = builtins.listToAttrs (builtins.map (system: lib.nameValuePair system - (lib.mapAttrs (name: _: nixosConfiguration { inherit name system; pkgs = nixpkgs.legacyPackages.${system}; }) allMachines)) + (lib.mapAttrs (name: _: nixosConfiguration { inherit name system; }) allMachines)) + supportedSystems); + + configsFuncPerSystem = builtins.listToAttrs + (builtins.map + (system: lib.nameValuePair system + (lib.mapAttrs (name: _: args: nixosConfiguration (args // { inherit name system; })) allMachines)) supportedSystems); in { @@ -85,6 +92,7 @@ in clanInternals = { machines = configsPerSystem; + machinesFunc = configsFuncPerSystem; all-machines-json = lib.mapAttrs (system: configs: nixpkgs.legacyPackages.${system}.writers.writeJSON "machines.json" (lib.mapAttrs (_: m: m.config.system.clan.deployment.data) configs)) configsPerSystem; diff --git a/pkgs/clan-cli/clan_cli/clan_modules.py b/pkgs/clan-cli/clan_cli/clan_modules.py deleted file mode 100644 index e6a36851..00000000 --- a/pkgs/clan-cli/clan_cli/clan_modules.py +++ /dev/null @@ -1,33 +0,0 @@ -import json -from pathlib import Path - -from clan_cli.nix import nix_eval - -from .cmd import run - - -def get_clan_module_names( - flake_dir: Path, -) -> list[str]: - """ - Get the list of clan modules from the clan-core flake input - """ - proc = run( - nix_eval( - [ - "--impure", - "--show-trace", - "--expr", - f""" - let - flake = builtins.getFlake (toString {flake_dir}); - in - builtins.attrNames flake.inputs.clan-core.clanModules - """, - ], - ), - cwd=flake_dir, - ) - - module_names = json.loads(proc.stdout) - return module_names diff --git a/pkgs/clan-cli/clan_cli/machines/machines.py b/pkgs/clan-cli/clan_cli/machines/machines.py index a09b1dca..9e6af35c 100644 --- a/pkgs/clan-cli/clan_cli/machines/machines.py +++ b/pkgs/clan-cli/clan_cli/machines/machines.py @@ -2,6 +2,7 @@ import json import logging from os import path from pathlib import Path +from tempfile import NamedTemporaryFile from time import sleep from clan_cli.dirs import vm_state_dir @@ -149,52 +150,128 @@ class Machine: meta={"machine": self, "target_host": self.target_host}, ) - def eval_nix(self, attr: str, refresh: bool = False) -> str: + def nix( + self, + method: str, + attr: str, + extra_config: None | dict = None, + impure: bool = False, + ) -> str | Path: """ - eval a nix attribute of the machine - @attr: the attribute to get + Build the machine and return the path to the result + accepts a secret store and a facts store # TODO """ config = nix_config() system = config["system"] - attr = f'clanInternals.machines."{system}".{self.name}.{attr}' + with NamedTemporaryFile(mode="w") as config_json: + if extra_config is not None: + json.dump(extra_config, config_json, indent=2) + else: + json.dump({}, config_json) + config_json.flush() - if attr in self.eval_cache and not refresh: + nar_hash = json.loads( + run( + nix_eval( + [ + "--impure", + "--expr", + f'(builtins.fetchTree {{ type = "file"; url = "{config_json.name}"; }}).narHash', + ] + ) + ).stdout.strip() + ) + + args = [] + + # get git commit from flake + if extra_config is not None: + metadata = nix_metadata(self.flake_dir) + url = metadata["url"] + if "dirtyRev" in metadata: + if not impure: + raise ClanError( + "The machine has a dirty revision, and impure mode is not allowed" + ) + else: + args += ["--impure"] + + if "dirtyRev" in nix_metadata(self.flake_dir): + dirty_rev = nix_metadata(self.flake_dir)["dirtyRevision"] + url = f"{url}?rev={dirty_rev}" + args += [ + "--expr", + f""" + ((builtins.getFlake "{url}").clanInternals.machinesFunc."{system}"."{self.name}" {{ + extraConfig = builtins.fromJSON (builtins.readFile (builtins.fetchTree {{ + type = "file"; + url = "{config_json.name}"; + narHash = "{nar_hash}"; + }})); + }}).{attr} + """, + ] + else: + if isinstance(self.flake, Path): + if (self.flake / ".git").exists(): + flake = f"git+file://{self.flake}" + else: + flake = f"path:{self.flake}" + else: + flake = self.flake + args += [ + f'{flake}#clanInternals.machines."{system}".{self.name}.{attr}' + ] + + if method == "eval": + output = run(nix_eval(args)).stdout.strip() + return output + elif method == "build": + outpath = run(nix_build(args)).stdout.strip() + return Path(outpath) + else: + raise ValueError(f"Unknown method {method}") + + def eval_nix( + self, + attr: str, + refresh: bool = False, + extra_config: None | dict = None, + impure: bool = False, + ) -> str: + """ + eval a nix attribute of the machine + @attr: the attribute to get + """ + if attr in self.eval_cache and not refresh and extra_config is None: return self.eval_cache[attr] - if isinstance(self.flake, Path): - if (self.flake / ".git").exists(): - flake = f"git+file://{self.flake}" - else: - flake = f"path:{self.flake}" + output = self.nix("eval", attr, extra_config, impure) + if isinstance(output, str): + self.eval_cache[attr] = output + return output else: - flake = self.flake + raise ClanError("eval_nix returned not a string") - cmd = nix_eval([f"{flake}#{attr}"]) - - output = run(cmd).stdout.strip() - self.eval_cache[attr] = output - return output - - def build_nix(self, attr: str, refresh: bool = False) -> Path: + def build_nix( + self, + attr: str, + refresh: bool = False, + extra_config: None | dict = None, + impure: bool = False, + ) -> Path: """ build a nix attribute of the machine @attr: the attribute to get """ - config = nix_config() - system = config["system"] - - attr = f'clanInternals.machines."{system}".{self.name}.{attr}' - - if attr in self.build_cache and not refresh: + if attr in self.build_cache and not refresh and extra_config is None: return self.build_cache[attr] - if isinstance(self.flake, Path): - flake = f"path:{self.flake}" + output = self.nix("build", attr, extra_config, impure) + if isinstance(output, Path): + self.build_cache[attr] = output + return output else: - flake = self.flake - - outpath = run(nix_build([f"{flake}#{attr}"])).stdout.strip() - self.build_cache[attr] = Path(outpath) - return Path(outpath) + raise ClanError("build_nix returned not a Path")