diff options
author | Alyssa Ross <hi@alyssa.is> | 2022-03-30 13:30:47 +0000 |
---|---|---|
committer | Alyssa Ross <hi@alyssa.is> | 2022-03-31 10:13:20 +0000 |
commit | f2e61678de300336b3666afd19af7565efb0c4cf (patch) | |
tree | 49f6906c9d557f7fdd58257ff85ec17fc4495f31 /nixpkgs/nixos/lib | |
parent | f920d5e07c29a9aa1b77d9b88bd604cf1a1f3664 (diff) | |
parent | 00e27c78d3d2de6964096ceee8d70e5b487365e3 (diff) | |
download | nixlib-f2e61678de300336b3666afd19af7565efb0c4cf.tar nixlib-f2e61678de300336b3666afd19af7565efb0c4cf.tar.gz nixlib-f2e61678de300336b3666afd19af7565efb0c4cf.tar.bz2 nixlib-f2e61678de300336b3666afd19af7565efb0c4cf.tar.lz nixlib-f2e61678de300336b3666afd19af7565efb0c4cf.tar.xz nixlib-f2e61678de300336b3666afd19af7565efb0c4cf.tar.zst nixlib-f2e61678de300336b3666afd19af7565efb0c4cf.zip |
Merge commit '00e27c78d3d2de6964096ceee8d70e5b487365e3'
Conflicts: nixpkgs/nixos/modules/system/boot/systemd.nix nixpkgs/pkgs/applications/networking/browsers/firefox/common.nix nixpkgs/pkgs/applications/version-management/git-and-tools/cgit/common.nix nixpkgs/pkgs/applications/version-management/git-and-tools/cgit/default.nix nixpkgs/pkgs/applications/version-management/git-and-tools/cgit/pink.nix nixpkgs/pkgs/top-level/all-packages.nix
Diffstat (limited to 'nixpkgs/nixos/lib')
-rw-r--r-- | nixpkgs/nixos/lib/make-options-doc/mergeJSON.py | 9 | ||||
-rw-r--r-- | nixpkgs/nixos/lib/systemd-lib.nix | 212 | ||||
-rw-r--r-- | nixpkgs/nixos/lib/test-driver/test_driver/driver.py | 1 | ||||
-rw-r--r-- | nixpkgs/nixos/lib/test-driver/test_driver/machine.py | 30 | ||||
-rw-r--r-- | nixpkgs/nixos/lib/testing-python.nix | 30 |
5 files changed, 260 insertions, 22 deletions
diff --git a/nixpkgs/nixos/lib/make-options-doc/mergeJSON.py b/nixpkgs/nixos/lib/make-options-doc/mergeJSON.py index 029787a31586..8e2ea322dc89 100644 --- a/nixpkgs/nixos/lib/make-options-doc/mergeJSON.py +++ b/nixpkgs/nixos/lib/make-options-doc/mergeJSON.py @@ -66,14 +66,21 @@ for (k, v) in overrides.items(): elif ov is not None or cur.get(ok, None) is None: cur[ok] = ov +severity = "error" if warningsAreErrors else "warning" + # check that every option has a description hasWarnings = False for (k, v) in options.items(): if v.value.get('description', None) is None: - severity = "error" if warningsAreErrors else "warning" hasWarnings = True print(f"\x1b[1;31m{severity}: option {v.name} has no description\x1b[0m", file=sys.stderr) v.value['description'] = "This option has no description." + if v.value.get('type', "unspecified") == "unspecified": + hasWarnings = True + print( + f"\x1b[1;31m{severity}: option {v.name} has no type. Please specify a valid type, see " + + "https://nixos.org/manual/nixos/stable/index.html#sec-option-types\x1b[0m", file=sys.stderr) + if hasWarnings and warningsAreErrors: print( "\x1b[1;31m" + diff --git a/nixpkgs/nixos/lib/systemd-lib.nix b/nixpkgs/nixos/lib/systemd-lib.nix index ab166d7327ce..37900b0b16f6 100644 --- a/nixpkgs/nixos/lib/systemd-lib.nix +++ b/nixpkgs/nixos/lib/systemd-lib.nix @@ -5,6 +5,7 @@ with lib; let cfg = config.systemd; lndir = "${pkgs.buildPackages.xorg.lndir}/bin/lndir"; + systemd = cfg.package; in rec { shellEscape = s: (replaceChars [ "\\" ] [ "\\\\" ] s); @@ -22,8 +23,9 @@ in rec { inherit (unit) text; } '' - mkdir -p $out - echo -n "$text" > $out/${shellEscape name} + name=${shellEscape name} + mkdir -p "$out/$(dirname "$name")" + echo -n "$text" > "$out/$name" '' else pkgs.runCommand "unit-${mkPathSafeName name}-disabled" @@ -31,8 +33,9 @@ in rec { allowSubstitutes = false; } '' - mkdir -p $out - ln -s /dev/null $out/${shellEscape name} + name=${shellEscape name} + mkdir -p "$out/$(dirname "$name")" + ln -s /dev/null "$out/$name" ''; boolValues = [true false "yes" "no"]; @@ -235,4 +238,205 @@ in rec { ''} ''; # */ + makeJobScript = name: text: + let + scriptName = replaceChars [ "\\" "@" ] [ "-" "_" ] (shellEscape name); + out = (pkgs.writeShellScriptBin scriptName '' + set -e + ${text} + '').overrideAttrs (_: { + # The derivation name is different from the script file name + # to keep the script file name short to avoid cluttering logs. + name = "unit-script-${scriptName}"; + }); + in "${out}/bin/${scriptName}"; + + unitConfig = { config, options, ... }: { + config = { + unitConfig = + optionalAttrs (config.requires != []) + { Requires = toString config.requires; } + // optionalAttrs (config.wants != []) + { Wants = toString config.wants; } + // optionalAttrs (config.after != []) + { After = toString config.after; } + // optionalAttrs (config.before != []) + { Before = toString config.before; } + // optionalAttrs (config.bindsTo != []) + { BindsTo = toString config.bindsTo; } + // optionalAttrs (config.partOf != []) + { PartOf = toString config.partOf; } + // optionalAttrs (config.conflicts != []) + { Conflicts = toString config.conflicts; } + // optionalAttrs (config.requisite != []) + { Requisite = toString config.requisite; } + // optionalAttrs (config.restartTriggers != []) + { X-Restart-Triggers = toString config.restartTriggers; } + // optionalAttrs (config.reloadTriggers != []) + { X-Reload-Triggers = toString config.reloadTriggers; } + // optionalAttrs (config.description != "") { + Description = config.description; } + // optionalAttrs (config.documentation != []) { + Documentation = toString config.documentation; } + // optionalAttrs (config.onFailure != []) { + OnFailure = toString config.onFailure; } + // optionalAttrs (options.startLimitIntervalSec.isDefined) { + StartLimitIntervalSec = toString config.startLimitIntervalSec; + } // optionalAttrs (options.startLimitBurst.isDefined) { + StartLimitBurst = toString config.startLimitBurst; + }; + }; + }; + + serviceConfig = { name, config, ... }: { + config = mkMerge + [ { # Default path for systemd services. Should be quite minimal. + path = mkAfter + [ pkgs.coreutils + pkgs.findutils + pkgs.gnugrep + pkgs.gnused + systemd + ]; + environment.PATH = "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}"; + } + (mkIf (config.preStart != "") + { serviceConfig.ExecStartPre = + [ (makeJobScript "${name}-pre-start" config.preStart) ]; + }) + (mkIf (config.script != "") + { serviceConfig.ExecStart = + makeJobScript "${name}-start" config.script + " " + config.scriptArgs; + }) + (mkIf (config.postStart != "") + { serviceConfig.ExecStartPost = + [ (makeJobScript "${name}-post-start" config.postStart) ]; + }) + (mkIf (config.reload != "") + { serviceConfig.ExecReload = + makeJobScript "${name}-reload" config.reload; + }) + (mkIf (config.preStop != "") + { serviceConfig.ExecStop = + makeJobScript "${name}-pre-stop" config.preStop; + }) + (mkIf (config.postStop != "") + { serviceConfig.ExecStopPost = + makeJobScript "${name}-post-stop" config.postStop; + }) + ]; + }; + + mountConfig = { config, ... }: { + config = { + mountConfig = + { What = config.what; + Where = config.where; + } // optionalAttrs (config.type != "") { + Type = config.type; + } // optionalAttrs (config.options != "") { + Options = config.options; + }; + }; + }; + + automountConfig = { config, ... }: { + config = { + automountConfig = + { Where = config.where; + }; + }; + }; + + commonUnitText = def: '' + [Unit] + ${attrsToSection def.unitConfig} + ''; + + targetToUnit = name: def: + { inherit (def) aliases wantedBy requiredBy enable; + text = + '' + [Unit] + ${attrsToSection def.unitConfig} + ''; + }; + + serviceToUnit = name: def: + { inherit (def) aliases wantedBy requiredBy enable; + text = commonUnitText def + + '' + [Service] + ${let env = cfg.globalEnvironment // def.environment; + in concatMapStrings (n: + let s = optionalString (env.${n} != null) + "Environment=${builtins.toJSON "${n}=${env.${n}}"}\n"; + # systemd max line length is now 1MiB + # https://github.com/systemd/systemd/commit/e6dde451a51dc5aaa7f4d98d39b8fe735f73d2af + in if stringLength s >= 1048576 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${name}.service’ is too long." else s) (attrNames env)} + ${if def.reloadIfChanged then '' + X-ReloadIfChanged=true + '' else if !def.restartIfChanged then '' + X-RestartIfChanged=false + '' else ""} + ${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"} + ${attrsToSection def.serviceConfig} + ''; + }; + + socketToUnit = name: def: + { inherit (def) aliases wantedBy requiredBy enable; + text = commonUnitText def + + '' + [Socket] + ${attrsToSection def.socketConfig} + ${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)} + ${concatStringsSep "\n" (map (s: "ListenDatagram=${s}") def.listenDatagrams)} + ''; + }; + + timerToUnit = name: def: + { inherit (def) aliases wantedBy requiredBy enable; + text = commonUnitText def + + '' + [Timer] + ${attrsToSection def.timerConfig} + ''; + }; + + pathToUnit = name: def: + { inherit (def) aliases wantedBy requiredBy enable; + text = commonUnitText def + + '' + [Path] + ${attrsToSection def.pathConfig} + ''; + }; + + mountToUnit = name: def: + { inherit (def) aliases wantedBy requiredBy enable; + text = commonUnitText def + + '' + [Mount] + ${attrsToSection def.mountConfig} + ''; + }; + + automountToUnit = name: def: + { inherit (def) aliases wantedBy requiredBy enable; + text = commonUnitText def + + '' + [Automount] + ${attrsToSection def.automountConfig} + ''; + }; + + sliceToUnit = name: def: + { inherit (def) aliases wantedBy requiredBy enable; + text = commonUnitText def + + '' + [Slice] + ${attrsToSection def.sliceConfig} + ''; + }; } diff --git a/nixpkgs/nixos/lib/test-driver/test_driver/driver.py b/nixpkgs/nixos/lib/test-driver/test_driver/driver.py index 880b1c5fdec0..0e5f013193fe 100644 --- a/nixpkgs/nixos/lib/test-driver/test_driver/driver.py +++ b/nixpkgs/nixos/lib/test-driver/test_driver/driver.py @@ -55,6 +55,7 @@ class Driver: tmp_dir = get_tmp_dir() with rootlog.nested("start all VLans"): + vlans = list(set(vlans)) self.vlans = [VLan(nr, tmp_dir) for nr in vlans] def cmd(scripts: List[str]) -> Iterator[NixStartScript]: diff --git a/nixpkgs/nixos/lib/test-driver/test_driver/machine.py b/nixpkgs/nixos/lib/test-driver/test_driver/machine.py index 569a0f3c61e4..f3e615fe5bf9 100644 --- a/nixpkgs/nixos/lib/test-driver/test_driver/machine.py +++ b/nixpkgs/nixos/lib/test-driver/test_driver/machine.py @@ -198,7 +198,7 @@ class StartCommand: ) -> subprocess.Popen: return subprocess.Popen( self.cmd(monitor_socket_path, shell_socket_path), - stdin=subprocess.DEVNULL, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, @@ -558,6 +558,28 @@ class Machine: pass_fds=[self.shell.fileno()], ) + def console_interact(self) -> None: + """Allows you to interact with QEMU's stdin + + The shell can be exited with Ctrl+D. Note that Ctrl+C is not allowed to be used. + QEMU's stdout is read line-wise. + + Should only be used during test development, not in the production test.""" + self.log("Terminal is ready (there is no prompt):") + + assert self.process + assert self.process.stdin + + while True: + try: + char = sys.stdin.buffer.read(1) + except KeyboardInterrupt: + break + if char == b"": # ctrl+d + self.log("Closing connection to the console") + break + self.send_console(char.decode()) + def succeed(self, *commands: str, timeout: Optional[int] = None) -> str: """Execute each command and check that it succeeds.""" output = "" @@ -834,6 +856,12 @@ class Machine: self.send_monitor_command("sendkey {}".format(key)) time.sleep(0.01) + def send_console(self, chars: str) -> None: + assert self.process + assert self.process.stdin + self.process.stdin.write(chars.encode()) + self.process.stdin.flush() + def start(self) -> None: if self.booted: return diff --git a/nixpkgs/nixos/lib/testing-python.nix b/nixpkgs/nixos/lib/testing-python.nix index 0d3c3a89e783..facc7a253a75 100644 --- a/nixpkgs/nixos/lib/testing-python.nix +++ b/nixpkgs/nixos/lib/testing-python.nix @@ -146,26 +146,28 @@ rec { # Make a full-blown test makeTest = - { testScript + { machine ? null + , nodes ? {} + , testScript , enableOCR ? false , name ? "unnamed" # Skip linting (mainly intended for faster dev cycles) , skipLint ? false , passthru ? {} + , meta ? {} , # For meta.position pos ? # position used in error messages and for meta.position - (if t.meta.description or null != null - then builtins.unsafeGetAttrPos "description" t.meta + (if meta.description or null != null + then builtins.unsafeGetAttrPos "description" meta else builtins.unsafeGetAttrPos "testScript" t) - , ... } @ t: let - nodes = qemu_pkg: + mkNodes = qemu_pkg: let testScript' = # Call the test script with the computed nodes. if lib.isFunction testScript - then testScript { nodes = nodes qemu_pkg; } + then testScript { nodes = mkNodes qemu_pkg; } else testScript; build-vms = import ./build-vms.nix { @@ -205,33 +207,29 @@ rec { }; in build-vms.buildVirtualNetwork ( - t.nodes or (if t ? machine then { machine = t.machine; } else { }) + nodes // lib.optionalAttrs (machine != null) { inherit machine; } ); driver = setupDriverForTest { inherit testScript enableOCR skipLint passthru; testName = name; qemu_pkg = pkgs.qemu_test; - nodes = nodes pkgs.qemu_test; + nodes = mkNodes pkgs.qemu_test; }; driverInteractive = setupDriverForTest { inherit testScript enableOCR skipLint passthru; testName = name; qemu_pkg = pkgs.qemu; - nodes = nodes pkgs.qemu; + nodes = mkNodes pkgs.qemu; interactive = true; }; - test = - let - passMeta = drv: drv // lib.optionalAttrs (t ? meta) { - meta = (drv.meta or { }) // t.meta; - }; - in passMeta (runTests { inherit driver pos driverInteractive; }); + test = lib.addMetaAttrs meta (runTests { inherit driver pos driverInteractive; }); in test // { - inherit test driver driverInteractive nodes; + inherit test driver driverInteractive; + inherit (driver) nodes; }; abortForFunction = functionName: abort ''The ${functionName} function was |