From e88f28965a7d76e83478d3ae6fcddc165b1c94f1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 5 Feb 2018 19:50:36 +0100 Subject: [PATCH] nixos-install: Make compatible with Nix 2.0 The use of Nix 2.0 significantly simplifies the installer, since we can just pass a different store URI (--store /mnt) - it's no longer needed to set up a chroot environment for the build, and to bootstrap Nix into the chroot. Also, commands that need to run in the installation (namely boot loader installation and setting a root password) are now executed using nixos-enter. This also removes the need for nixos-prepare-root since any required initialisation is done by Nix or by the activation script. --- .../manual/development/testing-installer.xml | 6 +- .../modules/installer/tools/nixos-install.sh | 183 ++++++------------ nixos/modules/installer/tools/tools.nix | 11 +- 3 files changed, 61 insertions(+), 139 deletions(-) diff --git a/nixos/doc/manual/development/testing-installer.xml b/nixos/doc/manual/development/testing-installer.xml index 20c8d51815ad..16bc8125d9ff 100644 --- a/nixos/doc/manual/development/testing-installer.xml +++ b/nixos/doc/manual/development/testing-installer.xml @@ -11,15 +11,17 @@ tedious, so here is a quick way to see if the installer works properly: -$ nix-build -A config.system.build.nixos-install # mount -t tmpfs none /mnt +# nixos-generate-config --root /mnt +$ nix-build '<nixpkgs/nixos>' -A config.system.build.nixos-install # ./result/bin/nixos-install To start a login shell in the new NixOS installation in /mnt: -# ./result/bin/nixos-install --chroot +$ nix-build '<nixpkgs/nixos>' -A config.system.build.nixos-enter +# ./result/bin/nixos-enter diff --git a/nixos/modules/installer/tools/nixos-install.sh b/nixos/modules/installer/tools/nixos-install.sh index f994d5b4bde1..69371f3e4132 100644 --- a/nixos/modules/installer/tools/nixos-install.sh +++ b/nixos/modules/installer/tools/nixos-install.sh @@ -1,30 +1,17 @@ #! @shell@ -# - make Nix store etc. -# - copy closure of Nix to target device -# - register validity -# - with a chroot to the target device: -# * nix-env -p /nix/var/nix/profiles/system -i -# * install the boot loader +set -e +shopt -s nullglob + +export PATH=@path@:$PATH # Ensure a consistent umask. umask 0022 -# Re-exec ourselves in a private mount namespace so that our bind -# mounts get cleaned up automatically. -if [ "$(id -u)" = 0 ]; then - if [ -z "$NIXOS_INSTALL_REEXEC" ]; then - export NIXOS_INSTALL_REEXEC=1 - exec unshare --mount --uts -- "$0" "$@" - else - mount --make-rprivate / - fi -fi - # Parse the command line for the -I flag extraBuildFlags=() -chrootCommand=(/run/current-system/sw/bin/bash) -buildUsersGroup="nixbld" + +mountPoint=/mnt while [ "$#" -gt 0 ]; do i="$1"; shift 1 @@ -42,8 +29,8 @@ while [ "$#" -gt 0 ]; do mountPoint="$1"; shift 1 ;; --closure) - closure="$1"; shift 1 - buildUsersGroup="" + # FIXME: --closure is a misnomer + system="$1"; shift 1 ;; --no-channel-copy) noChannelCopy=1 @@ -57,17 +44,13 @@ while [ "$#" -gt 0 ]; do --show-trace) extraBuildFlags+=("$i") ;; - --chroot) - runChroot=1 - if [[ "$@" != "" ]]; then - chrootCommand=("$@") - fi - break - ;; --help) exec man nixos-install exit 1 ;; + --debug) + set -x + ;; *) echo "$0: unknown option \`$i'" exit 1 @@ -75,132 +58,78 @@ while [ "$#" -gt 0 ]; do esac done -set -e -shopt -s nullglob - -if test -z "$mountPoint"; then - mountPoint=/mnt -fi - if ! test -e "$mountPoint"; then echo "mount point $mountPoint doesn't exist" exit 1 fi # Get the path of the NixOS configuration file. -if test -z "$NIXOS_CONFIG"; then - NIXOS_CONFIG=/etc/nixos/configuration.nix +if [[ -z $NIXOS_CONFIG ]]; then + NIXOS_CONFIG=$mountPoint/etc/nixos/configuration.nix fi -if [ ! -e "$mountPoint/$NIXOS_CONFIG" ] && [ -z "$closure" ]; then - echo "configuration file $mountPoint/$NIXOS_CONFIG doesn't exist" +if [[ ${NIXOS_CONFIG:0:1} != / ]]; then + echo "$0: \$NIXOS_CONFIG is not an absolute path" exit 1 fi - -# Builds will use users that are members of this group -extraBuildFlags+=(--option "build-users-group" "$buildUsersGroup") - -# Inherit binary caches from the host -# TODO: will this still work with Nix 1.12 now that it has no perl? Probably not... -binary_caches="$(@perl@/bin/perl -I @nix@/lib/perl5/site_perl/*/* -e 'use Nix::Config; Nix::Config::readConfig; print $Nix::Config::config{"binary-caches"};')" -extraBuildFlags+=(--option "binary-caches" "$binary_caches") - -# We only need nixpkgs in the path if we don't already have a system closure to install -if [[ -z "$closure" ]]; then - nixpkgs="$(readlink -f "$(nix-instantiate --find-file nixpkgs)")" - export NIX_PATH="nixpkgs=$nixpkgs:nixos-config=$mountPoint/$NIXOS_CONFIG" -fi -unset NIXOS_CONFIG - -# These get created in nixos-prepare-root as well, but we want to make sure they're here in case we're -# running with --chroot. TODO: --chroot should just be split into a separate tool. -mkdir -m 0755 -p "$mountPoint/dev" "$mountPoint/proc" "$mountPoint/sys" - -# Set up some bind mounts we'll want regardless of chroot or not -mount --rbind /dev "$mountPoint/dev" -mount --rbind /proc "$mountPoint/proc" -mount --rbind /sys "$mountPoint/sys" - -# If we asked for a chroot, that means we're not actually installing anything (yeah I was confused too) -# and we just want to run a command in the context of a $mountPoint that we're assuming has already been -# set up by a previous nixos-install invocation. In that case we set up some remaining bind mounts and -# exec the requested command, skipping the rest of the installation procedure. -if [ -n "$runChroot" ]; then - mount -t tmpfs -o "mode=0755" none $mountPoint/run - rm -rf $mountPoint/var/run - ln -s /run $mountPoint/var/run - for f in /etc/resolv.conf /etc/hosts; do rm -f $mountPoint/$f; [ -f "$f" ] && cp -Lf $f $mountPoint/etc/; done - for f in /etc/passwd /etc/group; do touch $mountPoint/$f; [ -f "$f" ] && mount --rbind -o ro $f $mountPoint/$f; done - - if ! [ -L $mountPoint/nix/var/nix/profiles/system ]; then - echo "$0: installation not finished; cannot chroot into installation directory" - exit 1 - fi - ln -s /nix/var/nix/profiles/system $mountPoint/run/current-system - exec chroot $mountPoint "${chrootCommand[@]}" +if [ ! -e "$NIXOS_CONFIG" ] && [ -z "$closure" ]; then + echo "configuration file $NIXOS_CONFIG doesn't exist" + exit 1 fi -# A place to drop temporary closures +# A place to drop temporary stuff. trap "rm -rf $tmpdir" EXIT tmpdir="$(mktemp -d)" -# Build a closure (on the host; we then copy it into the guest) -function closure() { - nix-build "${extraBuildFlags[@]}" --no-out-link -E "with import {}; runCommand \"closure\" { exportReferencesGraph = [ \"x\" (buildEnv { name = \"env\"; paths = [ ($1) stdenv ]; }) ]; } \"cp x \$out\"" -} +subs="local?trusted=1 https://cache.nixos.org/" -system_closure="$tmpdir/system.closure" -# Use a FIFO for piping nix-store --export into nix-store --import, saving disk -# I/O and space. nix-store --import is run by nixos-prepare-root. -mkfifo $system_closure - -if [ -z "$closure" ]; then - expr="(import {}).system" - system_root="$(nix-build -E "$expr")" - system_closure="$(closure "$expr")" -else - system_root=$closure - # Create a temporary file ending in .closure (so nixos-prepare-root knows to --import it) to transport the store closure - # to the filesytem we're preparing. Also delete it on exit! - # Run in background to avoid blocking while trying to write to the FIFO - # $system_closure refers to - nix-store --export $(nix-store -qR $closure) > $system_closure & +# Build the system configuration in the target filesystem. +if [[ -z $system ]]; then + echo "building the configuration in $NIXOS_CONFIG..." + outLink="$tmpdir/system" + nix build --out-link "$outLink" --store "$mountPoint" "${extraBuildFlags[@]}" \ + --substituters "$subs" \ + -f '' system -I "nixos-config=$NIXOS_CONFIG" + system=$(readlink -f $outLink) fi -channel_root="$(nix-env -p /nix/var/nix/profiles/per-user/root/channels -q nixos --no-name --out-path 2>/dev/null || echo -n "")" -channel_closure="$tmpdir/channel.closure" -nix-store --export $channel_root > $channel_closure +# Set the system profile to point to the configuration. TODO: combine +# this with the previous step once we have a nix-env replacement with +# a progress bar. +nix-env --store "$mountPoint" "${extraBuildFlags[@]}" \ + --substituters "$subs" \ + -p $mountPoint/nix/var/nix/profiles/system --set "$system" -# Populate the target root directory with the basics -@prepare_root@/bin/nixos-prepare-root "$mountPoint" "$channel_root" "$system_root" @nixClosure@ "$system_closure" "$channel_closure" +# Copy the NixOS/Nixpkgs sources to the target as the initial contents +# of the NixOS channel. +if [[ -z $noChannelCopy ]]; then + channelPath="$(nix-env -p /nix/var/nix/profiles/per-user/root/channels -q nixos --no-name --out-path 2>/dev/null || echo -n "")" + if [[ -n $channelPath ]]; then + echo "copying channel..." + mkdir -p $mountPoint/nix/var/nix/profiles/per-user/root + nix-env --store "$mountPoint" --substituters 'local?trusted=1' "${extraBuildFlags[@]}" \ + -p $mountPoint/nix/var/nix/profiles/per-user/root/channels --set "$channelPath" --quiet + fi +fi -# nixos-prepare-root doesn't currently do anything with file ownership, so we set it up here instead -chown @root_uid@:@nixbld_gid@ $mountPoint/nix/store - - - -# Grub needs an mtab. -ln -sfn /proc/mounts $mountPoint/etc/mtab +# Mark the target as a NixOS installation, otherwise switch-to-configuration will chicken out. +touch "$mountPoint/etc/NIXOS" # Switch to the new system configuration. This will install Grub with # a menu default pointing at the kernel/initrd/etc of the new # configuration. -echo "finalising the installation..." -if [ -z "$noBootLoader" ]; then - NIXOS_INSTALL_BOOTLOADER=1 chroot $mountPoint \ - /nix/var/nix/profiles/system/bin/switch-to-configuration boot +if [[ -z $noBootLoader ]]; then + echo "installing the boot loader..." + # Grub needs an mtab. + ln -sfn /proc/mounts $mountPoint/etc/mtab + NIXOS_INSTALL_BOOTLOADER=1 nixos-enter --root "$mountPoint" -- /run/current-system/bin/switch-to-configuration boot fi -# Run the activation script. -chroot $mountPoint /nix/var/nix/profiles/system/activate - - -# Ask the user to set a root password. -if [ -z "$noRootPasswd" ] && chroot $mountPoint [ -x /run/wrappers/bin/passwd ] && [ -t 0 ]; then - echo "setting root password..." - chroot $mountPoint /run/wrappers/bin/passwd +# Ask the user to set a root password, but only if the passwd command +# exists (i.e. when mutable user accounts are enabled). +if [[ -z $noRootPasswd ]] && [ -t 0 ]; then + nixos-enter --root "$mountPoint" -c '[[ -e /nix/var/nix/profiles/system/sw/bin/passwd ]] && echo "setting root password..." && /nix/var/nix/profiles/system/sw/bin/passwd' fi - echo "installation finished!" diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix index 9398e2dc1ebc..7be59e4ce258 100644 --- a/nixos/modules/installer/tools/tools.nix +++ b/nixos/modules/installer/tools/tools.nix @@ -29,17 +29,8 @@ let nixos-install = makeProg { name = "nixos-install"; src = ./nixos-install.sh; - - inherit (pkgs) perl pathsFromGraph rsync; nix = config.nix.package.out; - cacert = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; - root_uid = config.ids.uids.root; - nixbld_gid = config.ids.gids.nixbld; - prepare_root = nixos-prepare-root; - - nixClosure = pkgs.runCommand "closure" - { exportReferencesGraph = ["refs" config.nix.package.out]; } - "cp refs $out"; + path = makeBinPath [ nixos-enter ]; }; nixos-rebuild =