···103103 toClosureList = file: parentKey: imap1 (n: x:
104104 if isAttrs x || isFunction x then
105105 let key = "${parentKey}:anon-${toString n}"; in
106106- unifyModuleSyntax file key (unpackSubmodule (applyIfFunction key) x args)
106106+ unifyModuleSyntax file key (applyIfFunction key x args)
107107 else
108108 let file = toString x; key = toString x; in
109109 unifyModuleSyntax file key (applyIfFunction key (import x) args));
110110 in
111111 builtins.genericClosure {
112112 startSet = toClosureList unknownModule "" modules;
113113- operator = m: toClosureList m.file m.key m.imports;
113113+ operator = m: toClosureList m._file m.key m.imports;
114114 };
115115116116 /* Massage a module into canonical form, that is, a set consisting
117117 of ‘options’, ‘config’ and ‘imports’ attributes. */
118118 unifyModuleSyntax = file: key: m:
119119- let metaSet = if m ? meta
120120- then { meta = m.meta; }
121121- else {};
119119+ let addMeta = config: if m ? meta
120120+ then mkMerge [ config { meta = m.meta; } ]
121121+ else config;
122122 in
123123 if m ? config || m ? options then
124124 let badAttrs = removeAttrs m ["_file" "key" "disabledModules" "imports" "options" "config" "meta"]; in
125125 if badAttrs != {} then
126126 throw "Module `${key}' has an unsupported attribute `${head (attrNames badAttrs)}'. This is caused by assignments to the top-level attributes `config' or `options'."
127127 else
128128- { file = m._file or file;
128128+ { _file = m._file or file;
129129 key = toString m.key or key;
130130 disabledModules = m.disabledModules or [];
131131 imports = m.imports or [];
132132 options = m.options or {};
133133- config = mkMerge [ (m.config or {}) metaSet ];
133133+ config = addMeta (m.config or {});
134134 }
135135 else
136136- { file = m._file or file;
136136+ { _file = m._file or file;
137137 key = toString m.key or key;
138138 disabledModules = m.disabledModules or [];
139139 imports = m.require or [] ++ m.imports or [];
140140 options = {};
141141- config = mkMerge [ (removeAttrs m ["_file" "key" "disabledModules" "require" "imports"]) metaSet ];
141141+ config = addMeta (removeAttrs m ["_file" "key" "disabledModules" "require" "imports"]);
142142 };
143143144144 applyIfFunction = key: f: args@{ config, options, lib, ... }: if isFunction f then
···171171 else
172172 f;
173173174174- /* We have to pack and unpack submodules. We cannot wrap the expected
175175- result of the function as we would no longer be able to list the arguments
176176- of the submodule. (see applyIfFunction) */
177177- unpackSubmodule = unpack: m: args:
178178- if isType "submodule" m then
179179- { _file = m.file; } // (unpack m.submodule args)
180180- else unpack m args;
181181-182182- packSubmodule = file: m:
183183- { _type = "submodule"; file = file; submodule = m; };
184184-185174 /* Merge a list of modules. This will recurse over the option
186175 declarations in all modules, combining them into a single set.
187176 At the same time, for each option declaration, it will merge the
···189178 in the ‘value’ attribute of each option. */
190179 mergeModules = prefix: modules:
191180 mergeModules' prefix modules
192192- (concatMap (m: map (config: { inherit (m) file; inherit config; }) (pushDownProperties m.config)) modules);
181181+ (concatMap (m: map (config: { file = m._file; inherit config; }) (pushDownProperties m.config)) modules);
193182194183 mergeModules' = prefix: options: configs:
195184 let
···223212 ) {} modules;
224213 # an attrset 'name' => list of submodules that declare ‘name’.
225214 declsByName = byName "options" (module: option:
226226- [{ inherit (module) file; options = option; }]
215215+ [{ inherit (module) _file; options = option; }]
227216 ) options;
228217 # an attrset 'name' => list of submodules that define ‘name’.
229218 defnsByName = byName "config" (module: value:
···250239 firstOption = findFirst (m: isOption m.options) "" decls;
251240 firstNonOption = findFirst (m: !isOption m.options) "" decls;
252241 in
253253- throw "The option `${showOption loc}' in `${firstOption.file}' is a prefix of options in `${firstNonOption.file}'."
242242+ throw "The option `${showOption loc}' in `${firstOption._file}' is a prefix of options in `${firstNonOption._file}'."
254243 else
255244 mergeModules' loc decls defns
256245 ))
···267256268257 'opts' is a list of modules. Each module has an options attribute which
269258 correspond to the definition of 'loc' in 'opt.file'. */
270270- mergeOptionDecls = loc: opts:
259259+ mergeOptionDecls =
260260+ let
261261+ packSubmodule = file: m:
262262+ { _file = file; imports = [ m ]; };
263263+ coerceOption = file: opt:
264264+ if isFunction opt then packSubmodule file opt
265265+ else packSubmodule file { options = opt; };
266266+ in loc: opts:
271267 foldl' (res: opt:
272268 let t = res.type;
273269 t' = opt.options.type;
···284280 bothHave "apply" ||
285281 (bothHave "type" && (! typesMergeable))
286282 then
287287- throw "The option `${showOption loc}' in `${opt.file}' is already declared in ${showFiles res.declarations}."
283283+ throw "The option `${showOption loc}' in `${opt._file}' is already declared in ${showFiles res.declarations}."
288284 else
289285 let
290286 /* Add the modules of the current option to the list of modules
···293289 current option declaration as the file use for the submodule. If the
294290 submodule defines any filename, then we ignore the enclosing option file. */
295291 options' = toList opt.options.options;
296296- coerceOption = file: opt:
297297- if isFunction opt then packSubmodule file opt
298298- else packSubmodule file { options = opt; };
292292+299293 getSubModules = opt.options.type.getSubModules or null;
300294 submodules =
301301- if getSubModules != null then map (packSubmodule opt.file) getSubModules ++ res.options
302302- else if opt.options ? options then map (coerceOption opt.file) options' ++ res.options
295295+ if getSubModules != null then map (packSubmodule opt._file) getSubModules ++ res.options
296296+ else if opt.options ? options then map (coerceOption opt._file) options' ++ res.options
303297 else res.options;
304298 in opt.options // res //
305305- { declarations = res.declarations ++ [opt.file];
299299+ { declarations = res.declarations ++ [opt._file];
306300 options = submodules;
307301 } // typeSet
308302 ) { inherit loc; declarations = []; options = []; } opts;
+18
lib/tests/modules.sh
···164164checkConfigOutput "false" config.enable ./alias-with-priority-can-override.nix
165165checkConfigOutput "false" config.enableAlias ./alias-with-priority-can-override.nix
166166167167+# submoduleWith
168168+169169+## specialArgs should work
170170+checkConfigOutput "foo" config.submodule.foo ./declare-submoduleWith-special.nix
171171+172172+## shorthandOnlyDefines config behaves as expected
173173+checkConfigOutput "true" config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-shorthand.nix
174174+checkConfigError 'is not of type `boolean' config.submodule.config ./declare-submoduleWith-shorthand.nix ./define-submoduleWith-noshorthand.nix
175175+checkConfigError 'value is a boolean while a set was expected' config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-shorthand.nix
176176+checkConfigOutput "true" config.submodule.config ./declare-submoduleWith-noshorthand.nix ./define-submoduleWith-noshorthand.nix
177177+178178+## submoduleWith should merge all modules in one swoop
179179+checkConfigOutput "true" config.submodule.inner ./declare-submoduleWith-modules.nix
180180+checkConfigOutput "true" config.submodule.outer ./declare-submoduleWith-modules.nix
181181+182182+## Paths should be allowed as values and work as expected
183183+checkConfigOutput "true" config.submodule.enable ./declare-submoduleWith-path.nix
184184+167185cat <<EOF
168186====== module tests ======
169187$pass Pass
···257257 <listitem>
258258 <para>
259259 A set of sub options <replaceable>o</replaceable>.
260260- <replaceable>o</replaceable> can be an attribute set or a function
261261- returning an attribute set. Submodules are used in composed types to
262262- create modular options. Submodule are detailed in
260260+ <replaceable>o</replaceable> can be an attribute set, a function
261261+ returning an attribute set, or a path to a file containing such a value. Submodules are used in
262262+ composed types to create modular options. This is equivalent to
263263+ <literal>types.submoduleWith { modules = toList o; shorthandOnlyDefinesConfig = true; }</literal>.
264264+ Submodules are detailed in
263265 <xref
264266 linkend='section-option-types-submodule' />.
265267 </para>
266268 </listitem>
269269+ </varlistentry>
270270+ <varlistentry>
271271+ <term>
272272+ <varname>types.submoduleWith</varname> {
273273+ <replaceable>modules</replaceable>,
274274+ <replaceable>specialArgs</replaceable> ? {},
275275+ <replaceable>shorthandOnlyDefinesConfig</replaceable> ? false }
276276+ </term>
277277+ <listitem>
278278+ <para>
279279+ Like <varname>types.submodule</varname>, but more flexible and with better defaults.
280280+ It has parameters
281281+ <itemizedlist>
282282+ <listitem><para>
283283+ <replaceable>modules</replaceable>
284284+ A list of modules to use by default for this submodule type. This gets combined
285285+ with all option definitions to build the final list of modules that will be included.
286286+ <note><para>
287287+ Only options defined with this argument are included in rendered documentation.
288288+ </para></note>
289289+ </para></listitem>
290290+ <listitem><para>
291291+ <replaceable>specialArgs</replaceable>
292292+ An attribute set of extra arguments to be passed to the module functions.
293293+ The option <literal>_module.args</literal> should be used instead
294294+ for most arguments since it allows overriding. <replaceable>specialArgs</replaceable> should only be
295295+ used for arguments that can't go through the module fixed-point, because of
296296+ infinite recursion or other problems. An example is overriding the
297297+ <varname>lib</varname> argument, because <varname>lib</varname> itself is used
298298+ to define <literal>_module.args</literal>, which makes using
299299+ <literal>_module.args</literal> to define it impossible.
300300+ </para></listitem>
301301+ <listitem><para>
302302+ <replaceable>shorthandOnlyDefinesConfig</replaceable>
303303+ Whether definitions of this type should default to the <literal>config</literal>
304304+ section of a module (see <xref linkend='ex-module-syntax'/>) if it is an attribute
305305+ set. Enabling this only has a benefit when the submodule defines an option named
306306+ <literal>config</literal> or <literal>options</literal>. In such a case it would
307307+ allow the option to be set with <literal>the-submodule.config = "value"</literal>
308308+ instead of requiring <literal>the-submodule.config.config = "value"</literal>.
309309+ This is because only when modules <emphasis>don't</emphasis> set the
310310+ <literal>config</literal> or <literal>options</literal> keys, all keys are interpreted
311311+ as option definitions in the <literal>config</literal> section. Enabling this option
312312+ implicitly puts all attributes in the <literal>config</literal> section.
313313+ </para>
314314+ <para>
315315+ With this option enabled, defining a non-<literal>config</literal> section requires
316316+ using a function: <literal>the-submodule = { ... }: { options = { ... }; }</literal>.
317317+ </para></listitem>
318318+ </itemizedlist>
319319+ </para>
320320+ </listitem>
267321 </varlistentry>
268322 </variablelist>
269323 </section>
···11# This test runs haka and probes it with hakactl
2233-import ./make-test.nix ({ pkgs, ...} : {
33+import ./make-test-python.nix ({ pkgs, ...} : {
44 name = "haka";
55 meta = with pkgs.stdenv.lib.maintainers; {
66 maintainers = [ tvestelind ];
···1515 };
16161717 testScript = ''
1818- startAll;
1818+ start_all()
19192020- $haka->waitForUnit("haka.service");
2121- $haka->succeed("hakactl status");
2222- $haka->succeed("hakactl stop");
2020+ haka.wait_for_unit("haka.service")
2121+ haka.succeed("hakactl status")
2222+ haka.succeed("hakactl stop")
2323 '';
2424})
+12-6
nixos/tests/kexec.nix
···11# Test whether fast reboots via kexec work.
2233-import ./make-test.nix ({ pkgs, ...} : {
33+import ./make-test-python.nix ({ pkgs, lib, ...} : {
44 name = "kexec";
55- meta = with pkgs.stdenv.lib.maintainers; {
55+ meta = with lib.maintainers; {
66 maintainers = [ eelco ];
77+ # Currently hangs forever; last output is:
88+ # machine # [ 10.239914] dhcpcd[707]: eth0: adding default route via fe80::2
99+ # machine: waiting for the VM to finish booting
1010+ # machine # Cannot find the ESP partition mount point.
1111+ # machine # [ 28.681197] nscd[692]: 692 checking for monitored file `/etc/netgroup': No such file or directory
1212+ broken = true;
713 };
814915 machine = { ... }:
···11171218 testScript =
1319 ''
1414- $machine->waitForUnit("multi-user.target");
1515- $machine->execute("systemctl kexec &");
1616- $machine->{connected} = 0;
1717- $machine->waitForUnit("multi-user.target");
2020+ machine.wait_for_unit("multi-user.target")
2121+ machine.execute("systemctl kexec &")
2222+ machine.connected = False
2323+ machine.wait_for_unit("multi-user.target")
1824 '';
1925})
+16-2
nixos/tests/mysql.nix
···27272828 {
2929 users.users.testuser = { };
3030+ users.users.testuser2 = { };
3031 services.mysql.enable = true;
3132 services.mysql.initialScript = pkgs.writeText "mariadb-init.sql" ''
3233 ALTER USER root@localhost IDENTIFIED WITH unix_socket;
···3435 DELETE FROM mysql.user WHERE user = ''';
3536 FLUSH PRIVILEGES;
3637 '';
3737- services.mysql.ensureDatabases = [ "testdb" ];
3838+ services.mysql.ensureDatabases = [ "testdb" "testdb2" ];
3839 services.mysql.ensureUsers = [{
3940 name = "testuser";
4041 ensurePermissions = {
4142 "testdb.*" = "ALL PRIVILEGES";
4243 };
4444+ } {
4545+ name = "testuser2";
4646+ ensurePermissions = {
4747+ "testdb2.*" = "ALL PRIVILEGES";
4848+ };
4349 }];
4450 services.mysql.package = pkgs.mariadb;
4551 };
···4753 };
48544955 testScript = ''
5050- start_all
5656+ start_all()
51575258 mysql.wait_for_unit("mysql")
5359 mysql.succeed("echo 'use empty_testdb;' | mysql -u root")
···6167 )
6268 mariadb.succeed(
6369 "echo 'use testdb; insert into tests values (42);' | sudo -u testuser mysql -u testuser"
7070+ )
7171+ # Ensure testuser2 is not able to insert into testdb as mysql testuser2
7272+ mariadb.fail(
7373+ "echo 'use testdb; insert into tests values (23);' | sudo -u testuser2 mysql -u testuser2"
7474+ )
7575+ # Ensure testuser2 is not able to authenticate as mysql testuser
7676+ mariadb.fail(
7777+ "echo 'use testdb; insert into tests values (23);' | sudo -u testuser2 mysql -u testuser"
6478 )
6579 mariadb.succeed(
6680 "echo 'use testdb; select test_id from tests;' | sudo -u testuser mysql -u testuser -N | grep 42"
+1
nixos/tests/netdata.nix
···25252626 # check if the netdata main page loads.
2727 netdata.succeed("curl --fail http://localhost:19999/")
2828+ netdata.succeed("sleep 4")
28292930 # check if netdata can read disk ops for root owned processes.
3031 # if > 0, successful. verifies both netdata working and
···1212 pname = "gotify-server";
1313 # Note that when this is updated, along with the hash, the `ui.nix` file
1414 # should include the same changes to the version and the sha256.
1515- version = "2.0.12";
1515+ version = "2.0.13";
16161717 src = fetchFromGitHub {
1818 owner = "gotify";
1919 repo = "server";
2020 rev = "v${version}";
2121- sha256 = "0pkws83ymmlxcdxadb1w6rmibw84vzhx9xrhxc6b1rjncb80l0kk";
2121+ sha256 = "11ycs1ci1z8wm4fjgk4454kgszr4s8q9dc96pl77yvlngi4dk46d";
2222 };
23232424 modSha256 = "1awhbc8qs2bwv6y2vwd92r4ys0l1bzymrb36iamr040x961682wv";