mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-26 00:43:20 +00:00
32bf051ba4
This adds an implementation of switch-to-configuration that allows for closer interaction with the lifecycle of systemd units by using DBus APIs directly instead of using systemctl. It is disabled by default, but can be enabled by specifying `{ system.switch = { enable = false; enableNg = true; }; }`.
1420 lines
67 KiB
Nix
1420 lines
67 KiB
Nix
# Test configuration switching.
|
|
|
|
import ./make-test-python.nix ({ lib, pkgs, ng, ...} : 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, ... }: {
|
|
system.switch = {
|
|
enable = !ng;
|
|
enableNg = ng;
|
|
};
|
|
|
|
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);
|
|
|
|
dbusService = {
|
|
"dbus" = "dbus.service";
|
|
"broker" = "dbus-broker.service";
|
|
}.${nodes.machine.services.dbus.implementation};
|
|
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 ${dbusService} > /run/nixos/start-list")
|
|
out = switch_to_specialisation("${machine}", "modifiedSystemConf")
|
|
assert_contains(out, "starting the following units: ${dbusService}\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: ${dbusService}\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: ${dbusService}\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: ${dbusService}\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: ${dbusService}\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: ${dbusService}\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: ${dbusService}\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
|
|
'';
|
|
})
|