diff options
Diffstat (limited to 'nixos')
92 files changed, 2464 insertions, 1172 deletions
diff --git a/nixos/doc/manual/configuration/x-windows.xml b/nixos/doc/manual/configuration/x-windows.xml index 9206f43ea392..55ad9fe6e653 100644 --- a/nixos/doc/manual/configuration/x-windows.xml +++ b/nixos/doc/manual/configuration/x-windows.xml @@ -83,8 +83,7 @@ desktop environment. If you wanted no desktop environment and i3 as your your window manager, you'd define: <programlisting> -<xref linkend="opt-services.xserver.desktopManager.default"/> = "none"; -<xref linkend="opt-services.xserver.windowManager.default"/> = "i3"; +<xref linkend="opt-services.xserver.displayManager.defaultSession"/> = "none+i3"; </programlisting> And, finally, to enable auto-login for a user <literal>johndoe</literal>: <programlisting> diff --git a/nixos/doc/manual/configuration/xfce.xml b/nixos/doc/manual/configuration/xfce.xml index 6ac99c6b2bee..027828bb936d 100644 --- a/nixos/doc/manual/configuration/xfce.xml +++ b/nixos/doc/manual/configuration/xfce.xml @@ -7,9 +7,8 @@ <para> To enable the Xfce Desktop Environment, set <programlisting> -<link linkend="opt-services.xserver.desktopManager.default">services.xserver.desktopManager</link> = { - <link linkend="opt-services.xserver.desktopManager.xfce.enable">xfce.enable</link> = true; - <link linkend="opt-services.xserver.desktopManager.default">default</link> = "xfce"; +<xref linkend="opt-services.xserver.desktopManager.xfce.enable" /> = true; +<xref linkend="opt-services.xserver.displayManager.defaultSession" /> = "xfce"; }; </programlisting> </para> diff --git a/nixos/doc/manual/man-nixos-option.xml b/nixos/doc/manual/man-nixos-option.xml index beabf020c92a..b82f31256099 100644 --- a/nixos/doc/manual/man-nixos-option.xml +++ b/nixos/doc/manual/man-nixos-option.xml @@ -119,4 +119,13 @@ Defined by: bug, please report to Nicolas Pierron. </para> </refsection> + <refsection> + <title>See also</title> + <para> + <citerefentry> + <refentrytitle>configuration.nix</refentrytitle> + <manvolnum>5</manvolnum> + </citerefentry> + </para> + </refsection> </refentry> diff --git a/nixos/doc/manual/release-notes/rl-2003.xml b/nixos/doc/manual/release-notes/rl-2003.xml index 579b8d537444..5744de96b74b 100644 --- a/nixos/doc/manual/release-notes/rl-2003.xml +++ b/nixos/doc/manual/release-notes/rl-2003.xml @@ -55,6 +55,19 @@ and adding a <option>--all</option> option which prints all options and their values. </para> </listitem> + <listitem> + <para> + <option>services.xserver.desktopManager.default</option> and <option>services.xserver.windowManager.default</option> options were replaced by a single <xref linkend="opt-services.xserver.displayManager.defaultSession"/> option to improve support for upstream session files. If you used something like: +<programlisting> +services.xserver.desktopManager.default = "xfce"; +services.xserver.windowManager.default = "icewm"; +</programlisting> + you should change it to: +<programlisting> +services.xserver.displayManager.defaultSession = "xfce+icewm"; +</programlisting> + </para> + </listitem> </itemizedlist> </section> @@ -127,18 +140,18 @@ </listitem> <listitem> <para> - The <literal>99-main.network</literal> file was removed. Maching all - network interfaces caused many breakages, see - <link xlink:href="https://github.com/NixOS/nixpkgs/pull/18962">#18962</link> - and <link xlink:href="https://github.com/NixOS/nixpkgs/pull/71106">#71106</link>. + The <literal>99-main.network</literal> file was removed. Maching all + network interfaces caused many breakages, see + <link xlink:href="https://github.com/NixOS/nixpkgs/pull/18962">#18962</link> + and <link xlink:href="https://github.com/NixOS/nixpkgs/pull/71106">#71106</link>. </para> <para> - We already don't support the global <link linkend="opt-networking.useDHCP">networking.useDHCP</link>, - <link linkend="opt-networking.defaultGateway">networking.defaultGateway</link> and - <link linkend="opt-networking.defaultGateway6">networking.defaultGateway6</link> options - if <link linkend="opt-networking.useNetworkd">networking.useNetworkd</link> is enabled, - but direct users to configure the per-device - <link linkend="opt-networking.interfaces">networking.interfaces.<name>.…</link> options. + We already don't support the global <link linkend="opt-networking.useDHCP">networking.useDHCP</link>, + <link linkend="opt-networking.defaultGateway">networking.defaultGateway</link> and + <link linkend="opt-networking.defaultGateway6">networking.defaultGateway6</link> options + if <link linkend="opt-networking.useNetworkd">networking.useNetworkd</link> is enabled, + but direct users to configure the per-device + <link linkend="opt-networking.interfaces">networking.interfaces.<name>.…</link> options. </para> </listitem> <listitem> @@ -204,6 +217,14 @@ The <literal>buildRustCrate</literal> infrastructure now produces <literal>lib</literal> outputs in addition to the <literal>out</literal> output. This has led to drastically reduced closed sizes for some rust crates since development dependencies are now in the <literal>lib</literal> output. </para> + </listitem> + <listitem> + <para> + Pango was upgraded to 1.44, which no longer uses freetype for font loading. This means that type1 + and bitmap fonts are no longer supported in applications relying on Pango for font rendering + (notably, GTK application). See <link xlink:href="https://gitlab.gnome.org/GNOME/pango/issues/386"> + upstream issue</link> for more information. + </para> </listitem> <listitem> <para> @@ -235,6 +256,65 @@ choices (whether to perform the action as themselves with wheel permissions, or as the root user). </para> </listitem> + <listitem> + <para> + NixOS containers no longer build NixOS manual by default. This saves evaluation time, + especially if there are many declarative containers defined. Note that this is already done + when <literal><nixos/modules/profiles/minimal.nix></literal> module is included + in container config. + </para> + </listitem> + <listitem> + <para> + Virtual console options have been reorganized and can be found under + a single top-level attribute: <literal>console</literal>. + The full set of changes is as follows: + </para> + <itemizedlist> + <listitem> + <para> + <literal>i18n.consoleFont</literal> renamed to + <link linkend="opt-console.font">console.font</link> + </para> + </listitem> + <listitem> + <para> + <literal>i18n.consoleKeyMap</literal> renamed to + <link linkend="opt-console.keyMap">console.keyMap</link> + </para> + </listitem> + <listitem> + <para> + <literal>i18n.consoleColors</literal> renamed to + <link linkend="opt-console.colors">console.colors</link> + </para> + </listitem> + <listitem> + <para> + <literal>i18n.consolePackages</literal> renamed to + <link linkend="opt-console.packages">console.packages</link> + </para> + </listitem> + <listitem> + <para> + <literal>i18n.consoleUseXkbConfig</literal> renamed to + <link linkend="opt-console.useXkbConfig">console.useXkbConfig</link> + </para> + </listitem> + <listitem> + <para> + <literal>boot.earlyVconsoleSetup</literal> renamed to + <link linkend="opt-console.earlySetup">console.earlySetup</link> + </para> + </listitem> + <listitem> + <para> + <literal>boot.extraTTYs</literal> renamed to + <link linkend="opt-console.extraTTYs">console.extraTTYs</link> + </para> + </listitem> + </itemizedlist> + </listitem> </itemizedlist> </section> @@ -251,6 +331,18 @@ </listitem> <listitem> <para> + The nginx web server previously started its master process as root + privileged, then ran worker processes as a less privileged identity user. + This was changed to start all of nginx as a less privileged user (defined by + <literal>services.nginx.user</literal> and + <literal>services.nginx.group</literal>). As a consequence, all files that + are needed for nginx to run (included configuration fragments, SSL + certificates and keys, etc.) must now be readable by this less privileged + user/group. + </para> + </listitem> + <listitem> + <para> OpenSSH has been upgraded from 7.9 to 8.1, improving security and adding features but with potential incompatibilities. Consult the <link xlink:href="https://www.openssh.com/txt/release-8.1"> diff --git a/nixos/lib/make-ext4-fs.nix b/nixos/lib/make-ext4-fs.nix index 932adcd97967..f46d3990c06b 100644 --- a/nixos/lib/make-ext4-fs.nix +++ b/nixos/lib/make-ext4-fs.nix @@ -4,8 +4,11 @@ # generated image is sized to only fit its contents, with the expectation # that a script resizes the filesystem at boot time. { pkgs +, lib # List of derivations to be included , storePaths +# Whether or not to compress the resulting image with zstd +, compressImage ? false, zstd # Shell commands to populate the ./files directory. # All files in that directory are copied to the root of the FS. , populateImageCommands ? "" @@ -20,18 +23,20 @@ let sdClosureInfo = pkgs.buildPackages.closureInfo { rootPaths = storePaths; }; in - pkgs.stdenv.mkDerivation { - name = "ext4-fs.img"; + name = "ext4-fs.img${lib.optionalString compressImage ".zst"}"; - nativeBuildInputs = [e2fsprogs.bin libfaketime perl lkl]; + nativeBuildInputs = [ e2fsprogs.bin libfaketime perl lkl ] + ++ lib.optional compressImage zstd; buildCommand = '' + ${if compressImage then "img=temp.img" else "img=$out"} ( mkdir -p ./files ${populateImageCommands} ) + # Add the closures of the top-level store objects. storePaths=$(cat ${sdClosureInfo}/store-paths) @@ -42,28 +47,26 @@ pkgs.stdenv.mkDerivation { bytes=$((2 * 4096 * $numInodes + 4096 * $numDataBlocks)) echo "Creating an EXT4 image of $bytes bytes (numInodes=$numInodes, numDataBlocks=$numDataBlocks)" - truncate -s $bytes $out - faketime -f "1970-01-01 00:00:01" mkfs.ext4 -L ${volumeLabel} -U ${uuid} $out + truncate -s $bytes $img + faketime -f "1970-01-01 00:00:01" mkfs.ext4 -L ${volumeLabel} -U ${uuid} $img # Also include a manifest of the closures in a format suitable for nix-store --load-db. cp ${sdClosureInfo}/registration nix-path-registration - cptofs -t ext4 -i $out nix-path-registration / + cptofs -t ext4 -i $img nix-path-registration / # Create nix/store before copying paths faketime -f "1970-01-01 00:00:01" mkdir -p nix/store - cptofs -t ext4 -i $out nix / + cptofs -t ext4 -i $img nix / echo "copying store paths to image..." - cptofs -t ext4 -i $out $storePaths /nix/store/ + cptofs -t ext4 -i $img $storePaths /nix/store/ - ( echo "copying files to image..." - cd ./files - cptofs -t ext4 -i $out ./* / - ) + cptofs -t ext4 -i $img ./files/* / + # I have ended up with corrupted images sometimes, I suspect that happens when the build machine's disk gets full during the build. - if ! fsck.ext4 -n -f $out; then + if ! fsck.ext4 -n -f $img; then echo "--- Fsck failed for EXT4 image of $bytes bytes (numInodes=$numInodes, numDataBlocks=$numDataBlocks) ---" cat errorlog return 1 @@ -71,9 +74,9 @@ pkgs.stdenv.mkDerivation { ( # Resizes **snugly** to its actual limits (or closer to) - free=$(dumpe2fs $out | grep '^Free blocks:') - blocksize=$(dumpe2fs $out | grep '^Block size:') - blocks=$(dumpe2fs $out | grep '^Block count:') + free=$(dumpe2fs $img | grep '^Free blocks:') + blocksize=$(dumpe2fs $img | grep '^Block size:') + blocks=$(dumpe2fs $img | grep '^Block count:') blocks=$((''${blocks##*:})) # format the number. blocksize=$((''${blocksize##*:})) # format the number. # System can't boot with 0 blocks free. @@ -82,10 +85,15 @@ pkgs.stdenv.mkDerivation { size=$(( blocks - ''${free##*:} + fudge )) echo "Resizing from $blocks blocks to $size blocks. (~ $((size*blocksize/1024/1024))MiB)" - EXT2FS_NO_MTAB_OK=yes resize2fs $out -f $size + EXT2FS_NO_MTAB_OK=yes resize2fs $img -f $size ) # And a final fsck, because of the previous truncating. - fsck.ext4 -n -f $out + fsck.ext4 -n -f $img + + if [ ${builtins.toString compressImage} ]; then + echo "Compressing image" + zstd -v --no-progress ./$img -o $out + fi ''; } diff --git a/nixos/lib/testing-python.nix b/nixos/lib/testing-python.nix index d567d2687653..c4eb9328b1db 100644 --- a/nixos/lib/testing-python.nix +++ b/nixos/lib/testing-python.nix @@ -262,9 +262,8 @@ in rec { virtualisation.memorySize = 1024; services.xserver.enable = true; services.xserver.displayManager.auto.enable = true; - services.xserver.windowManager.default = "icewm"; + services.xserver.displayManager.defaultSession = "none+icewm"; services.xserver.windowManager.icewm.enable = true; - services.xserver.desktopManager.default = "none"; }; in runInMachine ({ diff --git a/nixos/lib/testing.nix b/nixos/lib/testing.nix index a5f060a8d8e3..ae8ecd6270ce 100644 --- a/nixos/lib/testing.nix +++ b/nixos/lib/testing.nix @@ -249,9 +249,8 @@ in rec { virtualisation.memorySize = 1024; services.xserver.enable = true; services.xserver.displayManager.auto.enable = true; - services.xserver.windowManager.default = "icewm"; + services.xserver.displayManager.defaultSession = "none+icewm"; services.xserver.windowManager.icewm.enable = true; - services.xserver.desktopManager.default = "none"; }; in runInMachine ({ diff --git a/nixos/modules/config/console.nix b/nixos/modules/config/console.nix new file mode 100644 index 000000000000..f662ed62d31d --- /dev/null +++ b/nixos/modules/config/console.nix @@ -0,0 +1,203 @@ + +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.console; + + makeColor = i: concatMapStringsSep "," (x: "0x" + substring (2*i) 2 x); + + isUnicode = hasSuffix "UTF-8" (toUpper config.i18n.defaultLocale); + + optimizedKeymap = pkgs.runCommand "keymap" { + nativeBuildInputs = [ pkgs.buildPackages.kbd ]; + LOADKEYS_KEYMAP_PATH = "${consoleEnv}/share/keymaps/**"; + preferLocalBuild = true; + } '' + loadkeys -b ${optionalString isUnicode "-u"} "${cfg.keyMap}" > $out + ''; + + # Sadly, systemd-vconsole-setup doesn't support binary keymaps. + vconsoleConf = pkgs.writeText "vconsole.conf" '' + KEYMAP=${cfg.keyMap} + FONT=${cfg.font} + ''; + + consoleEnv = pkgs.buildEnv { + name = "console-env"; + paths = [ pkgs.kbd ] ++ cfg.packages; + pathsToLink = [ + "/share/consolefonts" + "/share/consoletrans" + "/share/keymaps" + "/share/unimaps" + ]; + }; + + setVconsole = !config.boot.isContainer; +in + +{ + ###### interface + + options.console = { + font = mkOption { + type = types.str; + default = "Lat2-Terminus16"; + example = "LatArCyrHeb-16"; + description = '' + The font used for the virtual consoles. Leave empty to use + whatever the <command>setfont</command> program considers the + default font. + ''; + }; + + keyMap = mkOption { + type = with types; either str path; + default = "us"; + example = "fr"; + description = '' + The keyboard mapping table for the virtual consoles. + ''; + }; + + colors = mkOption { + type = types.listOf types.str; + default = []; + example = [ + "002b36" "dc322f" "859900" "b58900" + "268bd2" "d33682" "2aa198" "eee8d5" + "002b36" "cb4b16" "586e75" "657b83" + "839496" "6c71c4" "93a1a1" "fdf6e3" + ]; + description = '' + The 16 colors palette used by the virtual consoles. + Leave empty to use the default colors. + Colors must be in hexadecimal format and listed in + order from color 0 to color 15. + ''; + + }; + + packages = mkOption { + type = types.listOf types.package; + default = with pkgs.kbdKeymaps; [ dvp neo ]; + defaultText = ''with pkgs.kbdKeymaps; [ dvp neo ]''; + description = '' + List of additional packages that provide console fonts, keymaps and + other resources for virtual consoles use. + ''; + }; + + extraTTYs = mkOption { + default = []; + type = types.listOf types.str; + example = ["tty8" "tty9"]; + description = '' + TTY (virtual console) devices, in addition to the consoles on + which mingetty and syslogd run, that must be initialised. + Only useful if you have some program that you want to run on + some fixed console. For example, the NixOS installation CD + opens the manual in a web browser on console 7, so it sets + <option>console.extraTTYs</option> to <literal>["tty7"]</literal>. + ''; + }; + + useXkbConfig = mkOption { + type = types.bool; + default = false; + description = '' + If set, configure the virtual console keymap from the xserver + keyboard settings. + ''; + }; + + earlySetup = mkOption { + default = false; + type = types.bool; + description = '' + Enable setting virtual console options as early as possible (in initrd). + ''; + }; + + }; + + + ###### implementation + + config = mkMerge [ + { console.keyMap = with config.services.xserver; + mkIf cfg.useXkbConfig + (pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } '' + '${pkgs.ckbcomp}/bin/ckbcomp' -model '${xkbModel}' -layout '${layout}' \ + -option '${xkbOptions}' -variant '${xkbVariant}' > "$out" + ''); + } + + (mkIf (!setVconsole) { + systemd.services.systemd-vconsole-setup.enable = false; + }) + + (mkIf setVconsole (mkMerge [ + { environment.systemPackages = [ pkgs.kbd ]; + + # Let systemd-vconsole-setup.service do the work of setting up the + # virtual consoles. + environment.etc."vconsole.conf".source = vconsoleConf; + # Provide kbd with additional packages. + environment.etc.kbd.source = "${consoleEnv}/share"; + + boot.initrd.preLVMCommands = mkBefore '' + kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console + printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console + loadkmap < ${optimizedKeymap} + + ${optionalString cfg.earlySetup '' + setfont -C /dev/console $extraUtils/share/consolefonts/font.psf + ''} + ''; + + systemd.services.systemd-vconsole-setup = + { before = [ "display-manager.service" ]; + after = [ "systemd-udev-settle.service" ]; + restartTriggers = [ vconsoleConf consoleEnv ]; + }; + } + + (mkIf (cfg.colors != []) { + boot.kernelParams = [ + "vt.default_red=${makeColor 0 cfg.colors}" + "vt.default_grn=${makeColor 1 cfg.colors}" + "vt.default_blu=${makeColor 2 cfg.colors}" + ]; + }) + + (mkIf cfg.earlySetup { + boot.initrd.extraUtilsCommands = '' + mkdir -p $out/share/consolefonts + ${if substring 0 1 cfg.font == "/" then '' + font="${cfg.font}" + '' else '' + font="$(echo ${consoleEnv}/share/consolefonts/${cfg.font}.*)" + ''} + if [[ $font == *.gz ]]; then + gzip -cd $font > $out/share/consolefonts/font.psf + else + cp -L $font $out/share/consolefonts/font.psf + fi + ''; + }) + ])) + ]; + + imports = [ + (mkRenamedOptionModule [ "i18n" "consoleFont" ] [ "console" "font" ]) + (mkRenamedOptionModule [ "i18n" "consoleKeyMap" ] [ "console" "keyMap" ]) + (mkRenamedOptionModule [ "i18n" "consoleColors" ] [ "console" "colors" ]) + (mkRenamedOptionModule [ "i18n" "consolePackages" ] [ "console" "packages" ]) + (mkRenamedOptionModule [ "i18n" "consoleUseXkbConfig" ] [ "console" "useXkbConfig" ]) + (mkRenamedOptionModule [ "boot" "earlyVconsoleSetup" ] [ "console" "earlySetup" ]) + (mkRenamedOptionModule [ "boot" "extraTTYs" ] [ "console" "extraTTYs" ]) + ]; +} diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix index d0db8fedecd8..45691f4839c8 100644 --- a/nixos/modules/config/i18n.nix +++ b/nixos/modules/config/i18n.nix @@ -58,62 +58,6 @@ with lib; ''; }; - consolePackages = mkOption { - type = types.listOf types.package; - default = with pkgs.kbdKeymaps; [ dvp neo ]; - defaultText = ''with pkgs.kbdKeymaps; [ dvp neo ]''; - description = '' - List of additional packages that provide console fonts, keymaps and - other resources. - ''; - }; - - consoleFont = mkOption { - type = types.str; - default = "Lat2-Terminus16"; - example = "LatArCyrHeb-16"; - description = '' - The font used for the virtual consoles. Leave empty to use - whatever the <command>setfont</command> program considers the - default font. - ''; - }; - - consoleUseXkbConfig = mkOption { - type = types.bool; - default = false; - description = '' - If set, configure the console keymap from the xserver keyboard - settings. - ''; - }; - - consoleKeyMap = mkOption { - type = with types; either str path; - default = "us"; - example = "fr"; - description = '' - The keyboard mapping table for the virtual consoles. - ''; - }; - - consoleColors = mkOption { - type = types.listOf types.str; - default = []; - example = [ - "002b36" "dc322f" "859900" "b58900" - "268bd2" "d33682" "2aa198" "eee8d5" - "002b36" "cb4b16" "586e75" "657b83" - "839496" "6c71c4" "93a1a1" "fdf6e3" - ]; - description = '' - The 16 colors palette used by the virtual consoles. - Leave empty to use the default colors. - Colors must be in hexadecimal format and listed in - order from color 0 to color 15. - ''; - }; - }; }; @@ -123,13 +67,6 @@ with lib; config = { - i18n.consoleKeyMap = with config.services.xserver; - mkIf config.i18n.consoleUseXkbConfig - (pkgs.runCommand "xkb-console-keymap" { preferLocalBuild = true; } '' - '${pkgs.ckbcomp}/bin/ckbcomp' -model '${xkbModel}' -layout '${layout}' \ - -option '${xkbOptions}' -variant '${xkbVariant}' > "$out" - ''); - environment.systemPackages = optional (config.i18n.supportedLocales != []) config.i18n.glibcLocales; diff --git a/nixos/modules/installer/cd-dvd/sd-image.nix b/nixos/modules/installer/cd-dvd/sd-image.nix index 7865b767f0b7..901c60befb6c 100644 --- a/nixos/modules/installer/cd-dvd/sd-image.nix +++ b/nixos/modules/installer/cd-dvd/sd-image.nix @@ -18,6 +18,7 @@ with lib; let rootfsImage = pkgs.callPackage ../../../lib/make-ext4-fs.nix ({ inherit (config.sdImage) storePaths; + compressImage = true; populateImageCommands = config.sdImage.populateRootCommands; volumeLabel = "NIXOS_SD"; } // optionalAttrs (config.sdImage.rootPartitionUUID != null) { @@ -128,10 +129,11 @@ in sdImage.storePaths = [ config.system.build.toplevel ]; - system.build.sdImage = pkgs.callPackage ({ stdenv, dosfstools, e2fsprogs, mtools, libfaketime, utillinux, bzip2 }: stdenv.mkDerivation { + system.build.sdImage = pkgs.callPackage ({ stdenv, dosfstools, e2fsprogs, + mtools, libfaketime, utillinux, bzip2, zstd }: stdenv.mkDerivation { name = config.sdImage.imageName; - nativeBuildInputs = [ dosfstools e2fsprogs mtools libfaketime utillinux bzip2 ]; + nativeBuildInputs = [ dosfstools e2fsprogs mtools libfaketime utillinux bzip2 zstd ]; inherit (config.sdImage) compressImage; @@ -146,11 +148,14 @@ in echo "file sd-image $img" >> $out/nix-support/hydra-build-products fi + echo "Decompressing rootfs image" + zstd -d --no-progress "${rootfsImage}" -o ./root-fs.img + # Gap in front of the first partition, in MiB gap=8 # Create the image file sized to fit /boot/firmware and /, plus slack for the gap. - rootSizeBlocks=$(du -B 512 --apparent-size ${rootfsImage} | awk '{ print $1 }') + rootSizeBlocks=$(du -B 512 --apparent-size ./root-fs.img | awk '{ print $1 }') firmwareSizeBlocks=$((${toString config.sdImage.firmwareSize} * 1024 * 1024 / 512)) imageSize=$((rootSizeBlocks * 512 + firmwareSizeBlocks * 512 + gap * 1024 * 1024)) truncate -s $imageSize $img @@ -168,7 +173,7 @@ in # Copy the rootfs into the SD image eval $(partx $img -o START,SECTORS --nr 2 --pairs) - dd conv=notrunc if=${rootfsImage} of=$img seek=$START count=$SECTORS + dd conv=notrunc if=./root-fs.img of=$img seek=$START count=$SECTORS # Create a FAT32 /boot/firmware partition of suitable size into firmware_part.img eval $(partx $img -o START,SECTORS --nr 1 --pairs) diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl index f2ffe61c42cb..7f98756a7d9f 100644 --- a/nixos/modules/installer/tools/nixos-generate-config.pl +++ b/nixos/modules/installer/tools/nixos-generate-config.pl @@ -335,6 +335,9 @@ if (@swaps) { next unless -e $swapFilename; my $dev = findStableDevPath $swapFilename; if ($swapType =~ "partition") { + # zram devices are more likely created by configuration.nix, so + # ignore them here + next if ($swapFilename =~ /^\/dev\/zram/); push @swapDevices, "{ device = \"$dev\"; }"; } elsif ($swapType =~ "file") { # swap *files* are more likely specified in configuration.nix, so diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index bb217d873bb5..7650da89cbdd 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -11,6 +11,7 @@ ./config/xdg/mime.nix ./config/xdg/portal.nix ./config/appstream.nix + ./config/console.nix ./config/xdg/sounds.nix ./config/gtk/gtk-icon-cache.nix ./config/gnu.nix @@ -443,6 +444,7 @@ ./services/misc/logkeys.nix ./services/misc/leaps.nix ./services/misc/lidarr.nix + ./services/misc/mame.nix ./services/misc/mathics.nix ./services/misc/matrix-synapse.nix ./services/misc/mbpfan.nix @@ -556,6 +558,7 @@ ./services/network-filesystems/yandex-disk.nix ./services/network-filesystems/xtreemfs.nix ./services/network-filesystems/ceph.nix + ./services/networking/3proxy.nix ./services/networking/amuled.nix ./services/networking/aria2.nix ./services/networking/asterisk.nix @@ -866,6 +869,7 @@ ./services/x11/hardware/digimend.nix ./services/x11/hardware/cmt.nix ./services/x11/gdk-pixbuf.nix + ./services/x11/imwheel.nix ./services/x11/redshift.nix ./services/x11/urxvtd.nix ./services/x11/window-managers/awesome.nix @@ -936,7 +940,6 @@ ./tasks/filesystems/vfat.nix ./tasks/filesystems/xfs.nix ./tasks/filesystems/zfs.nix - ./tasks/kbd.nix ./tasks/lvm.nix ./tasks/network-interfaces.nix ./tasks/network-interfaces-systemd.nix diff --git a/nixos/modules/programs/sway.nix b/nixos/modules/programs/sway.nix index f92d09a7ef44..d685a5259324 100644 --- a/nixos/modules/programs/sway.nix +++ b/nixos/modules/programs/sway.nix @@ -24,6 +24,7 @@ let swayJoined = pkgs.symlinkJoin { name = "sway-joined"; paths = [ swayWrapped swayPackage ]; + passthru.providedSessions = [ "sway" ]; }; in { options.programs.sway = { @@ -87,6 +88,8 @@ in { hardware.opengl.enable = mkDefault true; fonts.enableDefaultFonts = mkDefault true; programs.dconf.enable = mkDefault true; + # To make a Sway session available if a display manager like SDDM is enabled: + services.xserver.displayManager.sessionPackages = [ swayJoined ]; }; meta.maintainers = with lib.maintainers; [ gnidorah primeos colemickens ]; diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix index 8b7d12552ed5..890c421b0ea9 100644 --- a/nixos/modules/security/acme.nix +++ b/nixos/modules/security/acme.nix @@ -241,9 +241,9 @@ in StateDirectoryMode = rights; WorkingDirectory = "/var/lib/${lpath}"; ExecStart = "${pkgs.simp_le}/bin/simp_le ${escapeShellArgs cmdline}"; - ExecStopPost = + ExecStartPost = let - script = pkgs.writeScript "acme-post-stop" '' + script = pkgs.writeScript "acme-post-start" '' #!${pkgs.runtimeShell} -e ${data.postRun} ''; diff --git a/nixos/modules/services/admin/oxidized.nix b/nixos/modules/services/admin/oxidized.nix index da81be3f23e8..885eaed1de6f 100644 --- a/nixos/modules/services/admin/oxidized.nix +++ b/nixos/modules/services/admin/oxidized.nix @@ -111,6 +111,7 @@ in Restart = "always"; WorkingDirectory = cfg.dataDir; KillSignal = "SIGKILL"; + PIDFile = "${cfg.dataDir}.config/oxidized/pid"; }; }; }; diff --git a/nixos/modules/services/backup/postgresql-backup.nix b/nixos/modules/services/backup/postgresql-backup.nix index e768d1b9918a..580c7ce68f1d 100644 --- a/nixos/modules/services/backup/postgresql-backup.nix +++ b/nixos/modules/services/backup/postgresql-backup.nix @@ -89,7 +89,7 @@ in { pgdumpOptions = mkOption { type = types.separatedString " "; - default = "-Cbo"; + default = "-C"; description = '' Command line options for pg_dump. This options is not used if <literal>config.services.postgresqlBackup.backupAll</literal> is enabled. diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index 3bedfe96a180..c8fdd89d0d8f 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -339,9 +339,9 @@ in '') cfg.ensureDatabases} '' + '' ${concatMapStrings (user: '' - $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc "CREATE USER ${user.name}" + $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"' ${concatStringsSep "\n" (mapAttrsToList (database: permission: '' - $PSQL -tAc 'GRANT ${permission} ON ${database} TO ${user.name}' + $PSQL -tAc 'GRANT ${permission} ON ${database} TO "${user.name}"' '') user.ensurePermissions)} '') cfg.ensureUsers} ''; diff --git a/nixos/modules/services/development/lorri.nix b/nixos/modules/services/development/lorri.nix index 68264ee869d1..c843aa56d133 100644 --- a/nixos/modules/services/development/lorri.nix +++ b/nixos/modules/services/development/lorri.nix @@ -32,7 +32,7 @@ in { description = "Lorri Daemon"; requires = [ "lorri.socket" ]; after = [ "lorri.socket" ]; - path = with pkgs; [ config.nix.package gnutar gzip ]; + path = with pkgs; [ config.nix.package git gnutar gzip ]; serviceConfig = { ExecStart = "${pkgs.lorri}/bin/lorri daemon"; PrivateTmp = true; diff --git a/nixos/modules/services/misc/mame.nix b/nixos/modules/services/misc/mame.nix new file mode 100644 index 000000000000..c5d5e9e48371 --- /dev/null +++ b/nixos/modules/services/misc/mame.nix @@ -0,0 +1,67 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.mame; + mame = "mame${lib.optionalString pkgs.stdenv.is64bit "64"}"; +in +{ + options = { + services.mame = { + enable = mkOption { + type = types.bool; + default = false; + description = '' + Whether to setup TUN/TAP Ethernet interface for MAME emulator. + ''; + }; + user = mkOption { + type = types.str; + description = '' + User from which you run MAME binary. + ''; + }; + hostAddr = mkOption { + type = types.str; + description = '' + IP address of the host system. Usually an address of the main network + adapter or the adapter through which you get an internet connection. + ''; + example = "192.168.31.156"; + }; + emuAddr = mkOption { + type = types.str; + description = '' + IP address of the guest system. The same you set inside guest OS under + MAME. Should be on the same subnet as <option>services.mame.hostAddr</option>. + ''; + example = "192.168.31.155"; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.mame ]; + + security.wrappers."${mame}" = { + source = "${pkgs.mame}/bin/${mame}"; + capabilities = "cap_net_admin,cap_net_raw+eip"; + }; + + systemd.services.mame = { + description = "MAME TUN/TAP Ethernet interface"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + path = [ pkgs.iproute ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.mame}/bin/taputil.sh -c ${cfg.user} ${cfg.emuAddr} ${cfg.hostAddr} -"; + ExecStop = "${pkgs.mame}/bin/taputil.sh -d ${cfg.user}"; + }; + }; + }; + + meta.maintainers = with lib.maintainers; [ gnidorah ]; +} diff --git a/nixos/modules/services/misc/matrix-synapse.nix b/nixos/modules/services/misc/matrix-synapse.nix index 50661b873f64..0bda8980720d 100644 --- a/nixos/modules/services/misc/matrix-synapse.nix +++ b/nixos/modules/services/misc/matrix-synapse.nix @@ -671,43 +671,30 @@ in { gid = config.ids.gids.matrix-synapse; } ]; - services.postgresql.enable = mkIf usePostgresql (mkDefault true); + services.postgresql = mkIf (usePostgresql && cfg.create_local_database) { + enable = mkDefault true; + ensureDatabases = [ cfg.database_name ]; + ensureUsers = [{ + name = cfg.database_user; + ensurePermissions = { "DATABASE \"${cfg.database_name}\"" = "ALL PRIVILEGES"; }; + }]; + }; systemd.services.matrix-synapse = { description = "Synapse Matrix homeserver"; - after = [ "network.target" "postgresql.service" ]; + after = [ "network.target" ] ++ lib.optional config.services.postgresql.enable "postgresql.service" ; wantedBy = [ "multi-user.target" ]; preStart = '' ${cfg.package}/bin/homeserver \ --config-path ${configFile} \ --keys-directory ${cfg.dataDir} \ --generate-keys - '' + optionalString (usePostgresql && cfg.create_local_database) '' - if ! test -e "${cfg.dataDir}/db-created"; then - ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \ - ${pg.package}/bin/createuser \ - --login \ - --no-createdb \ - --no-createrole \ - --encrypted \ - ${cfg.database_user} - ${pkgs.sudo}/bin/sudo -u ${pg.superUser} \ - ${pg.package}/bin/createdb \ - --owner=${cfg.database_user} \ - --encoding=UTF8 \ - --lc-collate=C \ - --lc-ctype=C \ - --template=template0 \ - ${cfg.database_name} - touch "${cfg.dataDir}/db-created" - fi ''; serviceConfig = { Type = "notify"; User = "matrix-synapse"; Group = "matrix-synapse"; WorkingDirectory = cfg.dataDir; - PermissionsStartOnly = true; ExecStart = '' ${cfg.package}/bin/homeserver \ ${ concatMapStringsSep "\n " (x: "--config-path ${x} \\") ([ configFile ] ++ cfg.extraConfigFiles) } diff --git a/nixos/modules/services/networking/3proxy.nix b/nixos/modules/services/networking/3proxy.nix new file mode 100644 index 000000000000..26aa16679467 --- /dev/null +++ b/nixos/modules/services/networking/3proxy.nix @@ -0,0 +1,424 @@ +{ config, lib, pkgs, ... }: +with lib; +let + pkg = pkgs._3proxy; + cfg = config.services._3proxy; + optionalList = list: if list == [ ] then "*" else concatMapStringsSep "," toString list; +in { + options.services._3proxy = { + enable = mkEnableOption "3proxy"; + confFile = mkOption { + type = types.path; + example = "/var/lib/3proxy/3proxy.conf"; + description = '' + Ignore all other 3proxy options and load configuration from this file. + ''; + }; + usersFile = mkOption { + type = types.nullOr types.path; + default = null; + example = "/var/lib/3proxy/3proxy.passwd"; + description = '' + Load users and passwords from this file. + + Example users file with plain-text passwords: + + <literal> + test1:CL:password1 + test2:CL:password2 + </literal> + + Example users file with md5-crypted passwords: + + <literal> + test1:CR:$1$tFkisVd2$1GA8JXkRmTXdLDytM/i3a1 + test2:CR:$1$rkpibm5J$Aq1.9VtYAn0JrqZ8M.1ME. + </literal> + + You can generate md5-crypted passwords via https://unix4lyfe.org/crypt/ + Note that htpasswd tool generates incompatible md5-crypted passwords. + Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/How-To-(incomplete)#USERS">documentation</link> for more information. + ''; + }; + services = mkOption { + type = types.listOf (types.submodule { + options = { + type = mkOption { + type = types.enum [ + "proxy" + "socks" + "pop3p" + "ftppr" + "admin" + "dnspr" + "tcppm" + "udppm" + ]; + example = "proxy"; + description = '' + Service type. The following values are valid: + + <itemizedlist> + <listitem><para> + <literal>"proxy"</literal>: HTTP/HTTPS proxy (default port 3128). + </para></listitem> + <listitem><para> + <literal>"socks"</literal>: SOCKS 4/4.5/5 proxy (default port 1080). + </para></listitem> + <listitem><para> + <literal>"pop3p"</literal>: POP3 proxy (default port 110). + </para></listitem> + <listitem><para> + <literal>"ftppr"</literal>: FTP proxy (default port 21). + </para></listitem> + <listitem><para> + <literal>"admin"</literal>: Web interface (default port 80). + </para></listitem> + <listitem><para> + <literal>"dnspr"</literal>: Caching DNS proxy (default port 53). + </para></listitem> + <listitem><para> + <literal>"tcppm"</literal>: TCP portmapper. + </para></listitem> + <listitem><para> + <literal>"udppm"</literal>: UDP portmapper. + </para></listitem> + </itemizedlist> + ''; + }; + bindAddress = mkOption { + type = types.str; + default = "[::]"; + example = "127.0.0.1"; + description = '' + Address used for service. + ''; + }; + bindPort = mkOption { + type = types.nullOr types.int; + default = null; + example = 3128; + description = '' + Override default port used for service. + ''; + }; + maxConnections = mkOption { + type = types.int; + default = 100; + example = 1000; + description = '' + Maximum number of simulationeous connections to this service. + ''; + }; + auth = mkOption { + type = types.listOf (types.enum [ "none" "iponly" "strong" ]); + example = [ "iponly" "strong" ]; + description = '' + Authentication type. The following values are valid: + + <itemizedlist> + <listitem><para> + <literal>"none"</literal>: disables both authentication and authorization. You can not use ACLs. + </para></listitem> + <listitem><para> + <literal>"iponly"</literal>: specifies no authentication. ACLs authorization is used. + </para></listitem> + <listitem><para> + <literal>"strong"</literal>: authentication by username/password. If user is not registered his access is denied regardless of ACLs. + </para></listitem> + </itemizedlist> + + Double authentication is possible, e.g. + + <literal> + { + auth = [ "iponly" "strong" ]; + acl = [ + { + rule = "allow"; + targets = [ "192.168.0.0/16" ]; + } + { + rule = "allow" + users = [ "user1" "user2" ]; + } + ]; + } + </literal> + In this example strong username authentication is not required to access 192.168.0.0/16. + ''; + }; + acl = mkOption { + type = types.listOf (types.submodule { + options = { + rule = mkOption { + type = types.enum [ "allow" "deny" ]; + example = "allow"; + description = '' + ACL rule. The following values are valid: + + <itemizedlist> + <listitem><para> + <literal>"allow"</literal>: connections allowed. + </para></listitem> + <listitem><para> + <literal>"deny"</literal>: connections not allowed. + </para></listitem> + </itemizedlist> + ''; + }; + users = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "user1" "user2" "user3" ]; + description = '' + List of users, use empty list for any. + ''; + }; + sources = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "127.0.0.1" "192.168.1.0/24" ]; + description = '' + List of source IP range, use empty list for any. + ''; + }; + targets = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "127.0.0.1" "192.168.1.0/24" ]; + description = '' + List of target IP ranges, use empty list for any. + May also contain host names instead of addresses. + It's possible to use wildmask in the begginning and in the the end of hostname, e.g. *badsite.com or *badcontent*. + Hostname is only checked if hostname presents in request. + ''; + }; + targetPorts = mkOption { + type = types.listOf types.int; + default = [ ]; + example = [ 80 443 ]; + description = '' + List of target ports, use empty list for any. + ''; + }; + }; + }); + default = [ ]; + example = literalExample '' + [ + { + rule = "allow"; + users = [ "user1" ]; + } + { + rule = "allow"; + sources = [ "192.168.1.0/24" ]; + } + { + rule = "deny"; + } + ] + ''; + description = '' + Use this option to limit user access to resources. + ''; + }; + extraArguments = mkOption { + type = types.nullOr types.str; + default = null; + example = "-46"; + description = '' + Extra arguments for service. + Consult "Options" section in <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available arguments. + ''; + }; + extraConfig = mkOption { + type = types.nullOr types.lines; + default = null; + description = '' + Extra configuration for service. Use this to configure things like bandwidth limiter or ACL-based redirection. + Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available options. + ''; + }; + }; + }); + default = [ ]; + example = literalExample '' + [ + { + type = "proxy"; + bindAddress = "192.168.1.24"; + bindPort = 3128; + auth = [ "none" ]; + } + { + type = "proxy"; + bindAddress = "10.10.1.20"; + bindPort = 3128; + auth = [ "iponly" ]; + } + { + type = "socks"; + bindAddress = "172.17.0.1"; + bindPort = 1080; + auth = [ "strong" ]; + } + ] + ''; + description = '' + Use this option to define 3proxy services. + ''; + }; + denyPrivate = mkOption { + type = types.bool; + default = true; + description = '' + Whether to deny access to private IP ranges including loopback. + ''; + }; + privateRanges = mkOption { + type = types.listOf types.str; + default = [ + "0.0.0.0/8" + "127.0.0.0/8" + "10.0.0.0/8" + "100.64.0.0/10" + "172.16.0.0/12" + "192.168.0.0/16" + "::" + "::1" + "fc00::/7" + ]; + example = [ + "0.0.0.0/8" + "127.0.0.0/8" + "10.0.0.0/8" + "100.64.0.0/10" + "172.16.0.0/12" + "192.168.0.0/16" + "::" + "::1" + "fc00::/7" + ]; + description = '' + What IP ranges to deny access when denyPrivate is set tu true. + ''; + }; + resolution = mkOption { + type = types.submodule { + options = { + nserver = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "127.0.0.53" "192.168.1.3:5353/tcp" ]; + description = '' + List of nameservers to use. + + Up to 5 nservers may be specified. If no nserver is configured, + default system name resolution functions are used. + ''; + }; + nscache = mkOption { + type = types.int; + default = 65535; + example = 65535; + description = "Set name cache size for IPv4."; + }; + nscache6 = mkOption { + type = types.int; + default = 65535; + example = 65535; + description = "Set name cache size for IPv6."; + }; + nsrecord = mkOption { + type = types.attrsOf types.str; + default = { }; + example = { + "files.local" = "192.168.1.12"; + "site.local" = "192.168.1.43"; + }; + description = "Adds static nsrecords."; + }; + }; + }; + default = { }; + description = '' + Use this option to configure name resolution and DNS caching. + ''; + }; + extraConfig = mkOption { + type = types.nullOr types.lines; + default = null; + description = '' + Extra configuration, appended to the 3proxy configuration file. + Consult <link xlink:href="https://github.com/z3APA3A/3proxy/wiki/3proxy.cfg">documentation</link> for available options. + ''; + }; + }; + + config = mkIf cfg.enable { + services._3proxy.confFile = mkDefault (pkgs.writeText "3proxy.conf" '' + # log to stdout + log + + ${concatMapStringsSep "\n" (x: "nserver " + x) cfg.resolution.nserver} + + nscache ${toString cfg.resolution.nscache} + nscache6 ${toString cfg.resolution.nscache6} + + ${concatMapStringsSep "\n" (x: "nsrecord " + x) + (mapAttrsToList (name: value: "${name} ${value}") + cfg.resolution.nsrecord)} + + ${optionalString (cfg.usersFile != null) + ''users $"${cfg.usersFile}"'' + } + + ${concatMapStringsSep "\n" (service: '' + auth ${concatStringsSep " " service.auth} + + ${optionalString (cfg.denyPrivate) + "deny * * ${optionalList cfg.privateRanges}"} + + ${concatMapStringsSep "\n" (acl: + "${acl.rule} ${ + concatMapStringsSep " " optionalList [ + acl.users + acl.sources + acl.targets + acl.targetPorts + ] + }") service.acl} + + maxconn ${toString service.maxConnections} + + ${optionalString (service.extraConfig != null) service.extraConfig} + + ${service.type} -i${toString service.bindAddress} ${ + optionalString (service.bindPort != null) + "-p${toString service.bindPort}" + } ${ + optionalString (service.extraArguments != null) service.extraArguments + } + + flush + '') cfg.services} + ${optionalString (cfg.extraConfig != null) cfg.extraConfig} + ''); + systemd.services."3proxy" = { + description = "Tiny free proxy server"; + documentation = [ "https://github.com/z3APA3A/3proxy/wiki" ]; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + DynamicUser = true; + StateDirectory = "3proxy"; + ExecStart = "${pkg}/bin/3proxy ${cfg.confFile}"; + Restart = "on-failure"; + }; + }; + }; + + meta.maintainers = with maintainers; [ misuzu ]; +} diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix index 5919962837a2..15aaf7410674 100644 --- a/nixos/modules/services/networking/firewall.nix +++ b/nixos/modules/services/networking/firewall.nix @@ -42,16 +42,7 @@ let kernelHasRPFilter = ((kernel.config.isEnabled or (x: false)) "IP_NF_MATCH_RPFILTER") || (kernel.features.netfilterRPFilter or false); - helpers = - '' - # Helper command to manipulate both the IPv4 and IPv6 tables. - ip46tables() { - iptables -w "$@" - ${optionalString config.networking.enableIPv6 '' - ip6tables -w "$@" - ''} - } - ''; + helpers = import ./helpers.nix { inherit config lib; }; writeShScript = name: text: let dir = pkgs.writeScriptBin name '' #! ${pkgs.runtimeShell} -e @@ -271,7 +262,7 @@ let apply = canonicalizePortList; example = [ 22 80 ]; description = - '' + '' List of TCP ports on which incoming connections are accepted. ''; @@ -282,7 +273,7 @@ let default = [ ]; example = [ { from = 8999; to = 9003; } ]; description = - '' + '' A range of TCP ports on which incoming connections are accepted. ''; diff --git a/nixos/modules/services/networking/helpers.nix b/nixos/modules/services/networking/helpers.nix new file mode 100644 index 000000000000..d7d42de0e3a8 --- /dev/null +++ b/nixos/modules/services/networking/helpers.nix @@ -0,0 +1,11 @@ +{ config, lib, ... }: '' + # Helper command to manipulate both the IPv4 and IPv6 tables. + ip46tables() { + iptables -w "$@" + ${ + lib.optionalString config.networking.enableIPv6 '' + ip6tables -w "$@" + '' + } + } +'' diff --git a/nixos/modules/services/networking/nat.nix b/nixos/modules/services/networking/nat.nix index c80db8472f0d..f1238bc6b168 100644 --- a/nixos/modules/services/networking/nat.nix +++ b/nixos/modules/services/networking/nat.nix @@ -7,12 +7,14 @@ with lib; let - cfg = config.networking.nat; dest = if cfg.externalIP == null then "-j MASQUERADE" else "-j SNAT --to-source ${cfg.externalIP}"; + helpers = import ./helpers.nix { inherit config lib; }; + flushNat = '' + ${helpers} ip46tables -w -t nat -D PREROUTING -j nixos-nat-pre 2>/dev/null|| true ip46tables -w -t nat -F nixos-nat-pre 2>/dev/null || true ip46tables -w -t nat -X nixos-nat-pre 2>/dev/null || true @@ -27,6 +29,7 @@ let ''; setupNat = '' + ${helpers} # Create subchain where we store rules ip46tables -w -t nat -N nixos-nat-pre ip46tables -w -t nat -N nixos-nat-post diff --git a/nixos/modules/services/networking/unbound.nix b/nixos/modules/services/networking/unbound.nix index 3cf82e8839bb..baed83591e1e 100644 --- a/nixos/modules/services/networking/unbound.nix +++ b/nixos/modules/services/networking/unbound.nix @@ -53,6 +53,13 @@ in enable = mkEnableOption "Unbound domain name server"; + package = mkOption { + type = types.package; + default = pkgs.unbound; + defaultText = "pkgs.unbound"; + description = "The unbound package to use"; + }; + allowedAccess = mkOption { default = [ "127.0.0.0/24" ]; type = types.listOf types.str; @@ -94,7 +101,7 @@ in config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.unbound ]; + environment.systemPackages = [ cfg.package ]; users.users.unbound = { description = "unbound daemon user"; @@ -114,7 +121,7 @@ in mkdir -m 0755 -p ${stateDir}/dev/ cp ${confFile} ${stateDir}/unbound.conf ${optionalString cfg.enableRootTrustAnchor '' - ${pkgs.unbound}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!" + ${cfg.package}/bin/unbound-anchor -a ${rootTrustAnchorFile} || echo "Root anchor updated!" chown unbound ${stateDir} ${rootTrustAnchorFile} ''} touch ${stateDir}/dev/random @@ -122,7 +129,7 @@ in ''; serviceConfig = { - ExecStart = "${pkgs.unbound}/bin/unbound -d -c ${stateDir}/unbound.conf"; + ExecStart = "${cfg.package}/bin/unbound -d -c ${stateDir}/unbound.conf"; ExecStopPost="${pkgs.utillinux}/bin/umount ${stateDir}/dev/random"; ProtectSystem = true; diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix index eb90dae94dfe..9b476ba7f1e5 100644 --- a/nixos/modules/services/web-servers/nginx/default.nix +++ b/nixos/modules/services/web-servers/nginx/default.nix @@ -47,7 +47,7 @@ let '')); configFile = pkgs.writers.writeNginxConfig "nginx.conf" '' - user ${cfg.user} ${cfg.group}; + pid /run/nginx/nginx.pid; error_log ${cfg.logError}; daemon off; @@ -366,12 +366,7 @@ in preStart = mkOption { type = types.lines; - default = '' - test -d ${cfg.stateDir}/logs || mkdir -m 750 -p ${cfg.stateDir}/logs - test `stat -c %a ${cfg.stateDir}` = "750" || chmod 750 ${cfg.stateDir} - test `stat -c %a ${cfg.stateDir}/logs` = "750" || chmod 750 ${cfg.stateDir}/logs - chown -R ${cfg.user}:${cfg.group} ${cfg.stateDir} - ''; + default = ""; description = " Shell commands executed before the service's nginx is started. "; @@ -673,23 +668,35 @@ in } ]; + systemd.tmpfiles.rules = [ + "d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -" + "d '${cfg.stateDir}/logs' 0750 ${cfg.user} ${cfg.group} - -" + ]; + systemd.services.nginx = { description = "Nginx Web Server"; wantedBy = [ "multi-user.target" ]; wants = concatLists (map (vhostConfig: ["acme-${vhostConfig.serverName}.service" "acme-selfsigned-${vhostConfig.serverName}.service"]) acmeEnabledVhosts); after = [ "network.target" ] ++ map (vhostConfig: "acme-selfsigned-${vhostConfig.serverName}.service") acmeEnabledVhosts; stopIfChanged = false; - preStart = - '' + preStart = '' ${cfg.preStart} - ${cfg.package}/bin/nginx -c ${configPath} -p ${cfg.stateDir} -t - ''; + ${cfg.package}/bin/nginx -c '${configPath}' -p '${cfg.stateDir}' -t + ''; serviceConfig = { - ExecStart = "${cfg.package}/bin/nginx -c ${configPath} -p ${cfg.stateDir}"; + ExecStart = "${cfg.package}/bin/nginx -c '${configPath}' -p '${cfg.stateDir}'"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; Restart = "always"; RestartSec = "10s"; StartLimitInterval = "1min"; + # User and group + User = cfg.user; + Group = cfg.group; + # Runtime directory and mode + RuntimeDirectory = "nginx"; + RuntimeDirectoryMode = "0750"; + # Capabilities + AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" "CAP_SYS_RESOURCE" ]; }; }; diff --git a/nixos/modules/services/web-servers/nginx/location-options.nix b/nixos/modules/services/web-servers/nginx/location-options.nix index 2b3749d8a744..3d9e391ecf20 100644 --- a/nixos/modules/services/web-servers/nginx/location-options.nix +++ b/nixos/modules/services/web-servers/nginx/location-options.nix @@ -67,7 +67,7 @@ with lib; return = mkOption { type = types.nullOr types.str; default = null; - example = "301 http://example.com$request_uri;"; + example = "301 http://example.com$request_uri"; description = '' Adds a return directive, for e.g. redirections. ''; diff --git a/nixos/modules/services/web-servers/unit/default.nix b/nixos/modules/services/web-servers/unit/default.nix index 32f6d475b34e..b07212580a55 100644 --- a/nixos/modules/services/web-servers/unit/default.nix +++ b/nixos/modules/services/web-servers/unit/default.nix @@ -85,7 +85,7 @@ in { systemd.tmpfiles.rules = [ "d '${cfg.stateDir}' 0750 ${cfg.user} ${cfg.group} - -" "d '${cfg.logDir}' 0750 ${cfg.user} ${cfg.group} - -" - ]; + ]; systemd.services.unit = { description = "Unit App Server"; @@ -93,23 +93,39 @@ in { wantedBy = [ "multi-user.target" ]; path = with pkgs; [ curl ]; preStart = '' - test -f '/run/unit/control.unit.sock' || rm -f '/run/unit/control.unit.sock' + test -f '${cfg.stateDir}/conf.json' || rm -f '${cfg.stateDir}/conf.json' ''; postStart = '' curl -X PUT --data-binary '@${configFile}' --unix-socket '/run/unit/control.unit.sock' 'http://localhost/config' ''; serviceConfig = { - User = cfg.user; - Group = cfg.group; - AmbientCapabilities = "CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID"; - CapabilityBoundingSet = "CAP_NET_BIND_SERVICE CAP_SETGID CAP_SETUID"; ExecStart = '' ${cfg.package}/bin/unitd --control 'unix:/run/unit/control.unit.sock' --pid '/run/unit/unit.pid' \ --log '${cfg.logDir}/unit.log' --state '${cfg.stateDir}' --no-daemon \ --user ${cfg.user} --group ${cfg.group} ''; + # User and group + User = cfg.user; + Group = cfg.group; + # Capabilities + AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" "CAP_SETGID" "CAP_SETUID" ]; + # Security + NoNewPrivileges = true; + # Sanboxing + ProtectSystem = "full"; + ProtectHome = true; RuntimeDirectory = "unit"; RuntimeDirectoryMode = "0750"; + PrivateTmp = true; + PrivateDevices = true; + ProtectHostname = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + RestrictRealtime = true; + PrivateMounts = true; }; }; diff --git a/nixos/modules/services/x11/desktop-managers/cde.nix b/nixos/modules/services/x11/desktop-managers/cde.nix new file mode 100644 index 000000000000..c1b6d3bf064a --- /dev/null +++ b/nixos/modules/services/x11/desktop-managers/cde.nix @@ -0,0 +1,55 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + xcfg = config.services.xserver; + cfg = xcfg.desktopManager.cde; +in { + options.services.xserver.desktopManager.cde = { + enable = mkEnableOption "Common Desktop Environment"; + }; + + config = mkIf (xcfg.enable && cfg.enable) { + services.rpcbind.enable = true; + + services.xinetd.enable = true; + services.xinetd.services = [ + { + name = "cmsd"; + protocol = "udp"; + user = "root"; + server = "${pkgs.cdesktopenv}/opt/dt/bin/rpc.cmsd"; + extraConfig = '' + type = RPC UNLISTED + rpc_number = 100068 + rpc_version = 2-5 + only_from = 127.0.0.1/0 + ''; + } + ]; + + users.groups.mail = {}; + security.wrappers = { + dtmail = { + source = "${pkgs.cdesktopenv}/bin/dtmail"; + group = "mail"; + setgid = true; + }; + }; + + system.activationScripts.setup-cde = '' + mkdir -p /var/dt/{tmp,appconfig/appmanager} + chmod a+w+t /var/dt/{tmp,appconfig/appmanager} + ''; + + services.xserver.desktopManager.session = [ + { name = "CDE"; + start = '' + exec ${pkgs.cdesktopenv}/opt/dt/bin/Xsession + ''; + }]; + }; + + meta.maintainers = [ maintainers.gnidorah ]; +} diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix index 671a959cdde1..970fa620c6b6 100644 --- a/nixos/modules/services/x11/desktop-managers/default.nix +++ b/nixos/modules/services/x11/desktop-managers/default.nix @@ -20,7 +20,7 @@ in imports = [ ./none.nix ./xterm.nix ./xfce.nix ./plasma5.nix ./lumina.nix ./lxqt.nix ./enlightenment.nix ./gnome3.nix ./kodi.nix ./maxx.nix - ./mate.nix ./pantheon.nix ./surf-display.nix + ./mate.nix ./pantheon.nix ./surf-display.nix ./cde.nix ]; options = { @@ -86,23 +86,14 @@ in }; default = mkOption { - type = types.str; - default = ""; + type = types.nullOr types.str; + default = null; example = "none"; - description = "Default desktop manager loaded if none have been chosen."; - apply = defaultDM: - if defaultDM == "" && cfg.session.list != [] then - (head cfg.session.list).name - else if any (w: w.name == defaultDM) cfg.session.list then - defaultDM - else - builtins.trace '' - Default desktop manager (${defaultDM}) not found at evaluation time. - These are the known valid session names: - ${concatMapStringsSep "\n " (w: "services.xserver.desktopManager.default = \"${w.name}\";") cfg.session.list} - It's also possible the default can be found in one of these packages: - ${concatMapStringsSep "\n " (p: p.name) config.services.xserver.displayManager.extraSessionFilePackages} - '' defaultDM; + description = '' + <emphasis role="strong">Deprecated</emphasis>, please use <xref linkend="opt-services.xserver.displayManager.defaultSession"/> instead. + + Default desktop manager loaded if none have been chosen. + ''; }; }; diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix index 6725595e1cfd..6d9bd284bc72 100644 --- a/nixos/modules/services/x11/desktop-managers/gnome3.nix +++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix @@ -144,7 +144,7 @@ in services.gnome3.core-shell.enable = true; services.gnome3.core-utilities.enable = mkDefault true; - services.xserver.displayManager.extraSessionFilePackages = [ pkgs.gnome3.gnome-session ]; + services.xserver.displayManager.sessionPackages = [ pkgs.gnome3.gnome-session ]; environment.extraInit = '' ${concatMapStrings (p: '' @@ -171,7 +171,7 @@ in }) (mkIf flashbackEnabled { - services.xserver.displayManager.extraSessionFilePackages = map + services.xserver.displayManager.sessionPackages = map (wm: pkgs.gnome3.gnome-flashback.mkSessionForWm { inherit (wm) wmName wmLabel wmCommand; }) (optional cfg.flashback.enableMetacity { diff --git a/nixos/modules/services/x11/desktop-managers/pantheon.nix b/nixos/modules/services/x11/desktop-managers/pantheon.nix index 99db5b17b649..e07d5b5eaad7 100644 --- a/nixos/modules/services/x11/desktop-managers/pantheon.nix +++ b/nixos/modules/services/x11/desktop-managers/pantheon.nix @@ -69,7 +69,7 @@ in config = mkIf cfg.enable { - services.xserver.displayManager.extraSessionFilePackages = [ pkgs.pantheon.elementary-session-settings ]; + services.xserver.displayManager.sessionPackages = [ pkgs.pantheon.elementary-session-settings ]; # Ensure lightdm is used when Pantheon is enabled # Without it screen locking will be nonfunctional because of the use of lightlocker @@ -81,9 +81,9 @@ in services.xserver.displayManager.lightdm.greeters.pantheon.enable = mkDefault true; - # If not set manually Pantheon session cannot be started - # Known issue of https://github.com/NixOS/nixpkgs/pull/43992 - services.xserver.desktopManager.default = mkForce "pantheon"; + # Without this, Elementary LightDM greeter will pre-select non-existent `default` session + # https://github.com/elementary/greeter/issues/368 + services.xserver.displayManager.defaultSession = "pantheon"; services.xserver.displayManager.sessionCommands = '' if test "$XDG_CURRENT_DESKTOP" = "Pantheon"; then diff --git a/nixos/modules/services/x11/desktop-managers/surf-display.nix b/nixos/modules/services/x11/desktop-managers/surf-display.nix index 140dde828daa..9aeb0bbd2a88 100644 --- a/nixos/modules/services/x11/desktop-managers/surf-display.nix +++ b/nixos/modules/services/x11/desktop-managers/surf-display.nix @@ -118,7 +118,7 @@ in { }; config = mkIf cfg.enable { - services.xserver.displayManager.extraSessionFilePackages = [ + services.xserver.displayManager.sessionPackages = [ pkgs.surf-display ]; diff --git a/nixos/modules/services/x11/display-managers/account-service-util.nix b/nixos/modules/services/x11/display-managers/account-service-util.nix new file mode 100644 index 000000000000..1dbe703b5662 --- /dev/null +++ b/nixos/modules/services/x11/display-managers/account-service-util.nix @@ -0,0 +1,39 @@ +{ accountsservice +, glib +, gobject-introspection +, python3 +, wrapGAppsHook +}: + +python3.pkgs.buildPythonApplication { + name = "set-session"; + + format = "other"; + + src = ./set-session.py; + + dontUnpack = true; + + strictDeps = false; + + nativeBuildInputs = [ + wrapGAppsHook + gobject-introspection + ]; + + buildInputs = [ + accountsservice + glib + ]; + + propagatedBuildInputs = with python3.pkgs; [ + pygobject3 + ordered-set + ]; + + installPhase = '' + mkdir -p $out/bin + cp $src $out/bin/set-session + chmod +x $out/bin/set-session + ''; +} diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix index c252c3c90c94..2d809b5cc9fd 100644 --- a/nixos/modules/services/x11/display-managers/default.nix +++ b/nixos/modules/services/x11/display-managers/default.nix @@ -27,16 +27,7 @@ let Xft.hintstyle: hintslight ''; - mkCases = session: - concatStrings ( - mapAttrsToList (name: starts: '' - (${name}) - ${concatMapStringsSep "\n " (n: n.start) starts} - ;; - '') (lib.groupBy (n: n.name) session) - ); - - # file provided by services.xserver.displayManager.session.wrapper + # file provided by services.xserver.displayManager.sessionData.wrapper xsessionWrapper = pkgs.writeScript "xsession-wrapper" '' #! ${pkgs.bash}/bin/bash @@ -116,94 +107,44 @@ let # Run the supplied session command. Remove any double quotes with eval. eval exec "$@" else - # Fall back to the default window/desktopManager - exec ${cfg.displayManager.session.script} + # TODO: Do we need this? Should not the session always exist? + echo "error: unknown session $1" 1>&2 + exit 1 fi ''; - # file provided by services.xserver.displayManager.session.script - xsession = wm: dm: pkgs.writeScript "xsession" - '' - #! ${pkgs.bash}/bin/bash - - # Legacy session script used to construct .desktop files from - # `services.xserver.displayManager.session` entries. Called from - # `sessionWrapper`. - - # Expected parameters: - # $1 = <desktop-manager>+<window-manager> - - # The first argument of this script is the session type. - sessionType="$1" - if [ "$sessionType" = default ]; then sessionType=""; fi - - # The session type is "<desktop-manager>+<window-manager>", so - # extract those (see: - # http://wiki.bash-hackers.org/syntax/pe#substring_removal). - windowManager="''${sessionType##*+}" - : ''${windowManager:=${cfg.windowManager.default}} - desktopManager="''${sessionType%%+*}" - : ''${desktopManager:=${cfg.desktopManager.default}} - - # Start the window manager. - case "$windowManager" in - ${mkCases wm} - (*) echo "$0: Window manager '$windowManager' not found.";; - esac - - # Start the desktop manager. - case "$desktopManager" in - ${mkCases dm} - (*) echo "$0: Desktop manager '$desktopManager' not found.";; - esac - - ${optionalString cfg.updateDbusEnvironment '' - ${lib.getBin pkgs.dbus}/bin/dbus-update-activation-environment --systemd --all - ''} - - test -n "$waitPID" && wait "$waitPID" - - ${config.systemd.package}/bin/systemctl --user stop graphical-session.target - - exit 0 - ''; - - # Desktop Entry Specification: - # - https://standards.freedesktop.org/desktop-entry-spec/latest/ - # - https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html - mkDesktops = names: pkgs.runCommand "desktops" + installedSessions = pkgs.runCommand "desktops" { # trivial derivation preferLocalBuild = true; allowSubstitutes = false; } '' - mkdir -p "$out/share/xsessions" - ${concatMapStrings (n: '' - cat - > "$out/share/xsessions/${n}.desktop" << EODESKTOP - [Desktop Entry] - Version=1.0 - Type=XSession - TryExec=${cfg.displayManager.session.script} - Exec=${cfg.displayManager.session.script} "${n}" - Name=${n} - Comment= - EODESKTOP - '') names} + mkdir -p "$out/share/"{xsessions,wayland-sessions} ${concatMapStrings (pkg: '' + for n in ${concatStringsSep " " pkg.providedSessions}; do + if ! test -f ${pkg}/share/wayland-sessions/$n.desktop -o \ + -f ${pkg}/share/xsessions/$n.desktop; then + echo "Couldn't find provided session name, $n.desktop, in session package ${pkg.name}:" + echo " ${pkg}" + return 1 + fi + done + if test -d ${pkg}/share/xsessions; then ${xorg.lndir}/bin/lndir ${pkg}/share/xsessions $out/share/xsessions fi - '') cfg.displayManager.extraSessionFilePackages} - - ${concatMapStrings (pkg: '' if test -d ${pkg}/share/wayland-sessions; then - mkdir -p "$out/share/wayland-sessions" ${xorg.lndir}/bin/lndir ${pkg}/share/wayland-sessions $out/share/wayland-sessions fi - '') cfg.displayManager.extraSessionFilePackages} + '') cfg.displayManager.sessionPackages} ''; + dmDefault = cfg.desktopManager.default; + wmDefault = cfg.windowManager.default; + + defaultSessionFromLegacyOptions = concatStringsSep "+" (filter (s: s != null) ([ dmDefault ] ++ optional (wmDefault != "none") wmDefault)); + in { @@ -261,11 +202,24 @@ in ''; }; - extraSessionFilePackages = mkOption { - type = types.listOf types.package; + sessionPackages = mkOption { + type = with types; listOf (package // { + description = "package with provided sessions"; + check = p: assertMsg + (package.check p && p ? providedSessions + && p.providedSessions != [] && all isString p.providedSessions) + '' + Package, '${p.name}', did not specify any session names, as strings, in + 'passthru.providedSessions'. This is required when used as a session package. + + The session names can be looked up in: + ${p}/share/xsessions + ${p}/share/wayland-sessions + ''; + }); default = []; description = '' - A list of packages containing xsession files to be passed to the display manager. + A list of packages containing x11 or wayland session files to be passed to the display manager. ''; }; @@ -296,18 +250,50 @@ in inside the display manager with the desktop manager name followed by the window manager name. ''; - apply = list: rec { - wm = filter (s: s.manage == "window") list; - dm = filter (s: s.manage == "desktop") list; - names = flip concatMap dm - (d: map (w: d.name + optionalString (w.name != "none") ("+" + w.name)) - (filter (w: d.name != "none" || w.name != "none") wm)); - desktops = mkDesktops names; - script = xsession wm dm; + }; + + sessionData = mkOption { + description = "Data exported for display managers’ convenience"; + internal = true; + default = {}; + apply = val: { wrapper = xsessionWrapper; + desktops = installedSessions; + sessionNames = concatMap (p: p.providedSessions) cfg.displayManager.sessionPackages; + # We do not want to force users to set defaultSession when they have only single DE. + autologinSession = + if cfg.displayManager.defaultSession != null then + cfg.displayManager.defaultSession + else if cfg.displayManager.sessionData.sessionNames != [] then + head cfg.displayManager.sessionData.sessionNames + else + null; }; }; + defaultSession = mkOption { + type = with types; nullOr str // { + description = "session name"; + check = d: + assertMsg (d != null -> (str.check d && elem d cfg.displayManager.sessionData.sessionNames)) '' + Default graphical session, '${d}', not found. + Valid names for 'services.xserver.displayManager.defaultSession' are: + ${concatStringsSep "\n " cfg.displayManager.sessionData.sessionNames} + ''; + }; + default = + if dmDefault != null || wmDefault != null then + defaultSessionFromLegacyOptions + else + null; + example = "gnome"; + description = '' + Graphical session to pre-select in the session chooser (only effective for GDM and LightDM). + + On GDM, LightDM and SDDM, it will also be used as a session for auto-login. + ''; + }; + job = { preStart = mkOption { @@ -356,6 +342,27 @@ in }; config = { + assertions = [ + { + assertion = cfg.desktopManager.default != null || cfg.windowManager.default != null -> cfg.displayManager.defaultSession == defaultSessionFromLegacyOptions; + message = "You cannot use both services.xserver.displayManager.defaultSession option and legacy options (services.xserver.desktopManager.default and services.xserver.windowManager.default)."; + } + ]; + + warnings = + mkIf (dmDefault != null || wmDefault != null) [ + '' + The following options are deprecated: + ${concatStringsSep "\n " (map ({c, t}: t) (filter ({c, t}: c != null) [ + { c = dmDefault; t = "- services.xserver.desktopManager.default"; } + { c = wmDefault; t = "- services.xserver.windowManager.default"; } + ]))} + Please use + services.xserver.displayManager.defaultSession = "${concatStringsSep "+" (filter (s: s != null) [ dmDefault wmDefault ])}"; + instead. + '' + ]; + services.xserver.displayManager.xserverBin = "${xorg.xorgserver.out}/bin/X"; systemd.user.targets.graphical-session = { @@ -364,6 +371,67 @@ in StopWhenUnneeded = false; }; }; + + # Create desktop files and scripts for starting sessions for WMs/DMs + # that do not have upstream session files (those defined using services.{display,desktop,window}Manager.session options). + services.xserver.displayManager.sessionPackages = + let + dms = filter (s: s.manage == "desktop") cfg.displayManager.session; + wms = filter (s: s.manage == "window") cfg.displayManager.session; + + # Script responsible for starting the window manager and the desktop manager. + xsession = wm: dm: pkgs.writeScript "xsession" '' + #! ${pkgs.bash}/bin/bash + + # Legacy session script used to construct .desktop files from + # `services.xserver.displayManager.session` entries. Called from + # `sessionWrapper`. + + # Start the window manager. + ${wm.start} + + # Start the desktop manager. + ${dm.start} + + ${optionalString cfg.updateDbusEnvironment '' + ${lib.getBin pkgs.dbus}/bin/dbus-update-activation-environment --systemd --all + ''} + + test -n "$waitPID" && wait "$waitPID" + + ${config.systemd.package}/bin/systemctl --user stop graphical-session.target + + exit 0 + ''; + in + # We will generate every possible pair of WM and DM. + concatLists ( + crossLists + (dm: wm: let + sessionName = "${dm.name}${optionalString (wm.name != "none") ("+" + wm.name)}"; + script = xsession dm wm; + in + optional (dm.name != "none" || wm.name != "none") + (pkgs.writeTextFile { + name = "${sessionName}-xsession"; + destination = "/share/xsessions/${sessionName}.desktop"; + # Desktop Entry Specification: + # - https://standards.freedesktop.org/desktop-entry-spec/latest/ + # - https://standards.freedesktop.org/desktop-entry-spec/latest/ar01s06.html + text = '' + [Desktop Entry] + Version=1.0 + Type=XSession + TryExec=${script} + Exec=${script} + Name=${sessionName} + ''; + } // { + providedSessions = [ sessionName ]; + }) + ) + [dms wms] + ); }; imports = [ @@ -371,6 +439,7 @@ in "The option is no longer necessary because all display managers have already delegated lid management to systemd.") (mkRenamedOptionModule [ "services" "xserver" "displayManager" "job" "logsXsession" ] [ "services" "xserver" "displayManager" "job" "logToFile" ]) (mkRenamedOptionModule [ "services" "xserver" "displayManager" "logToJournal" ] [ "services" "xserver" "displayManager" "job" "logToJournal" ]) + (mkRenamedOptionModule [ "services" "xserver" "displayManager" "extraSessionFilesPackages" ] [ "services" "xserver" "displayManager" "sessionPackages" ]) ]; } diff --git a/nixos/modules/services/x11/display-managers/gdm.nix b/nixos/modules/services/x11/display-managers/gdm.nix index 095569fa08aa..6630f012f04f 100644 --- a/nixos/modules/services/x11/display-managers/gdm.nix +++ b/nixos/modules/services/x11/display-managers/gdm.nix @@ -31,44 +31,9 @@ let load-module module-position-event-sounds ''; - dmDefault = config.services.xserver.desktopManager.default; - wmDefault = config.services.xserver.windowManager.default; - hasDefaultUserSession = dmDefault != "none" || wmDefault != "none"; - defaultSessionName = dmDefault + optionalString (wmDefault != "none") ("+" + wmDefault); - - setSessionScript = pkgs.python3.pkgs.buildPythonApplication { - name = "set-session"; - - format = "other"; - - src = ./set-session.py; - - dontUnpack = true; - - strictDeps = false; - - nativeBuildInputs = with pkgs; [ - wrapGAppsHook - gobject-introspection - ]; - - buildInputs = with pkgs; [ - accountsservice - glib - ]; - - propagatedBuildInputs = with pkgs.python3.pkgs; [ - pygobject3 - ordered-set - ]; - - installPhase = '' - mkdir -p $out/bin - cp $src $out/bin/set-session - chmod +x $out/bin/set-session - ''; - }; + defaultSessionName = config.services.xserver.displayManager.defaultSession; + setSessionScript = pkgs.callPackage ./account-service-util.nix { }; in { @@ -186,7 +151,7 @@ in environment = { GDM_X_SERVER_EXTRA_ARGS = toString (filter (arg: arg != "-terminate") cfg.xserverArgs); - XDG_DATA_DIRS = "${cfg.session.desktops}/share/"; + XDG_DATA_DIRS = "${cfg.sessionData.desktops}/share/"; } // optionalAttrs (xSessionWrapper != null) { # Make GDM use this wrapper before running the session, which runs the # configured setupCommands. This relies on a patched GDM which supports @@ -204,16 +169,19 @@ in cat - > /run/gdm/.config/gnome-initial-setup-done <<- EOF yes EOF - '' - # TODO: Make setSessionScript aware of previously used sessions - # + optionalString hasDefaultUserSession '' - # ${setSessionScript}/bin/set-session ${defaultSessionName} - # '' - ; + '' + optionalString (defaultSessionName != null) '' + # Set default session in session chooser to a specified values – basically ignore session history. + ${setSessionScript}/bin/set-session ${cfg.sessionData.autologinSession} + ''; }; - # Because sd_login_monitor_new requires /run/systemd/machines - systemd.services.display-manager.wants = [ "systemd-machined.service" ]; + systemd.services.display-manager.wants = [ + # Because sd_login_monitor_new requires /run/systemd/machines + "systemd-machined.service" + # setSessionScript wants AccountsService + "accounts-daemon.service" + ]; + systemd.services.display-manager.after = [ "rc-local.service" "systemd-machined.service" @@ -329,7 +297,7 @@ in ${optionalString cfg.gdm.debug "Enable=true"} ''; - environment.etc."gdm/Xsession".source = config.services.xserver.displayManager.session.wrapper; + environment.etc."gdm/Xsession".source = config.services.xserver.displayManager.sessionData.wrapper; # GDM LFS PAM modules, adapted somehow to NixOS security.pam.services = { diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix index fa9445af32e7..0025f9b36037 100644 --- a/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/mini.nix @@ -53,9 +53,8 @@ in Whether to enable lightdm-mini-greeter as the lightdm greeter. Note that this greeter starts only the default X session. - You can configure the default X session by - <option>services.xserver.desktopManager.default</option> and - <option>services.xserver.windowManager.default</option>. + You can configure the default X session using + <xref linkend="opt-services.xserver.displayManager.defaultSession"/>. ''; }; diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix index 29cb6ccbc06b..77c94114e6d9 100644 --- a/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/pantheon.nix @@ -35,6 +35,9 @@ in name = "io.elementary.greeter"; }; + # Show manual login card. + services.xserver.displayManager.lightdm.extraSeatDefaults = "greeter-show-manual-login=true"; + environment.etc."lightdm/io.elementary.greeter.conf".source = "${pkgs.pantheon.elementary-greeter}/etc/lightdm/io.elementary.greeter.conf"; environment.etc."wingpanel.d/io.elementary.greeter.whitelist".source = "${pkgs.pantheon.elementary-default-settings}/etc/wingpanel.d/io.elementary.greeter.whitelist"; diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix index cf4c05acbccd..f7face0adb7e 100644 --- a/nixos/modules/services/x11/display-managers/lightdm.nix +++ b/nixos/modules/services/x11/display-managers/lightdm.nix @@ -8,10 +8,9 @@ let dmcfg = xcfg.displayManager; xEnv = config.systemd.services.display-manager.environment; cfg = dmcfg.lightdm; + sessionData = dmcfg.sessionData; - dmDefault = xcfg.desktopManager.default; - wmDefault = xcfg.windowManager.default; - hasDefaultUserSession = dmDefault != "none" || wmDefault != "none"; + setSessionScript = pkgs.callPackage ./account-service-util.nix { }; inherit (pkgs) lightdm writeScript writeText; @@ -45,22 +44,19 @@ let greeter-user = ${config.users.users.lightdm.name} greeters-directory = ${cfg.greeter.package} ''} - sessions-directory = ${dmcfg.session.desktops}/share/xsessions + sessions-directory = ${dmcfg.sessionData.desktops}/share/xsessions:${dmcfg.sessionData.desktops}/share/wayland-sessions ${cfg.extraConfig} [Seat:*] xserver-command = ${xserverWrapper} - session-wrapper = ${dmcfg.session.wrapper} + session-wrapper = ${dmcfg.sessionData.wrapper} ${optionalString cfg.greeter.enable '' greeter-session = ${cfg.greeter.name} ''} ${optionalString cfg.autoLogin.enable '' autologin-user = ${cfg.autoLogin.user} autologin-user-timeout = ${toString cfg.autoLogin.timeout} - autologin-session = ${defaultSessionName} - ''} - ${optionalString hasDefaultUserSession '' - user-session=${defaultSessionName} + autologin-session = ${sessionData.autologinSession} ''} ${optionalString (dmcfg.setupCommands != "") '' display-setup-script=${pkgs.writeScript "lightdm-display-setup" '' @@ -71,7 +67,6 @@ let ${cfg.extraSeatDefaults} ''; - defaultSessionName = dmDefault + optionalString (wmDefault != "none") ("+" + wmDefault); in { # Note: the order in which lightdm greeter modules are imported @@ -199,11 +194,9 @@ in LightDM auto-login requires services.xserver.displayManager.lightdm.autoLogin.user to be set ''; } - { assertion = cfg.autoLogin.enable -> dmDefault != "none" || wmDefault != "none"; + { assertion = cfg.autoLogin.enable -> sessionData.autologinSession != null; message = '' - LightDM auto-login requires that services.xserver.desktopManager.default and - services.xserver.windowManager.default are set to valid values. The current - default session: ${defaultSessionName} is not valid. + LightDM auto-login requires that services.xserver.displayManager.defaultSession is set. ''; } { assertion = !cfg.greeter.enable -> (cfg.autoLogin.enable && cfg.autoLogin.timeout == 0); @@ -214,6 +207,20 @@ in } ]; + # Set default session in session chooser to a specified values – basically ignore session history. + # Auto-login is already covered by a config value. + services.xserver.displayManager.job.preStart = optionalString (!cfg.autoLogin.enable && dmcfg.defaultSession != null) '' + ${setSessionScript}/bin/set-session ${dmcfg.defaultSession} + ''; + + # setSessionScript needs session-files in XDG_DATA_DIRS + services.xserver.displayManager.job.environment.XDG_DATA_DIRS = "${dmcfg.sessionData.desktops}/share/"; + + # setSessionScript wants AccountsService + systemd.services.display-manager.wants = [ + "accounts-daemon.service" + ]; + # lightdm relaunches itself via just `lightdm`, so needs to be on the PATH services.xserver.displayManager.job.execCmd = '' export PATH=${lightdm}/sbin:$PATH diff --git a/nixos/modules/services/x11/display-managers/sddm.nix b/nixos/modules/services/x11/display-managers/sddm.nix index 24e120c4f2cf..4224c557ed63 100644 --- a/nixos/modules/services/x11/display-managers/sddm.nix +++ b/nixos/modules/services/x11/display-managers/sddm.nix @@ -50,8 +50,8 @@ let MinimumVT=${toString (if xcfg.tty != null then xcfg.tty else 7)} ServerPath=${xserverWrapper} XephyrPath=${pkgs.xorg.xorgserver.out}/bin/Xephyr - SessionCommand=${dmcfg.session.wrapper} - SessionDir=${dmcfg.session.desktops}/share/xsessions + SessionCommand=${dmcfg.sessionData.wrapper} + SessionDir=${dmcfg.sessionData.desktops}/share/xsessions XauthPath=${pkgs.xorg.xauth}/bin/xauth DisplayCommand=${Xsetup} DisplayStopCommand=${Xstop} @@ -59,23 +59,19 @@ let [Wayland] EnableHidpi=${if cfg.enableHidpi then "true" else "false"} - SessionDir=${dmcfg.session.desktops}/share/wayland-sessions + SessionDir=${dmcfg.sessionData.desktops}/share/wayland-sessions ${optionalString cfg.autoLogin.enable '' [Autologin] User=${cfg.autoLogin.user} - Session=${defaultSessionName}.desktop + Session=${autoLoginSessionName}.desktop Relogin=${boolToString cfg.autoLogin.relogin} ''} ${cfg.extraConfig} ''; - defaultSessionName = - let - dm = xcfg.desktopManager.default; - wm = xcfg.windowManager.default; - in dm + optionalString (wm != "none") ("+" + wm); + autoLoginSessionName = dmcfg.sessionData.autologinSession; in { @@ -210,11 +206,9 @@ in SDDM auto-login requires services.xserver.displayManager.sddm.autoLogin.user to be set ''; } - { assertion = cfg.autoLogin.enable -> elem defaultSessionName dmcfg.session.names; + { assertion = cfg.autoLogin.enable -> autoLoginSessionName != null; message = '' - SDDM auto-login requires that services.xserver.desktopManager.default and - services.xserver.windowManager.default are set to valid values. The current - default session: ${defaultSessionName} is not valid. + SDDM auto-login requires that services.xserver.displayManager.defaultSession is set. ''; } ]; diff --git a/nixos/modules/services/x11/imwheel.nix b/nixos/modules/services/x11/imwheel.nix new file mode 100644 index 000000000000..3923df498e79 --- /dev/null +++ b/nixos/modules/services/x11/imwheel.nix @@ -0,0 +1,68 @@ +{ config, lib, pkgs, ... }: +with lib; +let + cfg = config.services.xserver.imwheel; +in + { + options = { + services.xserver.imwheel = { + enable = mkEnableOption "IMWheel service"; + + extraOptions = mkOption { + type = types.listOf types.str; + default = [ "--buttons=45" ]; + example = [ "--debug" ]; + description = '' + Additional command-line arguments to pass to + <command>imwheel</command>. + ''; + }; + + rules = mkOption { + type = types.attrsOf types.str; + default = {}; + example = literalExample '' + ".*" = ''' + None, Up, Button4, 8 + None, Down, Button5, 8 + Shift_L, Up, Shift_L|Button4, 4 + Shift_L, Down, Shift_L|Button5, 4 + Control_L, Up, Control_L|Button4 + Control_L, Down, Control_L|Button5 + '''; + ''; + description = '' + Window class translation rules. + /etc/X11/imwheelrc is generated based on this config + which means this config is global for all users. + See <link xlink:href="http://imwheel.sourceforge.net/imwheel.1.html">offical man pages</link> + for more informations. + ''; + }; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.imwheel ]; + + environment.etc."X11/imwheel/imwheelrc".source = + pkgs.writeText "imwheelrc" (concatStringsSep "\n\n" + (mapAttrsToList + (rule: conf: "\"${rule}\"\n${conf}") cfg.rules + )); + + systemd.user.services.imwheel = { + description = "imwheel service"; + wantedBy = [ "graphical-session.target" ]; + partOf = [ "graphical-session.target" ]; + serviceConfig = { + ExecStart = "${pkgs.imwheel}/bin/imwheel " + escapeShellArgs ([ + "--detach" + "--kill" + ] ++ cfg.extraOptions); + ExecStop = "${pkgs.procps}/bin/pkill imwheel"; + Restart = "on-failure"; + }; + }; + }; + } diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix index c17f3830d0e9..04a9fc46628c 100644 --- a/nixos/modules/services/x11/window-managers/default.nix +++ b/nixos/modules/services/x11/window-managers/default.nix @@ -59,15 +59,14 @@ in }; default = mkOption { - type = types.str; - default = "none"; + type = types.nullOr types.str; + default = null; example = "wmii"; - description = "Default window manager loaded if none have been chosen."; - apply = defaultWM: - if any (w: w.name == defaultWM) cfg.session then - defaultWM - else - throw "Default window manager (${defaultWM}) not found."; + description = '' + <emphasis role="strong">Deprecated</emphasis>, please use <xref linkend="opt-services.xserver.displayManager.defaultSession"/> instead. + + Default window manager loaded if none have been chosen. + ''; }; }; diff --git a/nixos/modules/tasks/filesystems/nfs.nix b/nixos/modules/tasks/filesystems/nfs.nix index e0e8bb1f03de..ddcc0ed8f5a4 100644 --- a/nixos/modules/tasks/filesystems/nfs.nix +++ b/nixos/modules/tasks/filesystems/nfs.nix @@ -25,6 +25,9 @@ let ''; nfsConfFile = pkgs.writeText "nfs.conf" cfg.extraConfig; + requestKeyConfFile = pkgs.writeText "request-key.conf" '' + create id_resolver * * ${pkgs.nfs-utils}/bin/nfsidmap -t 600 %k %d + ''; cfg = config.services.nfs; @@ -57,9 +60,12 @@ in systemd.packages = [ pkgs.nfs-utils ]; + environment.systemPackages = [ pkgs.keyutils ]; + environment.etc = { "idmapd.conf".source = idmapdConfFile; "nfs.conf".source = nfsConfFile; + "request-key.conf".source = requestKeyConfFile; }; systemd.services.nfs-blkmap = diff --git a/nixos/modules/tasks/kbd.nix b/nixos/modules/tasks/kbd.nix deleted file mode 100644 index c6ba998b19e6..000000000000 --- a/nixos/modules/tasks/kbd.nix +++ /dev/null @@ -1,127 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; - -let - - makeColor = n: value: "COLOR_${toString n}=${value}"; - makeColorCS = - let positions = [ "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F" ]; - in n: value: "\\033]P${elemAt positions (n - 1)}${value}"; - colors = concatImapStringsSep "\n" makeColor config.i18n.consoleColors; - - isUnicode = hasSuffix "UTF-8" (toUpper config.i18n.defaultLocale); - - optimizedKeymap = pkgs.runCommand "keymap" { - nativeBuildInputs = [ pkgs.buildPackages.kbd ]; - LOADKEYS_KEYMAP_PATH = "${kbdEnv}/share/keymaps/**"; - preferLocalBuild = true; - } '' - loadkeys -b ${optionalString isUnicode "-u"} "${config.i18n.consoleKeyMap}" > $out - ''; - - # Sadly, systemd-vconsole-setup doesn't support binary keymaps. - vconsoleConf = pkgs.writeText "vconsole.conf" '' - KEYMAP=${config.i18n.consoleKeyMap} - FONT=${config.i18n.consoleFont} - ${colors} - ''; - - kbdEnv = pkgs.buildEnv { - name = "kbd-env"; - paths = [ pkgs.kbd ] ++ config.i18n.consolePackages; - pathsToLink = [ "/share/consolefonts" "/share/consoletrans" "/share/keymaps" "/share/unimaps" ]; - }; - - setVconsole = !config.boot.isContainer; -in - -{ - ###### interface - - options = { - - # most options are defined in i18n.nix - - # FIXME: still needed? - boot.extraTTYs = mkOption { - default = []; - type = types.listOf types.str; - example = ["tty8" "tty9"]; - description = '' - Tty (virtual console) devices, in addition to the consoles on - which mingetty and syslogd run, that must be initialised. - Only useful if you have some program that you want to run on - some fixed console. For example, the NixOS installation CD - opens the manual in a web browser on console 7, so it sets - <option>boot.extraTTYs</option> to <literal>["tty7"]</literal>. - ''; - }; - - boot.earlyVconsoleSetup = mkOption { - default = false; - type = types.bool; - description = '' - Enable setting font as early as possible (in initrd). - ''; - }; - - }; - - - ###### implementation - - config = mkMerge [ - (mkIf (!setVconsole) { - systemd.services.systemd-vconsole-setup.enable = false; - }) - - (mkIf setVconsole (mkMerge [ - { environment.systemPackages = [ pkgs.kbd ]; - - # Let systemd-vconsole-setup.service do the work of setting up the - # virtual consoles. - environment.etc."vconsole.conf".source = vconsoleConf; - # Provide kbd with additional packages. - environment.etc.kbd.source = "${kbdEnv}/share"; - - boot.initrd.preLVMCommands = mkBefore '' - kbd_mode ${if isUnicode then "-u" else "-a"} -C /dev/console - printf "\033%%${if isUnicode then "G" else "@"}" >> /dev/console - loadkmap < ${optimizedKeymap} - - ${optionalString config.boot.earlyVconsoleSetup '' - setfont -C /dev/console $extraUtils/share/consolefonts/font.psf - ''} - - ${concatImapStringsSep "\n" (n: color: '' - printf "${makeColorCS n color}" >> /dev/console - '') config.i18n.consoleColors} - ''; - - systemd.services.systemd-vconsole-setup = - { before = [ "display-manager.service" ]; - after = [ "systemd-udev-settle.service" ]; - restartTriggers = [ vconsoleConf kbdEnv ]; - }; - } - - (mkIf config.boot.earlyVconsoleSetup { - boot.initrd.extraUtilsCommands = '' - mkdir -p $out/share/consolefonts - ${if substring 0 1 config.i18n.consoleFont == "/" then '' - font="${config.i18n.consoleFont}" - '' else '' - font="$(echo ${kbdEnv}/share/consolefonts/${config.i18n.consoleFont}.*)" - ''} - if [[ $font == *.gz ]]; then - gzip -cd $font > $out/share/consolefonts/font.psf - else - cp -L $font $out/share/consolefonts/font.psf - fi - ''; - }) - ])) - ]; - -} diff --git a/nixos/modules/tasks/network-interfaces-systemd.nix b/nixos/modules/tasks/network-interfaces-systemd.nix index 9ffa1089ee69..e25dc0c0b39a 100644 --- a/nixos/modules/tasks/network-interfaces-systemd.nix +++ b/nixos/modules/tasks/network-interfaces-systemd.nix @@ -60,8 +60,8 @@ in let domains = cfg.search ++ (optional (cfg.domain != null) cfg.domain); genericNetwork = override: - let gateway = optional (cfg.defaultGateway != null) cfg.defaultGateway.address - ++ optional (cfg.defaultGateway6 != null) cfg.defaultGateway6.address; + let gateway = optional (cfg.defaultGateway != null && (cfg.defaultGateway.address or "") != "") cfg.defaultGateway.address + ++ optional (cfg.defaultGateway6 != null && (cfg.defaultGateway6.address or "") != "") cfg.defaultGateway6.address; in optionalAttrs (gateway != [ ]) { routes = override [ { diff --git a/nixos/modules/virtualisation/container-config.nix b/nixos/modules/virtualisation/container-config.nix index f7a37d8c9f3b..6ff6bdd30c20 100644 --- a/nixos/modules/virtualisation/container-config.nix +++ b/nixos/modules/virtualisation/container-config.nix @@ -10,6 +10,7 @@ with lib; nix.optimise.automatic = mkDefault false; # the store is host managed services.udisks2.enable = mkDefault false; powerManagement.enable = mkDefault false; + documentation.nixos.enable = mkDefault false; networking.useHostResolvConf = mkDefault true; diff --git a/nixos/modules/virtualisation/lxd.nix b/nixos/modules/virtualisation/lxd.nix index 505c11abd208..b4934a86cf56 100644 --- a/nixos/modules/virtualisation/lxd.nix +++ b/nixos/modules/virtualisation/lxd.nix @@ -35,6 +35,18 @@ in with nixos. ''; }; + recommendedSysctlSettings = mkOption { + type = types.bool; + default = false; + description = '' + enables various settings to avoid common pitfalls when + running containers requiring many file operations. + Fixes errors like "Too many open files" or + "neighbour: ndisc_cache: neighbor table overflow!". + See https://lxd.readthedocs.io/en/latest/production-setup/ + for details. + ''; + }; }; }; @@ -69,8 +81,11 @@ in ExecStart = "@${pkgs.lxd.bin}/bin/lxd lxd --group lxd"; Type = "simple"; KillMode = "process"; # when stopping, leave the containers alone + LimitMEMLOCK = "infinity"; + LimitNOFILE = "1048576"; + LimitNPROC = "infinity"; + TasksMax = "infinity"; }; - }; users.groups.lxd.gid = config.ids.gids.lxd; @@ -79,5 +94,16 @@ in subUidRanges = [ { startUid = 1000000; count = 65536; } ]; subGidRanges = [ { startGid = 1000000; count = 65536; } ]; }; + + boot.kernel.sysctl = mkIf cfg.recommendedSysctlSettings { + "fs.inotify.max_queued_events" = 1048576; + "fs.inotify.max_user_instances" = 1048576; + "fs.inotify.max_user_watches" = 1048576; + "vm.max_map_count" = 262144; + "kernel.dmesg_restrict" = 1; + "net.ipv4.neigh.default.gc_thresh3" = 8192; + "net.ipv6.neigh.default.gc_thresh3" = 8192; + "kernel.keys.maxkeys" = 2000; + }; }; } diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix index 678ce3c28800..ca9c6f9a7f91 100644 --- a/nixos/release-combined.nix +++ b/nixos/release-combined.nix @@ -120,8 +120,8 @@ in rec { (all nixos.tests.networking.scripted.macvlan) (all nixos.tests.networking.scripted.sit) (all nixos.tests.networking.scripted.vlan) - (all nixos.tests.nfs3) - (all nixos.tests.nfs4) + (all nixos.tests.nfs3.simple) + (all nixos.tests.nfs4.simple) (all nixos.tests.openssh) (all nixos.tests.php-pcre) (all nixos.tests.predictable-interface-names.predictable) diff --git a/nixos/tests/3proxy.nix b/nixos/tests/3proxy.nix new file mode 100644 index 000000000000..b8e1dac0e89e --- /dev/null +++ b/nixos/tests/3proxy.nix @@ -0,0 +1,162 @@ +import ./make-test.nix ({ pkgs, ...} : { + name = "3proxy"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ misuzu ]; + }; + + nodes = { + peer0 = { lib, ... }: { + networking.useDHCP = false; + networking.interfaces.eth1 = { + ipv4.addresses = [ + { + address = "192.168.0.1"; + prefixLength = 24; + } + { + address = "216.58.211.111"; + prefixLength = 24; + } + ]; + }; + }; + + peer1 = { lib, ... }: { + networking.useDHCP = false; + networking.interfaces.eth1 = { + ipv4.addresses = [ + { + address = "192.168.0.2"; + prefixLength = 24; + } + { + address = "216.58.211.112"; + prefixLength = 24; + } + ]; + }; + # test that binding to [::] is working when ipv6 is disabled + networking.enableIPv6 = false; + services._3proxy = { + enable = true; + services = [ + { + type = "admin"; + bindPort = 9999; + auth = [ "none" ]; + } + { + type = "proxy"; + bindPort = 3128; + auth = [ "none" ]; + } + ]; + }; + networking.firewall.allowedTCPPorts = [ 3128 9999 ]; + }; + + peer2 = { lib, ... }: { + networking.useDHCP = false; + networking.interfaces.eth1 = { + ipv4.addresses = [ + { + address = "192.168.0.3"; + prefixLength = 24; + } + { + address = "216.58.211.113"; + prefixLength = 24; + } + ]; + }; + services._3proxy = { + enable = true; + services = [ + { + type = "admin"; + bindPort = 9999; + auth = [ "none" ]; + } + { + type = "proxy"; + bindPort = 3128; + auth = [ "iponly" ]; + acl = [ + { + rule = "allow"; + } + ]; + } + ]; + }; + networking.firewall.allowedTCPPorts = [ 3128 9999 ]; + }; + + peer3 = { lib, ... }: { + networking.useDHCP = false; + networking.interfaces.eth1 = { + ipv4.addresses = [ + { + address = "192.168.0.4"; + prefixLength = 24; + } + { + address = "216.58.211.114"; + prefixLength = 24; + } + ]; + }; + services._3proxy = { + enable = true; + usersFile = pkgs.writeText "3proxy.passwd" '' + admin:CR:$1$.GUV4Wvk$WnEVQtaqutD9.beO5ar1W/ + ''; + services = [ + { + type = "admin"; + bindPort = 9999; + auth = [ "none" ]; + } + { + type = "proxy"; + bindPort = 3128; + auth = [ "strong" ]; + acl = [ + { + rule = "allow"; + } + ]; + } + ]; + }; + networking.firewall.allowedTCPPorts = [ 3128 9999 ]; + }; + }; + + testScript = '' + startAll; + + $peer1->waitForUnit("3proxy.service"); + + # test none auth + $peer0->succeed("${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://192.168.0.2:3128 -S -O /dev/null http://216.58.211.112:9999"); + $peer0->succeed("${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://192.168.0.2:3128 -S -O /dev/null http://192.168.0.2:9999"); + $peer0->succeed("${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://192.168.0.2:3128 -S -O /dev/null http://127.0.0.1:9999"); + + $peer2->waitForUnit("3proxy.service"); + + # test iponly auth + $peer0->succeed("${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://192.168.0.3:3128 -S -O /dev/null http://216.58.211.113:9999"); + $peer0->fail("${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://192.168.0.3:3128 -S -O /dev/null http://192.168.0.3:9999"); + $peer0->fail("${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://192.168.0.3:3128 -S -O /dev/null http://127.0.0.1:9999"); + + $peer3->waitForUnit("3proxy.service"); + + # test strong auth + $peer0->succeed("${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://admin:bigsecret\@192.168.0.4:3128 -S -O /dev/null http://216.58.211.114:9999"); + $peer0->fail("${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://admin:bigsecret\@192.168.0.4:3128 -S -O /dev/null http://192.168.0.4:9999"); + $peer0->fail("${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://192.168.0.4:3128 -S -O /dev/null http://216.58.211.114:9999"); + $peer0->fail("${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://192.168.0.4:3128 -S -O /dev/null http://192.168.0.4:9999"); + $peer0->fail("${pkgs.wget}/bin/wget -e use_proxy=yes -e http_proxy=http://192.168.0.4:3128 -S -O /dev/null http://127.0.0.1:9999"); + ''; +}) diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 39ee3206d806..6413895c4c5e 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -21,6 +21,7 @@ let else {}; in { + _3proxy = handleTest ./3proxy.nix {}; acme = handleTestOn ["x86_64-linux"] ./acme.nix {}; atd = handleTest ./atd.nix {}; automysqlbackup = handleTest ./automysqlbackup.nix {}; @@ -190,8 +191,9 @@ in networkingProxy = handleTest ./networking-proxy.nix {}; nextcloud = handleTest ./nextcloud {}; nexus = handleTest ./nexus.nix {}; - nfs3 = handleTest ./nfs.nix { version = 3; }; - nfs4 = handleTest ./nfs.nix { version = 4; }; + # TODO: Test nfsv3 + Kerberos + nfs3 = handleTest ./nfs { version = 3; }; + nfs4 = handleTest ./nfs { version = 4; }; nghttpx = handleTest ./nghttpx.nix {}; nginx = handleTest ./nginx.nix {}; nginx-sso = handleTest ./nginx-sso.nix {}; @@ -294,5 +296,6 @@ in xss-lock = handleTest ./xss-lock.nix {}; yabar = handleTest ./yabar.nix {}; yggdrasil = handleTest ./yggdrasil.nix {}; + zsh-history = handleTest ./zsh-history.nix {}; zookeeper = handleTest ./zookeeper.nix {}; } diff --git a/nixos/tests/common/x11.nix b/nixos/tests/common/x11.nix index c5a7c165d126..5ad0ac20fac8 100644 --- a/nixos/tests/common/x11.nix +++ b/nixos/tests/common/x11.nix @@ -1,12 +1,12 @@ +{ lib, ... }: + { services.xserver.enable = true; # Automatically log in. services.xserver.displayManager.auto.enable = true; # Use IceWM as the window manager. - services.xserver.windowManager.default = "icewm"; - services.xserver.windowManager.icewm.enable = true; - # Don't use a desktop manager. - services.xserver.desktopManager.default = "none"; + services.xserver.displayManager.defaultSession = lib.mkDefault "none+icewm"; + services.xserver.windowManager.icewm.enable = true; } diff --git a/nixos/tests/gnome3-xorg.nix b/nixos/tests/gnome3-xorg.nix index eb4c376319be..aa03501f6a55 100644 --- a/nixos/tests/gnome3-xorg.nix +++ b/nixos/tests/gnome3-xorg.nix @@ -16,7 +16,7 @@ import ./make-test.nix ({ pkgs, ...} : { services.xserver.displayManager.lightdm.autoLogin.enable = true; services.xserver.displayManager.lightdm.autoLogin.user = "alice"; services.xserver.desktopManager.gnome3.enable = true; - services.xserver.desktopManager.default = "gnome-xorg"; + services.xserver.displayManager.defaultSession = "gnome-xorg"; virtualisation.memorySize = 1024; }; diff --git a/nixos/tests/hadoop/hdfs.nix b/nixos/tests/hadoop/hdfs.nix index e7d72a56e1e7..85aaab34b158 100644 --- a/nixos/tests/hadoop/hdfs.nix +++ b/nixos/tests/hadoop/hdfs.nix @@ -1,4 +1,4 @@ -import ../make-test.nix ({...}: { +import ../make-test-python.nix ({...}: { nodes = { namenode = {pkgs, ...}: { services.hadoop = { @@ -35,20 +35,20 @@ import ../make-test.nix ({...}: { }; testScript = '' - startAll + start_all() - $namenode->waitForUnit("hdfs-namenode"); - $namenode->waitForUnit("network.target"); - $namenode->waitForOpenPort(8020); - $namenode->waitForOpenPort(9870); + namenode.wait_for_unit("hdfs-namenode") + namenode.wait_for_unit("network.target") + namenode.wait_for_open_port(8020) + namenode.wait_for_open_port(9870) - $datanode->waitForUnit("hdfs-datanode"); - $datanode->waitForUnit("network.target"); - $datanode->waitForOpenPort(9864); - $datanode->waitForOpenPort(9866); - $datanode->waitForOpenPort(9867); + datanode.wait_for_unit("hdfs-datanode") + datanode.wait_for_unit("network.target") + datanode.wait_for_open_port(9864) + datanode.wait_for_open_port(9866) + datanode.wait_for_open_port(9867) - $namenode->succeed("curl http://namenode:9870"); - $datanode->succeed("curl http://datanode:9864"); + namenode.succeed("curl http://namenode:9870") + datanode.succeed("curl http://datanode:9864") ''; }) diff --git a/nixos/tests/hadoop/yarn.nix b/nixos/tests/hadoop/yarn.nix index 031592301f17..2264ecaff155 100644 --- a/nixos/tests/hadoop/yarn.nix +++ b/nixos/tests/hadoop/yarn.nix @@ -1,4 +1,4 @@ -import ../make-test.nix ({...}: { +import ../make-test-python.nix ({...}: { nodes = { resourcemanager = {pkgs, ...}: { services.hadoop.package = pkgs.hadoop_3_1; @@ -28,19 +28,19 @@ import ../make-test.nix ({...}: { }; testScript = '' - startAll; + start_all() - $resourcemanager->waitForUnit("yarn-resourcemanager"); - $resourcemanager->waitForUnit("network.target"); - $resourcemanager->waitForOpenPort(8031); - $resourcemanager->waitForOpenPort(8088); + resourcemanager.wait_for_unit("yarn-resourcemanager") + resourcemanager.wait_for_unit("network.target") + resourcemanager.wait_for_open_port(8031) + resourcemanager.wait_for_open_port(8088) - $nodemanager->waitForUnit("yarn-nodemanager"); - $nodemanager->waitForUnit("network.target"); - $nodemanager->waitForOpenPort(8042); - $nodemanager->waitForOpenPort(8041); + nodemanager.wait_for_unit("yarn-nodemanager") + nodemanager.wait_for_unit("network.target") + nodemanager.wait_for_open_port(8042) + nodemanager.wait_for_open_port(8041) - $resourcemanager->succeed("curl http://localhost:8088"); - $nodemanager->succeed("curl http://localhost:8042"); + resourcemanager.succeed("curl http://localhost:8088") + nodemanager.succeed("curl http://localhost:8042") ''; }) diff --git a/nixos/tests/haproxy.nix b/nixos/tests/haproxy.nix index 72e77a68193e..b6fed3e2108f 100644 --- a/nixos/tests/haproxy.nix +++ b/nixos/tests/haproxy.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, ...}: { +import ./make-test-python.nix ({ pkgs, ...}: { name = "haproxy"; nodes = { machine = { ... }: { @@ -33,11 +33,13 @@ import ./make-test.nix ({ pkgs, ...}: { }; }; testScript = '' - startAll; - $machine->waitForUnit('multi-user.target'); - $machine->waitForUnit('haproxy.service'); - $machine->waitForUnit('httpd.service'); - $machine->succeed('curl -k http://localhost:80/index.txt | grep "We are all good!"'); - $machine->succeed('curl -k http://localhost:80/metrics | grep haproxy_process_pool_allocated_bytes'); + start_all() + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("haproxy.service") + machine.wait_for_unit("httpd.service") + assert "We are all good!" in machine.succeed("curl -k http://localhost:80/index.txt") + assert "haproxy_process_pool_allocated_bytes" in machine.succeed( + "curl -k http://localhost:80/metrics" + ) ''; }) diff --git a/nixos/tests/hitch/default.nix b/nixos/tests/hitch/default.nix index cb24c4dcffc2..106120256412 100644 --- a/nixos/tests/hitch/default.nix +++ b/nixos/tests/hitch/default.nix @@ -1,4 +1,4 @@ -import ../make-test.nix ({ pkgs, ... }: +import ../make-test-python.nix ({ pkgs, ... }: { name = "hitch"; meta = with pkgs.stdenv.lib.maintainers; { @@ -23,11 +23,11 @@ import ../make-test.nix ({ pkgs, ... }: testScript = '' - startAll; + start_all() - $machine->waitForUnit('multi-user.target'); - $machine->waitForUnit('hitch.service'); - $machine->waitForOpenPort(443); - $machine->succeed('curl -k https://localhost:443/index.txt | grep "We are all good!"'); + machine.wait_for_unit("multi-user.target") + machine.wait_for_unit("hitch.service") + machine.wait_for_open_port(443) + assert "We are all good!" in machine.succeed("curl -k https://localhost:443/index.txt") ''; }) diff --git a/nixos/tests/i3wm.nix b/nixos/tests/i3wm.nix index 8afa845f1e21..126178d11879 100644 --- a/nixos/tests/i3wm.nix +++ b/nixos/tests/i3wm.nix @@ -7,7 +7,7 @@ import ./make-test-python.nix ({ pkgs, ...} : { machine = { lib, ... }: { imports = [ ./common/x11.nix ./common/user-account.nix ]; services.xserver.displayManager.auto.user = "alice"; - services.xserver.windowManager.default = lib.mkForce "i3"; + services.xserver.displayManager.defaultSession = lib.mkForce "none+i3"; services.xserver.windowManager.i3.enable = true; }; diff --git a/nixos/tests/initrd-network.nix b/nixos/tests/initrd-network.nix index ed9b82e2da77..4796ff9b7c8d 100644 --- a/nixos/tests/initrd-network.nix +++ b/nixos/tests/initrd-network.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, ...} : { +import ./make-test-python.nix ({ pkgs, ...} : { name = "initrd-network"; meta.maintainers = [ pkgs.stdenv.lib.maintainers.eelco ]; @@ -15,8 +15,8 @@ import ./make-test.nix ({ pkgs, ...} : { testScript = '' - startAll; - $machine->waitForUnit("multi-user.target"); - $machine->succeed("ip link >&2"); + start_all() + machine.wait_for_unit("multi-user.target") + machine.succeed("ip link >&2") ''; }) diff --git a/nixos/tests/leaps.nix b/nixos/tests/leaps.nix index 6163fed56b6f..65b475d734ec 100644 --- a/nixos/tests/leaps.nix +++ b/nixos/tests/leaps.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, ... }: +import ./make-test-python.nix ({ pkgs, ... }: { name = "leaps"; @@ -22,9 +22,11 @@ import ./make-test.nix ({ pkgs, ... }: testScript = '' - startAll; - $server->waitForOpenPort(6666); - $client->waitForUnit("network.target"); - $client->succeed("${pkgs.curl}/bin/curl http://server:6666/leaps/ | grep -i 'leaps'"); + start_all() + server.wait_for_open_port(6666) + client.wait_for_unit("network.target") + assert "leaps" in client.succeed( + "${pkgs.curl}/bin/curl http://server:6666/leaps/" + ) ''; }) diff --git a/nixos/tests/lidarr.nix b/nixos/tests/lidarr.nix index 85fcbd21d8c0..d3f83e5d9145 100644 --- a/nixos/tests/lidarr.nix +++ b/nixos/tests/lidarr.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ lib, ... }: +import ./make-test-python.nix ({ lib, ... }: with lib; @@ -11,8 +11,10 @@ with lib; { services.lidarr.enable = true; }; testScript = '' - $machine->waitForUnit('lidarr.service'); - $machine->waitForOpenPort('8686'); - $machine->succeed("curl --fail http://localhost:8686/"); + start_all() + + machine.wait_for_unit("lidarr.service") + machine.wait_for_open_port("8686") + machine.succeed("curl --fail http://localhost:8686/") ''; }) diff --git a/nixos/tests/lightdm.nix b/nixos/tests/lightdm.nix index ef30f7741e23..46c2ed7ccc59 100644 --- a/nixos/tests/lightdm.nix +++ b/nixos/tests/lightdm.nix @@ -8,9 +8,8 @@ import ./make-test-python.nix ({ pkgs, ...} : { imports = [ ./common/user-account.nix ]; services.xserver.enable = true; services.xserver.displayManager.lightdm.enable = true; - services.xserver.windowManager.default = "icewm"; + services.xserver.displayManager.defaultSession = "none+icewm"; services.xserver.windowManager.icewm.enable = true; - services.xserver.desktopManager.default = "none"; }; enableOCR = true; diff --git a/nixos/tests/mailcatcher.nix b/nixos/tests/mailcatcher.nix index eb5b606ecc84..2ef38544fe0a 100644 --- a/nixos/tests/mailcatcher.nix +++ b/nixos/tests/mailcatcher.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ lib, ... }: +import ./make-test-python.nix ({ lib, ... }: { name = "mailcatcher"; @@ -16,11 +16,15 @@ import ./make-test.nix ({ lib, ... }: }; testScript = '' - startAll; + start_all() - $machine->waitForUnit('mailcatcher.service'); - $machine->waitForOpenPort('1025'); - $machine->succeed('echo "this is the body of the email" | mail -s "subject" root@example.org'); - $machine->succeed('curl http://localhost:1080/messages/1.source') =~ /this is the body of the email/ or die; + machine.wait_for_unit("mailcatcher.service") + machine.wait_for_open_port("1025") + machine.succeed( + 'echo "this is the body of the email" | mail -s "subject" root@example.org' + ) + assert "this is the body of the email" in machine.succeed( + "curl http://localhost:1080/messages/1.source" + ) ''; }) diff --git a/nixos/tests/mutable-users.nix b/nixos/tests/mutable-users.nix index e590703ab2f4..49c7f78b82ed 100644 --- a/nixos/tests/mutable-users.nix +++ b/nixos/tests/mutable-users.nix @@ -1,6 +1,6 @@ # Mutable users tests. -import ./make-test.nix ({ pkgs, ...} : { +import ./make-test-python.nix ({ pkgs, ...} : { name = "mutable-users"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ gleber ]; @@ -19,21 +19,27 @@ import ./make-test.nix ({ pkgs, ...} : { immutableSystem = nodes.machine.config.system.build.toplevel; mutableSystem = nodes.mutable.config.system.build.toplevel; in '' - $machine->start(); - $machine->waitForUnit("default.target"); + machine.start() + machine.wait_for_unit("default.target") # Machine starts in immutable mode. Add a user and test if reactivating # configuration removes the user. - $machine->fail("cat /etc/passwd | grep ^foobar:"); - $machine->succeed("sudo useradd foobar"); - $machine->succeed("cat /etc/passwd | grep ^foobar:"); - $machine->succeed("${immutableSystem}/bin/switch-to-configuration test"); - $machine->fail("cat /etc/passwd | grep ^foobar:"); + with subtest("Machine in immutable mode"): + assert "foobar" not in machine.succeed("cat /etc/passwd") + machine.succeed("sudo useradd foobar") + assert "foobar" in machine.succeed("cat /etc/passwd") + machine.succeed( + "${immutableSystem}/bin/switch-to-configuration test" + ) + assert "foobar" not in machine.succeed("cat /etc/passwd") # In immutable mode passwd is not wrapped, while in mutable mode it is # wrapped. - $machine->succeed('which passwd | grep /run/current-system/'); - $machine->succeed("${mutableSystem}/bin/switch-to-configuration test"); - $machine->succeed('which passwd | grep /run/wrappers/'); + with subtest("Password is wrapped in mutable mode"): + assert "/run/current-system/" in machine.succeed("which passwd") + machine.succeed( + "${mutableSystem}/bin/switch-to-configuration test" + ) + assert "/run/wrappers/" in machine.succeed("which passwd") ''; }) diff --git a/nixos/tests/mxisd.nix b/nixos/tests/mxisd.nix index 0039256f5861..b2b60db4d822 100644 --- a/nixos/tests/mxisd.nix +++ b/nixos/tests/mxisd.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, ... } : { +import ./make-test-python.nix ({ pkgs, ... } : { name = "mxisd"; meta = with pkgs.stdenv.lib.maintainers; { @@ -19,13 +19,12 @@ import ./make-test.nix ({ pkgs, ... } : { }; testScript = '' - startAll; - $server_mxisd->waitForUnit("mxisd.service"); - $server_mxisd->waitForOpenPort(8090); - $server_mxisd->succeed("curl -Ssf \"http://127.0.0.1:8090/_matrix/identity/api/v1\""); - $server_ma1sd->waitForUnit("mxisd.service"); - $server_ma1sd->waitForOpenPort(8090); - $server_ma1sd->succeed("curl -Ssf \"http://127.0.0.1:8090/_matrix/identity/api/v1\"") - + start_all() + server_mxisd.wait_for_unit("mxisd.service") + server_mxisd.wait_for_open_port(8090) + server_mxisd.succeed("curl -Ssf 'http://127.0.0.1:8090/_matrix/identity/api/v1'") + server_ma1sd.wait_for_unit("mxisd.service") + server_ma1sd.wait_for_open_port(8090) + server_ma1sd.succeed("curl -Ssf 'http://127.0.0.1:8090/_matrix/identity/api/v1'") ''; }) diff --git a/nixos/tests/nesting.nix b/nixos/tests/nesting.nix index 1306d6f8e0c5..6388b67a6e40 100644 --- a/nixos/tests/nesting.nix +++ b/nixos/tests/nesting.nix @@ -1,4 +1,4 @@ -import ./make-test.nix { +import ./make-test-python.nix { name = "nesting"; nodes = { clone = { pkgs, ... }: { @@ -19,24 +19,26 @@ import ./make-test.nix { }; }; testScript = '' - $clone->waitForUnit("default.target"); - $clone->succeed("cowsay hey"); - $clone->fail("hello"); + clone.wait_for_unit("default.target") + clone.succeed("cowsay hey") + clone.fail("hello") - # Nested clones do inherit from parent - $clone->succeed("/run/current-system/fine-tune/child-1/bin/switch-to-configuration test"); - $clone->succeed("cowsay hey"); - $clone->succeed("hello"); + with subtest("Nested clones do inherit from parent"): + clone.succeed( + "/run/current-system/fine-tune/child-1/bin/switch-to-configuration test" + ) + clone.succeed("cowsay hey") + clone.succeed("hello") + children.wait_for_unit("default.target") + children.succeed("cowsay hey") + children.fail("hello") - $children->waitForUnit("default.target"); - $children->succeed("cowsay hey"); - $children->fail("hello"); - - # Nested children do not inherit from parent - $children->succeed("/run/current-system/fine-tune/child-1/bin/switch-to-configuration test"); - $children->fail("cowsay hey"); - $children->succeed("hello"); - + with subtest("Nested children do not inherit from parent"): + children.succeed( + "/run/current-system/fine-tune/child-1/bin/switch-to-configuration test" + ) + children.fail("cowsay hey") + children.succeed("hello") ''; } diff --git a/nixos/tests/networking.nix b/nixos/tests/networking.nix index e0585d8f1bb4..9448a104073f 100644 --- a/nixos/tests/networking.nix +++ b/nixos/tests/networking.nix @@ -4,7 +4,7 @@ # bool: whether to use networkd in the tests , networkd }: -with import ../lib/testing.nix { inherit system pkgs; }; +with import ../lib/testing-python.nix { inherit system pkgs; }; with pkgs.lib; let @@ -75,10 +75,11 @@ let machine.networking.useDHCP = false; machine.networking.useNetworkd = networkd; testScript = '' - startAll; - $machine->waitForUnit("network.target"); - $machine->succeed("ip addr show lo | grep -q 'inet 127.0.0.1/8 '"); - $machine->succeed("ip addr show lo | grep -q 'inet6 ::1/128 '"); + start_all() + machine.wait_for_unit("network.target") + loopback_addresses = machine.succeed("ip addr show lo") + assert "inet 127.0.0.1/8" in loopback_addresses + assert "inet6 ::1/128" in loopback_addresses ''; }; static = { @@ -102,35 +103,35 @@ let }; testScript = { ... }: '' - startAll; + start_all() - $client->waitForUnit("network.target"); - $router->waitForUnit("network-online.target"); + client.wait_for_unit("network.target") + router.wait_for_unit("network-online.target") - # Make sure dhcpcd is not started - $client->fail("systemctl status dhcpcd.service"); + with subtest("Make sure dhcpcd is not started"): + client.fail("systemctl status dhcpcd.service") - # Test vlan 1 - $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.3"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.10"); + with subtest("Test vlan 1"): + client.wait_until_succeeds("ping -c 1 192.168.1.1") + client.wait_until_succeeds("ping -c 1 192.168.1.2") + client.wait_until_succeeds("ping -c 1 192.168.1.3") + client.wait_until_succeeds("ping -c 1 192.168.1.10") - $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.10"); + router.wait_until_succeeds("ping -c 1 192.168.1.1") + router.wait_until_succeeds("ping -c 1 192.168.1.2") + router.wait_until_succeeds("ping -c 1 192.168.1.3") + router.wait_until_succeeds("ping -c 1 192.168.1.10") - # Test vlan 2 - $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.2.2"); + with subtest("Test vlan 2"): + client.wait_until_succeeds("ping -c 1 192.168.2.1") + client.wait_until_succeeds("ping -c 1 192.168.2.2") - $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.2.2"); + router.wait_until_succeeds("ping -c 1 192.168.2.1") + router.wait_until_succeeds("ping -c 1 192.168.2.2") - # Test default gateway - $router->waitUntilSucceeds("ping -c 1 192.168.3.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.3.1"); + with subtest("Test default gateway"): + router.wait_until_succeeds("ping -c 1 192.168.3.1") + client.wait_until_succeeds("ping -c 1 192.168.3.1") ''; }; dhcpSimple = { @@ -155,38 +156,38 @@ let }; testScript = { ... }: '' - startAll; - - $client->waitForUnit("network.target"); - $router->waitForUnit("network-online.target"); - - # Wait until we have an ip address on each interface - $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); - $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'"); - $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q '192.168.2'"); - $client->waitUntilSucceeds("ip addr show dev eth2 | grep -q 'fd00:1234:5678:2:'"); - - # Test vlan 1 - $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1"); - $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::2"); - - $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1"); - $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::2"); - - # Test vlan 2 - $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.2.2"); - $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::1"); - $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::2"); - - $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.2.2"); - $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::1"); - $router->waitUntilSucceeds("ping -c 1 fd00:1234:5678:2::2"); + start_all() + + client.wait_for_unit("network.target") + router.wait_for_unit("network-online.target") + + with subtest("Wait until we have an ip address on each interface"): + client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'") + client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'") + client.wait_until_succeeds("ip addr show dev eth2 | grep -q '192.168.2'") + client.wait_until_succeeds("ip addr show dev eth2 | grep -q 'fd00:1234:5678:2:'") + + with subtest("Test vlan 1"): + client.wait_until_succeeds("ping -c 1 192.168.1.1") + client.wait_until_succeeds("ping -c 1 192.168.1.2") + client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1") + client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::2") + + router.wait_until_succeeds("ping -c 1 192.168.1.1") + router.wait_until_succeeds("ping -c 1 192.168.1.2") + router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1") + router.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::2") + + with subtest("Test vlan 2"): + client.wait_until_succeeds("ping -c 1 192.168.2.1") + client.wait_until_succeeds("ping -c 1 192.168.2.2") + client.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::1") + client.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::2") + + router.wait_until_succeeds("ping -c 1 192.168.2.1") + router.wait_until_succeeds("ping -c 1 192.168.2.2") + router.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::1") + router.wait_until_succeeds("ping -c 1 fd00:1234:5678:2::2") ''; }; dhcpOneIf = { @@ -206,28 +207,28 @@ let }; testScript = { ... }: '' - startAll; + start_all() - # Wait for networking to come up - $client->waitForUnit("network.target"); - $router->waitForUnit("network.target"); + with subtest("Wait for networking to come up"): + client.wait_for_unit("network.target") + router.wait_for_unit("network.target") - # Wait until we have an ip address on each interface - $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); + with subtest("Wait until we have an ip address on each interface"): + client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'") - # Test vlan 1 - $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); + with subtest("Test vlan 1"): + client.wait_until_succeeds("ping -c 1 192.168.1.1") + client.wait_until_succeeds("ping -c 1 192.168.1.2") - $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); + router.wait_until_succeeds("ping -c 1 192.168.1.1") + router.wait_until_succeeds("ping -c 1 192.168.1.2") - # Test vlan 2 - $client->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $client->fail("ping -c 1 192.168.2.2"); + with subtest("Test vlan 2"): + client.wait_until_succeeds("ping -c 1 192.168.2.1") + client.fail("ping -c 1 192.168.2.2") - $router->waitUntilSucceeds("ping -c 1 192.168.2.1"); - $router->fail("ping -c 1 192.168.2.2"); + router.wait_until_succeeds("ping -c 1 192.168.2.1") + router.fail("ping -c 1 192.168.2.2") ''; }; bond = let @@ -252,18 +253,18 @@ let nodes.client2 = node "192.168.1.2"; testScript = { ... }: '' - startAll; + start_all() - # Wait for networking to come up - $client1->waitForUnit("network.target"); - $client2->waitForUnit("network.target"); + with subtest("Wait for networking to come up"): + client1.wait_for_unit("network.target") + client2.wait_for_unit("network.target") - # Test bonding - $client1->waitUntilSucceeds("ping -c 2 192.168.1.1"); - $client1->waitUntilSucceeds("ping -c 2 192.168.1.2"); + with subtest("Test bonding"): + client1.wait_until_succeeds("ping -c 2 192.168.1.1") + client1.wait_until_succeeds("ping -c 2 192.168.1.2") - $client2->waitUntilSucceeds("ping -c 2 192.168.1.1"); - $client2->waitUntilSucceeds("ping -c 2 192.168.1.2"); + client2.wait_until_succeeds("ping -c 2 192.168.1.1") + client2.wait_until_succeeds("ping -c 2 192.168.1.2") ''; }; bridge = let @@ -294,25 +295,24 @@ let }; testScript = { ... }: '' - startAll; + start_all() - # Wait for networking to come up - $client1->waitForUnit("network.target"); - $client2->waitForUnit("network.target"); - $router->waitForUnit("network.target"); + with subtest("Wait for networking to come up"): + for machine in client1, client2, router: + machine.wait_for_unit("network.target") - # Test bridging - $client1->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client1->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $client1->waitUntilSucceeds("ping -c 1 192.168.1.3"); + with subtest("Test bridging"): + client1.wait_until_succeeds("ping -c 1 192.168.1.1") + client1.wait_until_succeeds("ping -c 1 192.168.1.2") + client1.wait_until_succeeds("ping -c 1 192.168.1.3") - $client2->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client2->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $client2->waitUntilSucceeds("ping -c 1 192.168.1.3"); + client2.wait_until_succeeds("ping -c 1 192.168.1.1") + client2.wait_until_succeeds("ping -c 1 192.168.1.2") + client2.wait_until_succeeds("ping -c 1 192.168.1.3") - $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); + router.wait_until_succeeds("ping -c 1 192.168.1.1") + router.wait_until_succeeds("ping -c 1 192.168.1.2") + router.wait_until_succeeds("ping -c 1 192.168.1.3") ''; }; macvlan = { @@ -340,35 +340,35 @@ let }; testScript = { ... }: '' - startAll; - - # Wait for networking to come up - $client->waitForUnit("network.target"); - $router->waitForUnit("network.target"); - - # Wait until we have an ip address on each interface - $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q '192.168.1'"); - $client->waitUntilSucceeds("ip addr show dev macvlan | grep -q '192.168.1'"); - - # Print lots of diagnostic information - $router->log('**********************************************'); - $router->succeed("ip addr >&2"); - $router->succeed("ip route >&2"); - $router->execute("iptables-save >&2"); - $client->log('=============================================='); - $client->succeed("ip addr >&2"); - $client->succeed("ip route >&2"); - $client->execute("iptables-save >&2"); - $client->log('##############################################'); - - # Test macvlan creates routable ips - $client->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $client->waitUntilSucceeds("ping -c 1 192.168.1.3"); - - $router->waitUntilSucceeds("ping -c 1 192.168.1.1"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.2"); - $router->waitUntilSucceeds("ping -c 1 192.168.1.3"); + start_all() + + with subtest("Wait for networking to come up"): + client.wait_for_unit("network.target") + router.wait_for_unit("network.target") + + with subtest("Wait until we have an ip address on each interface"): + client.wait_until_succeeds("ip addr show dev eth1 | grep -q '192.168.1'") + client.wait_until_succeeds("ip addr show dev macvlan | grep -q '192.168.1'") + + with subtest("Print lots of diagnostic information"): + router.log("**********************************************") + router.succeed("ip addr >&2") + router.succeed("ip route >&2") + router.execute("iptables-save >&2") + client.log("==============================================") + client.succeed("ip addr >&2") + client.succeed("ip route >&2") + client.execute("iptables-save >&2") + client.log("##############################################") + + with subtest("Test macvlan creates routable ips"): + client.wait_until_succeeds("ping -c 1 192.168.1.1") + client.wait_until_succeeds("ping -c 1 192.168.1.2") + client.wait_until_succeeds("ping -c 1 192.168.1.3") + + router.wait_until_succeeds("ping -c 1 192.168.1.1") + router.wait_until_succeeds("ping -c 1 192.168.1.2") + router.wait_until_succeeds("ping -c 1 192.168.1.3") ''; }; sit = let @@ -395,22 +395,22 @@ let nodes.client2 = node { address4 = "192.168.1.2"; remote = "192.168.1.1"; address6 = "fc00::2"; }; testScript = { ... }: '' - startAll; + start_all() - # Wait for networking to be configured - $client1->waitForUnit("network.target"); - $client2->waitForUnit("network.target"); + with subtest("Wait for networking to be configured"): + client1.wait_for_unit("network.target") + client2.wait_for_unit("network.target") - # Print diagnostic information - $client1->succeed("ip addr >&2"); - $client2->succeed("ip addr >&2"); + # Print diagnostic information + client1.succeed("ip addr >&2") + client2.succeed("ip addr >&2") - # Test ipv6 - $client1->waitUntilSucceeds("ping -c 1 fc00::1"); - $client1->waitUntilSucceeds("ping -c 1 fc00::2"); + with subtest("Test ipv6"): + client1.wait_until_succeeds("ping -c 1 fc00::1") + client1.wait_until_succeeds("ping -c 1 fc00::2") - $client2->waitUntilSucceeds("ping -c 1 fc00::1"); - $client2->waitUntilSucceeds("ping -c 1 fc00::2"); + client2.wait_until_succeeds("ping -c 1 fc00::1") + client2.wait_until_succeeds("ping -c 1 fc00::2") ''; }; vlan = let @@ -435,15 +435,15 @@ let nodes.client2 = node "192.168.1.2"; testScript = { ... }: '' - startAll; + start_all() - # Wait for networking to be configured - $client1->waitForUnit("network.target"); - $client2->waitForUnit("network.target"); + with subtest("Wait for networking to be configured"): + client1.wait_for_unit("network.target") + client2.wait_for_unit("network.target") - # Test vlan is setup - $client1->succeed("ip addr show dev vlan >&2"); - $client2->succeed("ip addr show dev vlan >&2"); + with subtest("Test vlan is setup"): + client1.succeed("ip addr show dev vlan >&2") + client2.succeed("ip addr show dev vlan >&2") ''; }; virtual = { @@ -464,33 +464,38 @@ let }; testScript = '' - my $targetList = <<'END'; + targetList = """ tap0: tap persist user 0 tun0: tun persist user 0 - END - - # Wait for networking to come up - $machine->start; - $machine->waitForUnit("network-online.target"); - - # Test interfaces set up - my $list = $machine->succeed("ip tuntap list | sort"); - "$list" eq "$targetList" or die( - "The list of virtual interfaces does not match the expected one:\n", - "Result:\n", "$list\n", - "Expected:\n", "$targetList\n" - ); - - # Test interfaces clean up - $machine->succeed("systemctl stop network-addresses-tap0"); - $machine->sleep(10); - $machine->succeed("systemctl stop network-addresses-tun0"); - $machine->sleep(10); - my $residue = $machine->succeed("ip tuntap list"); - $residue eq "" or die( - "Some virtual interface has not been properly cleaned:\n", - "$residue\n" - ); + """.strip() + + with subtest("Wait for networking to come up"): + machine.start() + machine.wait_for_unit("network-online.target") + + with subtest("Test interfaces set up"): + list = machine.succeed("ip tuntap list | sort").strip() + assert ( + list == targetList + ), """ + The list of virtual interfaces does not match the expected one: + Result: + {} + Expected: + {} + """.format( + list, targetList + ) + + with subtest("Test interfaces clean up"): + machine.succeed("systemctl stop network-addresses-tap0") + machine.sleep(10) + machine.succeed("systemctl stop network-addresses-tun0") + machine.sleep(10) + residue = machine.succeed("ip tuntap list") + assert ( + residue is "" + ), "Some virtual interface has not been properly cleaned:\n{}".format(residue) ''; }; privacy = { @@ -522,7 +527,7 @@ let ''; }; }; - nodes.clientWithPrivacy = { pkgs, ... }: with pkgs.lib; { + nodes.client_with_privacy = { pkgs, ... }: with pkgs.lib; { virtualisation.vlans = [ 1 ]; networking = { useNetworkd = networkd; @@ -550,25 +555,31 @@ let }; testScript = { ... }: '' - startAll; - - $client->waitForUnit("network.target"); - $clientWithPrivacy->waitForUnit("network.target"); - $router->waitForUnit("network-online.target"); - - # Wait until we have an ip address - $clientWithPrivacy->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'"); - $client->waitUntilSucceeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'"); - - # Test vlan 1 - $clientWithPrivacy->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1"); - $client->waitUntilSucceeds("ping -c 1 fd00:1234:5678:1::1"); - - # Test address used is temporary - $clientWithPrivacy->waitUntilSucceeds("! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'"); - - # Test address used is EUI-64 - $client->waitUntilSucceeds("ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'"); + start_all() + + client.wait_for_unit("network.target") + client_with_privacy.wait_for_unit("network.target") + router.wait_for_unit("network-online.target") + + with subtest("Wait until we have an ip address"): + client_with_privacy.wait_until_succeeds( + "ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'" + ) + client.wait_until_succeeds("ip addr show dev eth1 | grep -q 'fd00:1234:5678:1:'") + + with subtest("Test vlan 1"): + client_with_privacy.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1") + client.wait_until_succeeds("ping -c 1 fd00:1234:5678:1::1") + + with subtest("Test address used is temporary"): + client_with_privacy.wait_until_succeeds( + "! ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'" + ) + + with subtest("Test address used is EUI-64"): + client.wait_until_succeeds( + "ip route get fd00:1234:5678:1::1 | grep -q ':[a-f0-9]*ff:fe[a-f0-9]*:'" + ) ''; }; routes = { @@ -591,47 +602,57 @@ let }; testScript = '' - my $targetIPv4Table = <<'END'; + targetIPv4Table = """ 10.0.0.0/16 proto static scope link mtu 1500 192.168.1.0/24 proto kernel scope link src 192.168.1.2 192.168.2.0/24 via 192.168.1.1 proto static - END + """.strip() - my $targetIPv6Table = <<'END'; + targetIPv6Table = """ 2001:1470:fffd:2097::/64 proto kernel metric 256 pref medium 2001:1470:fffd:2098::/64 via fdfd:b3f0::1 proto static metric 1024 pref medium fdfd:b3f0::/48 proto static metric 1024 pref medium - END - - $machine->start; - $machine->waitForUnit("network.target"); - - # test routing tables - my $ipv4Table = $machine->succeed("ip -4 route list dev eth0 | head -n3"); - my $ipv6Table = $machine->succeed("ip -6 route list dev eth0 | head -n3"); - "$ipv4Table" eq "$targetIPv4Table" or die( - "The IPv4 routing table does not match the expected one:\n", - "Result:\n", "$ipv4Table\n", - "Expected:\n", "$targetIPv4Table\n" - ); - "$ipv6Table" eq "$targetIPv6Table" or die( - "The IPv6 routing table does not match the expected one:\n", - "Result:\n", "$ipv6Table\n", - "Expected:\n", "$targetIPv6Table\n" - ); - - # test clean-up of the tables - $machine->succeed("systemctl stop network-addresses-eth0"); - my $ipv4Residue = $machine->succeed("ip -4 route list dev eth0 | head -n-3"); - my $ipv6Residue = $machine->succeed("ip -6 route list dev eth0 | head -n-3"); - $ipv4Residue eq "" or die( - "The IPv4 routing table has not been properly cleaned:\n", - "$ipv4Residue\n" - ); - $ipv6Residue eq "" or die( - "The IPv6 routing table has not been properly cleaned:\n", - "$ipv6Residue\n" - ); + """.strip() + + machine.start() + machine.wait_for_unit("network.target") + + with subtest("test routing tables"): + ipv4Table = machine.succeed("ip -4 route list dev eth0 | head -n3").strip() + ipv6Table = machine.succeed("ip -6 route list dev eth0 | head -n3").strip() + assert ( + ipv4Table == targetIPv4Table + ), """ + The IPv4 routing table does not match the expected one: + Result: + {} + Expected: + {} + """.format( + ipv4Table, targetIPv4Table + ) + assert ( + ipv6Table == targetIPv6Table + ), """ + The IPv6 routing table does not match the expected one: + Result: + {} + Expected: + {} + """.format( + ipv6Table, targetIPv6Table + ) + + with subtest("test clean-up of the tables"): + machine.succeed("systemctl stop network-addresses-eth0") + ipv4Residue = machine.succeed("ip -4 route list dev eth0 | head -n-3").strip() + ipv6Residue = machine.succeed("ip -6 route list dev eth0 | head -n-3").strip() + assert ( + ipv4Residue is "" + ), "The IPv4 routing table has not been properly cleaned:\n{}".format(ipv4Residue) + assert ( + ipv6Residue is "" + ), "The IPv6 routing table has not been properly cleaned:\n{}".format(ipv6Residue) ''; }; }; diff --git a/nixos/tests/nfs.nix b/nixos/tests/nfs.nix deleted file mode 100644 index 2f655336e757..000000000000 --- a/nixos/tests/nfs.nix +++ /dev/null @@ -1,90 +0,0 @@ -import ./make-test.nix ({ pkgs, version ? 4, ... }: - -let - - client = - { pkgs, ... }: - { fileSystems = pkgs.lib.mkVMOverride - [ { mountPoint = "/data"; - # nfs4 exports the export with fsid=0 as a virtual root directory - device = if (version == 4) then "server:/" else "server:/data"; - fsType = "nfs"; - options = [ "vers=${toString version}" ]; - } - ]; - networking.firewall.enable = false; # FIXME: only open statd - }; - -in - -{ - name = "nfs"; - meta = with pkgs.stdenv.lib.maintainers; { - maintainers = [ eelco ]; - }; - - nodes = - { client1 = client; - client2 = client; - - server = - { ... }: - { services.nfs.server.enable = true; - services.nfs.server.exports = - '' - /data 192.168.1.0/255.255.255.0(rw,no_root_squash,no_subtree_check,fsid=0) - ''; - services.nfs.server.createMountPoints = true; - networking.firewall.enable = false; # FIXME: figure out what ports need to be allowed - }; - }; - - testScript = - '' - $server->waitForUnit("nfs-server"); - $server->succeed("systemctl start network-online.target"); - $server->waitForUnit("network-online.target"); - - startAll; - - $client1->waitForUnit("data.mount"); - $client1->succeed("echo bla > /data/foo"); - $server->succeed("test -e /data/foo"); - - $client2->waitForUnit("data.mount"); - $client2->succeed("echo bla > /data/bar"); - $server->succeed("test -e /data/bar"); - - # Test whether restarting ‘nfs-server’ works correctly. - $server->succeed("systemctl restart nfs-server"); - $client2->succeed("echo bla >> /data/bar"); # will take 90 seconds due to the NFS grace period - - # Test whether we can get a lock. - $client2->succeed("time flock -n -s /data/lock true"); - - # Test locking: client 1 acquires an exclusive lock, so client 2 - # should then fail to acquire a shared lock. - $client1->succeed("flock -x /data/lock -c 'touch locked; sleep 100000' &"); - $client1->waitForFile("locked"); - $client2->fail("flock -n -s /data/lock true"); - - # Test whether client 2 obtains the lock if we reset client 1. - $client2->succeed("flock -x /data/lock -c 'echo acquired; touch locked; sleep 100000' >&2 &"); - $client1->crash; - $client1->start; - $client2->waitForFile("locked"); - - # Test whether locks survive a reboot of the server. - $client1->waitForUnit("data.mount"); - $server->shutdown; - $server->start; - $client1->succeed("touch /data/xyzzy"); - $client1->fail("time flock -n -s /data/lock true"); - - # Test whether unmounting during shutdown happens quickly. - my $t1 = time; - $client1->shutdown; - my $duration = time - $t1; - die "shutdown took too long ($duration seconds)" if $duration > 30; - ''; -}) diff --git a/nixos/tests/nfs/default.nix b/nixos/tests/nfs/default.nix new file mode 100644 index 000000000000..6bc803c91b46 --- /dev/null +++ b/nixos/tests/nfs/default.nix @@ -0,0 +1,9 @@ +{ version ? 4 +, system ? builtins.currentSystem +, pkgs ? import ../../.. { inherit system; } +}: { + simple = import ./simple.nix { inherit version system pkgs; }; +} // pkgs.lib.optionalAttrs (version == 4) { + # TODO: Test kerberos + nfsv3 + kerberos = import ./kerberos.nix { inherit version system pkgs; }; +} diff --git a/nixos/tests/nfs/kerberos.nix b/nixos/tests/nfs/kerberos.nix new file mode 100644 index 000000000000..1f2d0d453ea0 --- /dev/null +++ b/nixos/tests/nfs/kerberos.nix @@ -0,0 +1,133 @@ +import ../make-test-python.nix ({ pkgs, lib, ... }: + +with lib; + +let + krb5 = + { enable = true; + domain_realm."nfs.test" = "NFS.TEST"; + libdefaults.default_realm = "NFS.TEST"; + realms."NFS.TEST" = + { admin_server = "server.nfs.test"; + kdc = "server.nfs.test"; + }; + }; + + hosts = + '' + 192.168.1.1 client.nfs.test + 192.168.1.2 server.nfs.test + ''; + + users = { + users.alice = { + isNormalUser = true; + name = "alice"; + uid = 1000; + }; + }; + +in + +{ + name = "nfsv4-with-kerberos"; + + nodes = { + client = { lib, ... }: + { inherit krb5 users; + + networking.extraHosts = hosts; + networking.domain = "nfs.test"; + networking.hostName = "client"; + + fileSystems = lib.mkVMOverride + { "/data" = { + device = "server.nfs.test:/"; + fsType = "nfs"; + options = [ "nfsvers=4" "sec=krb5p" "noauto" ]; + }; + }; + }; + + server = { lib, ...}: + { inherit krb5 users; + + networking.extraHosts = hosts; + networking.domain = "nfs.test"; + networking.hostName = "server"; + + networking.firewall.allowedTCPPorts = [ + 111 # rpc + 2049 # nfs + 88 # kerberos + 749 # kerberos admin + ]; + + services.kerberos_server.enable = true; + services.kerberos_server.realms = + { "NFS.TEST".acl = + [ { access = "all"; principal = "admin/admin"; } ]; + }; + + services.nfs.server.enable = true; + services.nfs.server.createMountPoints = true; + services.nfs.server.exports = + '' + /data *(rw,no_root_squash,fsid=0,sec=krb5p) + ''; + }; + }; + + testScript = + '' + server.succeed("mkdir -p /data/alice") + server.succeed("chown alice:users /data/alice") + + # set up kerberos database + server.succeed( + "kdb5_util create -s -r NFS.TEST -P master_key", + "systemctl restart kadmind.service kdc.service", + ) + server.wait_for_unit(f"kadmind.service") + server.wait_for_unit(f"kdc.service") + + # create principals + server.succeed( + "kadmin.local add_principal -randkey nfs/server.nfs.test", + "kadmin.local add_principal -randkey nfs/client.nfs.test", + "kadmin.local add_principal -pw admin_pw admin/admin", + "kadmin.local add_principal -pw alice_pw alice", + ) + + # add principals to server keytab + server.succeed("kadmin.local ktadd nfs/server.nfs.test") + server.succeed("systemctl start rpc-gssd.service rpc-svcgssd.service") + server.wait_for_unit(f"rpc-gssd.service") + server.wait_for_unit(f"rpc-svcgssd.service") + + client.wait_for_unit("network-online.target") + + # add principals to client keytab + client.succeed("echo admin_pw | kadmin -p admin/admin ktadd nfs/client.nfs.test") + client.succeed("systemctl start rpc-gssd.service") + client.wait_for_unit("rpc-gssd.service") + + with subtest("nfs share mounts"): + client.succeed("systemctl restart data.mount") + client.wait_for_unit("data.mount") + + with subtest("permissions on nfs share are enforced"): + client.fail("su alice -c 'ls /data'") + client.succeed("su alice -c 'echo alice_pw | kinit'") + client.succeed("su alice -c 'ls /data'") + + client.fail("su alice -c 'echo bla >> /data/foo'") + client.succeed("su alice -c 'echo bla >> /data/alice/foo'") + server.succeed("test -e /data/alice/foo") + + with subtest("uids/gids are mapped correctly on nfs share"): + ids = client.succeed("stat -c '%U %G' /data/alice").split() + expected = ["alice", "users"] + assert ids == expected, f"ids incorrect: got {ids} expected {expected}" + ''; +}) diff --git a/nixos/tests/nfs/simple.nix b/nixos/tests/nfs/simple.nix new file mode 100644 index 000000000000..a1a09ee0f45c --- /dev/null +++ b/nixos/tests/nfs/simple.nix @@ -0,0 +1,94 @@ +import ../make-test-python.nix ({ pkgs, version ? 4, ... }: + +let + + client = + { pkgs, ... }: + { fileSystems = pkgs.lib.mkVMOverride + [ { mountPoint = "/data"; + # nfs4 exports the export with fsid=0 as a virtual root directory + device = if (version == 4) then "server:/" else "server:/data"; + fsType = "nfs"; + options = [ "vers=${toString version}" ]; + } + ]; + networking.firewall.enable = false; # FIXME: only open statd + }; + +in + +{ + name = "nfs"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ eelco ]; + }; + + nodes = + { client1 = client; + client2 = client; + + server = + { ... }: + { services.nfs.server.enable = true; + services.nfs.server.exports = + '' + /data 192.168.1.0/255.255.255.0(rw,no_root_squash,no_subtree_check,fsid=0) + ''; + services.nfs.server.createMountPoints = true; + networking.firewall.enable = false; # FIXME: figure out what ports need to be allowed + }; + }; + + testScript = + '' + import time + + server.wait_for_unit("nfs-server") + server.succeed("systemctl start network-online.target") + server.wait_for_unit("network-online.target") + + start_all() + + client1.wait_for_unit("data.mount") + client1.succeed("echo bla > /data/foo") + server.succeed("test -e /data/foo") + + client2.wait_for_unit("data.mount") + client2.succeed("echo bla > /data/bar") + server.succeed("test -e /data/bar") + + with subtest("restarting 'nfs-server' works correctly"): + server.succeed("systemctl restart nfs-server") + # will take 90 seconds due to the NFS grace period + client2.succeed("echo bla >> /data/bar") + + with subtest("can get a lock"): + client2.succeed("time flock -n -s /data/lock true") + + with subtest("client 2 fails to acquire lock held by client 1"): + client1.succeed("flock -x /data/lock -c 'touch locked; sleep 100000' &") + client1.wait_for_file("locked") + client2.fail("flock -n -s /data/lock true") + + with subtest("client 2 obtains lock after resetting client 1"): + client2.succeed( + "flock -x /data/lock -c 'echo acquired; touch locked; sleep 100000' >&2 &" + ) + client1.crash() + client1.start() + client2.wait_for_file("locked") + + with subtest("locks survive server reboot"): + client1.wait_for_unit("data.mount") + server.shutdown() + server.start() + client1.succeed("touch /data/xyzzy") + client1.fail("time flock -n -s /data/lock true") + + with subtest("unmounting during shutdown happens quickly"): + t1 = time.monotonic() + client1.shutdown() + duration = time.monotonic() - t1 + assert duration < 30, f"shutdown took too long ({duration} seconds)" + ''; +}) diff --git a/nixos/tests/nghttpx.nix b/nixos/tests/nghttpx.nix index 11611bfe1063..d83c1c4cae63 100644 --- a/nixos/tests/nghttpx.nix +++ b/nixos/tests/nghttpx.nix @@ -1,7 +1,7 @@ let nginxRoot = "/run/nginx"; in - import ./make-test.nix ({...}: { + import ./make-test-python.nix ({...}: { name = "nghttpx"; nodes = { webserver = { @@ -52,10 +52,10 @@ in }; testScript = '' - startAll; + start_all() - $webserver->waitForOpenPort("80"); - $proxy->waitForOpenPort("80"); - $client->waitUntilSucceeds("curl -s --fail http://proxy/hello-world.txt"); + webserver.wait_for_open_port("80") + proxy.wait_for_open_port("80") + client.wait_until_succeeds("curl -s --fail http://proxy/hello-world.txt") ''; }) diff --git a/nixos/tests/novacomd.nix b/nixos/tests/novacomd.nix index 4eb60c0feb5c..940210dee235 100644 --- a/nixos/tests/novacomd.nix +++ b/nixos/tests/novacomd.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, ...} : { +import ./make-test-python.nix ({ pkgs, ...} : { name = "novacomd"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ dtzWill ]; @@ -9,26 +9,20 @@ import ./make-test.nix ({ pkgs, ...} : { }; testScript = '' - $machine->waitForUnit("multi-user.target"); + machine.wait_for_unit("novacomd.service") - # multi-user.target wants novacomd.service, but let's make sure - $machine->waitForUnit("novacomd.service"); + with subtest("Make sure the daemon is really listening"): + machine.wait_for_open_port(6968) + machine.succeed("novacom -l") - # Check status and try connecting with novacom - $machine->succeed("systemctl status novacomd.service >&2"); - # to prevent non-deterministic failure, - # make sure the daemon is really listening - $machine->waitForOpenPort(6968); - $machine->succeed("novacom -l"); + with subtest("Stop the daemon, double-check novacom fails if daemon isn't working"): + machine.stop_job("novacomd") + machine.fail("novacom -l") - # Stop the daemon, double-check novacom fails if daemon isn't working - $machine->stopJob("novacomd"); - $machine->fail("novacom -l"); - - # And back again for good measure - $machine->startJob("novacomd"); - # make sure the daemon is really listening - $machine->waitForOpenPort(6968); - $machine->succeed("novacom -l"); + with subtest("Make sure the daemon starts back up again"): + machine.start_job("novacomd") + # make sure the daemon is really listening + machine.wait_for_open_port(6968) + machine.succeed("novacom -l") ''; }) diff --git a/nixos/tests/nzbget.nix b/nixos/tests/nzbget.nix index 042ccec98cf6..12d8ed6ea8da 100644 --- a/nixos/tests/nzbget.nix +++ b/nixos/tests/nzbget.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, ...} : { +import ./make-test-python.nix ({ pkgs, ...} : { name = "nzbget"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ aanderse flokli ]; @@ -15,12 +15,16 @@ import ./make-test.nix ({ pkgs, ...} : { }; testScript = '' - startAll; + start_all() - $server->waitForUnit("nzbget.service"); - $server->waitForUnit("network.target"); - $server->waitForOpenPort(6789); - $server->succeed("curl -s -u nzbget:tegbzn6789 http://127.0.0.1:6789 | grep -q 'This file is part of nzbget'"); - $server->succeed("${pkgs.nzbget}/bin/nzbget -n -o ControlIP=127.0.0.1 -o ControlPort=6789 -o ControlPassword=tegbzn6789 -V"); + server.wait_for_unit("nzbget.service") + server.wait_for_unit("network.target") + server.wait_for_open_port(6789) + assert "This file is part of nzbget" in server.succeed( + "curl -s -u nzbget:tegbzn6789 http://127.0.0.1:6789" + ) + server.succeed( + "${pkgs.nzbget}/bin/nzbget -n -o Control_iP=127.0.0.1 -o Control_port=6789 -o Control_password=tegbzn6789 -V" + ) ''; }) diff --git a/nixos/tests/orangefs.nix b/nixos/tests/orangefs.nix index bdf4fc10c447..46d7a6a72f89 100644 --- a/nixos/tests/orangefs.nix +++ b/nixos/tests/orangefs.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ ... } : +import ./make-test-python.nix ({ ... } : let server = { pkgs, ... } : { @@ -52,37 +52,31 @@ in { testScript = '' # format storage - foreach my $server (($server1,$server2)) - { - $server->start(); - $server->waitForUnit("multi-user.target"); - $server->succeed("mkdir -p /data/storage /data/meta"); - $server->succeed("chown orangefs:orangefs /data/storage /data/meta"); - $server->succeed("chmod 0770 /data/storage /data/meta"); - $server->succeed("sudo -g orangefs -u orangefs pvfs2-server -f /etc/orangefs/server.conf"); - } + for server in server1, server2: + server.start() + server.wait_for_unit("multi-user.target") + server.succeed("mkdir -p /data/storage /data/meta") + server.succeed("chown orangefs:orangefs /data/storage /data/meta") + server.succeed("chmod 0770 /data/storage /data/meta") + server.succeed( + "sudo -g orangefs -u orangefs pvfs2-server -f /etc/orangefs/server.conf" + ) # start services after storage is formated on all machines - foreach my $server (($server1,$server2)) - { - $server->succeed("systemctl start orangefs-server.service"); - } + for server in server1, server2: + server.succeed("systemctl start orangefs-server.service") - # Check if clients can reach and mount the FS - foreach my $client (($client1,$client2)) - { - $client->start(); - $client->waitForUnit("orangefs-client.service"); - # Both servers need to be reachable - $client->succeed("pvfs2-check-server -h server1 -f orangefs -n tcp -p 3334"); - $client->succeed("pvfs2-check-server -h server2 -f orangefs -n tcp -p 3334"); - $client->waitForUnit("orangefs.mount"); - - } - - # R/W test between clients - $client1->succeed("echo test > /orangefs/file1"); - $client2->succeed("grep test /orangefs/file1"); + with subtest("clients can reach and mount the FS"): + for client in client1, client2: + client.start() + client.wait_for_unit("orangefs-client.service") + # Both servers need to be reachable + client.succeed("pvfs2-check-server -h server1 -f orangefs -n tcp -p 3334") + client.succeed("pvfs2-check-server -h server2 -f orangefs -n tcp -p 3334") + client.wait_for_unit("orangefs.mount") + with subtest("R/W test between clients"): + client1.succeed("echo test > /orangefs/file1") + client2.succeed("grep test /orangefs/file1") ''; }) diff --git a/nixos/tests/osrm-backend.nix b/nixos/tests/osrm-backend.nix index 6e2d098d4adb..db67a5a589f9 100644 --- a/nixos/tests/osrm-backend.nix +++ b/nixos/tests/osrm-backend.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, lib, ... }: +import ./make-test-python.nix ({ pkgs, lib, ... }: let port = 5000; in { @@ -45,9 +45,13 @@ in { testScript = let query = "http://localhost:${toString port}/route/v1/driving/7.41720,43.73304;7.42463,43.73886?steps=true"; in '' - $machine->waitForUnit("osrm.service"); - $machine->waitForOpenPort(${toString port}); - $machine->succeed("curl --silent '${query}' | jq .waypoints[0].name | grep -F 'Boulevard Rainier III'"); - $machine->succeed("curl --silent '${query}' | jq .waypoints[1].name | grep -F 'Avenue de la Costa'"); + machine.wait_for_unit("osrm.service") + machine.wait_for_open_port(${toString port}) + assert "Boulevard Rainier III" in machine.succeed( + "curl --silent '${query}' | jq .waypoints[0].name" + ) + assert "Avenue de la Costa" in machine.succeed( + "curl --silent '${query}' | jq .waypoints[1].name" + ) ''; }) diff --git a/nixos/tests/overlayfs.nix b/nixos/tests/overlayfs.nix index 99bb6b0f5531..33794deb9ed8 100644 --- a/nixos/tests/overlayfs.nix +++ b/nixos/tests/overlayfs.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, ... }: { +import ./make-test-python.nix ({ pkgs, ... }: { name = "overlayfs"; meta.maintainers = with pkgs.stdenv.lib.maintainers; [ bachp ]; @@ -9,49 +9,42 @@ import ./make-test.nix ({ pkgs, ... }: { }; testScript = '' - $machine->succeed("ls /dev"); + machine.succeed("ls /dev") - $machine->succeed("mkdir -p /tmp/mnt"); + machine.succeed("mkdir -p /tmp/mnt") # Test ext4 + overlayfs - $machine->succeed( - - "mkfs.ext4 -F -L overlay-ext4 /dev/vdb", - "mount -t ext4 /dev/vdb /tmp/mnt", - - "mkdir -p /tmp/mnt/upper /tmp/mnt/lower /tmp/mnt/work /tmp/mnt/merged", - - # Setup some existing files - "echo 'Replace' > /tmp/mnt/lower/replace.txt", - "echo 'Append' > /tmp/mnt/lower/append.txt", - "echo 'Overwrite' > /tmp/mnt/lower/overwrite.txt", - - "mount -t overlay overlay -o lowerdir=/tmp/mnt/lower,upperdir=/tmp/mnt/upper,workdir=/tmp/mnt/work /tmp/mnt/merged", - - # Test new - "echo 'New' > /tmp/mnt/merged/new.txt", - "[[ \"\$(cat /tmp/mnt/merged/new.txt)\" == \"New\" ]]", - - # Test replace - "[[ \"\$(cat /tmp/mnt/merged/replace.txt)\" == \"Replace\" ]]", - "echo 'Replaced' > /tmp/mnt/merged/replace-tmp.txt", - "mv /tmp/mnt/merged/replace-tmp.txt /tmp/mnt/merged/replace.txt", - "[[ \"\$(cat /tmp/mnt/merged/replace.txt)\" == \"Replaced\" ]]", - - # Overwrite - "[[ \"\$(cat /tmp/mnt/merged/overwrite.txt)\" == \"Overwrite\" ]]", - "echo 'Overwritten' > /tmp/mnt/merged/overwrite.txt", - "[[ \"\$(cat /tmp/mnt/merged/overwrite.txt)\" == \"Overwritten\" ]]", - - # Test append - "[[ \"\$(cat /tmp/mnt/merged/append.txt)\" == \"Append\" ]]", - "echo 'ed' >> /tmp/mnt/merged/append.txt", - #"cat /tmp/mnt/merged/append.txt && exit 1", - "[[ \"\$(cat /tmp/mnt/merged/append.txt)\" == \"Append\ned\" ]]", - - "umount /tmp/mnt/merged", - "umount /tmp/mnt", - "udevadm settle" - ); + machine.succeed( + """ + mkfs.ext4 -F -L overlay-ext4 /dev/vdb + mount -t ext4 /dev/vdb /tmp/mnt + mkdir -p /tmp/mnt/upper /tmp/mnt/lower /tmp/mnt/work /tmp/mnt/merged + # Setup some existing files + echo 'Replace' > /tmp/mnt/lower/replace.txt + echo 'Append' > /tmp/mnt/lower/append.txt + echo 'Overwrite' > /tmp/mnt/lower/overwrite.txt + mount -t overlay overlay -o lowerdir=/tmp/mnt/lower,upperdir=/tmp/mnt/upper,workdir=/tmp/mnt/work /tmp/mnt/merged + # Test new + echo 'New' > /tmp/mnt/merged/new.txt + [[ "\$(cat /tmp/mnt/merged/new.txt)" == "New" ]] + # Test replace + [[ "\$(cat /tmp/mnt/merged/replace.txt)" == "Replace" ]] + echo 'Replaced' > /tmp/mnt/merged/replace-tmp.txt + mv /tmp/mnt/merged/replace-tmp.txt /tmp/mnt/merged/replace.txt + [[ "\$(cat /tmp/mnt/merged/replace.txt)" == "Replaced" ]] + # Overwrite + [[ "\$(cat /tmp/mnt/merged/overwrite.txt)" == "Overwrite" ]] + echo 'Overwritten' > /tmp/mnt/merged/overwrite.txt + [[ "\$(cat /tmp/mnt/merged/overwrite.txt)" == "Overwritten" ]] + # Test append + [[ "\$(cat /tmp/mnt/merged/append.txt)" == "Append" ]] + echo 'ed' >> /tmp/mnt/merged/append.txt + #"cat /tmp/mnt/merged/append.txt && exit 1 + [[ "\$(cat /tmp/mnt/merged/append.txt)" == "Append\ned" ]] + umount /tmp/mnt/merged + umount /tmp/mnt + udevadm settle + """ + ) ''; }) diff --git a/nixos/tests/paperless.nix b/nixos/tests/paperless.nix index 860ad0a6218f..355e7041d3fe 100644 --- a/nixos/tests/paperless.nix +++ b/nixos/tests/paperless.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ lib, ... } : { +import ./make-test-python.nix ({ lib, ... } : { name = "paperless"; meta = with lib.maintainers; { maintainers = [ earvstedt ]; @@ -13,17 +13,24 @@ import ./make-test.nix ({ lib, ... } : { }; testScript = '' - $machine->waitForUnit("paperless-consumer.service"); + machine.wait_for_unit("paperless-consumer.service") + # Create test doc - $machine->succeed('convert -size 400x40 xc:white -font "DejaVu-Sans" -pointsize 20 -fill black \ - -annotate +5+20 "hello world 16-10-2005" /var/lib/paperless/consume/doc.png'); + machine.succeed( + "convert -size 400x40 xc:white -font 'DejaVu-Sans' -pointsize 20 -fill black -annotate +5+20 'hello world 16-10-2005' /var/lib/paperless/consume/doc.png" + ) + + with subtest("Service gets ready"): + machine.wait_for_unit("paperless-server.service") + # Wait until server accepts connections + machine.wait_until_succeeds("curl -s localhost:28981") - $machine->waitForUnit("paperless-server.service"); - # Wait until server accepts connections - $machine->waitUntilSucceeds("curl -s localhost:28981"); - # Wait until document is consumed - $machine->waitUntilSucceeds('(($(curl -s localhost:28981/api/documents/ | jq .count) == 1))'); - $machine->succeed("curl -s localhost:28981/api/documents/ | jq '.results | .[0] | .created'") - =~ /2005-10-16/ or die; + with subtest("Test document is consumed"): + machine.wait_until_succeeds( + "(($(curl -s localhost:28981/api/documents/ | jq .count) == 1))" + ) + assert "2005-10-16" in machine.succeed( + "curl -s localhost:28981/api/documents/ | jq '.results | .[0] | .created'" + ) ''; }) diff --git a/nixos/tests/pdns-recursor.nix b/nixos/tests/pdns-recursor.nix index bf6e6093d69c..de1b60e0b1c7 100644 --- a/nixos/tests/pdns-recursor.nix +++ b/nixos/tests/pdns-recursor.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, ... }: { +import ./make-test-python.nix ({ pkgs, ... }: { name = "powerdns"; nodes.server = { ... }: { @@ -6,7 +6,7 @@ import ./make-test.nix ({ pkgs, ... }: { }; testScript = '' - $server->waitForUnit("pdns-recursor"); - $server->waitForOpenPort("53"); + server.wait_for_unit("pdns-recursor") + server.wait_for_open_port("53") ''; }) diff --git a/nixos/tests/peerflix.nix b/nixos/tests/peerflix.nix index fae37fedaac7..37628604d49b 100644 --- a/nixos/tests/peerflix.nix +++ b/nixos/tests/peerflix.nix @@ -1,6 +1,6 @@ # This test runs peerflix and checks if peerflix starts -import ./make-test.nix ({ pkgs, ...} : { +import ./make-test-python.nix ({ pkgs, ...} : { name = "peerflix"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ offline ]; @@ -15,9 +15,9 @@ import ./make-test.nix ({ pkgs, ...} : { }; testScript = '' - startAll; + start_all() - $peerflix->waitForUnit("peerflix.service"); - $peerflix->waitUntilSucceeds("curl localhost:9000"); + peerflix.wait_for_unit("peerflix.service") + peerflix.wait_until_succeeds("curl localhost:9000") ''; }) diff --git a/nixos/tests/pgmanage.nix b/nixos/tests/pgmanage.nix index bacaf3f41588..4f5dbed24a97 100644 --- a/nixos/tests/pgmanage.nix +++ b/nixos/tests/pgmanage.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, ... } : +import ./make-test-python.nix ({ pkgs, ... } : let role = "test"; password = "secret"; @@ -29,11 +29,13 @@ in }; testScript = '' - startAll; - $one->waitForUnit("default.target"); - $one->requireActiveUnit("pgmanage.service"); + start_all() + one.wait_for_unit("default.target") + one.require_unit_state("pgmanage.service", "active") # Test if we can log in. - $one->waitUntilSucceeds("curl 'http://localhost:8080/pgmanage/auth' --data 'action=login&connname=${conn}&username=${role}&password=${password}' --fail"); + one.wait_until_succeeds( + "curl 'http://localhost:8080/pgmanage/auth' --data 'action=login&connname=${conn}&username=${role}&password=${password}' --fail" + ) ''; }) diff --git a/nixos/tests/php-pcre.nix b/nixos/tests/php-pcre.nix index ae44aec7944f..d5c22e0582a0 100644 --- a/nixos/tests/php-pcre.nix +++ b/nixos/tests/php-pcre.nix @@ -1,7 +1,7 @@ let testString = "can-use-subgroups"; in -import ./make-test.nix ({ ...}: { +import ./make-test-python.nix ({ ...}: { name = "php-httpd-pcre-jit-test"; machine = { lib, pkgs, ... }: { time.timeZone = "UTC"; @@ -31,9 +31,10 @@ import ./make-test.nix ({ ...}: { }; testScript = { ... }: '' - $machine->waitForUnit('httpd.service'); + machine.wait_for_unit("httpd.service") # Ensure php evaluation by matching on the var_dump syntax - $machine->succeed('curl -vvv -s http://127.0.0.1:80/index.php \ - | grep "string(${toString (builtins.stringLength testString)}) \"${testString}\""'); + assert 'string(${toString (builtins.stringLength testString)}) "${testString}"' in machine.succeed( + "curl -vvv -s http://127.0.0.1:80/index.php" + ) ''; }) diff --git a/nixos/tests/plasma5.nix b/nixos/tests/plasma5.nix index 6884f17aabbe..2eccfdf47f59 100644 --- a/nixos/tests/plasma5.nix +++ b/nixos/tests/plasma5.nix @@ -12,8 +12,8 @@ import ./make-test-python.nix ({ pkgs, ...} : imports = [ ./common/user-account.nix ]; services.xserver.enable = true; services.xserver.displayManager.sddm.enable = true; + services.xserver.displayManager.defaultSession = "plasma5"; services.xserver.desktopManager.plasma5.enable = true; - services.xserver.desktopManager.default = "plasma5"; services.xserver.displayManager.sddm.autoLogin = { enable = true; user = "alice"; diff --git a/nixos/tests/postgis.nix b/nixos/tests/postgis.nix index 294eb50b5fe5..84bbb0bc8ec6 100644 --- a/nixos/tests/postgis.nix +++ b/nixos/tests/postgis.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ pkgs, ...} : { +import ./make-test-python.nix ({ pkgs, ...} : { name = "postgis"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ lsix ]; @@ -20,10 +20,10 @@ import ./make-test.nix ({ pkgs, ...} : { }; testScript = '' - startAll; - $master->waitForUnit("postgresql"); - $master->sleep(10); # Hopefully this is long enough!! - $master->succeed("sudo -u postgres psql -c 'CREATE EXTENSION postgis;'"); - $master->succeed("sudo -u postgres psql -c 'CREATE EXTENSION postgis_topology;'"); + start_all() + master.wait_for_unit("postgresql") + master.sleep(10) # Hopefully this is long enough!! + master.succeed("sudo -u postgres psql -c 'CREATE EXTENSION postgis;'") + master.succeed("sudo -u postgres psql -c 'CREATE EXTENSION postgis_topology;'") ''; }) diff --git a/nixos/tests/quagga.nix b/nixos/tests/quagga.nix index 6aee7ea57f03..04590aa0eb38 100644 --- a/nixos/tests/quagga.nix +++ b/nixos/tests/quagga.nix @@ -5,7 +5,7 @@ # # All interfaces are in OSPF Area 0. -import ./make-test.nix ({ pkgs, ... }: +import ./make-test-python.nix ({ pkgs, ... }: let ifAddr = node: iface: (pkgs.lib.head node.config.networking.interfaces.${iface}.ipv4.addresses).address; @@ -74,23 +74,23 @@ import ./make-test.nix ({ pkgs, ... }: testScript = { ... }: '' - startAll; + start_all() # Wait for the networking to start on all machines - $_->waitForUnit("network.target") foreach values %vms; + for machine in client, router1, router2, server: + machine.wait_for_unit("network.target") - # Wait for OSPF to form adjacencies - for my $gw ($router1, $router2) { - $gw->waitForUnit("ospfd"); - $gw->waitUntilSucceeds("vtysh -c 'show ip ospf neighbor' | grep Full"); - $gw->waitUntilSucceeds("vtysh -c 'show ip route' | grep '^O>'"); - } + with subtest("Wait for OSPF to form adjacencies"): + for gw in router1, router2: + gw.wait_for_unit("ospfd") + gw.wait_until_succeeds("vtysh -c 'show ip ospf neighbor' | grep Full") + gw.wait_until_succeeds("vtysh -c 'show ip route' | grep '^O>'") - # Test ICMP. - $client->succeed("ping -c 3 server >&2"); + with subtest("Test ICMP"): + client.wait_until_succeeds("ping -c 3 server >&2") - # Test whether HTTP works. - $server->waitForUnit("httpd"); - $client->succeed("curl --fail http://server/ >&2"); + with subtest("Test whether HTTP works"): + server.wait_for_unit("httpd") + client.succeed("curl --fail http://server/ >&2") ''; }) diff --git a/nixos/tests/rspamd.nix b/nixos/tests/rspamd.nix index 0cc94728f80a..bf3f0de62044 100644 --- a/nixos/tests/rspamd.nix +++ b/nixos/tests/rspamd.nix @@ -3,20 +3,20 @@ pkgs ? import ../.. { inherit system config; } }: -with import ../lib/testing.nix { inherit system pkgs; }; +with import ../lib/testing-python.nix { inherit system pkgs; }; with pkgs.lib; let initMachine = '' - startAll - $machine->waitForUnit("rspamd.service"); - $machine->succeed("id \"rspamd\" >/dev/null"); + start_all() + machine.wait_for_unit("rspamd.service") + machine.succeed("id rspamd >/dev/null") ''; checkSocket = socket: user: group: mode: '' - $machine->succeed("ls ${socket} >/dev/null"); - $machine->succeed("[[ \"\$(stat -c %U ${socket})\" == \"${user}\" ]]"); - $machine->succeed("[[ \"\$(stat -c %G ${socket})\" == \"${group}\" ]]"); - $machine->succeed("[[ \"\$(stat -c %a ${socket})\" == \"${mode}\" ]]"); + machine.succeed("ls ${socket} >/dev/null") + machine.succeed('[[ "$(stat -c %U ${socket})" == "${user}" ]]') + machine.succeed('[[ "$(stat -c %G ${socket})" == "${group}" ]]') + machine.succeed('[[ "$(stat -c %a ${socket})" == "${mode}" ]]') ''; simple = name: enableIPv6: makeTest { name = "rspamd-${name}"; @@ -25,22 +25,23 @@ let networking.enableIPv6 = enableIPv6; }; testScript = '' - startAll - $machine->waitForUnit("multi-user.target"); - $machine->waitForOpenPort(11334); - $machine->waitForUnit("rspamd.service"); - $machine->succeed("id \"rspamd\" >/dev/null"); + start_all() + machine.wait_for_unit("multi-user.target") + machine.wait_for_open_port(11334) + machine.wait_for_unit("rspamd.service") + machine.succeed("id rspamd >/dev/null") ${checkSocket "/run/rspamd/rspamd.sock" "rspamd" "rspamd" "660" } - sleep 10; - $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf")); - $machine->log($machine->succeed("grep 'CONFDIR/worker-controller.inc' /etc/rspamd/rspamd.conf")); - $machine->log($machine->succeed("grep 'CONFDIR/worker-normal.inc' /etc/rspamd/rspamd.conf")); - $machine->log($machine->succeed("systemctl cat rspamd.service")); - $machine->log($machine->succeed("curl http://localhost:11334/auth")); - $machine->log($machine->succeed("curl http://127.0.0.1:11334/auth")); - ${optionalString enableIPv6 '' - $machine->log($machine->succeed("curl http://[::1]:11334/auth")); - ''} + machine.sleep(10) + machine.log(machine.succeed("cat /etc/rspamd/rspamd.conf")) + machine.log( + machine.succeed("grep 'CONFDIR/worker-controller.inc' /etc/rspamd/rspamd.conf") + ) + machine.log(machine.succeed("grep 'CONFDIR/worker-normal.inc' /etc/rspamd/rspamd.conf")) + machine.log(machine.succeed("systemctl cat rspamd.service")) + machine.log(machine.succeed("curl http://localhost:11334/auth")) + machine.log(machine.succeed("curl http://127.0.0.1:11334/auth")) + ${optionalString enableIPv6 ''machine.log(machine.succeed("curl http://[::1]:11334/auth"))''} + # would not reformat ''; }; in @@ -69,14 +70,18 @@ in testScript = '' ${initMachine} - $machine->waitForFile("/run/rspamd.sock"); + machine.wait_for_file("/run/rspamd.sock") ${checkSocket "/run/rspamd.sock" "root" "root" "600" } ${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" } - $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf")); - $machine->log($machine->succeed("grep 'CONFDIR/worker-controller.inc' /etc/rspamd/rspamd.conf")); - $machine->log($machine->succeed("grep 'CONFDIR/worker-normal.inc' /etc/rspamd/rspamd.conf")); - $machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat")); - $machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping")); + machine.log(machine.succeed("cat /etc/rspamd/rspamd.conf")) + machine.log( + machine.succeed("grep 'CONFDIR/worker-controller.inc' /etc/rspamd/rspamd.conf") + ) + machine.log(machine.succeed("grep 'CONFDIR/worker-normal.inc' /etc/rspamd/rspamd.conf")) + machine.log(machine.succeed("rspamc -h /run/rspamd-worker.sock stat")) + machine.log( + machine.succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping") + ) ''; }; @@ -111,18 +116,32 @@ in testScript = '' ${initMachine} - $machine->waitForFile("/run/rspamd.sock"); + machine.wait_for_file("/run/rspamd.sock") ${checkSocket "/run/rspamd.sock" "root" "root" "600" } ${checkSocket "/run/rspamd-worker.sock" "root" "root" "666" } - $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf")); - $machine->log($machine->succeed("grep 'CONFDIR/worker-controller.inc' /etc/rspamd/rspamd.conf")); - $machine->log($machine->succeed("grep 'CONFDIR/worker-normal.inc' /etc/rspamd/rspamd.conf")); - $machine->log($machine->succeed("grep 'LOCAL_CONFDIR/override.d/worker-controller2.inc' /etc/rspamd/rspamd.conf")); - $machine->log($machine->succeed("grep 'verysecretpassword' /etc/rspamd/override.d/worker-controller2.inc")); - $machine->waitUntilSucceeds("journalctl -u rspamd | grep -i 'starting controller process' >&2"); - $machine->log($machine->succeed("rspamc -h /run/rspamd-worker.sock stat")); - $machine->log($machine->succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping")); - $machine->log($machine->succeed("curl http://localhost:11335/ping")); + machine.log(machine.succeed("cat /etc/rspamd/rspamd.conf")) + machine.log( + machine.succeed("grep 'CONFDIR/worker-controller.inc' /etc/rspamd/rspamd.conf") + ) + machine.log(machine.succeed("grep 'CONFDIR/worker-normal.inc' /etc/rspamd/rspamd.conf")) + machine.log( + machine.succeed( + "grep 'LOCAL_CONFDIR/override.d/worker-controller2.inc' /etc/rspamd/rspamd.conf" + ) + ) + machine.log( + machine.succeed( + "grep 'verysecretpassword' /etc/rspamd/override.d/worker-controller2.inc" + ) + ) + machine.wait_until_succeeds( + "journalctl -u rspamd | grep -i 'starting controller process' >&2" + ) + machine.log(machine.succeed("rspamc -h /run/rspamd-worker.sock stat")) + machine.log( + machine.succeed("curl --unix-socket /run/rspamd-worker.sock http://localhost/ping") + ) + machine.log(machine.succeed("curl http://localhost:11335/ping")) ''; }; customLuaRules = makeTest { @@ -199,22 +218,34 @@ in }; testScript = '' ${initMachine} - $machine->waitForOpenPort(11334); - $machine->log($machine->succeed("cat /etc/rspamd/rspamd.conf")); - $machine->log($machine->succeed("cat /etc/rspamd/rspamd.local.lua")); - $machine->log($machine->succeed("cat /etc/rspamd/local.d/groups.conf")); + machine.wait_for_open_port(11334) + machine.log(machine.succeed("cat /etc/rspamd/rspamd.conf")) + machine.log(machine.succeed("cat /etc/rspamd/rspamd.local.lua")) + machine.log(machine.succeed("cat /etc/rspamd/local.d/groups.conf")) # Verify that redis.conf was not written - $machine->fail("cat /etc/rspamd/local.d/redis.conf >&2"); + machine.fail("cat /etc/rspamd/local.d/redis.conf >&2") # Verify that antivirus.conf was not written - $machine->fail("cat /etc/rspamd/local.d/antivirus.conf >&2"); + machine.fail("cat /etc/rspamd/local.d/antivirus.conf >&2") ${checkSocket "/run/rspamd/rspamd.sock" "rspamd" "rspamd" "660" } - $machine->log($machine->succeed("curl --unix-socket /run/rspamd/rspamd.sock http://localhost/ping")); - $machine->log($machine->succeed("rspamc -h 127.0.0.1:11334 stat")); - $machine->log($machine->succeed("cat /etc/tests/no-muh.eml | rspamc -h 127.0.0.1:11334")); - $machine->log($machine->succeed("cat /etc/tests/muh.eml | rspamc -h 127.0.0.1:11334 symbols")); - $machine->waitUntilSucceeds("journalctl -u rspamd | grep -i muh >&2"); - $machine->log($machine->fail("cat /etc/tests/no-muh.eml | rspamc -h 127.0.0.1:11334 symbols | grep NO_MUH")); - $machine->log($machine->succeed("cat /etc/tests/muh.eml | rspamc -h 127.0.0.1:11334 symbols | grep NO_MUH")); + machine.log( + machine.succeed("curl --unix-socket /run/rspamd/rspamd.sock http://localhost/ping") + ) + machine.log(machine.succeed("rspamc -h 127.0.0.1:11334 stat")) + machine.log(machine.succeed("cat /etc/tests/no-muh.eml | rspamc -h 127.0.0.1:11334")) + machine.log( + machine.succeed("cat /etc/tests/muh.eml | rspamc -h 127.0.0.1:11334 symbols") + ) + machine.wait_until_succeeds("journalctl -u rspamd | grep -i muh >&2") + machine.log( + machine.fail( + "cat /etc/tests/no-muh.eml | rspamc -h 127.0.0.1:11334 symbols | grep NO_MUH" + ) + ) + machine.log( + machine.succeed( + "cat /etc/tests/muh.eml | rspamc -h 127.0.0.1:11334 symbols | grep NO_MUH" + ) + ) ''; }; postfixIntegration = makeTest { @@ -250,16 +281,24 @@ in }; testScript = '' ${initMachine} - $machine->waitForOpenPort(11334); - $machine->waitForOpenPort(25); + machine.wait_for_open_port(11334) + machine.wait_for_open_port(25) ${checkSocket "/run/rspamd/rspamd-milter.sock" "rspamd" "postfix" "660" } - $machine->log($machine->succeed("rspamc -h 127.0.0.1:11334 stat")); - $machine->log($machine->succeed("msmtp --host=localhost -t --read-envelope-from < /etc/tests/example.eml")); - $machine->log($machine->fail("msmtp --host=localhost -t --read-envelope-from < /etc/tests/gtube.eml")); + machine.log(machine.succeed("rspamc -h 127.0.0.1:11334 stat")) + machine.log( + machine.succeed( + "msmtp --host=localhost -t --read-envelope-from < /etc/tests/example.eml" + ) + ) + machine.log( + machine.fail( + "msmtp --host=localhost -t --read-envelope-from < /etc/tests/gtube.eml" + ) + ) - $machine->waitUntilFails('[ "$(postqueue -p)" != "Mail queue is empty" ]'); - $machine->fail("journalctl -u postfix | grep -i error >&2"); - $machine->fail("journalctl -u postfix | grep -i warning >&2"); + machine.wait_until_fails('[ "$(postqueue -p)" != "Mail queue is empty" ]') + machine.fail("journalctl -u postfix | grep -i error >&2") + machine.fail("journalctl -u postfix | grep -i warning >&2") ''; }; } diff --git a/nixos/tests/sddm.nix b/nixos/tests/sddm.nix index 4bdcd701dcf1..a145705250f7 100644 --- a/nixos/tests/sddm.nix +++ b/nixos/tests/sddm.nix @@ -16,9 +16,8 @@ let imports = [ ./common/user-account.nix ]; services.xserver.enable = true; services.xserver.displayManager.sddm.enable = true; - services.xserver.windowManager.default = "icewm"; + services.xserver.displayManager.defaultSession = "none+icewm"; services.xserver.windowManager.icewm.enable = true; - services.xserver.desktopManager.default = "none"; }; enableOCR = true; @@ -52,9 +51,8 @@ let user = "alice"; }; }; - services.xserver.windowManager.default = "icewm"; + services.xserver.displayManager.defaultSession = "none+icewm"; services.xserver.windowManager.icewm.enable = true; - services.xserver.desktopManager.default = "none"; }; testScript = { nodes, ... }: let diff --git a/nixos/tests/sonarr.nix b/nixos/tests/sonarr.nix index 3e84445099ab..764a4d05b381 100644 --- a/nixos/tests/sonarr.nix +++ b/nixos/tests/sonarr.nix @@ -1,4 +1,4 @@ -import ./make-test.nix ({ lib, ... }: +import ./make-test-python.nix ({ lib, ... }: with lib; @@ -11,8 +11,8 @@ with lib; { services.sonarr.enable = true; }; testScript = '' - $machine->waitForUnit('sonarr.service'); - $machine->waitForOpenPort('8989'); - $machine->succeed("curl --fail http://localhost:8989/"); + machine.wait_for_unit("sonarr.service") + machine.wait_for_open_port("8989") + machine.succeed("curl --fail http://localhost:8989/") ''; }) diff --git a/nixos/tests/switch-test.nix b/nixos/tests/switch-test.nix index 0dba3697980f..7076bd77b770 100644 --- a/nixos/tests/switch-test.nix +++ b/nixos/tests/switch-test.nix @@ -1,6 +1,6 @@ # Test configuration switching. -import ./make-test.nix ({ pkgs, ...} : { +import ./make-test-python.nix ({ pkgs, ...} : { name = "switch-test"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ gleber ]; @@ -28,7 +28,11 @@ import ./make-test.nix ({ pkgs, ...} : { exec env -i "$@" | tee /dev/stderr ''; in '' - $machine->succeed("${stderrRunner} ${originalSystem}/bin/switch-to-configuration test"); - $machine->succeed("${stderrRunner} ${otherSystem}/bin/switch-to-configuration test"); + machine.succeed( + "${stderrRunner} ${originalSystem}/bin/switch-to-configuration test" + ) + machine.succeed( + "${stderrRunner} ${otherSystem}/bin/switch-to-configuration test" + ) ''; }) diff --git a/nixos/tests/systemd-timesyncd.nix b/nixos/tests/systemd-timesyncd.nix index d12b8eb2bf7e..ad5b9a47383b 100644 --- a/nixos/tests/systemd-timesyncd.nix +++ b/nixos/tests/systemd-timesyncd.nix @@ -1,7 +1,7 @@ # Regression test for systemd-timesync having moved the state directory without # upstream providing a migration path. https://github.com/systemd/systemd/issues/12131 -import ./make-test.nix (let +import ./make-test-python.nix (let common = { lib, ... }: { # override the `false` value from the qemu-vm base profile services.timesyncd.enable = lib.mkForce true; @@ -25,28 +25,28 @@ in { }; testScript = '' - startAll; - $current->succeed('systemctl status systemd-timesyncd.service'); + start_all() + current.succeed("systemctl status systemd-timesyncd.service") # on a new install with a recent systemd there should not be any # leftovers from the dynamic user mess - $current->succeed('test -e /var/lib/systemd/timesync'); - $current->succeed('test ! -L /var/lib/systemd/timesync'); + current.succeed("test -e /var/lib/systemd/timesync") + current.succeed("test ! -L /var/lib/systemd/timesync") # timesyncd should be running on the upgrading system since we fixed the # file bits in the activation script - $pre1909->succeed('systemctl status systemd-timesyncd.service'); + pre1909.succeed("systemctl status systemd-timesyncd.service") # the path should be gone after the migration - $pre1909->succeed('test ! -e /var/lib/private/systemd/timesync'); + pre1909.succeed("test ! -e /var/lib/private/systemd/timesync") # and the new path should no longer be a symlink - $pre1909->succeed('test -e /var/lib/systemd/timesync'); - $pre1909->succeed('test ! -L /var/lib/systemd/timesync'); + pre1909.succeed("test -e /var/lib/systemd/timesync") + pre1909.succeed("test ! -L /var/lib/systemd/timesync") # after a restart things should still work and not fail in the activation # scripts and cause the boot to fail.. - $pre1909->shutdown; - $pre1909->start; - $pre1909->succeed('systemctl status systemd-timesyncd.service'); + pre1909.shutdown() + pre1909.start() + pre1909.succeed("systemctl status systemd-timesyncd.service") ''; }) diff --git a/nixos/tests/wireguard/namespaces.nix b/nixos/tests/wireguard/namespaces.nix index 94f993d9475d..c8a4e3bb52a1 100644 --- a/nixos/tests/wireguard/namespaces.nix +++ b/nixos/tests/wireguard/namespaces.nix @@ -13,7 +13,7 @@ let in -import ../make-test.nix ({ pkgs, ...} : { +import ../make-test-python.nix ({ pkgs, ...} : { name = "wireguard-with-namespaces"; meta = with pkgs.stdenv.lib.maintainers; { maintainers = [ asymmetric ]; @@ -65,16 +65,14 @@ import ../make-test.nix ({ pkgs, ...} : { }; testScript = '' - startAll(); + start_all() - $peer0->waitForUnit("wireguard-wg0.service"); - $peer1->waitForUnit("wireguard-wg0.service"); - $peer2->waitForUnit("wireguard-wg0.service"); - $peer3->waitForUnit("wireguard-wg0.service"); + for machine in peer0, peer1, peer2, peer3: + machine.wait_for_unit("wireguard-wg0.service") - $peer0->succeed("ip -n ${socketNamespace} link show wg0"); - $peer1->succeed("ip -n ${interfaceNamespace} link show wg0"); - $peer2->succeed("ip -n ${interfaceNamespace} link show wg0"); - $peer3->succeed("ip link show wg0"); + peer0.succeed("ip -n ${socketNamespace} link show wg0") + peer1.succeed("ip -n ${interfaceNamespace} link show wg0") + peer2.succeed("ip -n ${interfaceNamespace} link show wg0") + peer3.succeed("ip link show wg0") ''; }) diff --git a/nixos/tests/xmonad.nix b/nixos/tests/xmonad.nix index c2e5ba60d7bc..ef711f8dcf6a 100644 --- a/nixos/tests/xmonad.nix +++ b/nixos/tests/xmonad.nix @@ -4,10 +4,10 @@ import ./make-test-python.nix ({ pkgs, ...} : { maintainers = [ nequissimus ]; }; - machine = { lib, pkgs, ... }: { + machine = { pkgs, ... }: { imports = [ ./common/x11.nix ./common/user-account.nix ]; services.xserver.displayManager.auto.user = "alice"; - services.xserver.windowManager.default = lib.mkForce "xmonad"; + services.xserver.displayManager.defaultSession = "none+xmonad"; services.xserver.windowManager.xmonad = { enable = true; enableContribAndExtras = true; @@ -27,13 +27,13 @@ import ./make-test-python.nix ({ pkgs, ...} : { machine.wait_for_x() machine.wait_for_file("${user.home}/.Xauthority") machine.succeed("xauth merge ${user.home}/.Xauthority") - machine.send_chars("alt-ctrl-x") + machine.send_key("alt-ctrl-x") machine.wait_for_window("${user.name}.*machine") machine.sleep(1) machine.screenshot("terminal") machine.wait_until_succeeds("xmonad --restart") machine.sleep(3) - machine.send_chars("alt-shift-ret") + machine.send_key("alt-shift-ret") machine.wait_for_window("${user.name}.*machine") machine.sleep(1) machine.screenshot("terminal") diff --git a/nixos/tests/zsh-history.nix b/nixos/tests/zsh-history.nix new file mode 100644 index 000000000000..4380ec9adfd2 --- /dev/null +++ b/nixos/tests/zsh-history.nix @@ -0,0 +1,35 @@ +import ./make-test-python.nix ({ pkgs, ...} : { + name = "zsh-history"; + meta = with pkgs.stdenv.lib.maintainers; { + maintainers = [ kampka ]; + }; + + nodes.default = { ... }: { + programs = { + zsh.enable = true; + }; + environment.systemPackages = [ pkgs.zsh-history ]; + programs.zsh.interactiveShellInit = '' + source ${pkgs.zsh-history.out}/share/zsh/init.zsh + ''; + users.users.root.shell = "${pkgs.zsh}/bin/zsh"; + }; + + testScript = '' + start_all() + default.wait_for_unit("multi-user.target") + default.wait_until_succeeds("pgrep -f 'agetty.*tty1'") + + # Login + default.wait_until_tty_matches(1, "login: ") + default.send_chars("root\n") + default.wait_until_tty_matches(1, "root@default>") + + # Generate some history + default.send_chars("echo foobar\n") + default.wait_until_tty_matches(1, "foobar") + + # Ensure that command was recorded in history + default.succeed("/run/current-system/sw/bin/history list | grep -q foobar") + ''; +}) |