From 20ac9952b9609565b8dec45b3f5d865cb48caa4f Mon Sep 17 00:00:00 2001 From: paki23 Date: Sun, 20 Feb 2022 23:59:55 +0100 Subject: [PATCH] eval-machine-info.nix: ported to modules --- doc/release-notes/index.rst | 4 + flake.nix | 5 + nix/eval-machine-info.nix | 207 ++++++++++++++++++++---------------- 3 files changed, 124 insertions(+), 92 deletions(-) diff --git a/doc/release-notes/index.rst b/doc/release-notes/index.rst index af96ee319..0d3a35ba4 100644 --- a/doc/release-notes/index.rst +++ b/doc/release-notes/index.rst @@ -16,6 +16,10 @@ Release 2.0 - Major code cleanups. +- Now the network specification is using the module system from ``nixpkgs.lib`` + - Now network specification files can import other files via ``imports``. + - We have a ``nodes.*`` option where we put every NixOS configuration for the configured nodes. We suggest to use it instead of defining nodes in the top level. + - Removed NixOS Options - ``deployment.autoLuks.*`` - moved to `nixos-modules-contrib`_. diff --git a/flake.nix b/flake.nix index 8af0905a0..3e2794b05 100644 --- a/flake.nix +++ b/flake.nix @@ -41,6 +41,7 @@ git_root=$(${pkgs.git}/bin/git rev-parse --show-toplevel) export PYTHONPATH=$git_root:$PYTHONPATH export PATH=$git_root/scripts:$PATH + export NIX_PATH="nixpkgs=${nixpkgs}:$NIX_PATH" ''; }; @@ -65,6 +66,10 @@ overrides ]; + postPatch = '' + substituteInPlace nix/eval-machine-info.nix --replace "" "${nixpkgs}" + ''; + # TODO: Re-add manual build }; diff --git a/nix/eval-machine-info.nix b/nix/eval-machine-info.nix index f92d34bad..be49d3513 100644 --- a/nix/eval-machine-info.nix +++ b/nix/eval-machine-info.nix @@ -11,104 +11,139 @@ let call = x: if builtins.isFunction x then x args else x; - # Copied from nixpkgs to avoid import - optional = cond: elem: if cond then [elem] else []; - zipAttrs = set: builtins.listToAttrs ( map (name: { inherit name; value = builtins.catAttrs name set; }) (builtins.concatMap builtins.attrNames set)); - flakeExpr = (builtins.getFlake flakeUri).outputs.nixopsConfigurations.default; - - networks = - let - getNetworkFromExpr = networkExpr: - (call (import networkExpr)) // { _file = networkExpr; }; + flake = builtins.getFlake flakeUri; + flakeExpr = (builtins.tryEval flake.outputs.nixopsConfigurations.default).value or { }; - exprToKey = key: { key = toString key; }; + nixpkgsBoot = ; # this will be replaced on install by nixops' nixpkgs input + libBoot = import "${nixpkgsBoot}/lib"; - networkExprClosure = builtins.genericClosure { - startSet = map exprToKey networkExprs; - operator = { key }: map exprToKey ((getNetworkFromExpr key).require or []); + baseMods = lib: [ + { + options.nixpkgs = lib.mkOption { + type = lib.types.path; + description = "Path to the nixpkgs instance used to buld the machines."; + defaultText = lib.literalDocBook "The 'nixpkgs' input to either the provided flake or nixops' own."; + default = (builtins.tryEval flake.inputs.nixpkgs).value or nixpkgsBoot; }; - in - map ({ key }: getNetworkFromExpr key) networkExprClosure - ++ optional (flakeUri != null) - ((call flakeExpr) // { _file = "<${flakeUri}>"; }); + config._module.freeformType = with lib.types;attrsOf anything; + } + flakeExpr + ] ++ networkExprs; - network = zipAttrs networks; + evalBoot = libBoot.evalModules { + specialArgs = args; + modules = baseMods libBoot; + }; - evalConfig = - if flakeUri != null - then - if network ? nixpkgs - then (builtins.head (network.nixpkgs)).lib.nixosSystem - else throw "NixOps network must have a 'nixpkgs' attribute" - else import (pkgs.path + "/nixos/lib/eval-config.nix"); + inherit (evalBoot.config) nixpkgs; - pkgs = if flakeUri != null - then - if network ? nixpkgs - then (builtins.head network.nixpkgs).legacyPackages.${system} - else throw "NixOps network must have a 'nixpkgs' attribute" - else (builtins.head (network.network)).nixpkgs or (import { inherit system; }); + pkgs = nixpkgs.legacyPackages.${system} or import nixpkgs { inherit system; }; + lib = nixpkgs.lib or pkgs.lib or libBoot; + inherit (lib) mkOption types removeAttrs; - inherit (pkgs) lib; +in +rec { + inherit nixpkgs; - # Expose path to imported nixpkgs (currently only used to find version suffix) - nixpkgs = builtins.unsafeDiscardStringContext pkgs.path; + netConfig = (lib.evalModules { + specialArgs = args; + modules = baseMods lib ++ [ + ({ config, options, ... }: { + options = { + network = { + enableRollback = lib.mkEnableOption "network wide rollback"; + description = mkOption { + type = types.str; + description = "A description of the entire network."; + default = ""; + }; + nodesExtraArgs = mkOption { + description = "Extra inputs to be passed to every node."; + type = with types;attrsOf anything; + default = {}; + }; + }; + resources = mkOption { + type = types.submoduleWith { + modules = [{ + # so what is this trying to do? + sshKeyPairs = evalResources ./ssh-keypair.nix (lib.zipAttrs resourcesByType.sshKeyPairs or [ ]); + commandOutput = evalResources ./command-output.nix (lib.zipAttrs resourcesByType.commandOutput or [ ]); + machines = config.nodes; + _module.check = false; + }] ++ pluginResources; + specialArgs = { + inherit evalResources resourcesByType; + inherit (lib) zipAttrs; + }; + }; + }; + # Compute the definitions of the machines. + nodes = mkOption { + type = types.attrsOf (types.submoduleWith { + specialArgs = { + inherit uuid deploymentName; + inherit (config) nodes resources; + } // config.network.nodesExtraArgs; + modules = (import "${nixpkgs}/nixos/modules/module-list.nix") ++ + config.defaults ++ + # Make NixOps's deployment.* options available. + pluginOptions ++ + [ + ./options.nix + ./resource.nix + deploymentInfoModule + ({ name, ... }: rec{ + _file = ./eval-machine-info.nix; + key = _file; + # Provide a default hostname and deployment target equal + # to the attribute name of the machine in the model. + networking.hostName = lib.mkOverride 900 name; + deployment.targetHost = lib.mkOverride 900 name; + environment.checkConfigurationOptions = lib.mkOverride 900 checkConfigurationOptions; + nixpkgs.system = lib.mkDefault system; + }) + ]; + }); + }; + defaults = mkOption { + type = with types;listOf anything; + default = [ ]; + description = '' + Extra NixOS options to add to all nodes. + ''; + }; + }; + config = let + nodes = removeAttrs config (builtins.attrNames options); + in mkIf ({} != nodes) { #TODO: actual warning/assert module impl. + nodes = lib.warn "Please use the actual nodes.* option instead of assigning machines to the config's top level" nodes; + }; + }) + ]; + }).config; -in rec { + inherit (netConfig) resources nodes; + defaults = [ netConfig.defaults ]; - inherit networks network; - inherit nixpkgs; + # for backward compatibility + network = lib.mapAttrs (n: v: [v]) netConfig; + networks = [ netConfig ]; importedPluginNixExprs = map (expr: import expr) pluginNixExprs; - pluginOptions = { imports = (lib.foldl (a: e: a ++ e.options) [] importedPluginNixExprs); }; + pluginOptions = lib.foldl (a: e: a ++ e.options) [ ] importedPluginNixExprs; pluginResources = map (e: e.resources) importedPluginNixExprs; - pluginDeploymentConfigExporters = (lib.foldl (a: e: a ++ (e.config_exporters { - inherit pkgs; - inherit (lib) optionalAttrs; - })) [] importedPluginNixExprs); - - defaults = network.defaults or []; - - # Compute the definitions of the machines. - nodes = - lib.listToAttrs (map (machineName: - let - # Get the configuration of this machine from each network - # expression, attaching _file attributes so the NixOS module - # system can give sensible error messages. - modules = - lib.concatMap (n: lib.optional (lib.hasAttr machineName n) - { imports = [(lib.getAttr machineName n)]; inherit (n) _file; }) - networks; - in - { name = machineName; - value = evalConfig { - modules = - modules ++ - defaults ++ - [ deploymentInfoModule ] ++ - [ { key = "nixops-stuff"; - # Make NixOps's deployment.* options available. - imports = [ ./options.nix ./resource.nix pluginOptions ]; - # Provide a default hostname and deployment target equal - # to the attribute name of the machine in the model. - networking.hostName = lib.mkOverride 900 machineName; - deployment.targetHost = lib.mkOverride 900 machineName; - environment.checkConfigurationOptions = lib.mkOverride 900 checkConfigurationOptions; - - nixpkgs.system = lib.mkDefault system; - - _module.args = { inherit nodes resources uuid deploymentName; name = machineName; }; - } - ]; - }; - } - ) (lib.attrNames (removeAttrs network [ "network" "defaults" "resources" "require" "nixpkgs" "_file" ]))); + pluginDeploymentConfigExporters = (lib.foldl + (a: e: a ++ (e.config_exporters { + inherit pkgs; + inherit (lib) optionalAttrs; + })) [ ] + importedPluginNixExprs); # Compute the definitions of the non-machine resources. resourcesByType = lib.zipAttrs (network.resources or []); @@ -158,18 +193,6 @@ in rec { publicIPv4 = "config.networking.publicIPv4"; }.${key} or "config.deployment.${key}"; - resources = lib.foldl - (a: b: a // (b { - inherit evalResources resourcesByType; - inherit (lib) zipAttrs; - })) - { - sshKeyPairs = evalResources ./ssh-keypair.nix (lib.zipAttrs resourcesByType.sshKeyPairs or []); - commandOutput = evalResources ./command-output.nix (lib.zipAttrs resourcesByType.commandOutput or []); - machines = lib.mapAttrs (n: v: v.config) nodes; - } - pluginResources; - # check if there are duplicate elements in a sorted list noDups = l: if lib.length l > 1