nixos/switch-to-configuration: Never unmount / or /nix

Also adds a huge test for fstab handling

Changed files
+86 -6
nixos
doc
modules
system
tests
+3 -2
nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md
···
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.
+14 -4
nixos/modules/system/activation/switch-to-configuration.pl
···
# Filesystem entry disappeared, so unmount it.
$units_to_stop{$unit} = 1;
} elsif ($cur->{fsType} ne $new->{fsType} || $cur->{device} ne $new->{device}) {
-
# 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);
+
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_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;
+69
nixos/tests/switch-test.nix
···
# 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";
+
};
+
simpleService.configuration = {
systemd.services.test = {
wantedBy = [ "multi-user.target" ];
···
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("services"):
switch_to_specialisation("${machine}", "")