Loading nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md +3 −2 Original line number Diff line number Diff line Loading @@ -21,8 +21,9 @@ If the action is `switch` or `test`, the currently running system is inspected and the actions to switch to the new system are calculated. This process takes two data sources into account: `/etc/fstab` and the current systemd status. Mounts and swaps are read from `/etc/fstab` and the corresponding actions are generated. If a new mount is added, for example, the proper `.mount` unit is marked to be started. The current systemd state is inspected, the difference generated. If the options of a mount are modified, for example, the proper `.mount` unit is reloaded (or restarted if anything else changed and it's neither the root mount or the nix store). The current systemd state is inspected, the difference between the current system and the desired configuration is calculated and actions are generated to get to this state. There are a lot of nuances that can be controlled by the units which are explained here. Loading nixos/modules/system/activation/switch-to-configuration.pl +15 −5 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ if ("@localeArchive@" ne "") { if (!defined($action) || ($action ne "switch" && $action ne "boot" && $action ne "test" && $action ne "dry-activate")) { print STDERR <<"EOF"; Usage: $0 [switch|boot|test] Usage: $0 [switch|boot|test|dry-activate] switch: make the configuration the boot default and activate now boot: make the configuration the boot default Loading Loading @@ -661,10 +661,20 @@ foreach my $mount_point (keys(%{$cur_fss})) { # Filesystem entry disappeared, so unmount it. $units_to_stop{$unit} = 1; } elsif ($cur->{fsType} ne $new->{fsType} || $cur->{device} ne $new->{device}) { if ($mount_point eq '/' or $mount_point eq '/nix') { if ($cur->{options} ne $new->{options}) { # Mount options changed, so remount it. $units_to_reload{$unit} = 1; record_unit($reload_list_file, $unit); } else { # Don't unmount / or /nix if the device changed $units_to_skip{$unit} = 1; } } else { # Filesystem type or device changed, so unmount and mount it. $units_to_stop{$unit} = 1; $units_to_start{$unit} = 1; record_unit($start_list_file, $unit); $units_to_restart{$unit} = 1; record_unit($restart_list_file, $unit); } } elsif ($cur->{options} ne $new->{options}) { # Mount options changes, so remount it. $units_to_reload{$unit} = 1; Loading nixos/tests/switch-test.nix +122 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,37 @@ in { ''); specialisation = rec { brokenInitInterface.configuration.config.system.extraSystemBuilderCmds = '' echo "systemd 0" > $out/init-interface-version ''; modifiedSystemConf.configuration.systemd.extraConfig = '' # Hello world! ''; addedMount.configuration.virtualisation.fileSystems."/test" = { device = "tmpfs"; fsType = "tmpfs"; }; addedMountOptsModified.configuration = { imports = [ addedMount.configuration ]; virtualisation.fileSystems."/test".options = [ "x-test" ]; }; addedMountDevModified.configuration = { imports = [ addedMountOptsModified.configuration ]; virtualisation.fileSystems."/test".device = lib.mkForce "ramfs"; }; storeMountModified.configuration = { virtualisation.fileSystems."/".device = lib.mkForce "auto"; }; swap.configuration.swapDevices = lib.mkVMOverride [ { device = "/swapfile"; size = 1; } ]; simpleService.configuration = { systemd.services.test = { wantedBy = [ "multi-user.target" ]; Loading Loading @@ -643,6 +674,97 @@ in { # test and dry-activate actions are tested further down below # invalid action fails the script switch_to_specialisation("${machine}", "", action="broken-action", fail=True) # no action fails the script assert "Usage:" in machine.fail("${machine}/bin/switch-to-configuration 2>&1") with subtest("init interface version"): # Do not try to switch to an invalid init interface version assert "incompatible" in switch_to_specialisation("${machine}", "brokenInitInterface", fail=True) with subtest("systemd restarts"): # systemd is restarted when its system.conf changes out = switch_to_specialisation("${machine}", "modifiedSystemConf") assert_contains(out, "restarting systemd...") with subtest("continuing from an aborted switch"): # An aborted switch will write into a file what it tried to start # and a second switch should continue from this machine.succeed("echo dbus.service > /run/nixos/start-list") out = switch_to_specialisation("${machine}", "modifiedSystemConf") assert_contains(out, "starting the following units: dbus.service\n") with subtest("fstab mounts"): switch_to_specialisation("${machine}", "") # add a mountpoint out = switch_to_specialisation("${machine}", "addedMount") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_contains(out, "the following new units were started: test.mount\n") # modify the mountpoint's options out = switch_to_specialisation("${machine}", "addedMountOptsModified") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_contains(out, "reloading the following units: test.mount\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") # modify the device out = switch_to_specialisation("${machine}", "addedMountDevModified") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_lacks(out, "reloading the following units:") assert_contains(out, "\nrestarting the following units: test.mount\n") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") # modify both out = switch_to_specialisation("${machine}", "addedMount") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_lacks(out, "reloading the following units:") assert_contains(out, "\nrestarting the following units: test.mount\n") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") # remove the mount out = switch_to_specialisation("${machine}", "") assert_contains(out, "stopping the following units: test.mount\n") assert_lacks(out, "NOT restarting the following changed units:") assert_contains(out, "reloading the following units: dbus.service\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") # change something about the / mount out = switch_to_specialisation("${machine}", "storeMountModified") assert_lacks(out, "stopping the following units:") assert_contains(out, "NOT restarting the following changed units: -.mount") assert_contains(out, "reloading the following units: dbus.service\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") with subtest("swaps"): switch_to_specialisation("${machine}", "") # add a swap out = switch_to_specialisation("${machine}", "swap") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_contains(out, "reloading the following units: dbus.service\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_contains(out, "the following new units were started: swapfile.swap") # remove it out = switch_to_specialisation("${machine}", "") assert_contains(out, "stopping swap device: /swapfile") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_contains(out, "reloading the following units: dbus.service\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") with subtest("services"): switch_to_specialisation("${machine}", "") # Nothing happens when nothing is changed Loading Loading
nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md +3 −2 Original line number Diff line number Diff line Loading @@ -21,8 +21,9 @@ If the action is `switch` or `test`, the currently running system is inspected and the actions to switch to the new system are calculated. This process takes two data sources into account: `/etc/fstab` and the current systemd status. Mounts and swaps are read from `/etc/fstab` and the corresponding actions are generated. If a new mount is added, for example, the proper `.mount` unit is marked to be started. The current systemd state is inspected, the difference generated. If the options of a mount are modified, for example, the proper `.mount` unit is reloaded (or restarted if anything else changed and it's neither the root mount or the nix store). The current systemd state is inspected, the difference between the current system and the desired configuration is calculated and actions are generated to get to this state. There are a lot of nuances that can be controlled by the units which are explained here. Loading
nixos/modules/system/activation/switch-to-configuration.pl +15 −5 Original line number Diff line number Diff line Loading @@ -74,7 +74,7 @@ if ("@localeArchive@" ne "") { if (!defined($action) || ($action ne "switch" && $action ne "boot" && $action ne "test" && $action ne "dry-activate")) { print STDERR <<"EOF"; Usage: $0 [switch|boot|test] Usage: $0 [switch|boot|test|dry-activate] switch: make the configuration the boot default and activate now boot: make the configuration the boot default Loading Loading @@ -661,10 +661,20 @@ foreach my $mount_point (keys(%{$cur_fss})) { # Filesystem entry disappeared, so unmount it. $units_to_stop{$unit} = 1; } elsif ($cur->{fsType} ne $new->{fsType} || $cur->{device} ne $new->{device}) { if ($mount_point eq '/' or $mount_point eq '/nix') { if ($cur->{options} ne $new->{options}) { # Mount options changed, so remount it. $units_to_reload{$unit} = 1; record_unit($reload_list_file, $unit); } else { # Don't unmount / or /nix if the device changed $units_to_skip{$unit} = 1; } } else { # Filesystem type or device changed, so unmount and mount it. $units_to_stop{$unit} = 1; $units_to_start{$unit} = 1; record_unit($start_list_file, $unit); $units_to_restart{$unit} = 1; record_unit($restart_list_file, $unit); } } elsif ($cur->{options} ne $new->{options}) { # Mount options changes, so remount it. $units_to_reload{$unit} = 1; Loading
nixos/tests/switch-test.nix +122 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,37 @@ in { ''); specialisation = rec { brokenInitInterface.configuration.config.system.extraSystemBuilderCmds = '' echo "systemd 0" > $out/init-interface-version ''; modifiedSystemConf.configuration.systemd.extraConfig = '' # Hello world! ''; addedMount.configuration.virtualisation.fileSystems."/test" = { device = "tmpfs"; fsType = "tmpfs"; }; addedMountOptsModified.configuration = { imports = [ addedMount.configuration ]; virtualisation.fileSystems."/test".options = [ "x-test" ]; }; addedMountDevModified.configuration = { imports = [ addedMountOptsModified.configuration ]; virtualisation.fileSystems."/test".device = lib.mkForce "ramfs"; }; storeMountModified.configuration = { virtualisation.fileSystems."/".device = lib.mkForce "auto"; }; swap.configuration.swapDevices = lib.mkVMOverride [ { device = "/swapfile"; size = 1; } ]; simpleService.configuration = { systemd.services.test = { wantedBy = [ "multi-user.target" ]; Loading Loading @@ -643,6 +674,97 @@ in { # test and dry-activate actions are tested further down below # invalid action fails the script switch_to_specialisation("${machine}", "", action="broken-action", fail=True) # no action fails the script assert "Usage:" in machine.fail("${machine}/bin/switch-to-configuration 2>&1") with subtest("init interface version"): # Do not try to switch to an invalid init interface version assert "incompatible" in switch_to_specialisation("${machine}", "brokenInitInterface", fail=True) with subtest("systemd restarts"): # systemd is restarted when its system.conf changes out = switch_to_specialisation("${machine}", "modifiedSystemConf") assert_contains(out, "restarting systemd...") with subtest("continuing from an aborted switch"): # An aborted switch will write into a file what it tried to start # and a second switch should continue from this machine.succeed("echo dbus.service > /run/nixos/start-list") out = switch_to_specialisation("${machine}", "modifiedSystemConf") assert_contains(out, "starting the following units: dbus.service\n") with subtest("fstab mounts"): switch_to_specialisation("${machine}", "") # add a mountpoint out = switch_to_specialisation("${machine}", "addedMount") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_contains(out, "the following new units were started: test.mount\n") # modify the mountpoint's options out = switch_to_specialisation("${machine}", "addedMountOptsModified") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_contains(out, "reloading the following units: test.mount\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") # modify the device out = switch_to_specialisation("${machine}", "addedMountDevModified") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_lacks(out, "reloading the following units:") assert_contains(out, "\nrestarting the following units: test.mount\n") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") # modify both out = switch_to_specialisation("${machine}", "addedMount") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_lacks(out, "reloading the following units:") assert_contains(out, "\nrestarting the following units: test.mount\n") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") # remove the mount out = switch_to_specialisation("${machine}", "") assert_contains(out, "stopping the following units: test.mount\n") assert_lacks(out, "NOT restarting the following changed units:") assert_contains(out, "reloading the following units: dbus.service\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") # change something about the / mount out = switch_to_specialisation("${machine}", "storeMountModified") assert_lacks(out, "stopping the following units:") assert_contains(out, "NOT restarting the following changed units: -.mount") assert_contains(out, "reloading the following units: dbus.service\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") with subtest("swaps"): switch_to_specialisation("${machine}", "") # add a swap out = switch_to_specialisation("${machine}", "swap") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_contains(out, "reloading the following units: dbus.service\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_contains(out, "the following new units were started: swapfile.swap") # remove it out = switch_to_specialisation("${machine}", "") assert_contains(out, "stopping swap device: /swapfile") assert_lacks(out, "stopping the following units:") assert_lacks(out, "NOT restarting the following changed units:") assert_contains(out, "reloading the following units: dbus.service\n") assert_lacks(out, "\nrestarting the following units:") assert_lacks(out, "\nstarting the following units:") assert_lacks(out, "the following new units were started:") with subtest("services"): switch_to_specialisation("${machine}", "") # Nothing happens when nothing is changed Loading