Commit
This modification add a way to re-evaluate the module system with a different version of NixOS, or with a different set of arguments.
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
{ config, options, lib, ... }: | ||
|
||
# This modules is used to inject a different NixOS version as well as its | ||
# argument such that one can pin a specific version with the versionning | ||
# system of the configuration. | ||
let | ||
nixosReentry = import config.nixos.path { | ||
inherit (config.nixos) configuration extraModules; | ||
inherit (config.nixpkgs) system; | ||
reEnter = true; | ||
}; | ||
in | ||
|
||
with lib; | ||
|
||
{ | ||
options = { | ||
nixos.path = mkOption { | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
nbp
Author
Member
|
||
default = null; | ||
example = literalExample "./nixpkgs-15.09/nixos"; | ||
type = types.nullOr types.path; | ||
description = '' | ||
This option give the ability to evaluate the current set of modules | ||
with a different version of NixOS. This option can be used version | ||
the version of NixOS with the configuration without relying on the | ||
<literal>NIX_PATH</literal> environment variable. | ||
''; | ||
}; | ||
|
||
nixos.system = mkOption { | ||
This comment has been minimized.
Sorry, something went wrong. |
||
example = "i686-linux"; | ||
type = types.uniq types.str; | ||
description = '' | ||
Name of the system used to compile NixOS. | ||
''; | ||
}; | ||
|
||
nixos.extraModules = mkOption { | ||
default = []; | ||
example = literalExample "mkIf config.services.openssh.enable [ ./sshd-config.nix ]"; | ||
type = types.listOf types.unspecified; | ||
description = '' | ||
Define additional modules which would be loaded to evaluate the | ||
configuration. | ||
''; | ||
}; | ||
|
||
nixos.configuration = mkOption { | ||
type = types.unspecified; | ||
internal = true; | ||
description = '' | ||
Option used by <filename>nixos/default.nix</filename> to re-inject | ||
the same configuration module as the one used for the current | ||
execution. | ||
''; | ||
}; | ||
|
||
nixos.reflect = mkOption { | ||
default = { inherit config options; }; | ||
type = types.unspecified; | ||
internal = true; | ||
description = '' | ||
Provides <literal>config</literal> and <literal>options</literal> | ||
computed by the module system and given as argument to all | ||
modules. These are used for introspection of options and | ||
configuration by tools such as <literal>nixos-option</literal>. | ||
''; | ||
}; | ||
}; | ||
|
||
config = mkMerge [ | ||
(mkIf (config.nixos.path != null) (mkForce { | ||
system.build.toplevel = nixosReentry.system; | ||
system.build.vm = nixosReentry.vm; | ||
nixos.reflect = { inherit (nixosReentry) config options; }; | ||
})) | ||
|
||
{ meta.maintainers = singleton lib.maintainers.pierron; | ||
meta.doc = ./nixos.xml; | ||
} | ||
]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
<chapter xmlns="http://docbook.org/ns/docbook" | ||
xmlns:xlink="http://www.w3.org/1999/xlink" | ||
xmlns:xi="http://www.w3.org/2001/XInclude" | ||
version="5.0" | ||
xml:id="module-misc-nixos"> | ||
|
||
<title>NixOS Reentry</title> | ||
|
||
<!-- FIXME: render nicely --> | ||
|
||
<!-- FIXME: source can be added automatically --> | ||
<para><emphasis>Source:</emphasis> <filename>modules/misc/nixos.nix</filename></para> | ||
|
||
<!-- FIXME: more stuff, like maintainer? --> | ||
|
||
<para>NixOS reentry can be used for both pinning the evaluation to a | ||
This comment has been minimized.
Sorry, something went wrong. |
||
specific version of NixOS, and to dynamically add additional modules into | ||
the Module evaluation.</para> | ||
|
||
<section><title>NixOS Version Pinning</title> | ||
|
||
<para>To pin a specific version of NixOS, you need a version that you can | ||
either clone localy, or that you can fetch remotely.</para> | ||
|
||
<para>If you already have a cloned version of NixOS in the directory | ||
<filename>/etc/nixos/nixpkgs-16-03</filename>, then you can specify the | ||
<option>nixos.path</option> with either the path or the relative path of | ||
your NixOS clone. For example, you can add the following to your | ||
<filename>/etc/nixos/configuration.nix</filename> file: | ||
|
||
<programlisting> | ||
nixos.path = ./nixpkgs-16-03/nixos; | ||
</programlisting> | ||
</para> | ||
|
||
<para>Another option is to fetch a specific version of NixOS, with either | ||
the <literal>fetchTarball</literal> builtin, or the | ||
<literal>pkgs.fetchFromGithub</literal> function and use the result as an | ||
input. | ||
|
||
<programlisting> | ||
nixos.path = "${builtins.fetchTarball https://github.com/NixOS/nixpkgs/archive/1f27976e03c15183191d1b4aa1a40d1f14666cd2.tar.gz}/nixos"; | ||
</programlisting> | ||
</para> | ||
|
||
</section> | ||
|
||
|
||
<section><title>Adding Module Dynamically</title> | ||
|
||
<para>To add additional module, the recommended way is to use statically | ||
known modules in the list of imported arguments as described in <xref | ||
linkend="sec-modularity" />. Unfortunately, this recommended method has | ||
limitation, such that the list of imported files cannot be selected based on | ||
the content of the configuration. | ||
|
||
This comment has been minimized.
Sorry, something went wrong. |
||
Fortunately, NixOS reentry system can be used as an alternative to register | ||
new imported modules based on the content of the configuration. To do so, | ||
one should define both <option>nixos.path</option> and | ||
<option>nixos.extraModules</option> options. | ||
|
||
<programlisting> | ||
nixos.path = <nixos>; | ||
nixos.extraModules = | ||
if config.networking.hostName == "server" then | ||
[ ./server.nix ] else [ ./client.nix ]; | ||
</programlisting> | ||
|
||
Also note, that the above can be reimplemented in a different way which is | ||
not as expensive, by using <literal>mkIf</literal> at the top each | ||
configuration if both modules are present on the file system (see <xref | ||
linkend="sec-option-definitions" />) and by always inmporting both | ||
modules.</para> | ||
|
||
</section> | ||
|
||
<section><title>Options</title> | ||
|
||
<para>FIXME: auto-generated list of module options.</para> | ||
|
||
</section> | ||
|
||
|
||
</chapter> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
{ system ? builtins.currentSystem }: | ||
|
||
with import ../lib/testing.nix { inherit system; }; | ||
let | ||
in | ||
|
||
pkgs.stdenv.mkDerivation rec { | ||
name = "nixos-pin-version"; | ||
src = ../..; | ||
buildInputs = with pkgs; [ nix gnugrep ]; | ||
|
||
withoutPath = pkgs.writeText "configuration.nix" '' | ||
{ | ||
nixos.extraModules = [ ({lib, ...}: { system.nixosRevision = lib.mkForce "ABCDEF"; }) ]; | ||
} | ||
''; | ||
|
||
withPath = pkgs.writeText "configuration.nix" '' | ||
{ | ||
nixos.path = ${src}/nixos ; | ||
nixos.extraModules = [ ({lib, ...}: { system.nixosRevision = lib.mkForce "ABCDEF"; }) ]; | ||
} | ||
''; | ||
|
||
phases = "buildPhase"; | ||
buildPhase = '' | ||
datadir="${pkgs.nix}/share" | ||
export TEST_ROOT=$(pwd)/test-tmp | ||
export NIX_STORE_DIR=$TEST_ROOT/store | ||
export NIX_LOCALSTATE_DIR=$TEST_ROOT/var | ||
export NIX_LOG_DIR=$TEST_ROOT/var/log/nix | ||
export NIX_STATE_DIR=$TEST_ROOT/var/nix | ||
export NIX_DB_DIR=$TEST_ROOT/db | ||
export NIX_CONF_DIR=$TEST_ROOT/etc | ||
export NIX_MANIFESTS_DIR=$TEST_ROOT/var/nix/manifests | ||
export NIX_BUILD_HOOK= | ||
export PAGER=cat | ||
cacheDir=$TEST_ROOT/binary-cache | ||
nix-store --init | ||
export NIX_PATH="nixpkgs=$src:nixos=$src/nixos:nixos-config=${withoutPath}" ; | ||
if test $(nix-instantiate $src/nixos -A config.system.nixosRevision --eval-only) != '"ABCDEF"' ; then :; | ||
else | ||
echo "Unexpected re-entry without the nixos.path option defined."; | ||
exit 1; | ||
fi; | ||
export NIX_PATH="nixpkgs=$src:nixos=$src/nixos:nixos-config=${withPath}" ; | ||
if test $(nix-instantiate $src/nixos -A config.system.nixosRevision --eval-only) = '"ABCDEF"' ; then :; | ||
else | ||
echo "Expected a re-entry when the nixos.path option is defined."; | ||
exit 1; | ||
fi; | ||
touch $out; | ||
''; | ||
} |
5 comments
on commit a5992ad
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is a good idea:
- It's expensive: you need to evaluate the config once just to get
nixos.path
, and then again to evaluate the system. - There is no guarantee that the current Nixpkgs can evaluate the config. E.g. if
<nixpkgs>
is 14.12 andnixos.path
is 16.12, and the config uses 16.12 features. - It only allows overriding
<nixpkgs>
, but no other $NIX_PATH entries. - The "re-entry" terminology is unclear and exposes an implementation detail.
- The re-entry evaluates
system.build.toplevel
andsystem.build.vm
but not other top-level attributes that might be used (e.g.system.build.amazonImage
).
I think it would be better to have a more direct way to specify a $NIX_PATH to nixos-rebuild
(e.g. it could read the desired path from /etc/nixos/path
or something like that).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or nixos-rebuild
could evaluate nix.nixPath
and add it $NIX_PATH for the final system evaluation, like like it already evaluates nix.package
to get the Nix package. This does suffer from problem 2 though.
Another solution that avoids all these problems is to have nixos-rebuild
build a file named /etc/nixos/system.nix
(or something like that) if it exists, which would be responsible for returning the same result as <nixpkgs/nixos>
(i.e. the evaluated NixOS system). E.g.
import "${fetchTarball https://github.com/NixOS/nixpkgs/<hash>.tar.gz}/nixos"
{ configuration =
{ config, pkgs, ... }:
{ ... };
}
It could also use scopedImport
to set a Nix search path declaratively.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+100 for nix.nixPath
. That would pretty much remove the channel state needed for different nixops deployments and would make everything together declarative.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that in NixOps you can already avoid a dependency on the channel state by doing nixops create/modify -I nixpkgs=https://github.com/NixOS/nixpkgs/archive/<commit>.tar.gz
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's expensive: you need to evaluate the config once just to get nixos.path, and then again to evaluate the system.
Yes, we would parse all the files twice, but we only evaluate the nixos options to resolve the new resolution when needed. Thus, this should not evaluate the configuration twice, as we only request the configuration from the pinned version.
There is no guarantee that the current Nixpkgs can evaluate the config. E.g. if is 14.12 and nixos.path is 16.12, and the config uses 16.12 features.
This feature is made to pin a specific version of nixos, knowing that all other options are ignored, the configuration should be the configuration of the pinned version.
Which is why we should use the pkgs
, or relative path within nixpkgs and not the bracket notation.
As pkgs
is defined relative to nixos.path
.
It only allows overriding , but no other $NIX_PATH entries.
There is no need, because you can add extra modules to the sub-evaluation. Extra modules can carry all the information without the need for NIX_PATH.
Note, adding nix.nixPath
using scopedImport
to this re-entry mechanism should be easy.
Having a
nixos
option prefix seems very strange to me, given that all NixOS options are about NixOS.