From 18672bf2751bbae1d7eeec59f5f82229b79b8873 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 3 Dec 2021 12:01:13 +0000 Subject: [PATCH 01/33] lib.nixos: init --- lib/default.nix | 3 +++ nixos/nixos-core.nix | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 nixos/nixos-core.nix diff --git a/lib/default.nix b/lib/default.nix index 626a751cb10a1..d699817b7da93 100644 --- a/lib/default.nix +++ b/lib/default.nix @@ -34,6 +34,9 @@ let options = callLibs ./options.nix; types = callLibs ./types.nix; + # NixOS + nixos = callLibs ../nixos/nixos-core.nix; + # constants licenses = callLibs ./licenses.nix; systems = callLibs ./systems; diff --git a/nixos/nixos-core.nix b/nixos/nixos-core.nix new file mode 100644 index 0000000000000..af6b502f3314c --- /dev/null +++ b/nixos/nixos-core.nix @@ -0,0 +1,36 @@ +{ lib }: + +let + inherit (lib.nixos) publicModules evalModules core; +in { + publicModules = { + # Attributes that refer to modules that are unique to certain kinds of systems. + # For instance, some systems are bootable, some can rebuild themselves, whereas + # need only very minimal facilities. + # These form the public interface to the module graph. + + invokeNixpkgs = ./modules/misc/nixpkgs.nix; + invokeNixpkgsImpure = { modules, ... }: { + imports = [ modules.invokeNixpkgs ]; + nixpkgs.system = builtins.currentSystem; + }; + }; + + evalModules = { + prefix ? [], + modules ? [], + specialArgs ? {}, + }: lib.evalModules { + inherit prefix; + modules = modules; + specialArgs = { + modulesPath = builtins.toString ../modules; + modules = publicModules; + } // specialArgs; + }; + + core = module: evalModules { + modules = [ module ]; + }; + +} From 62e7f0eda1c5acf0beb13a00a23f577912a6b8eb Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 3 Dec 2021 12:04:36 +0000 Subject: [PATCH 02/33] nixos/nixpkgs.nix: Make independent --- nixos/modules/misc/nixpkgs.nix | 2 ++ nixos/modules/misc/nixpkgs/test.nix | 8 ++++++++ nixos/tests/all-tests.nix | 1 + 3 files changed, 11 insertions(+) create mode 100644 nixos/modules/misc/nixpkgs/test.nix diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix index 08bc4398555b7..9098ae53790af 100644 --- a/nixos/modules/misc/nixpkgs.nix +++ b/nixos/modules/misc/nixpkgs.nix @@ -64,6 +64,8 @@ let in { + imports = [ ./assertions.nix ]; + options.nixpkgs = { pkgs = mkOption { diff --git a/nixos/modules/misc/nixpkgs/test.nix b/nixos/modules/misc/nixpkgs/test.nix new file mode 100644 index 0000000000000..c2fe3657d4206 --- /dev/null +++ b/nixos/modules/misc/nixpkgs/test.nix @@ -0,0 +1,8 @@ +{ lib, stdenv }: +lib.recurseIntoAttrs { + invokeNixpkgsSimple = + (lib.nixos.core ({ config, modules, ... }: { + imports = [ modules.invokeNixpkgs ]; + nixpkgs.system = stdenv.hostPlatform.system; + }))._module.args.pkgs.hello; +} diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 63be67892017b..62ac35bf290d9 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -322,6 +322,7 @@ in nix-serve-ssh = handleTest ./nix-serve-ssh.nix {}; nixops = handleTest ./nixops/default.nix {}; nixos-generate-config = handleTest ./nixos-generate-config.nix {}; + nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { }; node-red = handleTest ./node-red.nix {}; nomad = handleTest ./nomad.nix {}; novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {}; From dd6d8e3f0c38370ac8fb082ea7a3be06c949deed Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 3 Dec 2021 12:15:20 +0000 Subject: [PATCH 03/33] pkgs.nixosModule: init --- pkgs/top-level/all-packages.nix | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 9bcd0a7e2ed1c..0f6d60dc2b159 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -32626,6 +32626,20 @@ with pkgs; in c.config.system.build // c; + /* + A NixOS module that sets the `pkgs` module argument. + */ + nixosModule = { lib, options, ... }: { + config = + if options?nixpkgs.pkgs then { + # legacy / nixpkgs.nix style + nixpkgs.pkgs = pkgs; + } + else { + # minimal + _module.args.pkgs = pkgs; + }; + }; /* * Run a NixOS VM network test using this evaluation of Nixpkgs. From 7f129e32362b0eeb439c811ba286c2bbc79b151c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 3 Dec 2021 12:17:41 +0000 Subject: [PATCH 04/33] nixos/build.nix: Make independent --- nixos/modules/system/activation/top-level.nix | 10 +--------- nixos/modules/system/build.nix | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 nixos/modules/system/build.nix diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 501998fa399e2..7e53a901c3401 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -141,19 +141,11 @@ in imports = [ (mkRemovedOptionModule [ "nesting" "clone" ] "Use `specialisation.«name» = { inheritParentConfig = true; configuration = { ... }; }` instead.") (mkRemovedOptionModule [ "nesting" "children" ] "Use `specialisation.«name».configuration = { ... }` instead.") + ../build.nix ]; options = { - system.build = mkOption { - internal = true; - default = {}; - type = types.attrs; - description = '' - Attribute set of derivations used to setup the system. - ''; - }; - specialisation = mkOption { default = {}; example = lib.literalExpression "{ fewJobsManyCores.configuration = { nix.buildCores = 0; nix.maxJobs = 1; }; }"; diff --git a/nixos/modules/system/build.nix b/nixos/modules/system/build.nix new file mode 100644 index 0000000000000..a2a2e02f81a2e --- /dev/null +++ b/nixos/modules/system/build.nix @@ -0,0 +1,16 @@ +{ lib, ... }: +let + inherit (lib) mkOption types; +in { + options = { + # TODO: replace by submodule with free-form type + system.build = mkOption { + internal = true; + default = {}; + type = types.attrs; + description = '' + Attribute set of derivations used to set up the system. + ''; + }; + }; +} From 56c283e5c8dfd4d8d5daa15dfa3896beef2ac012 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 3 Dec 2021 12:21:16 +0000 Subject: [PATCH 05/33] nixos/etc.nix: Make independent --- nixos/modules/module-list.nix | 2 +- nixos/modules/system/etc/etc-activation.nix | 12 ++++ nixos/modules/system/etc/etc.nix | 6 +- nixos/modules/system/etc/test.nix | 69 +++++++++++++++++++++ nixos/nixos-core.nix | 2 + nixos/tests/all-tests.nix | 1 + 6 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 nixos/modules/system/etc/etc-activation.nix create mode 100644 nixos/modules/system/etc/test.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 8cb7c39005c52..eb10a412e0317 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1142,7 +1142,7 @@ ./system/boot/systemd-nspawn.nix ./system/boot/timesyncd.nix ./system/boot/tmp.nix - ./system/etc/etc.nix + ./system/etc/etc-activation.nix ./tasks/auto-upgrade.nix ./tasks/bcache.nix ./tasks/cpu-freq.nix diff --git a/nixos/modules/system/etc/etc-activation.nix b/nixos/modules/system/etc/etc-activation.nix new file mode 100644 index 0000000000000..7801049501860 --- /dev/null +++ b/nixos/modules/system/etc/etc-activation.nix @@ -0,0 +1,12 @@ +{ config, lib, ... }: +let + inherit (lib) stringAfter; +in { + + imports = [ ./etc.nix ]; + + config = { + system.activationScripts.etc = + stringAfter [ "users" "groups" ] config.system.build.etcActivationCommands; + }; +} diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix index 6cc8c341e6dfa..ed552fecec53a 100644 --- a/nixos/modules/system/etc/etc.nix +++ b/nixos/modules/system/etc/etc.nix @@ -66,6 +66,8 @@ in { + imports = [ ../build.nix ]; + ###### interface options = { @@ -188,14 +190,12 @@ in config = { system.build.etc = etc; - - system.activationScripts.etc = stringAfter [ "users" "groups" ] + system.build.etcActivationCommands = '' # Set up the statically computed bits of /etc. echo "setting up /etc..." ${pkgs.perl.withPackages (p: [ p.FileSlurp ])}/bin/perl ${./setup-etc.pl} ${etc}/etc ''; - }; } diff --git a/nixos/modules/system/etc/test.nix b/nixos/modules/system/etc/test.nix new file mode 100644 index 0000000000000..dd15bd8066eb7 --- /dev/null +++ b/nixos/modules/system/etc/test.nix @@ -0,0 +1,69 @@ +{ lib +, coreutils +, fakechroot +, fakeroot +, nixosModule +, runCommand +, util-linux +, vmTools +, writeText +}: +let + node = lib.nixos.core ({ config, modules, ... }: { + imports = [ nixosModule modules.etc ]; + environment.etc."passwd" = { + text = passwdText; + }; + environment.etc."hosts" = { + text = hostsText; + mode = "0751"; + }; + }); + passwdText = '' + root:x:0:0:System administrator:/root:/run/current-system/sw/bin/bash + ''; + hostsText = '' + 127.0.0.1 localhost + ::1 localhost + # testing... + ''; +in +lib.recurseIntoAttrs { + test-etc-vm = + vmTools.runInLinuxVM (runCommand "test-etc-vm" { } '' + mkdir -p /etc + ${node.config.system.build.etcActivationCommands} + set -x + [[ -L /etc/passwd ]] + diff /etc/passwd ${writeText "expected-passwd" passwdText} + [[ 751 = $(stat --format %a /etc/hosts) ]] + diff /etc/hosts ${writeText "expected-hosts" hostsText} + set +x + touch $out + ''); + + # fakeroot is behaving weird + test-etc-fakeroot = + runCommand "test-etc" + { + nativeBuildInputs = [ + fakeroot + fakechroot + # for chroot + coreutils + # fakechroot needs getopt, which is provided by util-linux + util-linux + ]; + fakeRootCommands = '' + mkdir -p /etc + ${node.config.system.build.etcActivationCommands} + diff /etc/hosts ${writeText "expected-hosts" hostsText} + touch $out + ''; + } '' + mkdir fake-root + export FAKECHROOT_EXCLUDE_PATH=/dev:/proc:/sys:${builtins.storeDir}:$out + fakechroot fakeroot chroot $PWD/fake-root bash -c 'source $stdenv/setup; eval "$fakeRootCommands"' + ''; + +} diff --git a/nixos/nixos-core.nix b/nixos/nixos-core.nix index af6b502f3314c..4ce67b8947432 100644 --- a/nixos/nixos-core.nix +++ b/nixos/nixos-core.nix @@ -14,6 +14,8 @@ in { imports = [ modules.invokeNixpkgs ]; nixpkgs.system = builtins.currentSystem; }; + + etc = ./modules/system/etc/etc.nix; }; evalModules = { diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 62ac35bf290d9..0acaffe5ab48b 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -122,6 +122,7 @@ in enlightenment = handleTest ./enlightenment.nix {}; env = handleTest ./env.nix {}; ergo = handleTest ./ergo.nix {}; + etc = pkgs.callPackage ../modules/system/etc/test.nix { }; etcd = handleTestOn ["x86_64-linux"] ./etcd.nix {}; etcd-cluster = handleTestOn ["x86_64-linux"] ./etcd-cluster.nix {}; etebase-server = handleTest ./etebase-server.nix {}; From 9b2af8673be82d48ce76c8c152de85ad921d26ba Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Fri, 3 Dec 2021 12:23:23 +0000 Subject: [PATCH 06/33] dockerTools: Add example of using NixOS' etc --- nixos/tests/docker-tools.nix | 5 +++++ pkgs/build-support/docker/examples.nix | 27 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix index 19ebed3ebd0bd..3965817505c16 100644 --- a/nixos/tests/docker-tools.nix +++ b/nixos/tests/docker-tools.nix @@ -413,5 +413,10 @@ import ./make-test-python.nix ({ pkgs, ... }: { "docker rmi layered-image-with-path", ) + with subtest("etc"): + docker.succeed("${examples.etc} | docker load") + docker.succeed("docker run --rm etc | grep localhost") + docker.succeed("docker image rm etc:latest") + ''; }) diff --git a/pkgs/build-support/docker/examples.nix b/pkgs/build-support/docker/examples.nix index f2d4f809ae4e3..9705cf865bfd1 100644 --- a/pkgs/build-support/docker/examples.nix +++ b/pkgs/build-support/docker/examples.nix @@ -559,6 +559,33 @@ rec { includeStorePaths = false; }; + etc = + let + inherit (pkgs) lib; + nixosCore = (lib.nixos.core ({ config, modules, ... }: { + imports = [ pkgs.nixosModule modules.etc ]; + environment.etc."hosts" = { + text = '' + 127.0.0.1 localhost + ::1 localhost + ''; + mode = "0456"; + }; + })); + in pkgs.dockerTools.streamLayeredImage { + name = "etc"; + tag = "latest"; + enableFakechroot = true; + fakeRootCommands = '' + mkdir -p /etc + ${nixosCore.config.system.build.etcActivationCommands} + ''; + config.Cmd = pkgs.writeScript "etc-cmd" '' + #!${pkgs.busybox}/bin/sh + ${pkgs.busybox}/bin/cat /etc/hosts + ''; + }; + # Example export of the bash image exportBash = pkgs.dockerTools.exportImage { fromImage = bash; }; From 4ec415cff9bcbaef08e0d900406a5c7181e71881 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 5 Dec 2021 11:33:43 +0000 Subject: [PATCH 07/33] nixos/top-level.nix: Make extensible --- nixos/modules/system/activation/top-level.nix | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 7e53a901c3401..2bf44487f2cc0 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -88,6 +88,8 @@ let echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies + ${config.system.systemBuilderCommands} + ${config.system.extraSystemBuilderCmds} ''; @@ -96,7 +98,7 @@ let # kernel, systemd units, init scripts, etc.) as well as a script # `switch-to-configuration' that activates the configuration and # makes it bootable. - baseSystem = pkgs.stdenvNoCC.mkDerivation { + baseSystem = pkgs.stdenvNoCC.mkDerivation ({ name = "nixos-system-${config.system.name}-${config.system.nixos.label}"; preferLocalBuild = true; allowSubstitutes = false; @@ -120,7 +122,7 @@ let # Needed by switch-to-configuration. perl = pkgs.perl.withPackages (p: with p; [ FileSlurp NetDBus XMLParser XMLTwig ]); - }; + } // config.system.systemBuilderAttrs); # Handle assertions and warnings @@ -228,6 +230,24 @@ in ''; }; + system.systemBuilderCommands = mkOption { + type = types.lines; + internal = true; + default = ""; + description = '' + This code will be added to the builder creating the system store path. + ''; + }; + + system.systemBuilderAttrs = mkOption { + type = types.attrsOf types.unspecified; + internal = true; + default = {}; + description = '' + Derivation attributes that will be passed to the top level system builder. + ''; + }; + system.extraSystemBuilderCmds = mkOption { type = types.lines; internal = true; From baa96b8b592092af7700fe40700d3e12eb4f3a74 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 5 Dec 2021 11:48:44 +0000 Subject: [PATCH 08/33] nixos/system-with-activation.nix: Extract module --- nixos/modules/module-list.nix | 1 + .../activation/system-with-activation.nix | 32 +++++++++++++++++++ nixos/modules/system/activation/top-level.nix | 23 ------------- 3 files changed, 33 insertions(+), 23 deletions(-) create mode 100644 nixos/modules/system/activation/system-with-activation.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index eb10a412e0317..4213a77f02813 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1111,6 +1111,7 @@ ./services/x11/xfs.nix ./services/x11/xserver.nix ./system/activation/activation-script.nix + ./system/activation/system-with-activation.nix ./system/activation/top-level.nix ./system/boot/binfmt.nix ./system/boot/emergency-mode.nix diff --git a/nixos/modules/system/activation/system-with-activation.nix b/nixos/modules/system/activation/system-with-activation.nix new file mode 100644 index 0000000000000..9d8627ccd7d7e --- /dev/null +++ b/nixos/modules/system/activation/system-with-activation.nix @@ -0,0 +1,32 @@ +{ config, lib, pkgs, ... }: +{ + config = { + system.systemBuilderAttrs = { + activationScript = config.system.activationScripts.script; + dryActivationScript = config.system.dryActivationScript; + }; + + system.systemBuilderCommands = '' + echo "$activationScript" > $out/activate + echo "$dryActivationScript" > $out/dry-activate + substituteInPlace $out/activate --subst-var out + substituteInPlace $out/dry-activate --subst-var out + chmod u+x $out/activate $out/dry-activate + unset activationScript dryActivationScript + ${pkgs.stdenv.shell} -n $out/activate + ${pkgs.stdenv.shell} -n $out/dry-activate + + mkdir -p $out/bin + export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive" + substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration + chmod +x $out/bin/switch-to-configuration + ${lib.optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) '' + if ! output=$($perl/bin/perl -c $out/bin/switch-to-configuration 2>&1); then + echo "switch-to-configuration syntax is not valid:" + echo "$output" + exit 1 + fi + ''} + ''; + }; +} diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 2bf44487f2cc0..6161b32dcbbb4 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -49,15 +49,6 @@ let ln -s ${config.hardware.firmware}/lib/firmware $out/firmware ''} - echo "$activationScript" > $out/activate - echo "$dryActivationScript" > $out/dry-activate - substituteInPlace $out/activate --subst-var out - substituteInPlace $out/dry-activate --subst-var out - chmod u+x $out/activate $out/dry-activate - unset activationScript dryActivationScript - ${pkgs.stdenv.shell} -n $out/activate - ${pkgs.stdenv.shell} -n $out/dry-activate - cp ${config.system.build.bootStage2} $out/init substituteInPlace $out/init --subst-var-by systemConfig $out @@ -74,18 +65,6 @@ let ${concatStringsSep "\n" (mapAttrsToList (name: path: "ln -s ${path} $out/specialisation/${name}") children)} - mkdir $out/bin - export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive" - substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration - chmod +x $out/bin/switch-to-configuration - ${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) '' - if ! output=$($perl/bin/perl -c $out/bin/switch-to-configuration 2>&1); then - echo "switch-to-configuration syntax is not valid:" - echo "$output" - exit 1 - fi - ''} - echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies ${config.system.systemBuilderCommands} @@ -114,8 +93,6 @@ let installBootLoader = config.system.build.installBootLoader or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true"; - activationScript = config.system.activationScripts.script; - dryActivationScript = config.system.dryActivationScript; nixosLabel = config.system.nixos.label; configurationName = config.boot.loader.grub.configurationName; From 8da928c46b6b8f0035d03939b6ff0eaa3b31ce08 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 5 Dec 2021 11:58:34 +0000 Subject: [PATCH 09/33] nixos/bootable.nix: Extract module --- nixos/modules/module-list.nix | 1 + nixos/modules/system/activation/bootable.nix | 72 +++++++++++++++++++ nixos/modules/system/activation/top-level.nix | 59 +-------------- 3 files changed, 74 insertions(+), 58 deletions(-) create mode 100644 nixos/modules/system/activation/bootable.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 4213a77f02813..484250d39f300 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1111,6 +1111,7 @@ ./services/x11/xfs.nix ./services/x11/xserver.nix ./system/activation/activation-script.nix + ./system/activation/bootable.nix ./system/activation/system-with-activation.nix ./system/activation/top-level.nix ./system/boot/binfmt.nix diff --git a/nixos/modules/system/activation/bootable.nix b/nixos/modules/system/activation/bootable.nix new file mode 100644 index 0000000000000..8fe1fb5ca61e3 --- /dev/null +++ b/nixos/modules/system/activation/bootable.nix @@ -0,0 +1,72 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) + mkIf + mkOption + optionalString + types + ; + + kernelPath = "${config.boot.kernelPackages.kernel}/" + + "${config.system.boot.loader.kernelFile}"; + initrdPath = "${config.system.build.initialRamdisk}/" + + "${config.system.boot.loader.initrdFile}"; +in +{ + options = { + system.boot.loader.id = mkOption { + internal = true; + default = ""; + description = '' + Id string of the used bootloader. + ''; + }; + + system.boot.loader.kernelFile = mkOption { + internal = true; + default = pkgs.stdenv.hostPlatform.linux-kernel.target; + defaultText = lib.literalExpression "pkgs.stdenv.hostPlatform.linux-kernel.target"; + type = types.str; + description = '' + Name of the kernel file to be passed to the bootloader. + ''; + }; + + system.boot.loader.initrdFile = mkOption { + internal = true; + default = "initrd"; + type = types.str; + description = '' + Name of the initrd file to be passed to the bootloader. + ''; + }; + }; + + config = { + # Containers don't have their own kernel or initrd. They boot + # directly into stage 2. + system.systemBuilderCommands = mkIf (!config.boot.isContainer) '' + if [ ! -f ${kernelPath} ]; then + echo "The bootloader cannot find the proper kernel image." + echo "(Expecting ${kernelPath})" + false + fi + + ln -s ${kernelPath} $out/kernel + ln -s ${config.system.modulesTree} $out/kernel-modules + ${optionalString (config.hardware.deviceTree.package != null) '' + ln -s ${config.hardware.deviceTree.package} $out/dtbs + ''} + + echo -n "$kernelParams" > $out/kernel-params + + ln -s ${initrdPath} $out/initrd + + ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out + + ln -s ${config.hardware.firmware}/lib/firmware $out/firmware + + echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system + ''; + }; +} diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 6161b32dcbbb4..66c89760ebffb 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -17,38 +17,9 @@ let config.specialisation; systemBuilder = - let - kernelPath = "${config.boot.kernelPackages.kernel}/" + - "${config.system.boot.loader.kernelFile}"; - initrdPath = "${config.system.build.initialRamdisk}/" + - "${config.system.boot.loader.initrdFile}"; - in '' + '' mkdir $out - # Containers don't have their own kernel or initrd. They boot - # directly into stage 2. - ${optionalString (!config.boot.isContainer) '' - if [ ! -f ${kernelPath} ]; then - echo "The bootloader cannot find the proper kernel image." - echo "(Expecting ${kernelPath})" - false - fi - - ln -s ${kernelPath} $out/kernel - ln -s ${config.system.modulesTree} $out/kernel-modules - ${optionalString (config.hardware.deviceTree.package != null) '' - ln -s ${config.hardware.deviceTree.package} $out/dtbs - ''} - - echo -n "$kernelParams" > $out/kernel-params - - ln -s ${initrdPath} $out/initrd - - ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out - - ln -s ${config.hardware.firmware}/lib/firmware $out/firmware - ''} - cp ${config.system.build.bootStage2} $out/init substituteInPlace $out/init --subst-var-by systemConfig $out @@ -59,7 +30,6 @@ let echo -n "$configurationName" > $out/configuration-name echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version echo -n "$nixosLabel" > $out/nixos-version - echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system mkdir $out/specialisation ${concatStringsSep "\n" @@ -168,33 +138,6 @@ in ); }; - system.boot.loader.id = mkOption { - internal = true; - default = ""; - description = '' - Id string of the used bootloader. - ''; - }; - - system.boot.loader.kernelFile = mkOption { - internal = true; - default = pkgs.stdenv.hostPlatform.linux-kernel.target; - defaultText = literalExpression "pkgs.stdenv.hostPlatform.linux-kernel.target"; - type = types.str; - description = '' - Name of the kernel file to be passed to the bootloader. - ''; - }; - - system.boot.loader.initrdFile = mkOption { - internal = true; - default = "initrd"; - type = types.str; - description = '' - Name of the initrd file to be passed to the bootloader. - ''; - }; - system.copySystemConfiguration = mkOption { type = types.bool; default = false; From 2bf147c7c798e7df033c0cd7af18e46eb9155f8b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 5 Dec 2021 12:07:06 +0000 Subject: [PATCH 10/33] nixos/specialisation.nix: Extract module --- nixos/modules/module-list.nix | 1 + .../system/activation/specialisation.nix | 81 +++++++++++++++++++ nixos/modules/system/activation/top-level.nix | 60 -------------- 3 files changed, 82 insertions(+), 60 deletions(-) create mode 100644 nixos/modules/system/activation/specialisation.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 484250d39f300..e1877b71575a4 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1112,6 +1112,7 @@ ./services/x11/xserver.nix ./system/activation/activation-script.nix ./system/activation/bootable.nix + ./system/activation/specialisation.nix ./system/activation/system-with-activation.nix ./system/activation/top-level.nix ./system/boot/binfmt.nix diff --git a/nixos/modules/system/activation/specialisation.nix b/nixos/modules/system/activation/specialisation.nix new file mode 100644 index 0000000000000..ae732dfc607c1 --- /dev/null +++ b/nixos/modules/system/activation/specialisation.nix @@ -0,0 +1,81 @@ +{ config, lib, pkgs, extendModules, noUserModules, ... }: + +let + inherit (lib) + concatStringsSep + mapAttrs + mapAttrsToList + mkOption + types + ; + + # This attribute is responsible for creating boot entries for + # child configuration. They are only (directly) accessible + # when the parent configuration is boot default. For example, + # you can provide an easy way to boot the same configuration + # as you use, but with another kernel + # !!! fix this + children = + mapAttrs + (childName: childConfig: childConfig.configuration.system.build.toplevel) + config.specialisation; + +in +{ + config = { + system.systemBuilderCommands = lib.mkIf (config.specialisation != { }) '' + mkdir $out/specialisation + ${concatStringsSep "\n" + (mapAttrsToList (name: path: "ln -s ${path} $out/specialisation/${name}") children)} + ''; + }; + + options = { + + specialisation = mkOption { + default = { }; + example = lib.literalExpression "{ fewJobsManyCores.configuration = { nix.buildCores = 0; nix.maxJobs = 1; }; }"; + description = '' + Additional configurations to build. If + inheritParentConfig is true, the system + will be based on the overall system configuration. + + To switch to a specialised configuration + (e.g. fewJobsManyCores) at runtime, run: + + + # sudo /run/current-system/specialisation/fewJobsManyCores/bin/switch-to-configuration test + + ''; + type = types.attrsOf (types.submodule ( + local@{ ... }: + let + extend = + if local.config.inheritParentConfig + then extendModules + else noUserModules.extendModules; + in + { + options.inheritParentConfig = mkOption { + type = types.bool; + default = true; + description = "Include the entire system's configuration. Set to false to make a completely differently configured system."; + }; + + options.configuration = mkOption { + default = { }; + description = '' + Arbitrary NixOS configuration. + + Anything you can add to a normal NixOS configuration, you can add + here, including imports and config values, although nested + specialisations will be ignored. + ''; + visible = "shallow"; + inherit (extend { modules = [ ./no-clone.nix ]; }) type; + }; + } + )); + }; + }; +} diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 66c89760ebffb..691cea0055357 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -3,19 +3,6 @@ with lib; let - - - # This attribute is responsible for creating boot entries for - # child configuration. They are only (directly) accessible - # when the parent configuration is boot default. For example, - # you can provide an easy way to boot the same configuration - # as you use, but with another kernel - # !!! fix this - children = - mapAttrs - (childName: childConfig: childConfig.configuration.system.build.toplevel) - config.specialisation; - systemBuilder = '' mkdir $out @@ -31,10 +18,6 @@ let echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version echo -n "$nixosLabel" > $out/nixos-version - mkdir $out/specialisation - ${concatStringsSep "\n" - (mapAttrsToList (name: path: "ln -s ${path} $out/specialisation/${name}") children)} - echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies ${config.system.systemBuilderCommands} @@ -95,49 +78,6 @@ in options = { - specialisation = mkOption { - default = {}; - example = lib.literalExpression "{ fewJobsManyCores.configuration = { nix.buildCores = 0; nix.maxJobs = 1; }; }"; - description = '' - Additional configurations to build. If - inheritParentConfig is true, the system - will be based on the overall system configuration. - - To switch to a specialised configuration - (e.g. fewJobsManyCores) at runtime, run: - - - # sudo /run/current-system/specialisation/fewJobsManyCores/bin/switch-to-configuration test - - ''; - type = types.attrsOf (types.submodule ( - local@{ ... }: let - extend = if local.config.inheritParentConfig - then extendModules - else noUserModules.extendModules; - in { - options.inheritParentConfig = mkOption { - type = types.bool; - default = true; - description = "Include the entire system's configuration. Set to false to make a completely differently configured system."; - }; - - options.configuration = mkOption { - default = {}; - description = '' - Arbitrary NixOS configuration. - - Anything you can add to a normal NixOS configuration, you can add - here, including imports and config values, although nested - specialisations will be ignored. - ''; - visible = "shallow"; - inherit (extend { modules = [ ./no-clone.nix ]; }) type; - }; - }) - ); - }; - system.copySystemConfiguration = mkOption { type = types.bool; default = false; From c78d41a3ead8fda3f7230d63f0d72fba77891a3f Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 5 Dec 2021 12:19:20 +0000 Subject: [PATCH 11/33] nixos: Move top-level stage2 def to stage-2.nix --- nixos/modules/system/activation/top-level.nix | 5 ----- nixos/modules/system/boot/stage-2.nix | 7 +++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 691cea0055357..5d787335bd8de 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -7,15 +7,10 @@ let '' mkdir $out - cp ${config.system.build.bootStage2} $out/init - substituteInPlace $out/init --subst-var-by systemConfig $out - ln -s ${config.system.build.etc}/etc $out/etc ln -s ${config.system.path} $out/sw - ln -s "$systemd" $out/systemd echo -n "$configurationName" > $out/configuration-name - echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version echo -n "$nixosLabel" > $out/nixos-version echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix index f6b6a8e4b0b44..dd64c374203c7 100644 --- a/nixos/modules/system/boot/stage-2.nix +++ b/nixos/modules/system/boot/stage-2.nix @@ -104,5 +104,12 @@ in system.build.bootStage2 = bootStage2; + system.systemBuilderCommands = '' + cp ${config.system.build.bootStage2} $out/init + substituteInPlace $out/init --subst-var-by systemConfig $out + ln -s "$systemd" $out/systemd + echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version + ''; + }; } From c8da2d49cffd9ba004f095afe12a8b8df30cc195 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Sun, 5 Dec 2021 12:47:28 +0000 Subject: [PATCH 12/33] nixos: Move system.name logic to network-interfaces.nix --- nixos/modules/system/activation/top-level.nix | 11 +++++++---- nixos/modules/tasks/network-interfaces.nix | 6 ++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 5d787335bd8de..bb88be635169b 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -151,10 +151,7 @@ in system.name = mkOption { type = types.str; - default = - if config.networking.hostName == "" - then "unnamed" - else config.networking.hostName; + # when using full NixOS or importing the network-interfaces.nix module. defaultText = literalExpression '' if config.networking.hostName == "" then "unnamed" @@ -182,6 +179,12 @@ in system.build.toplevel = system; + # Traditionally, the option default contained the logic for taking this from + # the network.hostName option, which is expected to define it at + # mkOptionDefault priority. However, we'd also like to have a default when + # network.hostName is not imported. + system.name = mkOverride ((mkOptionDefault {}).priority + 100) "unnamed"; + }; } diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix index 49901cda848df..9e8af022d420c 100644 --- a/nixos/modules/tasks/network-interfaces.nix +++ b/nixos/modules/tasks/network-interfaces.nix @@ -1222,6 +1222,12 @@ in } ]; + system.name = lib.mkOptionDefault ( + if config.networking.hostName == "" + then "unnamed" + else config.networking.hostName + ); + boot.kernelModules = [ ] ++ optional hasVirtuals "tun" ++ optional hasSits "sit" From c6ea188fb9ebaa5622f8976616549787b03c246c Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 10:29:10 +0000 Subject: [PATCH 13/33] nixos/system-path-core.nix: Extract module --- nixos/modules/config/system-path-core.nix | 87 +++++++++++++++++++++++ nixos/modules/config/system-path.nix | 77 +++----------------- 2 files changed, 95 insertions(+), 69 deletions(-) create mode 100644 nixos/modules/config/system-path-core.nix diff --git a/nixos/modules/config/system-path-core.nix b/nixos/modules/config/system-path-core.nix new file mode 100644 index 0000000000000..9fe1c72d32db7 --- /dev/null +++ b/nixos/modules/config/system-path-core.nix @@ -0,0 +1,87 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) + mkOption + types + literalExpression + ; +in +{ + options = { + + environment = { + + systemPackages = mkOption { + type = types.listOf types.package; + default = [ ]; + example = literalExpression "[ pkgs.firefox pkgs.htop ]"; + description = '' + The set of packages that appear in + /run/current-system/sw. These packages are + automatically available to all users, and are + automatically updated every time you rebuild the system + configuration. (The latter is the main difference with + installing them in the default profile, + /nix/var/nix/profiles/default. + ''; + }; + + pathsToLink = mkOption { + type = types.listOf types.str; + # Note: We need `/lib' to be among `pathsToLink' for NSS modules + # to work. + default = [ ]; + example = [ "/" ]; + description = "List of directories to be symlinked in /run/current-system/sw."; + }; + + extraOutputsToInstall = mkOption { + type = types.listOf types.str; + default = [ ]; + example = [ "doc" "info" "devdoc" ]; + description = "List of additional package outputs to be symlinked into /run/current-system/sw."; + }; + + extraSetup = mkOption { + type = types.lines; + default = ""; + description = "Shell fragments to be run after the system environment has been created. This should only be used for things that need to modify the internals of the environment, e.g. generating MIME caches. The environment being built can be accessed at $out."; + }; + + }; + + system = { + + path = mkOption { + internal = true; + description = '' + The packages you want in the boot environment. + ''; + }; + + }; + + }; + config = { + environment.pathsToLink = [ + "/bin" + ]; + + system.path = pkgs.buildEnv { + name = "system-path"; + paths = config.environment.systemPackages; + inherit (config.environment) pathsToLink extraOutputsToInstall; + ignoreCollisions = true; + # !!! Hacky, should modularise. + # outputs TODO: note that the tools will often not be linked by default + postBuild = + '' + # Remove wrapped binaries, they shouldn't be accessible via PATH. + find $out/bin -maxdepth 1 -name ".*-wrapped" -type l -delete + + ${config.environment.extraSetup} + ''; + }; + + }; +} diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix index 6ff4ec2921cf8..0b62a442f0b38 100644 --- a/nixos/modules/config/system-path.nix +++ b/nixos/modules/config/system-path.nix @@ -51,25 +51,12 @@ let in { + imports = [ ./system-path-core.nix ]; + options = { environment = { - systemPackages = mkOption { - type = types.listOf types.package; - default = []; - example = literalExpression "[ pkgs.firefox pkgs.thunderbird ]"; - description = '' - The set of packages that appear in - /run/current-system/sw. These packages are - automatically available to all users, and are - automatically updated every time you rebuild the system - configuration. (The latter is the main difference with - installing them in the default profile, - /nix/var/nix/profiles/default. - ''; - }; - defaultPackages = mkOption { type = types.listOf types.package; default = defaultPackages; @@ -93,39 +80,6 @@ in ''; }; - pathsToLink = mkOption { - type = types.listOf types.str; - # Note: We need `/lib' to be among `pathsToLink' for NSS modules - # to work. - default = []; - example = ["/"]; - description = "List of directories to be symlinked in /run/current-system/sw."; - }; - - extraOutputsToInstall = mkOption { - type = types.listOf types.str; - default = [ ]; - example = [ "doc" "info" "devdoc" ]; - description = "List of additional package outputs to be symlinked into /run/current-system/sw."; - }; - - extraSetup = mkOption { - type = types.lines; - default = ""; - description = "Shell fragments to be run after the system environment has been created. This should only be used for things that need to modify the internals of the environment, e.g. generating MIME caches. The environment being built can be accessed at $out."; - }; - - }; - - system = { - - path = mkOption { - internal = true; - description = '' - The packages you want in the boot environment. - ''; - }; - }; }; @@ -135,8 +89,7 @@ in environment.systemPackages = requiredPackages ++ config.environment.defaultPackages; environment.pathsToLink = - [ "/bin" - "/etc/xdg" + [ "/etc/xdg" "/etc/gtk-2.0" "/etc/gtk-3.0" "/lib" # FIXME: remove and update debug-info.nix @@ -155,25 +108,11 @@ in "/share/thumbnailers" ]; - system.path = pkgs.buildEnv { - name = "system-path"; - paths = config.environment.systemPackages; - inherit (config.environment) pathsToLink extraOutputsToInstall; - ignoreCollisions = true; - # !!! Hacky, should modularise. - # outputs TODO: note that the tools will often not be linked by default - postBuild = - '' - # Remove wrapped binaries, they shouldn't be accessible via PATH. - find $out/bin -maxdepth 1 -name ".*-wrapped" -type l -delete - - if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then - $out/bin/glib-compile-schemas $out/share/glib-2.0/schemas - fi - - ${config.environment.extraSetup} - ''; - }; + environment.extraSetup = '' + if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then + $out/bin/glib-compile-schemas $out/share/glib-2.0/schemas + fi + ''; }; } From dc2b3962c3de7d8f264d5a8b583f74c7dce3359a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 10:56:12 +0000 Subject: [PATCH 14/33] nixos/users-groups.nix: Make independent --- nixos/modules/config/users-groups.nix | 15 +++++++++++++++ nixos/modules/programs/shadow.nix | 12 ------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix index a34d281434185..5cc5e30159ae6 100644 --- a/nixos/modules/config/users-groups.nix +++ b/nixos/modules/config/users-groups.nix @@ -442,12 +442,27 @@ in { (cfg: if cfg.security.initialRootPassword == "!" then null else cfg.security.initialRootPassword)) + ../misc/ids.nix + ../misc/extra-arguments.nix ]; ###### interface options = { + users.defaultUserShell = lib.mkOption { + description = '' + This option defines the default shell assigned to user + accounts. This can be either a full system path or a shell package. + + This must not be a store path, since the path is + used outside the store (in particular in /etc/passwd). + ''; + example = literalExpression "pkgs.zsh"; + type = types.either types.path types.shellPackage; + default = "/bin/false"; + }; + users.mutableUsers = mkOption { type = types.bool; default = true; diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix index 963cd8853dbbb..2dff92d6b06a9 100644 --- a/nixos/modules/programs/shadow.nix +++ b/nixos/modules/programs/shadow.nix @@ -58,18 +58,6 @@ in options = { - users.defaultUserShell = lib.mkOption { - description = '' - This option defines the default shell assigned to user - accounts. This can be either a full system path or a shell package. - - This must not be a store path, since the path is - used outside the store (in particular in /etc/passwd). - ''; - example = literalExpression "pkgs.zsh"; - type = types.either types.path types.shellPackage; - }; - }; From 42b59e3fa23d3b4c9831e4da8de3ddd462034b78 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 10:57:24 +0000 Subject: [PATCH 15/33] nixos/update-users-groups.pl: Fall back to root group for shadow The shadow group does not need to exist in some minimal scenarios like container images. This sets the shadow file group to root when the shadow group does not exist. --- nixos/modules/config/update-users-groups.pl | 1 + 1 file changed, 1 insertion(+) diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl index 232f886789d38..cadae90437450 100644 --- a/nixos/modules/config/update-users-groups.pl +++ b/nixos/modules/config/update-users-groups.pl @@ -306,6 +306,7 @@ sub parseUser { my $uid = getpwnam "root"; my $gid = getgrnam "shadow"; my $path = "/etc/shadow"; + $gid = 0 unless defined $gid; (chown($uid, $gid, $path) || die "Failed to change ownership of $path: $!") unless $is_dry; } From b9cb1828875635c04c264653e2275b5bef2454f2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 11:23:00 +0000 Subject: [PATCH 16/33] nixos: Move specialfs activation script to filesystems.nix A system may not need to configure file system; for instance a container image does not manage its own mounts. --- .../system/activation/activation-script.nix | 18 ------------------ nixos/modules/tasks/filesystems.nix | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix index 4a32387db8da5..0775d023c1b3f 100644 --- a/nixos/modules/system/activation/activation-script.nix +++ b/nixos/modules/system/activation/activation-script.nix @@ -238,24 +238,6 @@ in rmdir --ignore-fail-on-non-empty /usr/bin /usr ''; - system.activationScripts.specialfs = - '' - specialMount() { - local device="$1" - local mountPoint="$2" - local options="$3" - local fsType="$4" - - if mountpoint -q "$mountPoint"; then - local options="remount,$options" - else - mkdir -m 0755 -p "$mountPoint" - fi - mount -t "$fsType" -o "$options" "$device" "$mountPoint" - } - source ${config.system.build.earlyMountScript} - ''; - systemd.user = { services.nixos-activation = { description = "Run user-specific NixOS activation"; diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix index 225bcbe58e017..523b6a147cd0c 100644 --- a/nixos/modules/tasks/filesystems.nix +++ b/nixos/modules/tasks/filesystems.nix @@ -241,6 +241,24 @@ in system.build.fileSystems = fileSystems; system.build.earlyMountScript = makeSpecialMounts (toposort fsBefore (attrValues config.boot.specialFileSystems)).result; + system.activationScripts.specialfs = + '' + specialMount() { + local device="$1" + local mountPoint="$2" + local options="$3" + local fsType="$4" + + if mountpoint -q "$mountPoint"; then + local options="remount,$options" + else + mkdir -m 0755 -p "$mountPoint" + fi + mount -t "$fsType" -o "$options" "$device" "$mountPoint" + } + source ${config.system.build.earlyMountScript} + ''; + boot.supportedFilesystems = map (fs: fs.fsType) fileSystems; # Add the mount helpers to the system path so that `mount' can find them. From db05855521be357be0058e8318e7fba3d720b194 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 11:27:59 +0000 Subject: [PATCH 17/33] nixos/top-level.nix: Move configurationName to grub.nix The configuration-name file is grub specific, so it should not be in top-level.nix. --- nixos/modules/system/activation/top-level.nix | 3 --- nixos/modules/system/boot/loader/grub/grub.nix | 5 +++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index bb88be635169b..b2f5148d69cfb 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -10,7 +10,6 @@ let ln -s ${config.system.build.etc}/etc $out/etc ln -s ${config.system.path} $out/sw - echo -n "$configurationName" > $out/configuration-name echo -n "$nixosLabel" > $out/nixos-version echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies @@ -43,8 +42,6 @@ let or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true"; nixosLabel = config.system.nixos.label; - configurationName = config.boot.loader.grub.configurationName; - # Needed by switch-to-configuration. perl = pkgs.perl.withPackages (p: with p; [ FileSlurp NetDBus XMLParser XMLTwig ]); } // config.system.systemBuilderAttrs); diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix index 8db271f871352..b8c450ec62e74 100644 --- a/nixos/modules/system/boot/loader/grub/grub.nix +++ b/nixos/modules/system/boot/loader/grub/grub.nix @@ -724,6 +724,11 @@ in boot.loader.supportsInitrdSecrets = true; + system.systemBuilderAttrs.configurationName = cfg.configurationName; + system.systemBuilderCommands = '' + echo -n "$configurationName" > $out/configuration-name + ''; + system.build.installBootLoader = let install-grub-pl = pkgs.substituteAll { From 8313fd36f4e71da13ff516ba109862f7afa3fa83 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 11:35:47 +0000 Subject: [PATCH 18/33] nixos/top-level.nix: Make independent of systemd.package --- nixos/modules/system/activation/top-level.nix | 1 - nixos/modules/system/boot/stage-2.nix | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index b2f5148d69cfb..8548d875a4125 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -31,7 +31,6 @@ let buildCommand = systemBuilder; inherit (pkgs) coreutils; - systemd = config.systemd.package; shell = "${pkgs.bash}/bin/sh"; su = "${pkgs.shadow.su}/bin/su"; utillinux = pkgs.util-linux; diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix index dd64c374203c7..eb89c0b864646 100644 --- a/nixos/modules/system/boot/stage-2.nix +++ b/nixos/modules/system/boot/stage-2.nix @@ -104,6 +104,9 @@ in system.build.bootStage2 = bootStage2; + system.systemBuilderAttrs = { + systemd = config.systemd.package; + }; system.systemBuilderCommands = '' cp ${config.system.build.bootStage2} $out/init substituteInPlace $out/init --subst-var-by systemConfig $out From 87724859d07b45c35bb8e56dadc67c69eb9dbd3b Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 11:41:02 +0000 Subject: [PATCH 19/33] nixos/user-profiles.nix: Extract module --- nixos/modules/config/user-profiles.nix | 25 +++++++++++++++++++++++++ nixos/modules/config/users-groups.nix | 15 --------------- nixos/modules/module-list.nix | 1 + 3 files changed, 26 insertions(+), 15 deletions(-) create mode 100644 nixos/modules/config/user-profiles.nix diff --git a/nixos/modules/config/user-profiles.nix b/nixos/modules/config/user-profiles.nix new file mode 100644 index 0000000000000..db86debf8cd74 --- /dev/null +++ b/nixos/modules/config/user-profiles.nix @@ -0,0 +1,25 @@ +{ config, lib, pkgs, ... }: +let + inherit (lib) + mapAttrs' + filterAttrs + ; +in +{ + config = { + environment.etc = mapAttrs' (_: { packages, name, ... }: { + name = "profiles/per-user/${name}"; + value.source = pkgs.buildEnv { + name = "user-environment"; + paths = packages; + inherit (config.environment) pathsToLink extraOutputsToInstall; + inherit (config.system.path) ignoreCollisions postBuild; + }; + }) (filterAttrs (_: u: u.packages != []) config.users.users); + + environment.profiles = [ + "$HOME/.nix-profile" + "/etc/profiles/per-user/$USER" + ]; + }; +} diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix index 5cc5e30159ae6..34fec82849795 100644 --- a/nixos/modules/config/users-groups.nix +++ b/nixos/modules/config/users-groups.nix @@ -594,21 +594,6 @@ in { # Install all the user shells environment.systemPackages = systemShells; - environment.etc = (mapAttrs' (_: { packages, name, ... }: { - name = "profiles/per-user/${name}"; - value.source = pkgs.buildEnv { - name = "user-environment"; - paths = packages; - inherit (config.environment) pathsToLink extraOutputsToInstall; - inherit (config.system.path) ignoreCollisions postBuild; - }; - }) (filterAttrs (_: u: u.packages != []) cfg.users)); - - environment.profiles = [ - "$HOME/.nix-profile" - "/etc/profiles/per-user/$USER" - ]; - assertions = [ { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique); message = "UIDs and GIDs must be unique!"; diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index e1877b71575a4..c9f6e685bfc22 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -35,6 +35,7 @@ ./config/system-path.nix ./config/terminfo.nix ./config/unix-odbc-drivers.nix + ./config/user-profiles.nix ./config/users-groups.nix ./config/vte.nix ./config/zram.nix From f2cd71449b87035f98ea7883e2e07b0a879627bf Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 11:47:01 +0000 Subject: [PATCH 20/33] nixos/systemd-user-activation.nix: Extract module --- nixos/modules/module-list.nix | 1 + .../system/activation/activation-script.nix | 9 --------- .../system/boot/systemd-user-activation.nix | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 nixos/modules/system/boot/systemd-user-activation.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index c9f6e685bfc22..251f745c2ab4f 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1144,6 +1144,7 @@ ./system/boot/stage-2.nix ./system/boot/systemd.nix ./system/boot/systemd-nspawn.nix + ./system/boot/systemd-user-activation.nix ./system/boot/timesyncd.nix ./system/boot/tmp.nix ./system/etc/etc-activation.nix diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix index 0775d023c1b3f..1ff2ccb8aa371 100644 --- a/nixos/modules/system/activation/activation-script.nix +++ b/nixos/modules/system/activation/activation-script.nix @@ -238,15 +238,6 @@ in rmdir --ignore-fail-on-non-empty /usr/bin /usr ''; - systemd.user = { - services.nixos-activation = { - description = "Run user-specific NixOS activation"; - script = config.system.userActivationScripts.script; - unitConfig.ConditionUser = "!@system"; - serviceConfig.Type = "oneshot"; - wantedBy = [ "default.target" ]; - }; - }; }; } diff --git a/nixos/modules/system/boot/systemd-user-activation.nix b/nixos/modules/system/boot/systemd-user-activation.nix new file mode 100644 index 0000000000000..a0e49f6fd4728 --- /dev/null +++ b/nixos/modules/system/boot/systemd-user-activation.nix @@ -0,0 +1,15 @@ +{ config, lib, pkgs, ... }: + +{ + config = { + systemd.user = { + services.nixos-activation = { + description = "Run user-specific NixOS activation"; + script = config.system.userActivationScripts.script; + unitConfig.ConditionUser = "!@system"; + serviceConfig.Type = "oneshot"; + wantedBy = [ "default.target" ]; + }; + }; + }; +} From a7a1438c1539aef4f4b7ba4046be010359415565 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 12:10:29 +0000 Subject: [PATCH 21/33] nixos/top-level.nix: Make independent of boot modules --- nixos/modules/system/activation/bootable.nix | 7 +++++++ nixos/modules/system/activation/top-level.nix | 4 ---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/nixos/modules/system/activation/bootable.nix b/nixos/modules/system/activation/bootable.nix index 8fe1fb5ca61e3..20bcca824e975 100644 --- a/nixos/modules/system/activation/bootable.nix +++ b/nixos/modules/system/activation/bootable.nix @@ -68,5 +68,12 @@ in echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system ''; + system.systemBuilderAttrs = { + kernelParams = config.boot.kernelParams; + installBootLoader = + config.system.build.installBootLoader + or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true"; + + }; }; } diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 8548d875a4125..79ba60c3e8058 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -35,10 +35,6 @@ let su = "${pkgs.shadow.su}/bin/su"; utillinux = pkgs.util-linux; - kernelParams = config.boot.kernelParams; - installBootLoader = - config.system.build.installBootLoader - or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true"; nixosLabel = config.system.nixos.label; # Needed by switch-to-configuration. From 7c0c2e2842d1b7929ddca6bd8a3b015f58475da7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 12:46:05 +0000 Subject: [PATCH 22/33] nixos: Add system.activation.externalActivationScript --- .../system/activation/activation-script.nix | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix index 1ff2ccb8aa371..7c53d5f871640 100644 --- a/nixos/modules/system/activation/activation-script.nix +++ b/nixos/modules/system/activation/activation-script.nix @@ -137,6 +137,23 @@ in }; }; + system.activation.externalActivationScript = mkOption { + description = '' + Activation script for systems that are not managed by users of the system. + + In this scenario, the activation script does not need to be part of the + "top-level" system store path. + + In the case of an image, this allows activation-only dependencies to + be omitted from the runtime dependencies. + + In case of a mutable system, it may allow a few dependencies to be + to be garbage collected after activation. + ''; + type = types.package; + readOnly = true; + }; + system.dryActivationScript = mkOption { description = "The shell script that is to be run when dry-activating a system."; readOnly = true; @@ -238,6 +255,14 @@ in rmdir --ignore-fail-on-non-empty /usr/bin /usr ''; + system.activation.externalActivationScript = + pkgs.writeScript "activate" ( + lib.strings.replaceStrings + ["@out@"] + ["${config.system.build.toplevel}"] + config.system.activationScripts.script + ); + }; } From 413be2c77e68fde0476a9154387b2f14e18a2ba2 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 12:59:00 +0000 Subject: [PATCH 23/33] nixos/top-level.nix: Declare dependencies --- nixos/modules/system/activation/top-level.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 79ba60c3e8058..38e6e79f4264a 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -61,6 +61,10 @@ in (mkRemovedOptionModule [ "nesting" "clone" ] "Use `specialisation.«name» = { inheritParentConfig = true; configuration = { ... }; }` instead.") (mkRemovedOptionModule [ "nesting" "children" ] "Use `specialisation.«name».configuration = { ... }` instead.") ../build.nix + ../../misc/assertions.nix + ../../misc/version.nix + ../../misc/label.nix + ../../config/system-path-core.nix ]; options = { From b692a9239abb5ff197835388b36dc2245530d864 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 14:43:10 +0000 Subject: [PATCH 24/33] nixos/users-groups.nix: Declare dependencies --- nixos/modules/config/users-groups.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix index 34fec82849795..aea0ad1e229aa 100644 --- a/nixos/modules/config/users-groups.nix +++ b/nixos/modules/config/users-groups.nix @@ -444,6 +444,9 @@ in { else cfg.security.initialRootPassword)) ../misc/ids.nix ../misc/extra-arguments.nix + ../misc/assertions.nix + ../system/activation/activation-script.nix + ../config/system-path-core.nix ]; ###### interface From cc748856af6ce75af0ab802451f9980e4d327eca Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 14:43:26 +0000 Subject: [PATCH 25/33] nixos/activation-script: Declare dependencies --- nixos/modules/system/activation/activation-script.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix index 7c53d5f871640..d35df203b068e 100644 --- a/nixos/modules/system/activation/activation-script.nix +++ b/nixos/modules/system/activation/activation-script.nix @@ -103,6 +103,8 @@ in { + imports = [ ./top-level.nix ]; + ###### interface options = { From 226d20e3e775047aeb9dc2b91a298f9f2aedcd82 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 14:44:12 +0000 Subject: [PATCH 26/33] lib.nixos: Add users module --- nixos/nixos-core.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nixos/nixos-core.nix b/nixos/nixos-core.nix index 4ce67b8947432..a729e4ef02b5f 100644 --- a/nixos/nixos-core.nix +++ b/nixos/nixos-core.nix @@ -16,6 +16,8 @@ in { }; etc = ./modules/system/etc/etc.nix; + + users = ./modules/config/users-groups.nix; }; evalModules = { From 77e8c73303aa3a087f957893db3714540f7dd129 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 14:44:51 +0000 Subject: [PATCH 27/33] dockerTools.streamLayeredImage: Make robust against cd in fakeRootCommands --- pkgs/build-support/docker/default.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/build-support/docker/default.nix b/pkgs/build-support/docker/default.nix index 9a20df57777cd..543087c123304 100644 --- a/pkgs/build-support/docker/default.nix +++ b/pkgs/build-support/docker/default.nix @@ -871,7 +871,7 @@ rec { ${optionalString enableFakechroot ''fakechroot chroot $PWD/old_out ''}fakeroot bash -c ' source $stdenv/setup ${optionalString (!enableFakechroot) ''cd old_out''} - eval "$fakeRootCommands" + (eval "$fakeRootCommands") tar \ --sort name \ --numeric-owner --mtime "@$SOURCE_DATE_EPOCH" \ From bc6808e8987efceea6a0d0933a5083f6b351281d Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 15:05:58 +0000 Subject: [PATCH 28/33] nixos/nixos-core.nix: Add etcActivation module --- nixos/nixos-core.nix | 1 + 1 file changed, 1 insertion(+) diff --git a/nixos/nixos-core.nix b/nixos/nixos-core.nix index a729e4ef02b5f..9266bc63bafbf 100644 --- a/nixos/nixos-core.nix +++ b/nixos/nixos-core.nix @@ -16,6 +16,7 @@ in { }; etc = ./modules/system/etc/etc.nix; + etcActivation = ./modules/system/etc/etc-activation.nix; users = ./modules/config/users-groups.nix; }; From eb3fc9a07da1eec35c6974a66eded9623136e6a7 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Wed, 8 Dec 2021 15:09:38 +0000 Subject: [PATCH 29/33] dockerTools: Update etc example --- nixos/tests/docker-tools.nix | 5 ++-- pkgs/build-support/docker/examples.nix | 37 +++++++++++++++++--------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix index 3965817505c16..e793c2b9a5626 100644 --- a/nixos/tests/docker-tools.nix +++ b/nixos/tests/docker-tools.nix @@ -415,8 +415,9 @@ import ./make-test-python.nix ({ pkgs, ... }: { with subtest("etc"): docker.succeed("${examples.etc} | docker load") - docker.succeed("docker run --rm etc | grep localhost") - docker.succeed("docker image rm etc:latest") + docker.succeed("docker run --rm etc-img | grep -i hello") + docker.succeed("docker run --rm etc-img cat /etc/foo | grep 'foo: bar'") + docker.succeed("docker image rm etc-img:latest") ''; }) diff --git a/pkgs/build-support/docker/examples.nix b/pkgs/build-support/docker/examples.nix index 9705cf865bfd1..6c7926c3bc0b7 100644 --- a/pkgs/build-support/docker/examples.nix +++ b/pkgs/build-support/docker/examples.nix @@ -563,28 +563,41 @@ rec { let inherit (pkgs) lib; nixosCore = (lib.nixos.core ({ config, modules, ... }: { - imports = [ pkgs.nixosModule modules.etc ]; - environment.etc."hosts" = { + imports = [ + pkgs.nixosModule + modules.etcActivation + modules.users + ]; + environment.etc."foo" = { text = '' - 127.0.0.1 localhost - ::1 localhost + foo: bar ''; mode = "0456"; }; + environment.systemPackages = [ + pkgs.hello + pkgs.coreutils + ]; + users.users.foo = { isSystemUser = true; group = "foo"; }; + users.groups.foo = {}; + users.users.nobody.shell = "/run/current-system/sw/bin/false"; + users.users.root.shell = "/run/current-system/sw/bin/false"; + users.users.foo.shell = "/run/current-system/sw/bin/false"; + users.defaultUserShell = "/run/current-system/sw/bin/false"; })); in pkgs.dockerTools.streamLayeredImage { - name = "etc"; + name = "etc-img"; tag = "latest"; enableFakechroot = true; fakeRootCommands = '' - mkdir -p /etc - ${nixosCore.config.system.build.etcActivationCommands} + mkdir -p /etc /run /nix/var/nix/gcroots + ${nixosCore.config.system.activation.externalActivationScript} + chmod -R a-w / ''; - config.Cmd = pkgs.writeScript "etc-cmd" '' - #!${pkgs.busybox}/bin/sh - ${pkgs.busybox}/bin/cat /etc/hosts - ''; - }; + config.Cmd = [ "hello" ]; + config.Env = [ "PATH=/run/current-system/sw/bin" ]; + config.User = "foo"; + } // { nixos = nixosCore; }; # Example export of the bash image exportBash = pkgs.dockerTools.exportImage { fromImage = bash; }; From 6adab4ce541a282d5a2a5bcc21c0e625d7b5ff20 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 9 Dec 2021 10:47:26 +0000 Subject: [PATCH 30/33] nixos/top-level.nix: Add system.checks --- nixos/modules/system/activation/top-level.nix | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix index 38e6e79f4264a..f8208bc23d3ed 100644 --- a/nixos/modules/system/activation/top-level.nix +++ b/nixos/modules/system/activation/top-level.nix @@ -113,8 +113,19 @@ in default = []; description = '' A list of packages that should be included in the system - closure but not otherwise made available to users. This is - primarily used by the installation tests. + closure but not otherwise made available to users. + ''; + }; + + system.checks = mkOption { + type = types.listOf types.package; + default = []; + description = '' + A list of packages that are added as dependencies of the activation + script build, for the purpose of validating the configuration. + + Unlike system.extraDependencies, these paths do not + become part of the runtime closure of the system. ''; }; @@ -173,6 +184,8 @@ in "$out/configuration.nix" ''; + system.systemBuilderAttrs.passedTests = concatStringsSep " " config.system.checks; + system.build.toplevel = system; # Traditionally, the option default contained the logic for taking this from From 25093d76c8a283f56779cdbf82c51475f2dc2b40 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 9 Dec 2021 11:49:07 +0000 Subject: [PATCH 31/33] nixos/binsh.nix: Extract module --- nixos/modules/config/binsh.nix | 40 +++++++++++++++++++++ nixos/modules/config/shells-environment.nix | 25 ------------- nixos/modules/module-list.nix | 1 + 3 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 nixos/modules/config/binsh.nix diff --git a/nixos/modules/config/binsh.nix b/nixos/modules/config/binsh.nix new file mode 100644 index 0000000000000..5fac1295cd1e4 --- /dev/null +++ b/nixos/modules/config/binsh.nix @@ -0,0 +1,40 @@ +{ config, lib, utils, pkgs, ... }: + +with lib; + +let + + cfg = config.environment; +in +{ + options = { + environment.binsh = mkOption { + default = "${config.system.build.binsh}/bin/sh"; + defaultText = literalExpression ''"''${config.system.build.binsh}/bin/sh"''; + example = literalExpression ''"''${pkgs.dash}/bin/dash"''; + type = types.path; + visible = false; + description = '' + The shell executable that is linked system-wide to + /bin/sh. Please note that NixOS assumes all + over the place that shell to be Bash, so override the default + setting only if you know exactly what you're doing. + ''; + }; + }; + + + config = { + system.build.binsh = pkgs.bashInteractive; + + system.activationScripts.binsh = stringAfter [ "stdio" ] + '' + # Create the required /bin/sh symlink; otherwise lots of things + # (notably the system() function) won't work. + mkdir -m 0755 -p /bin + ln -sfn "${cfg.binsh}" /bin/.sh.tmp + mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh + ''; + + }; +} diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix index ae3f618e273c3..3687debeb925a 100644 --- a/nixos/modules/config/shells-environment.nix +++ b/nixos/modules/config/shells-environment.nix @@ -134,20 +134,6 @@ in type = types.bool; }; - environment.binsh = mkOption { - default = "${config.system.build.binsh}/bin/sh"; - defaultText = literalExpression ''"''${config.system.build.binsh}/bin/sh"''; - example = literalExpression ''"''${pkgs.dash}/bin/dash"''; - type = types.path; - visible = false; - description = '' - The shell executable that is linked system-wide to - /bin/sh. Please note that NixOS assumes all - over the place that shell to be Bash, so override the default - setting only if you know exactly what you're doing. - ''; - }; - environment.shells = mkOption { default = []; example = literalExpression "[ pkgs.bashInteractive pkgs.zsh ]"; @@ -163,8 +149,6 @@ in config = { - system.build.binsh = pkgs.bashInteractive; - # Set session variables in the shell as well. This is usually # unnecessary, but it allows changes to session variables to take # effect without restarting the session (e.g. by opening a new @@ -210,15 +194,6 @@ in ''} ''; - system.activationScripts.binsh = stringAfter [ "stdio" ] - '' - # Create the required /bin/sh symlink; otherwise lots of things - # (notably the system() function) won't work. - mkdir -m 0755 -p /bin - ln -sfn "${cfg.binsh}" /bin/.sh.tmp - mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh - ''; - }; } diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 251f745c2ab4f..0c1b9cdaaadcd 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1,4 +1,5 @@ [ + ./config/binsh.nix ./config/debug-info.nix ./config/fonts/fontconfig.nix ./config/fonts/fontdir.nix From 2f3e9f4dde8d95254e9e2cfe08857dcde8356384 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 9 Dec 2021 11:54:21 +0000 Subject: [PATCH 32/33] nixos/environment-variables.nix: Extract module --- .../modules/config/environment-variables.nix | 27 +++++++++++++++++++ nixos/modules/config/shells-environment.nix | 16 ++--------- 2 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 nixos/modules/config/environment-variables.nix diff --git a/nixos/modules/config/environment-variables.nix b/nixos/modules/config/environment-variables.nix new file mode 100644 index 0000000000000..a708dd7a0386a --- /dev/null +++ b/nixos/modules/config/environment-variables.nix @@ -0,0 +1,27 @@ +{ config, lib, ... }: +let + inherit (lib) + mkOption + types + isList + mapAttrs + concatStringsSep + ; +in +{ + options = { + environment.variables = mkOption { + default = { }; + example = { EDITOR = "nvim"; VISUAL = "nvim"; }; + description = '' + A set of environment variables used in the global environment. + These variables will be set on shell initialisation (e.g. in /etc/profile). + The value of each variable can be either a string or a list of + strings. The latter is concatenated, interspersed with colon + characters. + ''; + type = with types; attrsOf (either str (listOf str)); + apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v); + }; + }; +} diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix index 3687debeb925a..8b18d9e40b2ef 100644 --- a/nixos/modules/config/shells-environment.nix +++ b/nixos/modules/config/shells-environment.nix @@ -30,21 +30,9 @@ in { - options = { + imports = [ ./environment-variables.nix ]; - environment.variables = mkOption { - default = {}; - example = { EDITOR = "nvim"; VISUAL = "nvim"; }; - description = '' - A set of environment variables used in the global environment. - These variables will be set on shell initialisation (e.g. in /etc/profile). - The value of each variable can be either a string or a list of - strings. The latter is concatenated, interspersed with colon - characters. - ''; - type = with types; attrsOf (either str (listOf str)); - apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v); - }; + options = { environment.profiles = mkOption { default = []; From d9e94baeec65057cb67d8ee023490a042dd8ce82 Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 9 Dec 2021 11:50:32 +0000 Subject: [PATCH 33/33] nixos/postgresql.nix: Split module, create container, test it --- nixos/modules/module-list.nix | 1 + .../databases/postgresql-container.nix | 32 ++++ .../services/databases/postgresql-systemd.nix | 56 ++++++ .../modules/services/databases/postgresql.nix | 166 ++++++++---------- nixos/tests/all-tests.nix | 1 + nixos/tests/postgresql-container.nix | 79 +++++++++ 6 files changed, 238 insertions(+), 97 deletions(-) create mode 100644 nixos/modules/services/databases/postgresql-container.nix create mode 100644 nixos/modules/services/databases/postgresql-systemd.nix create mode 100644 nixos/tests/postgresql-container.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 0c1b9cdaaadcd..50ab06af3193e 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -344,6 +344,7 @@ ./services/databases/opentsdb.nix ./services/databases/pgmanage.nix ./services/databases/postgresql.nix + ./services/databases/postgresql-systemd.nix ./services/databases/redis.nix ./services/databases/riak.nix ./services/databases/victoriametrics.nix diff --git a/nixos/modules/services/databases/postgresql-container.nix b/nixos/modules/services/databases/postgresql-container.nix new file mode 100644 index 0000000000000..664ce2c5fddea --- /dev/null +++ b/nixos/modules/services/databases/postgresql-container.nix @@ -0,0 +1,32 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.postgresql; + + cmd = pkgs.writeScriptBin "postgresql-cmd" '' + #!${pkgs.runtimeShell} + set -eux + ${cfg.preStartCommands} + (export MAINPID=1; ${cfg.postStartCommands}) & + exec postgres + ''; +in + +{ + imports = [ + ./postgresql.nix + ../../config/environment-variables.nix + ../../config/binsh.nix + ]; + config = { + environment.variables.PGDATA = cfg.dataDir; + environment.systemPackages = [ + cfg.postgresqlWithExtraPlugins + pkgs.busybox + cmd + ]; + services.postgresql.enable = true; + services.postgresql.enableTCPIP = true; + }; +} diff --git a/nixos/modules/services/databases/postgresql-systemd.nix b/nixos/modules/services/databases/postgresql-systemd.nix new file mode 100644 index 0000000000000..5d0d675e9439e --- /dev/null +++ b/nixos/modules/services/databases/postgresql-systemd.nix @@ -0,0 +1,56 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + cfg = config.services.postgresql; + + groupAccessAvailable = versionAtLeast cfg.package.version "11.0"; + +in +{ + config = mkIf cfg.enable { + systemd.services.postgresql = + { description = "PostgreSQL Server"; + + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + environment.PGDATA = cfg.dataDir; + + path = [ cfg.postgresqlWithExtraPlugins ]; + + preStart = cfg.preStartCommands; + + # Wait for PostgreSQL to be ready to accept connections. + postStart = cfg.postStartCommands; + + serviceConfig = mkMerge [ + { ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + User = "postgres"; + Group = "postgres"; + RuntimeDirectory = "postgresql"; + Type = if versionAtLeast cfg.package.version "9.6" + then "notify" + else "simple"; + + # Shut down Postgres using SIGINT ("Fast Shutdown mode"). See + # http://www.postgresql.org/docs/current/static/server-shutdown.html + KillSignal = "SIGINT"; + KillMode = "mixed"; + + # Give Postgres a decent amount of time to clean up after + # receiving systemd's SIGINT. + TimeoutSec = 120; + + ExecStart = "${cfg.postgresqlWithExtraPlugins}/bin/postgres"; + } + (mkIf (cfg.dataDir == "/var/lib/postgresql/${cfg.package.psqlSchema}") { + StateDirectory = "postgresql postgresql/${cfg.package.psqlSchema}"; + StateDirectoryMode = if groupAccessAvailable then "0750" else "0700"; + }) + ]; + + unitConfig.RequiresMountsFor = "${cfg.dataDir}"; + }; + }; +} diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix index 2919022496a36..6b282dd6042d4 100644 --- a/nixos/modules/services/databases/postgresql.nix +++ b/nixos/modules/services/databases/postgresql.nix @@ -6,11 +6,6 @@ let cfg = config.services.postgresql; - postgresql = - if cfg.extraPlugins == [] - then cfg.package - else cfg.package.withPackages (_: cfg.extraPlugins); - toStr = value: if true == value then "yes" else if false == value then "no" @@ -25,13 +20,12 @@ let touch $out ''; - groupAccessAvailable = versionAtLeast postgresql.version "11.0"; - in { imports = [ (mkRemovedOptionModule [ "services" "postgresql" "extraConfig" ] "Use services.postgresql.settings instead.") + ../../misc/meta.nix ]; ###### interface @@ -42,6 +36,72 @@ in enable = mkEnableOption "PostgreSQL Server"; + preStartCommands = mkOption { + description = '' + The init-agnostic script to run before postgresql starts. + ''; + internal = true; + default = '' + if ! test -e ${cfg.dataDir}/PG_VERSION; then + # Cleanup the data directory. + rm -f ${cfg.dataDir}/*.conf + + # Initialise the database. + initdb -U ${cfg.superUser} ${concatStringsSep " " cfg.initdbArgs} + + # See postStart! + touch "${cfg.dataDir}/.first_startup" + fi + + ln -sfn "${configFile}/postgresql.conf" "${cfg.dataDir}/postgresql.conf" + ${optionalString (cfg.recoveryConfig != null) '' + ln -sfn "${pkgs.writeText "recovery.conf" cfg.recoveryConfig}" \ + "${cfg.dataDir}/recovery.conf" + ''} + ''; + }; + + postStartCommands = mkOption { + description = '' + The init-agnostic script to run after postgresql starts. + ''; + internal = true; + default = '' + PSQL="psql --port=${toString cfg.port}" + + while ! $PSQL -d postgres -c "" 2> /dev/null; do + if ! kill -0 "$MAINPID"; then exit 1; fi + sleep 0.1 + done + + if test -e "${cfg.dataDir}/.first_startup"; then + ${optionalString (cfg.initialScript != null) '' + $PSQL -f "${cfg.initialScript}" -d postgres + ''} + rm -f "${cfg.dataDir}/.first_startup" + fi + '' + optionalString (cfg.ensureDatabases != []) '' + ${concatMapStrings (database: '' + $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${database}'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "${database}"' + '') cfg.ensureDatabases} + '' + '' + ${concatMapStrings (user: '' + $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}"' + '') user.ensurePermissions)} + '') cfg.ensureUsers} + ''; + }; + + postgresqlWithExtraPlugins = mkOption { + internal = true; + default = + if cfg.extraPlugins == [] + then cfg.package + else cfg.package.withPackages (_: cfg.extraPlugins); + }; + package = mkOption { type = types.package; example = literalExpression "pkgs.postgresql_11"; @@ -321,101 +381,13 @@ in users.groups.postgres.gid = config.ids.gids.postgres; - environment.systemPackages = [ postgresql ]; + environment.systemPackages = [ cfg.postgresqlWithExtraPlugins ]; environment.pathsToLink = [ "/share/postgresql" ]; - system.extraDependencies = lib.optional (cfg.checkConfig && pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) configFileCheck; - - systemd.services.postgresql = - { description = "PostgreSQL Server"; - - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - - environment.PGDATA = cfg.dataDir; - - path = [ postgresql ]; - - preStart = - '' - if ! test -e ${cfg.dataDir}/PG_VERSION; then - # Cleanup the data directory. - rm -f ${cfg.dataDir}/*.conf - - # Initialise the database. - initdb -U ${cfg.superUser} ${concatStringsSep " " cfg.initdbArgs} - - # See postStart! - touch "${cfg.dataDir}/.first_startup" - fi - - ln -sfn "${configFile}/postgresql.conf" "${cfg.dataDir}/postgresql.conf" - ${optionalString (cfg.recoveryConfig != null) '' - ln -sfn "${pkgs.writeText "recovery.conf" cfg.recoveryConfig}" \ - "${cfg.dataDir}/recovery.conf" - ''} - ''; - - # Wait for PostgreSQL to be ready to accept connections. - postStart = - '' - PSQL="psql --port=${toString cfg.port}" - - while ! $PSQL -d postgres -c "" 2> /dev/null; do - if ! kill -0 "$MAINPID"; then exit 1; fi - sleep 0.1 - done - - if test -e "${cfg.dataDir}/.first_startup"; then - ${optionalString (cfg.initialScript != null) '' - $PSQL -f "${cfg.initialScript}" -d postgres - ''} - rm -f "${cfg.dataDir}/.first_startup" - fi - '' + optionalString (cfg.ensureDatabases != []) '' - ${concatMapStrings (database: '' - $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${database}'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "${database}"' - '') cfg.ensureDatabases} - '' + '' - ${concatMapStrings (user: '' - $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}"' - '') user.ensurePermissions)} - '') cfg.ensureUsers} - ''; - - serviceConfig = mkMerge [ - { ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - User = "postgres"; - Group = "postgres"; - RuntimeDirectory = "postgresql"; - Type = if versionAtLeast cfg.package.version "9.6" - then "notify" - else "simple"; - - # Shut down Postgres using SIGINT ("Fast Shutdown mode"). See - # http://www.postgresql.org/docs/current/static/server-shutdown.html - KillSignal = "SIGINT"; - KillMode = "mixed"; - - # Give Postgres a decent amount of time to clean up after - # receiving systemd's SIGINT. - TimeoutSec = 120; - - ExecStart = "${postgresql}/bin/postgres"; - } - (mkIf (cfg.dataDir == "/var/lib/postgresql/${cfg.package.psqlSchema}") { - StateDirectory = "postgresql postgresql/${cfg.package.psqlSchema}"; - StateDirectoryMode = if groupAccessAvailable then "0750" else "0700"; - }) - ]; - - unitConfig.RequiresMountsFor = "${cfg.dataDir}"; - }; + system.checks = lib.optional (cfg.checkConfig && pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) configFileCheck; }; diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 0acaffe5ab48b..b3611fc39cfd9 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -381,6 +381,7 @@ in postgis = handleTest ./postgis.nix {}; postgresql = handleTest ./postgresql.nix {}; postgresql-wal-receiver = handleTest ./postgresql-wal-receiver.nix {}; + postgresql-container = handleTest ./postgresql-container.nix {}; powerdns = handleTest ./powerdns.nix {}; power-profiles-daemon = handleTest ./power-profiles-daemon.nix {}; pppd = handleTest ./pppd.nix {}; diff --git a/nixos/tests/postgresql-container.nix b/nixos/tests/postgresql-container.nix new file mode 100644 index 0000000000000..4d978a54149da --- /dev/null +++ b/nixos/tests/postgresql-container.nix @@ -0,0 +1,79 @@ +{ system ? builtins.currentSystem, + config ? {}, + pkgs ? import ../.. { inherit system config; } +}: + +with import ../lib/testing-python.nix { inherit system pkgs; }; +with pkgs.lib; + +let + test-sql = pkgs.writeText "postgresql-test" '' + CREATE EXTENSION pgcrypto; -- just to check if lib loading works + CREATE TABLE sth ( + id int + ); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + INSERT INTO sth (id) VALUES (1); + CREATE TABLE xmltest ( doc xml ); + INSERT INTO xmltest (doc) VALUES ('ok'); -- check if libxml2 enabled + ''; + +in makeTest { + name = "postgresql-container"; + meta = with pkgs.lib.maintainers; { + maintainers = [ roberth ]; + }; + + machine = {...}: + { + virtualisation.docker = { + enable = true; + }; + virtualisation.memorySize = 4096; + virtualisation.diskSize = 4096; + environment.systemPackages = [ + # add postgresql for its client; psql + pkgs.postgresql + ]; + }; + + testScript = '' + def check_count(statement, lines): + return 'test $(psql --host=127.0.0.1 --username=postgres postgres -tAc "{}"|wc -l) -eq {}'.format( + statement, lines + ) + + + machine.start() + machine.wait_for_unit("docker.socket") + machine.succeed("${pkgs.dockerTools.examples.postgresql} | docker load") + machine.succeed("docker run -d -p5432:5432 --name pg --restart=always --stop-signal SIGINT nixpkgs-postgresql") + machine.succeed("docker logs --tail 1000 -f pg >&2 &") + + with subtest("Postgresql comes up"): + machine.wait_until_succeeds( + "cat ${test-sql} | psql --host=127.0.0.1 --username=postgres" + ) + + with subtest("Postgresql survives restart"): + machine.shutdown() + import time + time.sleep(2) + machine.start() + machine.wait_until_succeeds(""" + echo 'SELECT 1;' | psql --host=127.0.0.1 --username=postgres + """) + + machine.fail(check_count("SELECT * FROM sth;", 3)) + machine.succeed(check_count("SELECT * FROM sth;", 5)) + machine.fail(check_count("SELECT * FROM sth;", 4)) + machine.succeed(check_count("SELECT xpath('/test/text()', doc) FROM xmltest;", 1)) + + machine.shutdown() + ''; + + } +