diff options
Diffstat (limited to 'nixpkgs/nixos/tests/switch-test.nix')
-rw-r--r-- | nixpkgs/nixos/tests/switch-test.nix | 1409 |
1 files changed, 1409 insertions, 0 deletions
diff --git a/nixpkgs/nixos/tests/switch-test.nix b/nixpkgs/nixos/tests/switch-test.nix new file mode 100644 index 000000000000..5ffdf180d5e3 --- /dev/null +++ b/nixpkgs/nixos/tests/switch-test.nix @@ -0,0 +1,1409 @@ +# Test configuration switching. + +import ./make-test-python.nix ({ lib, pkgs, ...} : let + + # Simple service that can either be socket-activated or that will + # listen on port 1234 if not socket-activated. + # A connection to the socket causes 'hello' to be written to the client. + socketTest = pkgs.writeScript "socket-test.py" /* python */ '' + #!${pkgs.python3}/bin/python3 + + from socketserver import TCPServer, StreamRequestHandler + import socket + import os + + + class Handler(StreamRequestHandler): + def handle(self): + self.wfile.write("hello".encode("utf-8")) + + + class Server(TCPServer): + def __init__(self, server_address, handler_cls): + listenFds = os.getenv('LISTEN_FDS') + if listenFds is None or int(listenFds) < 1: + print(f'Binding to {server_address}') + TCPServer.__init__( + self, server_address, handler_cls, bind_and_activate=True) + else: + TCPServer.__init__( + self, server_address, handler_cls, bind_and_activate=False) + # Override socket + print(f'Got activated by {os.getenv("LISTEN_FDNAMES")} ' + f'with {listenFds} FDs') + self.socket = socket.fromfd(3, self.address_family, + self.socket_type) + + + if __name__ == "__main__": + server = Server(("localhost", 1234), Handler) + server.serve_forever() + ''; + +in { + name = "switch-test"; + meta = with pkgs.lib.maintainers; { + maintainers = [ gleber das_j ]; + }; + + nodes = { + machine = { pkgs, lib, ... }: { + environment.systemPackages = [ pkgs.socat ]; # for the socket activation stuff + users.mutableUsers = false; + + # For boot/switch testing + system.build.installBootLoader = lib.mkForce (pkgs.writeShellScript "install-dummy-loader" '' + echo "installing dummy bootloader" + touch /tmp/bootloader-installed + ''); + + 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" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.coreutils}/bin/true"; + ExecReload = "${pkgs.coreutils}/bin/true"; + }; + }; + }; + + simpleServiceSeparateActivationScript.configuration = { + system.activatable = false; + systemd.services.test = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.coreutils}/bin/true"; + ExecReload = "${pkgs.coreutils}/bin/true"; + }; + }; + }; + + simpleServiceDifferentDescription.configuration = { + imports = [ simpleService.configuration ]; + systemd.services.test.description = "Test unit"; + }; + + simpleServiceModified.configuration = { + imports = [ simpleService.configuration ]; + systemd.services.test.serviceConfig.X-Test = true; + }; + + simpleServiceNostop.configuration = { + imports = [ simpleService.configuration ]; + systemd.services.test.stopIfChanged = false; + }; + + simpleServiceReload.configuration = { + imports = [ simpleService.configuration ]; + systemd.services.test = { + reloadIfChanged = true; + serviceConfig.ExecReload = "${pkgs.coreutils}/bin/true"; + }; + }; + + simpleServiceNorestart.configuration = { + imports = [ simpleService.configuration ]; + systemd.services.test.restartIfChanged = false; + }; + + simpleServiceFailing.configuration = { + imports = [ simpleServiceModified.configuration ]; + systemd.services.test.serviceConfig.ExecStart = lib.mkForce "${pkgs.coreutils}/bin/false"; + }; + + autorestartService.configuration = { + # A service that immediately goes into restarting (but without failing) + systemd.services.autorestart = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + Restart = "always"; + RestartSec = "20y"; # Should be long enough + ExecStart = "${pkgs.coreutils}/bin/true"; + }; + }; + }; + + autorestartServiceFailing.configuration = { + imports = [ autorestartService.configuration ]; + systemd.services.autorestart.serviceConfig = { + ExecStart = lib.mkForce "${pkgs.coreutils}/bin/false"; + }; + }; + + simpleServiceWithExtraSection.configuration = { + imports = [ simpleServiceNostop.configuration ]; + systemd.packages = [ (pkgs.writeTextFile { + name = "systemd-extra-section"; + destination = "/etc/systemd/system/test.service"; + text = '' + [X-Test] + X-Test-Value=a + ''; + }) ]; + }; + + simpleServiceWithExtraSectionOtherName.configuration = { + imports = [ simpleServiceNostop.configuration ]; + systemd.packages = [ (pkgs.writeTextFile { + name = "systemd-extra-section"; + destination = "/etc/systemd/system/test.service"; + text = '' + [X-Test2] + X-Test-Value=a + ''; + }) ]; + }; + + simpleServiceWithInstallSection.configuration = { + imports = [ simpleServiceNostop.configuration ]; + systemd.packages = [ (pkgs.writeTextFile { + name = "systemd-extra-section"; + destination = "/etc/systemd/system/test.service"; + text = '' + [Install] + WantedBy=multi-user.target + ''; + }) ]; + }; + + simpleServiceWithExtraKey.configuration = { + imports = [ simpleServiceNostop.configuration ]; + systemd.services.test.serviceConfig."X-Test" = "test"; + }; + + simpleServiceWithExtraKeyOtherValue.configuration = { + imports = [ simpleServiceNostop.configuration ]; + systemd.services.test.serviceConfig."X-Test" = "test2"; + }; + + simpleServiceWithExtraKeyOtherName.configuration = { + imports = [ simpleServiceNostop.configuration ]; + systemd.services.test.serviceConfig."X-Test2" = "test"; + }; + + simpleServiceReloadTrigger.configuration = { + imports = [ simpleServiceNostop.configuration ]; + systemd.services.test.reloadTriggers = [ "/dev/null" ]; + }; + + simpleServiceReloadTriggerModified.configuration = { + imports = [ simpleServiceNostop.configuration ]; + systemd.services.test.reloadTriggers = [ "/dev/zero" ]; + }; + + simpleServiceReloadTriggerModifiedAndSomethingElse.configuration = { + imports = [ simpleServiceNostop.configuration ]; + systemd.services.test = { + reloadTriggers = [ "/dev/zero" ]; + serviceConfig."X-Test" = "test"; + }; + }; + + simpleServiceReloadTriggerModifiedSomethingElse.configuration = { + imports = [ simpleServiceNostop.configuration ]; + systemd.services.test.serviceConfig."X-Test" = "test"; + }; + + unitWithBackslash.configuration = { + systemd.services."escaped\\x2ddash" = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.coreutils}/bin/true"; + ExecReload = "${pkgs.coreutils}/bin/true"; + }; + }; + }; + + unitWithBackslashModified.configuration = { + imports = [ unitWithBackslash.configuration ]; + systemd.services."escaped\\x2ddash".serviceConfig.X-Test = "test"; + }; + + unitStartingWithDash.configuration = { + systemd.services."-" = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.coreutils}/bin/true"; + }; + }; + }; + + unitStartingWithDashModified.configuration = { + imports = [ unitStartingWithDash.configuration ]; + systemd.services."-" = { + reloadIfChanged = true; + serviceConfig.ExecReload = "${pkgs.coreutils}/bin/true"; + }; + }; + + unitWithRequirement.configuration = { + systemd.services.required-service = { + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.coreutils}/bin/true"; + ExecReload = "${pkgs.coreutils}/bin/true"; + }; + }; + systemd.services.test-service = { + wantedBy = [ "multi-user.target" ]; + requires = [ "required-service.service" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.coreutils}/bin/true"; + ExecReload = "${pkgs.coreutils}/bin/true"; + }; + }; + }; + + unitWithRequirementModified.configuration = { + imports = [ unitWithRequirement.configuration ]; + systemd.services.required-service.serviceConfig.X-Test = "test"; + systemd.services.test-service.reloadTriggers = [ "test" ]; + }; + + unitWithRequirementModifiedNostart.configuration = { + imports = [ unitWithRequirement.configuration ]; + systemd.services.test-service.unitConfig.RefuseManualStart = true; + }; + + unitWithTemplate.configuration = { + systemd.services."instantiated@".serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.coreutils}/bin/true"; + ExecReload = "${pkgs.coreutils}/bin/true"; + }; + systemd.services."instantiated@one" = { + wantedBy = [ "multi-user.target" ]; + overrideStrategy = "asDropin"; + }; + systemd.services."instantiated@two" = { + wantedBy = [ "multi-user.target" ]; + overrideStrategy = "asDropin"; + }; + }; + + unitWithTemplateModified.configuration = { + imports = [ unitWithTemplate.configuration ]; + systemd.services."instantiated@".serviceConfig.X-Test = "test"; + }; + + restart-and-reload-by-activation-script.configuration = { + systemd.services = rec { + simple-service = { + # No wantedBy so we can check if the activation script restart triggers them + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.coreutils}/bin/true"; + ExecReload = "${pkgs.coreutils}/bin/true"; + }; + }; + "templated-simple-service@" = simple-service; + "templated-simple-service@instance".overrideStrategy = "asDropin"; + + simple-restart-service = simple-service // { + stopIfChanged = false; + }; + "templated-simple-restart-service@" = simple-restart-service; + "templated-simple-restart-service@instance".overrideStrategy = "asDropin"; + + simple-reload-service = simple-service // { + reloadIfChanged = true; + }; + "templated-simple-reload-service@" = simple-reload-service; + "templated-simple-reload-service@instance".overrideStrategy = "asDropin"; + + no-restart-service = simple-service // { + restartIfChanged = false; + }; + "templated-no-restart-service@" = no-restart-service; + "templated-no-restart-service@instance".overrideStrategy = "asDropin"; + + reload-triggers = simple-service // { + wantedBy = [ "multi-user.target" ]; + }; + "templated-reload-triggers@" = simple-service; + "templated-reload-triggers@instance" = { + overrideStrategy = "asDropin"; + wantedBy = [ "multi-user.target" ]; + }; + + reload-triggers-and-restart-by-as = simple-service; + "templated-reload-triggers-and-restart-by-as@" = reload-triggers-and-restart-by-as; + "templated-reload-triggers-and-restart-by-as@instance".overrideStrategy = "asDropin"; + + reload-triggers-and-restart = simple-service // { + stopIfChanged = false; # easier to check for this + wantedBy = [ "multi-user.target" ]; + }; + "templated-reload-triggers-and-restart@" = simple-service; + "templated-reload-triggers-and-restart@instance" = { + overrideStrategy = "asDropin"; + stopIfChanged = false; # easier to check for this + wantedBy = [ "multi-user.target" ]; + }; + }; + + system.activationScripts.restart-and-reload-test = { + supportsDryActivation = true; + deps = []; + text = '' + if [ "$NIXOS_ACTION" = dry-activate ]; then + f=/run/nixos/dry-activation-restart-list + g=/run/nixos/dry-activation-reload-list + else + f=/run/nixos/activation-restart-list + g=/run/nixos/activation-reload-list + fi + cat <<EOF >> "$f" + simple-service.service + simple-restart-service.service + simple-reload-service.service + no-restart-service.service + reload-triggers-and-restart-by-as.service + templated-simple-service@instance.service + templated-simple-restart-service@instance.service + templated-simple-reload-service@instance.service + templated-no-restart-service@instance.service + templated-reload-triggers-and-restart-by-as@instance.service + EOF + + cat <<EOF >> "$g" + reload-triggers.service + reload-triggers-and-restart-by-as.service + reload-triggers-and-restart.service + templated-reload-triggers@instance.service + templated-reload-triggers-and-restart-by-as@instance.service + templated-reload-triggers-and-restart@instance.service + EOF + ''; + }; + }; + + restart-and-reload-by-activation-script-modified.configuration = { + imports = [ restart-and-reload-by-activation-script.configuration ]; + systemd.services.reload-triggers-and-restart.serviceConfig.X-Modified = "test"; + systemd.services."templated-reload-triggers-and-restart@instance" = { + overrideStrategy = "asDropin"; + serviceConfig.X-Modified = "test"; + }; + }; + + simple-socket.configuration = { + systemd.services.socket-activated = { + description = "A socket-activated service"; + stopIfChanged = lib.mkDefault false; + serviceConfig = { + ExecStart = socketTest; + ExecReload = "${pkgs.coreutils}/bin/true"; + }; + }; + systemd.sockets.socket-activated = { + wantedBy = [ "sockets.target" ]; + listenStreams = [ "/run/test.sock" ]; + socketConfig.SocketMode = lib.mkDefault "0777"; + }; + }; + + simple-socket-service-modified.configuration = { + imports = [ simple-socket.configuration ]; + systemd.services.socket-activated.serviceConfig.X-Test = "test"; + }; + + simple-socket-stop-if-changed.configuration = { + imports = [ simple-socket.configuration ]; + systemd.services.socket-activated.stopIfChanged = true; + }; + + simple-socket-stop-if-changed-and-reloadtrigger.configuration = { + imports = [ simple-socket.configuration ]; + systemd.services.socket-activated = { + stopIfChanged = true; + reloadTriggers = [ "test" ]; + }; + }; + + mount.configuration = { + systemd.mounts = [ + { + description = "Testmount"; + what = "tmpfs"; + type = "tmpfs"; + where = "/testmount"; + options = "size=1M"; + wantedBy = [ "local-fs.target" ]; + } + ]; + }; + + mountOptionsModified.configuration = { + systemd.mounts = [ + { + description = "Testmount"; + what = "tmpfs"; + type = "tmpfs"; + where = "/testmount"; + options = "size=10M"; + wantedBy = [ "local-fs.target" ]; + } + ]; + }; + + mountModified.configuration = { + systemd.mounts = [ + { + description = "Testmount"; + what = "ramfs"; + type = "ramfs"; + where = "/testmount"; + options = "size=10M"; + wantedBy = [ "local-fs.target" ]; + } + ]; + }; + + timer.configuration = { + systemd.timers.test-timer = { + wantedBy = [ "timers.target" ]; + timerConfig.OnCalendar = "@1395716396"; # chosen by fair dice roll + }; + systemd.services.test-timer = { + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.coreutils}/bin/true"; + }; + }; + }; + + timerModified.configuration = { + imports = [ timer.configuration ]; + systemd.timers.test-timer.timerConfig.OnCalendar = lib.mkForce "Fri 2012-11-23 16:00:00"; + }; + + hybridSleepModified.configuration = { + systemd.targets.hybrid-sleep.unitConfig.X-Test = true; + }; + + target.configuration = { + systemd.targets.test-target.wantedBy = [ "multi-user.target" ]; + # We use this service to figure out whether the target was modified. + # This is the only way because targets are filtered and therefore not + # printed when they are started/stopped. + systemd.services.test-service = { + bindsTo = [ "test-target.target" ]; + serviceConfig.ExecStart = "${pkgs.coreutils}/bin/sleep infinity"; + }; + }; + + targetModified.configuration = { + imports = [ target.configuration ]; + systemd.targets.test-target.unitConfig.X-Test = true; + }; + + targetModifiedStopOnReconfig.configuration = { + imports = [ target.configuration ]; + systemd.targets.test-target.unitConfig.X-StopOnReconfiguration = true; + }; + + path.configuration = { + systemd.paths.test-watch = { + wantedBy = [ "paths.target" ]; + pathConfig.PathExists = "/testpath"; + }; + systemd.services.test-watch = { + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.coreutils}/bin/touch /testpath-modified"; + }; + }; + }; + + pathModified.configuration = { + imports = [ path.configuration ]; + systemd.paths.test-watch.pathConfig.PathExists = lib.mkForce "/testpath2"; + }; + + slice.configuration = { + systemd.slices.testslice.sliceConfig.MemoryMax = "1"; # don't allow memory allocation + systemd.services.testservice = { + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = "${pkgs.coreutils}/bin/true"; + Slice = "testslice.slice"; + }; + }; + }; + + sliceModified.configuration = { + imports = [ slice.configuration ]; + systemd.slices.testslice.sliceConfig.MemoryMax = lib.mkForce null; + }; + }; + }; + + other = { + users.mutableUsers = true; + }; + }; + + testScript = { nodes, ... }: let + originalSystem = nodes.machine.system.build.toplevel; + otherSystem = nodes.other.system.build.toplevel; + machine = nodes.machine.system.build.toplevel; + + # Ensures failures pass through using pipefail, otherwise failing to + # switch-to-configuration is hidden by the success of `tee`. + stderrRunner = pkgs.writeScript "stderr-runner" '' + #! ${pkgs.runtimeShell} + set -e + set -o pipefail + exec env -i "$@" | tee /dev/stderr + ''; + + # Returns a comma separated representation of the given list in sorted + # order, that matches the output format of switch-to-configuration.pl + sortedUnits = xs: lib.concatStringsSep ", " (builtins.sort builtins.lessThan xs); + in /* python */ '' + def switch_to_specialisation(system, name, action="test", fail=False): + if name == "": + switcher = f"{system}/bin/switch-to-configuration" + else: + switcher = f"{system}/specialisation/{name}/bin/switch-to-configuration" + return run_switch(switcher, action, fail) + + # like above but stc = switcher + def run_switch(switcher, action="test", fail=False): + out = machine.fail(f"{switcher} {action} 2>&1") if fail \ + else machine.succeed(f"{switcher} {action} 2>&1") + assert_lacks(out, "switch-to-configuration line") # Perl warnings + return out + + def assert_contains(haystack, needle): + if needle not in haystack: + print("The haystack that will cause the following exception is:") + print("---") + print(haystack) + print("---") + raise Exception(f"Expected string '{needle}' was not found") + + def assert_lacks(haystack, needle): + if needle in haystack: + print("The haystack that will cause the following exception is:") + print("---") + print(haystack, end="") + print("---") + raise Exception(f"Unexpected string '{needle}' was found") + + + machine.wait_for_unit("multi-user.target") + + machine.succeed( + "${stderrRunner} ${originalSystem}/bin/switch-to-configuration test" + ) + # This tests whether the /etc/os-release parser works which is a fallback + # when /etc/NIXOS is missing. If the parser does not work, switch-to-configuration + # would fail. + machine.succeed("rm /etc/NIXOS") + machine.succeed( + "${stderrRunner} ${otherSystem}/bin/switch-to-configuration test" + ) + + + with subtest("actions"): + # boot action + machine.fail("test -f /tmp/bootloader-installed") + out = switch_to_specialisation("${machine}", "simpleService", action="boot") + assert_contains(out, "installing dummy bootloader") + assert_lacks(out, "activating the configuration...") # good indicator of a system activation + machine.succeed("test -f /tmp/bootloader-installed") + machine.succeed("rm /tmp/bootloader-installed") + + # switch action + machine.fail("test -f /tmp/bootloader-installed") + out = switch_to_specialisation("${machine}", "", action="switch") + assert_contains(out, "installing dummy bootloader") + assert_contains(out, "activating the configuration...") # good indicator of a system activation + machine.succeed("test -f /tmp/bootloader-installed") + + # 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 + out = switch_to_specialisation("${machine}", "") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Start a simple service + out = switch_to_specialisation("${machine}", "simpleService") + assert_lacks(out, "installing dummy bootloader") # test does not install a bootloader + 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") # huh + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_contains(out, "the following new units were started: test.service\n") + + # Not changing anything doesn't do anything + out = switch_to_specialisation("${machine}", "simpleService") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Only changing the description does nothing + out = switch_to_specialisation("${machine}", "simpleServiceDifferentDescription") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Restart the simple service + out = switch_to_specialisation("${machine}", "simpleServiceModified") + assert_contains(out, "stopping the following units: test.service\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_contains(out, "\nstarting the following units: test.service\n") + assert_lacks(out, "the following new units were started:") + + # Restart the service with stopIfChanged=false + out = switch_to_specialisation("${machine}", "simpleServiceNostop") + 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.service\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Reload the service with reloadIfChanged=true + out = switch_to_specialisation("${machine}", "simpleServiceReload") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_contains(out, "reloading the following units: test.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:") + + # Nothing happens when restartIfChanged=false + out = switch_to_specialisation("${machine}", "simpleServiceNorestart") + assert_lacks(out, "stopping the following units:") + assert_contains(out, "NOT restarting the following changed units: test.service\n") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Dry mode shows different messages + out = switch_to_specialisation("${machine}", "simpleService", action="dry-activate") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + assert_contains(out, "would start the following units: test.service\n") + + out = switch_to_specialisation("${machine}", "", action="test") + + # Ensure the service can be started when the activation script isn't in toplevel + # This is a lot like "Start a simple service", except activation-only deps could be gc-ed + out = run_switch("${nodes.machine.specialisation.simpleServiceSeparateActivationScript.configuration.system.build.separateActivationScript}/bin/switch-to-configuration"); + assert_lacks(out, "installing dummy bootloader") # test does not install a bootloader + 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") # huh + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_contains(out, "the following new units were started: test.service\n") + machine.succeed("! test -e /run/current-system/activate") + machine.succeed("! test -e /run/current-system/dry-activate") + machine.succeed("! test -e /run/current-system/bin/switch-to-configuration") + + # Ensure \ works in unit names + out = switch_to_specialisation("${machine}", "unitWithBackslash") + assert_contains(out, "stopping the following units: test.service\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_contains(out, "the following new units were started: escaped\\x2ddash.service\n") + + out = switch_to_specialisation("${machine}", "unitWithBackslashModified") + assert_contains(out, "stopping the following units: escaped\\x2ddash.service\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_contains(out, "\nstarting the following units: escaped\\x2ddash.service\n") + assert_lacks(out, "the following new units were started:") + + # Ensure units can start with a dash + out = switch_to_specialisation("${machine}", "unitStartingWithDash") + assert_contains(out, "stopping the following units: escaped\\x2ddash.service\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_contains(out, "the following new units were started: -.service\n") + + # The regression only occurs when reloading units + out = switch_to_specialisation("${machine}", "unitStartingWithDashModified") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_contains(out, "reloading the following units: -.service") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Ensure units that require changed units are properly reloaded + out = switch_to_specialisation("${machine}", "unitWithRequirement") + assert_contains(out, "stopping the following units: -.service\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_contains(out, "the following new units were started: required-service.service, test-service.service\n") + + out = switch_to_specialisation("${machine}", "unitWithRequirementModified") + assert_contains(out, "stopping the following units: required-service.service\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_contains(out, "\nstarting the following units: required-service.service, test-service.service\n") + assert_lacks(out, "the following new units were started:") + + # Unless the unit asks to be not restarted + out = switch_to_specialisation("${machine}", "unitWithRequirementModifiedNostart") + assert_contains(out, "stopping the following units: required-service.service\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_contains(out, "\nstarting the following units: required-service.service\n") + assert_lacks(out, "the following new units were started:") + + # Ensure templated units are restarted when the base unit changes + switch_to_specialisation("${machine}", "unitWithTemplate") + out = switch_to_specialisation("${machine}", "unitWithTemplateModified") + assert_contains(out, "stopping the following units: instantiated@one.service, instantiated@two.service\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_contains(out, "\nstarting the following units: instantiated@one.service, instantiated@two.service\n") + assert_lacks(out, "the following new units were started:") + + with subtest("failing units"): + # Let the simple service fail + switch_to_specialisation("${machine}", "simpleServiceModified") + out = switch_to_specialisation("${machine}", "simpleServiceFailing", fail=True) + assert_contains(out, "stopping the following units: test.service\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_contains(out, "\nstarting the following units: test.service\n") + assert_lacks(out, "the following new units were started:") + assert_contains(out, "warning: the following units failed: test.service\n") + assert_contains(out, "Main PID:") # output of systemctl + + # A unit that gets into autorestart without failing is not treated as failed + out = switch_to_specialisation("${machine}", "autorestartService") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_contains(out, "the following new units were started: autorestart.service\n") + machine.systemctl('stop autorestart.service') # cancel the 20y timer + + # Switching to the same system should do nothing (especially not treat the unit as failed) + out = switch_to_specialisation("${machine}", "autorestartService") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_contains(out, "the following new units were started: autorestart.service\n") + machine.systemctl('stop autorestart.service') # cancel the 20y timer + + # If systemd thinks the unit has failed and is in autorestart, we should show it as failed + out = switch_to_specialisation("${machine}", "autorestartServiceFailing", fail=True) + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + assert_contains(out, "warning: the following units failed: autorestart.service\n") + assert_contains(out, "Main PID:") # output of systemctl + + with subtest("unit file parser"): + # Switch to a well-known state + switch_to_specialisation("${machine}", "simpleServiceNostop") + + # Add a section + out = switch_to_specialisation("${machine}", "simpleServiceWithExtraSection") + 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.service\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Rename it + out = switch_to_specialisation("${machine}", "simpleServiceWithExtraSectionOtherName") + 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.service\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Remove it + out = switch_to_specialisation("${machine}", "simpleServiceNostop") + 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.service\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # [Install] section is ignored + out = switch_to_specialisation("${machine}", "simpleServiceWithInstallSection") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Add a key + out = switch_to_specialisation("${machine}", "simpleServiceWithExtraKey") + 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.service\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Change its value + out = switch_to_specialisation("${machine}", "simpleServiceWithExtraKeyOtherValue") + 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.service\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Rename it + out = switch_to_specialisation("${machine}", "simpleServiceWithExtraKeyOtherName") + 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.service\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Remove it + out = switch_to_specialisation("${machine}", "simpleServiceNostop") + 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.service\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Add a reload trigger + out = switch_to_specialisation("${machine}", "simpleServiceReloadTrigger") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_contains(out, "reloading the following units: test.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:") + + # Modify the reload trigger + out = switch_to_specialisation("${machine}", "simpleServiceReloadTriggerModified") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_contains(out, "reloading the following units: test.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:") + + # Modify the reload trigger and something else + out = switch_to_specialisation("${machine}", "simpleServiceReloadTriggerModifiedAndSomethingElse") + 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.service\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Remove the reload trigger + out = switch_to_specialisation("${machine}", "simpleServiceReloadTriggerModifiedSomethingElse") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + 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("restart and reload by activation script"): + switch_to_specialisation("${machine}", "simpleServiceNorestart") + out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script") + assert_contains(out, "stopping the following units: test.service\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "restarting the following units:") + assert_contains(out, "\nstarting the following units: ${sortedUnits [ + "no-restart-service.service" + "reload-triggers-and-restart-by-as.service" + "simple-reload-service.service" + "simple-restart-service.service" + "simple-service.service" + "templated-no-restart-service@instance.service" + "templated-reload-triggers-and-restart-by-as@instance.service" + "templated-simple-reload-service@instance.service" + "templated-simple-restart-service@instance.service" + "templated-simple-service@instance.service" + ]}\n") + assert_contains(out, "the following new units were started: ${sortedUnits [ + "no-restart-service.service" + "reload-triggers-and-restart-by-as.service" + "reload-triggers-and-restart.service" + "reload-triggers.service" + "simple-reload-service.service" + "simple-restart-service.service" + "simple-service.service" + "system-templated\\\\x2dno\\\\x2drestart\\\\x2dservice.slice" + "system-templated\\\\x2dreload\\\\x2dtriggers.slice" + "system-templated\\\\x2dreload\\\\x2dtriggers\\\\x2dand\\\\x2drestart.slice" + "system-templated\\\\x2dreload\\\\x2dtriggers\\\\x2dand\\\\x2drestart\\\\x2dby\\\\x2das.slice" + "system-templated\\\\x2dsimple\\\\x2dreload\\\\x2dservice.slice" + "system-templated\\\\x2dsimple\\\\x2drestart\\\\x2dservice.slice" + "system-templated\\\\x2dsimple\\\\x2dservice.slice" + "templated-no-restart-service@instance.service" + "templated-reload-triggers-and-restart-by-as@instance.service" + "templated-reload-triggers-and-restart@instance.service" + "templated-reload-triggers@instance.service" + "templated-simple-reload-service@instance.service" + "templated-simple-restart-service@instance.service" + "templated-simple-service@instance.service" + ]}\n") + # Switch to the same system where the example services get restarted + # and reloaded by the activation script + out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_contains(out, "reloading the following units: ${sortedUnits [ + "reload-triggers-and-restart.service" + "reload-triggers.service" + "simple-reload-service.service" + "templated-reload-triggers-and-restart@instance.service" + "templated-reload-triggers@instance.service" + "templated-simple-reload-service@instance.service" + ]}\n") + assert_contains(out, "restarting the following units: ${sortedUnits [ + "reload-triggers-and-restart-by-as.service" + "simple-restart-service.service" + "simple-service.service" + "templated-reload-triggers-and-restart-by-as@instance.service" + "templated-simple-restart-service@instance.service" + "templated-simple-service@instance.service" + ]}\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + # Switch to the same system and see if the service gets restarted when it's modified + # while the fact that it's supposed to be reloaded by the activation script is ignored. + out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script-modified") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_contains(out, "reloading the following units: ${sortedUnits [ + "reload-triggers.service" + "simple-reload-service.service" + "templated-reload-triggers@instance.service" + "templated-simple-reload-service@instance.service" + ]}\n") + assert_contains(out, "restarting the following units: ${sortedUnits [ + "reload-triggers-and-restart-by-as.service" + "reload-triggers-and-restart.service" + "simple-restart-service.service" + "simple-service.service" + "templated-reload-triggers-and-restart-by-as@instance.service" + "templated-reload-triggers-and-restart@instance.service" + "templated-simple-restart-service@instance.service" + "templated-simple-service@instance.service" + ]}\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + # The same, but in dry mode + out = switch_to_specialisation("${machine}", "restart-and-reload-by-activation-script", action="dry-activate") + assert_lacks(out, "would stop the following units:") + assert_lacks(out, "would NOT stop the following changed units:") + assert_contains(out, "would reload the following units: ${sortedUnits [ + "reload-triggers.service" + "simple-reload-service.service" + "templated-reload-triggers@instance.service" + "templated-simple-reload-service@instance.service" + ]}\n") + assert_contains(out, "would restart the following units: ${sortedUnits [ + "reload-triggers-and-restart-by-as.service" + "reload-triggers-and-restart.service" + "simple-restart-service.service" + "simple-service.service" + "templated-reload-triggers-and-restart-by-as@instance.service" + "templated-reload-triggers-and-restart@instance.service" + "templated-simple-restart-service@instance.service" + "templated-simple-service@instance.service" + ]}\n") + assert_lacks(out, "\nwould start the following units:") + + with subtest("socket-activated services"): + # Socket-activated services don't get started, just the socket + machine.fail("[ -S /run/test.sock ]") + out = switch_to_specialisation("${machine}", "simple-socket") + # assert_lacks(out, "stopping the following units:") not relevant + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_contains(out, "the following new units were started: socket-activated.socket\n") + machine.succeed("[ -S /run/test.sock ]") + + # Changing a non-activated service does nothing + out = switch_to_specialisation("${machine}", "simple-socket-service-modified") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + machine.succeed("[ -S /run/test.sock ]") + # The unit is properly activated when the socket is accessed + if machine.succeed("socat - UNIX-CONNECT:/run/test.sock") != "hello": + raise Exception("Socket was not properly activated") # idk how that would happen tbh + + # Changing an activated service with stopIfChanged=false restarts the service + out = switch_to_specialisation("${machine}", "simple-socket") + 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: socket-activated.service\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + machine.succeed("[ -S /run/test.sock ]") + # Socket-activation of the unit still works + if machine.succeed("socat - UNIX-CONNECT:/run/test.sock") != "hello": + raise Exception("Socket was not properly activated after the service was restarted") + + # Changing an activated service with stopIfChanged=true stops the service and + # socket and starts the socket + out = switch_to_specialisation("${machine}", "simple-socket-stop-if-changed") + assert_contains(out, "stopping the following units: socket-activated.service, socket-activated.socket\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_contains(out, "\nstarting the following units: socket-activated.socket\n") + assert_lacks(out, "the following new units were started:") + machine.succeed("[ -S /run/test.sock ]") + # Socket-activation of the unit still works + if machine.succeed("socat - UNIX-CONNECT:/run/test.sock") != "hello": + raise Exception("Socket was not properly activated after the service was restarted") + + # Changing a reload trigger of a socket-activated unit only reloads it + out = switch_to_specialisation("${machine}", "simple-socket-stop-if-changed-and-reloadtrigger") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_contains(out, "reloading the following units: socket-activated.service\n") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units: socket-activated.socket") + assert_lacks(out, "the following new units were started:") + machine.succeed("[ -S /run/test.sock ]") + # Socket-activation of the unit still works + if machine.succeed("socat - UNIX-CONNECT:/run/test.sock") != "hello": + raise Exception("Socket was not properly activated after the service was restarted") + + with subtest("mounts"): + switch_to_specialisation("${machine}", "mount") + out = machine.succeed("mount | grep 'on /testmount'") + assert_contains(out, "size=1024k") + # Changing options reloads the unit + out = switch_to_specialisation("${machine}", "mountOptionsModified") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_contains(out, "reloading the following units: testmount.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:") + # It changed + out = machine.succeed("mount | grep 'on /testmount'") + assert_contains(out, "size=10240k") + # Changing anything but `Options=` restarts the unit + out = switch_to_specialisation("${machine}", "mountModified") + 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: testmount.mount\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + # It changed + out = machine.succeed("mount | grep 'on /testmount'") + assert_contains(out, "ramfs") + + with subtest("timers"): + switch_to_specialisation("${machine}", "timer") + out = machine.succeed("systemctl show test-timer.timer") + assert_contains(out, "OnCalendar=2014-03-25 02:59:56 UTC") + out = switch_to_specialisation("${machine}", "timerModified") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following units:") + assert_lacks(out, "reloading the following units:") + assert_contains(out, "\nrestarting the following units: test-timer.timer\n") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + # It changed + out = machine.succeed("systemctl show test-timer.timer") + assert_contains(out, "OnCalendar=Fri 2012-11-23 16:00:00") + + with subtest("targets"): + # Modifying some special targets like hybrid-sleep.target does nothing + out = switch_to_specialisation("${machine}", "hybridSleepModified") + assert_contains(out, "stopping the following units: test-timer.timer\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + + # Adding a new target starts it + out = switch_to_specialisation("${machine}", "target") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following 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-target.target\n") + + # Changing a target doesn't print anything because the unit is filtered + machine.systemctl("start test-service.service") + out = switch_to_specialisation("${machine}", "targetModified") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + machine.succeed("systemctl is-active test-service.service") # target was not restarted + + # With X-StopOnReconfiguration, the target gets stopped and started + out = switch_to_specialisation("${machine}", "targetModifiedStopOnReconfig") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + machine.fail("systemctl is-active test-service.servce") # target was restarted + + # Remove the target by switching to the old specialisation + out = switch_to_specialisation("${machine}", "timerModified") + assert_contains(out, "stopping the following units: test-target.target\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following 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-timer.timer\n") + + with subtest("paths"): + out = switch_to_specialisation("${machine}", "path") + assert_contains(out, "stopping the following units: test-timer.timer\n") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following 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-watch.path\n") + machine.fail("test -f /testpath-modified") + + # touch the file, unit should be triggered + machine.succeed("touch /testpath") + machine.wait_until_succeeds("test -f /testpath-modified") + machine.succeed("rm /testpath /testpath-modified") + machine.systemctl("stop test-watch.service") + switch_to_specialisation("${machine}", "pathModified") + machine.succeed("touch /testpath") + machine.fail("test -f /testpath-modified") + machine.succeed("touch /testpath2") + machine.wait_until_succeeds("test -f /testpath-modified") + + # This test ensures that changes to slice configuration get applied. + # We test this by having a slice that allows no memory allocation at + # all and starting a service within it. If the service crashes, the slice + # is applied and if we modify the slice to allow memory allocation, the + # service should successfully start. + with subtest("slices"): + machine.succeed("echo 0 > /proc/sys/vm/panic_on_oom") # allow OOMing + out = switch_to_specialisation("${machine}", "slice") + # assert_lacks(out, "stopping the following units:") not relevant + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + machine.fail("systemctl start testservice.service") + + out = switch_to_specialisation("${machine}", "sliceModified") + assert_lacks(out, "stopping the following units:") + assert_lacks(out, "NOT restarting the following changed units:") + assert_lacks(out, "reloading the following units:") + assert_lacks(out, "\nrestarting the following units:") + assert_lacks(out, "\nstarting the following units:") + assert_lacks(out, "the following new units were started:") + machine.succeed("systemctl start testservice.service") + machine.succeed("echo 1 > /proc/sys/vm/panic_on_oom") # disallow OOMing + ''; +}) |