Skip to content

Commit

Permalink
eval-machine-info.nix: ported to modules
Browse files Browse the repository at this point in the history
  • Loading branch information
pasqui23 committed Feb 21, 2022
1 parent 06a182f commit 20ac995
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 92 deletions.
4 changes: 4 additions & 0 deletions doc/release-notes/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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`_.
Expand Down
5 changes: 5 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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"
'';
};

Expand All @@ -65,6 +66,10 @@
overrides
];

postPatch = ''
substituteInPlace nix/eval-machine-info.nix --replace "<nixpkgs>" "${nixpkgs}"
'';

# TODO: Re-add manual build
};

Expand Down
207 changes: 115 additions & 92 deletions nix/eval-machine-info.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,104 +11,139 @@
let
call = x: if builtins.isFunction x then x args else x;

# Copied from nixpkgs to avoid <nixpkgs> 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 = <nixpkgs> ; # 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 <nixpkgs> { 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 []);
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 20ac995

Please sign in to comment.