Merge pull request #254993 from helsinki-systems/feat/stc-mount-improvements

nixos/switch-to-configuration: Mount improvements and a lot more test cases
This commit is contained in:
Janne Heß 2023-09-23 17:03:13 +02:00 committed by GitHub
commit cc55ef9d55
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 140 additions and 7 deletions

View file

@ -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 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. 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 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 generated. If the options of a mount are modified, for example, the proper `.mount`
marked to be started. The current systemd state is inspected, the difference 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 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 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. be controlled by the units which are explained here.

View file

@ -74,7 +74,7 @@ if ("@localeArchive@" ne "") {
if (!defined($action) || ($action ne "switch" && $action ne "boot" && $action ne "test" && $action ne "dry-activate")) { if (!defined($action) || ($action ne "switch" && $action ne "boot" && $action ne "test" && $action ne "dry-activate")) {
print STDERR <<"EOF"; 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 switch: make the configuration the boot default and activate now
boot: make the configuration the boot default boot: make the configuration the boot default
@ -661,10 +661,20 @@ foreach my $mount_point (keys(%{$cur_fss})) {
# Filesystem entry disappeared, so unmount it. # Filesystem entry disappeared, so unmount it.
$units_to_stop{$unit} = 1; $units_to_stop{$unit} = 1;
} elsif ($cur->{fsType} ne $new->{fsType} || $cur->{device} ne $new->{device}) { } 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. # Filesystem type or device changed, so unmount and mount it.
$units_to_stop{$unit} = 1; $units_to_restart{$unit} = 1;
$units_to_start{$unit} = 1; record_unit($restart_list_file, $unit);
record_unit($start_list_file, $unit); }
} elsif ($cur->{options} ne $new->{options}) { } elsif ($cur->{options} ne $new->{options}) {
# Mount options changes, so remount it. # Mount options changes, so remount it.
$units_to_reload{$unit} = 1; $units_to_reload{$unit} = 1;

View file

@ -58,6 +58,37 @@ in {
''); '');
specialisation = rec { 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 = { simpleService.configuration = {
systemd.services.test = { systemd.services.test = {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
@ -643,6 +674,97 @@ in {
# test and dry-activate actions are tested further down below # 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"): with subtest("services"):
switch_to_specialisation("${machine}", "") switch_to_specialisation("${machine}", "")
# Nothing happens when nothing is changed # Nothing happens when nothing is changed