1testModuleArgs@{
2 config,
3 lib,
4 hostPkgs,
5 nodes,
6 options,
7 ...
8}:
9
10let
11 inherit (lib)
12 literalExpression
13 literalMD
14 mapAttrs
15 mkDefault
16 mkIf
17 mkMerge
18 mkOption
19 mkForce
20 optional
21 optionalAttrs
22 types
23 ;
24
25 inherit (hostPkgs.stdenv) hostPlatform;
26
27 guestSystem =
28 if hostPlatform.isLinux then
29 hostPlatform.system
30 else
31 let
32 hostToGuest = {
33 "x86_64-darwin" = "x86_64-linux";
34 "aarch64-darwin" = "aarch64-linux";
35 };
36
37 supportedHosts = lib.concatStringsSep ", " (lib.attrNames hostToGuest);
38
39 message = "NixOS Test: don't know which VM guest system to pair with VM host system: ${hostPlatform.system}. Perhaps you intended to run the tests on a Linux host, or one of the following systems that may run NixOS tests: ${supportedHosts}";
40 in
41 hostToGuest.${hostPlatform.system} or (throw message);
42
43 baseOS = import ../eval-config.nix {
44 inherit lib;
45 system = null; # use modularly defined system
46 inherit (config.node) specialArgs;
47 modules = [ config.defaults ];
48 baseModules = (import ../../modules/module-list.nix) ++ [
49 ./nixos-test-base.nix
50 {
51 key = "nodes";
52 _module.args.nodes = config.nodesCompat;
53 }
54 (
55 { config, ... }:
56 {
57 virtualisation.qemu.package = testModuleArgs.config.qemu.package;
58 virtualisation.host.pkgs = hostPkgs;
59 }
60 )
61 (
62 { options, ... }:
63 {
64 key = "nodes.nix-pkgs";
65 config = optionalAttrs (!config.node.pkgsReadOnly) (
66 mkIf (!options.nixpkgs.pkgs.isDefined) {
67 # TODO: switch to nixpkgs.hostPlatform and make sure containers-imperative test still evaluates.
68 nixpkgs.system = guestSystem;
69 }
70 );
71 }
72 )
73 testModuleArgs.config.extraBaseModules
74 ];
75 };
76
77 # TODO (lib): Dedup with run.nix, add to lib/options.nix
78 mkOneUp = opt: f: lib.mkOverride (opt.highestPrio - 1) (f opt.value);
79
80in
81
82{
83
84 options = {
85 sshBackdoor = {
86 enable = mkOption {
87 default = config.enableDebugHook;
88 defaultText = lib.literalExpression "config.enableDebugHook";
89 type = types.bool;
90 description = "Whether to turn on the VSOCK-based access to all VMs. This provides an unauthenticated access intended for debugging.";
91 };
92 vsockOffset = mkOption {
93 default = 2;
94 type = types.ints.between 2 4294967296;
95 description = ''
96 This field is only relevant when multiple users run the (interactive)
97 driver outside the sandbox and with the SSH backdoor activated.
98 The typical symptom for this being a problem are error messages like this:
99 `vhost-vsock: unable to set guest cid: Address already in use`
100
101 This option allows to assign an offset to each vsock number to
102 resolve this.
103
104 This is a 32bit number. The lowest possible vsock number is `3`
105 (i.e. with the lowest node number being `1`, this is 2+1).
106 '';
107 };
108 };
109
110 node.type = mkOption {
111 type = types.raw;
112 default = baseOS.type;
113 internal = true;
114 };
115
116 nodes = mkOption {
117 type = types.lazyAttrsOf config.node.type;
118 visible = "shallow";
119 description = ''
120 An attribute set of NixOS configuration modules.
121
122 The configurations are augmented by the [`defaults`](#test-opt-defaults) option.
123
124 They are assigned network addresses according to the `nixos/lib/testing/network.nix` module.
125
126 A few special options are available, that aren't in a plain NixOS configuration. See [Configuring the nodes](#sec-nixos-test-nodes)
127 '';
128 };
129
130 defaults = mkOption {
131 description = ''
132 NixOS configuration that is applied to all [{option}`nodes`](#test-opt-nodes).
133 '';
134 type = types.deferredModule;
135 default = { };
136 };
137
138 extraBaseModules = mkOption {
139 description = ''
140 NixOS configuration that, like [{option}`defaults`](#test-opt-defaults), is applied to all [{option}`nodes`](#test-opt-nodes) and can not be undone with [`specialisation.<name>.inheritParentConfig`](https://search.nixos.org/options?show=specialisation.%3Cname%3E.inheritParentConfig&from=0&size=50&sort=relevance&type=packages&query=specialisation).
141 '';
142 type = types.deferredModule;
143 default = { };
144 };
145
146 node.pkgs = mkOption {
147 description = ''
148 The Nixpkgs to use for the nodes.
149
150 Setting this will make the `nixpkgs.*` options read-only, to avoid mistakenly testing with a Nixpkgs configuration that diverges from regular use.
151 '';
152 type = types.nullOr types.pkgs;
153 default = null;
154 defaultText = literalMD ''
155 `null`, so construct `pkgs` according to the `nixpkgs.*` options as usual.
156 '';
157 };
158
159 node.pkgsReadOnly = mkOption {
160 description = ''
161 Whether to make the `nixpkgs.*` options read-only. This is only relevant when [`node.pkgs`](#test-opt-node.pkgs) is set.
162
163 Set this to `false` when any of the [`nodes`](#test-opt-nodes) needs to configure any of the `nixpkgs.*` options. This will slow down evaluation of your test a bit.
164 '';
165 type = types.bool;
166 default = config.node.pkgs != null;
167 defaultText = literalExpression ''node.pkgs != null'';
168 };
169
170 node.specialArgs = mkOption {
171 type = types.lazyAttrsOf types.raw;
172 default = { };
173 description = ''
174 An attribute set of arbitrary values that will be made available as module arguments during the resolution of module `imports`.
175
176 Note that it is not possible to override these from within the NixOS configurations. If you argument is not relevant to `imports`, consider setting {option}`defaults._module.args.<name>` instead.
177 '';
178 };
179
180 nodesCompat = mkOption {
181 internal = true;
182 description = ''
183 Basically `_module.args.nodes`, but with backcompat and warnings added.
184
185 This will go away.
186 '';
187 };
188 };
189
190 config = {
191 _module.args.nodes = config.nodesCompat;
192 nodesCompat = mapAttrs (
193 name: config:
194 config
195 // {
196 config =
197 lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2211)
198 "Module argument `nodes.${name}.config` is deprecated. Use `nodes.${name}` instead."
199 config;
200 }
201 ) config.nodes;
202
203 passthru.nodes = config.nodesCompat;
204
205 extraDriverArgs = mkIf config.sshBackdoor.enable [
206 "--dump-vsocks=${toString config.sshBackdoor.vsockOffset}"
207 ];
208
209 defaults = mkMerge [
210 (mkIf config.node.pkgsReadOnly {
211 nixpkgs.pkgs = config.node.pkgs;
212 imports = [ ../../modules/misc/nixpkgs/read-only.nix ];
213 })
214 (mkIf config.sshBackdoor.enable (
215 let
216 inherit (config.sshBackdoor) vsockOffset;
217 in
218 { config, ... }:
219 {
220 services.openssh = {
221 enable = true;
222 settings = {
223 PermitRootLogin = "yes";
224 PermitEmptyPasswords = "yes";
225 };
226 };
227
228 security.pam.services.sshd = {
229 allowNullPassword = true;
230 };
231
232 virtualisation.qemu.options = [
233 "-device vhost-vsock-pci,guest-cid=${
234 toString (config.virtualisation.test.nodeNumber + vsockOffset)
235 }"
236 ];
237 }
238 ))
239 ];
240
241 # Docs: nixos/doc/manual/development/writing-nixos-tests.section.md
242 /**
243 See https://nixos.org/manual/nixos/unstable#sec-override-nixos-test
244 */
245 passthru.extendNixOS =
246 {
247 module,
248 specialArgs ? { },
249 }:
250 config.passthru.extend {
251 modules = [
252 {
253 extraBaseModules = module;
254 node.specialArgs = mkOneUp options.node.specialArgs (_: specialArgs);
255 }
256 ];
257 };
258
259 };
260}