From def3336af35d89c07072124039a40be009f908ed Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Mon, 24 Jun 2024 15:14:48 +0200 Subject: [PATCH] Inventory: simplify build-clan interface --- clanModules/borgbackup/default.nix | 49 ++++++++- inventory/default.nix | 16 ++- lib/build-clan/default.nix | 159 +++++++++-------------------- 3 files changed, 100 insertions(+), 124 deletions(-) diff --git a/clanModules/borgbackup/default.nix b/clanModules/borgbackup/default.nix index a625b83f..f708fe2e 100644 --- a/clanModules/borgbackup/default.nix +++ b/clanModules/borgbackup/default.nix @@ -27,8 +27,50 @@ let exit 1 fi ''; + + # Each .nix file in the roles directory is a role + # TODO: Helper function to set available roles within module meta. + # roles = lib.pipe ./roles [ + # (p: if builtins.pathExists p then p else throw "Role directory does not exist") + # builtins.readDir + # (lib.filterAttrs (_n: v: v == "regular")) + # lib.attrNames + # (map (fileName: lib.removeSuffix ".nix" fileName)) + # ]; + + # TODO: make this an interface of every module + # Maybe load from readme.md + metaInfoOption = lib.mkOption { + readOnly = true; + default = { + description = "A category for the service. This is used to group services in the clan ui"; + }; + type = lib.types.submodule { + options = { + description = lib.mkOption { + description = "A category for the service. This is used to group services in the clan ui"; + type = lib.types.str; + }; + # icon = lib.mkOption { + # description = "A category for the service. This is used to group services in the clan ui"; + # type = lib.types.str; + # }; + # screenshots = lib.mkOption { + # description = "A category for the service. This is used to group services in the clan ui"; + # type = lib.types.str; + # }; + # category = lib.mkOption { + # description = "A category for the service. This is used to group services in the clan ui"; + # type = lib.types.enum ["backup" "network"]; + # }; + }; + }; + }; in { + + options.clan.borgbackup.meta = metaInfoOption; + options.clan.borgbackup.destinations = lib.mkOption { type = lib.types.attrsOf ( lib.types.submodule ( @@ -63,6 +105,11 @@ in }; imports = [ + { + config.clan.borgbackup.meta = { + description = "backup"; + }; + } (lib.mkRemovedOptionModule [ "clan" "borgbackup" @@ -76,7 +123,7 @@ in lib.nameValuePair "borgbackup-job-${dest.name}" { # since borgbackup mounts the system read-only, we need to run in a ExecStartPre script, so we can generate additional files. serviceConfig.ExecStartPre = [ - (''+${pkgs.writeShellScript "borgbackup-job-${dest.name}-pre-backup-commands" preBackupScript}'') + ''+${pkgs.writeShellScript "borgbackup-job-${dest.name}-pre-backup-commands" preBackupScript}'' ]; } ) cfg.destinations; diff --git a/inventory/default.nix b/inventory/default.nix index a60c392a..2ca5ac00 100644 --- a/inventory/default.nix +++ b/inventory/default.nix @@ -4,23 +4,19 @@ let in { clan = clan-core.lib.buildClan { - meta.name = "kenjis clan"; # Should usually point to the directory of flake.nix directory = self; - # service config - # Useful alias: "inventory.services.borgbackup.default" - services = { - borgbackup = { - roles.server.machines = [ "vyr_machine" ]; - roles.client.tags = [ "laptop" ]; + inventory = { + services = { + borgbackup.instance_1 = { + roles.server.machines = [ "vyr_machine" ]; + roles.client.tags = [ "laptop" ]; + }; }; }; - # merged with - inventory = builtins.fromJSON (builtins.readFile ./src/tests/borgbackup.json); - # merged with machines = { "vyr_machine" = { }; diff --git a/lib/build-clan/default.nix b/lib/build-clan/default.nix index e09975d5..271f4254 100644 --- a/lib/build-clan/default.nix +++ b/lib/build-clan/default.nix @@ -15,15 +15,6 @@ # A map from arch to pkgs, if specified this nixpkgs will be only imported once for each system. # This improves performance, but all nipxkgs.* options will be ignored. pkgsForSystem ? (_system: null), - /* - Distributed services configuration. - - This configures a default instance in the inventory with the name "default". - - If you need multiple instances of a service configure them via: - inventory.services.[serviceName].[instanceName] = ... - */ - services ? { }, /* Low level inventory configuration. Overrides the services configuration. @@ -44,15 +35,15 @@ let modules = [ ./interface.nix { inherit meta; } - # Default instances configured via 'services' - { - services = lib.mapAttrs (_name: value: { - default = value // { - meta.name = lib.mkDefault _name; - }; - }) services; - } - # The inventory overrides + ( + if + builtins.pathExists "${directory}/inventory.json" + # Is recursively applied. Any explicit nix will override. + then + lib.mkDefault (builtins.fromJSON (builtins.readFile "${directory}/inventory.json")) + else + { } + ) inventory # Machines explicitly configured via 'machines' argument { @@ -79,13 +70,17 @@ let } ) machines; } - # Machines that exist in the machines directory - { machines = lib.mapAttrs (name: _: { inherit name; }) machinesDirs; } + + # Deprecated interface + (if clanName != null then { meta.name = clanName; } else { }) + (if clanIcon != null then { meta.icon = clanIcon; } else { }) ]; }).config; buildInventory = import ./inventory.nix { inherit lib clan-core; }; + # map from machine name to service configuration + # { ${machineName} :: Config } serviceConfigs = buildInventory mergedInventory; deprecationWarnings = [ @@ -95,62 +90,6 @@ let (lib.warnIf (clanIcon != null) "clanIcon is deprecated, please use meta.icon instead" null) ]; - machinesDirs = lib.optionalAttrs (builtins.pathExists "${directory}/machines") ( - builtins.readDir (directory + /machines) - ); - - mergedMeta = - let - metaFromFile = - if (builtins.pathExists "${directory}/clan/meta.json") then - let - settings = builtins.fromJSON (builtins.readFile "${directory}/clan/meta.json"); - in - settings - else - { }; - legacyMeta = lib.filterAttrs (_: v: v != null) { - name = clanName; - icon = clanIcon; - }; - optionsMeta = lib.filterAttrs (_: v: v != null) meta; - - warnings = - builtins.map ( - name: - if - metaFromFile.${name} or null != optionsMeta.${name} or null && optionsMeta.${name} or null != null - then - lib.warn "meta.${name} is set in different places. (exlicit option meta.${name} overrides ${directory}/clan/meta.json)" null - else - null - ) (builtins.attrNames metaFromFile) - ++ [ (if (res.name or null == null) then (throw "meta.name should be set") else null) ]; - res = metaFromFile // legacyMeta // optionsMeta; - in - # Print out warnings before returning the merged result - builtins.deepSeq warnings res; - - machineSettings = - machineName: - # CLAN_MACHINE_SETTINGS_FILE allows to override the settings file temporarily - # This is useful for doing a dry-run before writing changes into the settings.json - # Using CLAN_MACHINE_SETTINGS_FILE requires passing --impure to nix eval - if builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE" != "" then - builtins.fromJSON (builtins.readFile (builtins.getEnv "CLAN_MACHINE_SETTINGS_FILE")) - else - lib.optionalAttrs (builtins.pathExists "${directory}/machines/${machineName}/settings.json") ( - builtins.fromJSON (builtins.readFile (directory + /machines/${machineName}/settings.json)) - ); - - # Read additional imports specified via a config option in settings.json - # This is not an infinite recursion, because the imports are discovered here - # before calling evalModules. - # It is still useful to have the imports as an option, as this allows for type - # checking and easy integration with the config frontend(s) - machineImports = - machineSettings: map (module: clan-core.clanModules.${module}) (machineSettings.clanImports or [ ]); - # TODO: remove default system once we have a hardware-config mechanism nixosConfiguration = { @@ -160,47 +99,41 @@ let extraConfig ? { }, }: nixpkgs.lib.nixosSystem { - modules = - let - settings = machineSettings name; - in - (machineImports settings) - ++ [ - settings - clan-core.nixosModules.clanCore - extraConfig - (machines.${name} or { }) - # Inherit the inventory assertions ? - { inherit (mergedInventory) assertions; } - { imports = serviceConfigs.${name} or { }; } - ( - { - # Settings - clan.core.clanDir = directory; - # Inherited from clan wide settings - clan.core.clanName = meta.name or clanName; - clan.core.clanIcon = meta.icon or clanIcon; + modules = [ + clan-core.nixosModules.clanCore + extraConfig + (machines.${name} or { }) + # Inherit the inventory assertions ? + { inherit (mergedInventory) assertions; } + { imports = serviceConfigs.${name} or { }; } + ( + { + # Settings + clan.core.clanDir = directory; + # Inherited from clan wide settings + clan.core.clanName = meta.name or clanName; + clan.core.clanIcon = meta.icon or clanIcon; - # Machine specific settings - clan.core.machineName = name; - networking.hostName = lib.mkDefault name; - nixpkgs.hostPlatform = lib.mkDefault system; + # Machine specific settings + clan.core.machineName = name; + networking.hostName = lib.mkDefault name; + nixpkgs.hostPlatform = lib.mkDefault system; - # speeds up nix commands by using the nixpkgs from the host system (especially useful in VMs) - nix.registry.nixpkgs.to = { - type = "path"; - path = lib.mkDefault nixpkgs; - }; - } - // lib.optionalAttrs (pkgs != null) { nixpkgs.pkgs = lib.mkForce pkgs; } - ) - ]; + # speeds up nix commands by using the nixpkgs from the host system (especially useful in VMs) + nix.registry.nixpkgs.to = { + type = "path"; + path = lib.mkDefault nixpkgs; + }; + } + // lib.optionalAttrs (pkgs != null) { nixpkgs.pkgs = lib.mkForce pkgs; } + ) + ]; specialArgs = { inherit clan-core; } // specialArgs; }; - allMachines = machinesDirs // machines; + allMachines = mergedInventory.machines or { }; supportedSystems = [ "x86_64-linux" @@ -252,11 +185,11 @@ builtins.deepSeq deprecationWarnings { inherit nixosConfigurations; clanInternals = { - # Evaluated clan meta - # Merged /clan/meta.json with overrides from buildClan - meta = mergedMeta; + meta = mergedInventory.meta; inventory = mergedInventory; + invFile = "${directory}/inventory.json"; + # machine specifics machines = configsPerSystem; machinesFunc = configsFuncPerSystem;