summary refs log tree commit diff
diff options
context:
space:
mode:
authorDaiderd Jordan <daiderd@gmail.com>2017-02-14 23:18:44 +0100
committerVladimír Čunát <vcunat@gmail.com>2017-03-03 13:45:22 +0100
commitd88721e4408988202a0ae3cea3f5551ebfa13168 (patch)
tree5eca06d1133b1615b7c6e667ba7c42f7e97548b3
parentbb9a37a2a56249483cd98ef09c254b78e736af1a (diff)
downloadnixpkgs-d88721e4408988202a0ae3cea3f5551ebfa13168.tar
nixpkgs-d88721e4408988202a0ae3cea3f5551ebfa13168.tar.gz
nixpkgs-d88721e4408988202a0ae3cea3f5551ebfa13168.tar.bz2
nixpkgs-d88721e4408988202a0ae3cea3f5551ebfa13168.tar.lz
nixpkgs-d88721e4408988202a0ae3cea3f5551ebfa13168.tar.xz
nixpkgs-d88721e4408988202a0ae3cea3f5551ebfa13168.tar.zst
nixpkgs-d88721e4408988202a0ae3cea3f5551ebfa13168.zip
modules: add support for module replacement with disabledModules
This is based on a prototype Nicolas B. Pierron worked on during a
discussion we had at FOSDEM.

A new version with a workaround for problems of the reverted original.
Discussion: https://github.com/NixOS/nixpkgs/commit/3f2566689
-rw-r--r--lib/modules.nix24
-rwxr-xr-xlib/tests/modules.sh8
-rw-r--r--lib/tests/modules/default.nix1
-rw-r--r--lib/tests/modules/disable-declare-enable.nix5
-rw-r--r--lib/tests/modules/disable-define-enable.nix5
-rw-r--r--lib/tests/modules/disable-enable-modules.nix5
-rw-r--r--nixos/doc/manual/development/replace-modules.xml75
-rw-r--r--nixos/doc/manual/development/writing-modules.xml1
-rw-r--r--nixos/doc/manual/release-notes/rl-1703.xml10
9 files changed, 127 insertions, 7 deletions
diff --git a/lib/modules.nix b/lib/modules.nix
index 4eee41306cd..a7c397d2cf4 100644
--- a/lib/modules.nix
+++ b/lib/modules.nix
@@ -20,7 +20,8 @@ rec {
                 , prefix ? []
                 , # This should only be used for special arguments that need to be evaluated
                   # when resolving module structure (like in imports). For everything else,
-                  # there's _module.args.
+                  # there's _module.args. If specialArgs.modulesPath is defined it will be
+                  # used as the base path for disabledModules.
                   specialArgs ? {}
                 , # This would be remove in the future, Prefer _module.args option instead.
                   args ? {}
@@ -58,10 +59,7 @@ rec {
 
       closed = closeModules (modules ++ [ internalModule ]) ({ inherit config options; lib = import ./.; } // specialArgs);
 
-      # Note: the list of modules is reversed to maintain backward
-      # compatibility with the old module system.  Not sure if this is
-      # the most sensible policy.
-      options = mergeModules prefix (reverseList closed);
+      options = mergeModules prefix (reverseList (filterModules (specialArgs.modulesPath or "") closed));
 
       # Traverse options and extract the option values into the final
       # config set.  At the same time, check whether all option
@@ -87,6 +85,16 @@ rec {
       result = { inherit options config; };
     in result;
 
+
+ # Filter disabled modules. Modules can be disabled allowing
+ # their implementation to be replaced.
+ filterModules = modulesPath: modules:
+   let
+     moduleKey = m: if isString m then toString modulesPath + "/" + m else toString m;
+     disabledKeys = map moduleKey (concatMap (m: m.disabledModules) modules);
+   in
+     filter (m: !(elem m.key disabledKeys)) modules;
+
   /* Close a set of modules under the ‘imports’ relation. */
   closeModules = modules: args:
     let
@@ -111,12 +119,13 @@ rec {
       else {};
     in
     if m ? config || m ? options then
-      let badAttrs = removeAttrs m ["imports" "options" "config" "key" "_file" "meta"]; in
+      let badAttrs = removeAttrs m ["_file" "key" "disabledModules" "imports" "options" "config" "meta"]; in
       if badAttrs != {} then
         throw "Module `${key}' has an unsupported attribute `${head (attrNames badAttrs)}'. This is caused by assignments to the top-level attributes `config' or `options'."
       else
         { file = m._file or file;
           key = toString m.key or key;
+          disabledModules = m.disabledModules or [];
           imports = m.imports or [];
           options = m.options or {};
           config = mkMerge [ (m.config or {}) metaSet ];
@@ -124,9 +133,10 @@ rec {
     else
       { file = m._file or file;
         key = toString m.key or key;
+        disabledModules = m.disabledModules or [];
         imports = m.require or [] ++ m.imports or [];
         options = {};
-        config = mkMerge [ (removeAttrs m ["key" "_file" "require" "imports"]) metaSet ];
+        config = mkMerge [ (removeAttrs m ["_file" "key" "disabledModules" "require" "imports"]) metaSet ];
       };
 
   applyIfFunction = key: f: args@{ config, options, lib, ... }: if isFunction f then
diff --git a/lib/tests/modules.sh b/lib/tests/modules.sh
index 8b476a5d3dc..ba0c67fb7d4 100755
--- a/lib/tests/modules.sh
+++ b/lib/tests/modules.sh
@@ -99,6 +99,14 @@ checkConfigOutput 'true' "$@" ./define-enable.nix ./define-loaOfSub-if-foo-enabl
 checkConfigOutput 'true' "$@" ./define-enable.nix ./define-loaOfSub-foo-if-enable.nix
 checkConfigOutput 'true' "$@" ./define-enable.nix ./define-loaOfSub-foo-enable-if.nix
 
+# Check disabledModules with config definitions and option declarations.
+set -- config.enable ./define-enable.nix ./declare-enable.nix
+checkConfigOutput "true" "$@"
+checkConfigOutput "false" "$@" ./disable-define-enable.nix
+checkConfigError "The option .*enable.* defined in .* does not exist" "$@" ./disable-declare-enable.nix
+checkConfigError "attribute .*enable.* in selection path .*config.enable.* not found" "$@" ./disable-define-enable.nix ./disable-declare-enable.nix
+checkConfigError "attribute .*enable.* in selection path .*config.enable.* not found" "$@" ./disable-enable-modules.nix
+
 # Check _module.args.
 set -- config.enable ./declare-enable.nix ./define-enable-with-custom-arg.nix
 checkConfigError 'while evaluating the module argument .*custom.* in .*define-enable-with-custom-arg.nix.*:' "$@"
diff --git a/lib/tests/modules/default.nix b/lib/tests/modules/default.nix
index c5ce9cb3e3b..5b094710419 100644
--- a/lib/tests/modules/default.nix
+++ b/lib/tests/modules/default.nix
@@ -3,5 +3,6 @@
 {
   inherit (lib.evalModules {
     inherit modules;
+    specialArgs.modulesPath = ./.;
   }) config options;
 }
diff --git a/lib/tests/modules/disable-declare-enable.nix b/lib/tests/modules/disable-declare-enable.nix
new file mode 100644
index 00000000000..a373ee7e550
--- /dev/null
+++ b/lib/tests/modules/disable-declare-enable.nix
@@ -0,0 +1,5 @@
+{ lib, ... }:
+
+{
+  disabledModules = [ ./declare-enable.nix ];
+}
diff --git a/lib/tests/modules/disable-define-enable.nix b/lib/tests/modules/disable-define-enable.nix
new file mode 100644
index 00000000000..0d84a7c3cb6
--- /dev/null
+++ b/lib/tests/modules/disable-define-enable.nix
@@ -0,0 +1,5 @@
+{ lib, ... }:
+
+{
+  disabledModules = [ ./define-enable.nix ];
+}
diff --git a/lib/tests/modules/disable-enable-modules.nix b/lib/tests/modules/disable-enable-modules.nix
new file mode 100644
index 00000000000..c325f4e0743
--- /dev/null
+++ b/lib/tests/modules/disable-enable-modules.nix
@@ -0,0 +1,5 @@
+{ lib, ... }:
+
+{
+  disabledModules = [ "define-enable.nix" "declare-enable.nix" ];
+}
diff --git a/nixos/doc/manual/development/replace-modules.xml b/nixos/doc/manual/development/replace-modules.xml
new file mode 100644
index 00000000000..cc0539ec510
--- /dev/null
+++ b/nixos/doc/manual/development/replace-modules.xml
@@ -0,0 +1,75 @@
+<section 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="sec-replace-modules">
+
+<title>Replace Modules</title>
+
+<para>Modules that are imported can also be disabled.  The option
+  declarations and config implementation of a disabled module will be
+  ignored, allowing another to take it's place.  This can be used to
+  import a set of modules from another channel while keeping the rest
+  of the system on a stable release.</para>
+<para><literal>disabledModules</literal> is a top level attribute like
+  <literal>imports</literal>, <literal>options</literal> and
+  <literal>config</literal>.  It contains a list of modules that will
+  be disabled. This can either be the full path to the module or a
+  string with the filename relative to the modules path
+  (eg. &lt;nixpkgs/nixos/modules&gt; for nixos).
+  </para>
+
+<para>This example will replace the existing postgresql module with
+  the version defined in the nixos-unstable channel while keeping the
+  rest of the modules and packages from the original nixos channel.
+  This only overrides the module definition, this won't use postgresql
+  from nixos-unstable unless explicitly configured to do so.</para>
+
+<programlisting>
+{ config, lib, pkgs, ... }:
+
+{
+  disabledModules = [ "services/databases/postgresql.nix" ];
+
+  imports =
+    [ # Use postgresql service from nixos-unstable channel.
+      # sudo nix-channel --add http://nixos.org/channels/nixos-unstable nixos-unstable
+      &lt;nixos-unstable/nixos/modules/services/databases/postgresql.nix&gt;
+    ];
+
+  services.postgresql.enable = true;
+}
+</programlisting>
+
+<para>This example shows how to define a custom module as a
+  replacement for an existing module. Importing this module will
+  disable the original module without having to know it's
+  implementation details.</para>
+
+<programlisting>
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.man;
+in
+
+{
+  disabledModules = [ "services/programs/man.nix" ];
+
+  options = {
+    programs.man.enable = mkOption {
+      type = types.bool;
+      default = true;
+      description = "Whether to enable manual pages.";
+    };
+  };
+
+  config = mkIf cfg.enabled {
+    warnings = [ "disabled manpages for production deployments." ];
+  };
+}
+</programlisting>
+
+</section>
diff --git a/nixos/doc/manual/development/writing-modules.xml b/nixos/doc/manual/development/writing-modules.xml
index ef6920160e6..5bdcad5ceb5 100644
--- a/nixos/doc/manual/development/writing-modules.xml
+++ b/nixos/doc/manual/development/writing-modules.xml
@@ -179,5 +179,6 @@ in {
 <xi:include href="option-types.xml" />
 <xi:include href="option-def.xml" />
 <xi:include href="meta-attributes.xml" />
+<xi:include href="replace-modules.xml" />
 
 </chapter>
diff --git a/nixos/doc/manual/release-notes/rl-1703.xml b/nixos/doc/manual/release-notes/rl-1703.xml
index fda46217144..f40b03ec9b9 100644
--- a/nixos/doc/manual/release-notes/rl-1703.xml
+++ b/nixos/doc/manual/release-notes/rl-1703.xml
@@ -271,6 +271,16 @@ following incompatible changes:</para>
     </para>
   </listitem>
 
+  <listitem>
+    <para>
+      Modules can now be disabled by using <link
+      xlink:href="https://nixos.org/nixpkgs/manual/#sec-replace-modules">
+      disabledModules</link>, allowing another to take it's place.  This can be
+      used to import a set of modules from another channel while keeping the
+      rest of the system on a stable release.
+    </para>
+  </listitem>
+
 </itemizedlist>