6 Commits

Author SHA1 Message Date
Maciej Borzecki
6bfd07904c wrappers: fix unreliable tests to not use mocked systemctl command (#13612)
* wrappers: fix test to not use mocked systemctl command

Fix the test to not use a mocked systemctl command so that we can get more
reliable execution even when running in a slow environment. The issue is that
parts of the 'stop' chain run in a goroutine, so the order of `systemctl show`
and `systemctl stop` calls isn't guaranteed. No top of this, since this can run
in parallel now, the code would need to use `testutilg.MockLockedCommand` to
prevent corruption of the call log.

Fixes the following issue seen in unit tests or OBS builds:

```
----------------------------------------------------------------------
FAIL: services_test.go:3371: servicesTestSuite.TestStartServicesStopsServicesIncludingActivation

using shellcheck: "/usr/bin/shellcheck"
services_test.go:3433:
    c.Check(r.Calls(), DeepEquals, [][]string{
        // Enable phase for the service activation units, we have one set of system daemon and one set of user daemon
        {"systemctl", "daemon-reload"},
        {"systemctl", "--user", "daemon-reload"},
        {"systemctl", "--no-reload", "enable", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket"},
        {"systemctl", "daemon-reload"},
        {"systemctl", "--user", "--global", "--no-reload", "enable", "snap.hello-snap.svc2.sock1.socket", "snap.hello-snap.svc2.sock2.socket"},

        // Start phase for service activation units, we have rigged the game by making sure this stage fails,
        // so only one of the services will attempt to start
        {"systemctl", "start", "snap.hello-snap.svc1.sock1.socket"},

        // Stop phase, where we attempt to stop the activation units and the primary services
        // We first attempt to stop the user services, then the system services
        {"systemctl", "--user", "stop", "snap.hello-snap.svc2.sock1.socket"},
        {"systemctl", "--user", "show", "--property=ActiveState", "snap.hello-snap.svc2.sock1.socket"},
        {"systemctl", "--user", "stop", "snap.hello-snap.svc2.sock2.socket"},
        {"systemctl", "--user", "show", "--property=ActiveState", "snap.hello-snap.svc2.sock2.socket"},
        {"systemctl", "--user", "stop", "snap.hello-snap.svc2.service"},
        {"systemctl", "--user", "show", "--property=ActiveState", "snap.hello-snap.svc2.service"},

        {"systemctl", "stop", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket", "snap.hello-snap.svc1.service"},
        {"systemctl", "show", "--property=ActiveState", "snap.hello-snap.svc1.sock1.socket"},

        // Disable phase, where the activation units are being disabled
        {"systemctl", "--no-reload", "disable", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket"},
        {"systemctl", "daemon-reload"},
        {"systemctl", "--user", "--global", "--no-reload", "disable", "snap.hello-snap.svc2.sock1.socket", "snap.hello-snap.svc2.sock2.socket"},
    })
... obtained [][]string = [][]string{[]string{"systemctl", "daemon-reload"}, []string{"systemctl", "--user", "daemon-reload"}, []string{"systemctl", "--no-reload", "enable", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket"}, []string{"systemctl", "daemon-reload"}, []string{"systemctl", "--user", "--global", "--no-reload", "enable", "snap.hello-snap.svc2.soc
k1.socket", "snap.hello-snap.svc2.sock2.socket"}, []string{"systemctl", "start", "snap.hello-snap.svc1.sock1.socket"}, []string{"systemctl", "--user", "stop", "snap.hello-snap.svc2.sock1.socket"}, []string{"systemctl", "--user", "show", "--property=ActiveState", "snap.hello-snap.svc2.sock1.socket"}, []string{"systemctl", "--user", "show", "--property=ActiveState", "snap.hello-snap
.svc2.sock2.socket"}, []string{"systemctl", "--user", "stop", "snap.hello-snap.svc2.sock2.socket"}, []string{"systemctl", "--user", "stop", "snap.hello-snap.svc2.service"}, []string{"systemctl", "--user", "show", "--property=ActiveState", "snap.hello-snap.svc2.service"}, []string{"systemctl", "stop", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket", "snap.h
ello-snap.svc1.service"}, []string{"systemctl", "show", "--property=ActiveState", "snap.hello-snap.svc1.sock1.socket"}, []string{"systemctl", "--no-reload", "disable", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket"}, []string{"systemctl", "daemon-reload"}, []string{"systemctl", "--user", "--global", "--no-reload", "disable", "snap.hello-snap.svc2.sock1.so
cket", "snap.hello-snap.svc2.sock2.socket"}}
... expected [][]string = [][]string{[]string{"systemctl", "daemon-reload"}, []string{"systemctl", "--user", "daemon-reload"}, []string{"systemctl", "--no-reload", "enable", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket"}, []string{"systemctl", "daemon-reload"}, []string{"systemctl", "--user", "--global", "--no-reload", "enable", "snap.hello-snap.svc2.soc
k1.socket", "snap.hello-snap.svc2.sock2.socket"}, []string{"systemctl", "start", "snap.hello-snap.svc1.sock1.socket"}, []string{"systemctl", "--user", "stop", "snap.hello-snap.svc2.sock1.socket"}, []string{"systemctl", "--user", "show", "--property=ActiveState", "snap.hello-snap.svc2.sock1.socket"}, []string{"systemctl", "--user", "stop", "snap.hello-snap.svc2.sock2.socket"}, []st
ring{"systemctl", "--user", "show", "--property=ActiveState", "snap.hello-snap.svc2.sock2.socket"}, []string{"systemctl", "--user", "stop", "snap.hello-snap.svc2.service"}, []string{"systemctl", "--user", "show", "--property=ActiveState", "snap.hello-snap.svc2.service"}, []string{"systemctl", "stop", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket", "snap.h
ello-snap.svc1.service"}, []string{"systemctl", "show", "--property=ActiveState", "snap.hello-snap.svc1.sock1.socket"}, []string{"systemctl", "--no-reload", "disable", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket"}, []string{"systemctl", "daemon-reload"}, []string{"systemctl", "--user", "--global", "--no-reload", "disable", "snap.hello-snap.svc2.sock1.so
cket", "snap.hello-snap.svc2.sock2.socket"}}
... Difference:
...     [8]: []string[5] != []string[4]
...     [9]: []string[4] != []string[5]
```

Signed-off-by: Maciej Borzecki <maciej.borzecki@canonical.com>

* wrappers: fix more unreliable unit tests

The following tests were found to be unreliable on very slow systems:

```
----------------------------------------------------------------------
FAIL: services_test.go:3043: servicesTestSuite.TestAddSnapServicesWithDisabledServices

services_test.go:3102:
    c.Assert(r.Calls(), DeepEquals, [][]string{
        {"systemctl", "daemon-reload"},
    })
... obtained [][]string = [][]string(nil)
... expected [][]string = [][]string{[]string{"systemctl", "daemon-reload"}}
... Difference:
...     [][]string[0] != [][]string[1]

----------------------------------------------------------------------
FAIL: services_test.go:3500: servicesTestSuite.TestNoStartDisabledServices

services_test.go:3545:
    c.Assert(r.Calls(), DeepEquals, [][]string{
        {"systemctl", "--no-reload", "enable", svc2Name},
        {"systemctl", "daemon-reload"},
        {"systemctl", "start", svc2Name},
    })
... obtained [][]string = [][]string(nil)
... expected [][]string = [][]string{[]string{"systemctl", "--no-reload", "enable", "snap.hello-snap.svc2.service"}, []string{"systemctl", "daemon-reload"}, []string{"systemctl", "start", "snap.hello-snap.svc2.service"}}
... Difference:
...     [][]string[0] != [][]string[3]

----------------------------------------------------------------------
FAIL: services_test.go:2867: servicesTestSuite.TestQueryDisabledServices

services_test.go:2924:
    c.Assert(err, IsNil)
... value *errors.errorString = &errors.errorString{s:"cannot get unit \"snap.hello-snap.svc2.service\" status: missing Id, UnitFileState, Type, Names, NeedDaemonReload in ‘systemctl show’ output"} ("cannot get unit \"snap.hello-snap.svc2.service\" status: missing Id, UnitFileState, Type, Names, NeedDaemonReload in ‘systemctl show’ output")

----------------------------------------------------------------------
FAIL: services_test.go:2943: servicesTestSuite.TestQueryDisabledServicesActivatedServices

services_test.go:3023:
    c.Assert(err, IsNil)
... value *errors.errorString = &errors.errorString{s:"cannot get unit \"snap.hello-snap.svc1.service\" status: missing Id, UnitFileState, Type, Names, NeedDaemonReload in ‘systemctl show’ output"} ("cannot get unit \"snap.hello-snap.svc1.service\" status: missing Id, UnitFileState, Type, Names, NeedDaemonReload in ‘systemctl show’ output")

OOPS: 188 passed, 4 FAILED
--- FAIL: TestWrappers (3.50s)

```

Signed-off-by: Maciej Borzecki <maciej.borzecki@canonical.com>

* systemd/systemdtest: handle non service units when mocking outputs

Signed-off-by: Maciej Borzecki <maciej.borzecki@canonical.com>

* wrappers: simplify mocks by using systemdtest helpers

Signed-off-by: Maciej Borzecki <maciej.borzecki@canonical.com>

---------

Signed-off-by: Maciej Borzecki <maciej.borzecki@canonical.com>
2024-02-20 11:00:38 +01:00
Ondrej Kubik
eece24d343 systemd: add support for "NeedDaemonReload" in unit state
Signed-off-by: Ondrej Kubik <ondrej.kubik@canonical.com>
2022-01-12 12:11:29 +00:00
Fred Lotter
8f12a7dd63 systemd: add support for systemd unit alias names
systemd/systemd.go provides a systemctl interface to control and
monitor systemd units (such as services, targets etc...)

The code currently assumes that the unit name given to
systemctl will match the 'Id' DBUS property returned. This
assumption has worked fine so far, but does not work when using
unit name aliases to perform actions.

A unit alias (defined in the [Install] section of the real unit
file with Alias="...") creates a symlink to the real unit file,
when the unit is enabled.

For example:

bash$ systemctl enable reboot.target

Created symlink /etc/systemd/system/ctrl-alt-del.target →
                /lib/systemd/system/reboot.target.

bash$ systemctl show --property Id, Names ctrl-alt-del.target

Id=reboot.target
Names=ctrl-alt-del.target runlevel6.target reboot.target

If this request and result is added to the systemd unit test,
the following error is reported:

FAIL: systemd_test.go:238: SystemdTestSuite.TestStatus

systemd_test.go:277:
"cannot get unit status: queried status of ctrl-alt-del.target
but got status of reboot.target"

The 'Id' DBUS property represents the unit the alias points to.
The 'Names' DBUS property is constructed by systemd and consists
of both the id and all aliases defined for the unit.

The assumption made that the 'Id' is part of the 'Names' list
simplifies the code, and can be verified here (this has been
stable for at least the last 8 years):

src/core/dbus-unit.c:96 property_get_names(...)

- Add the 'Names' DBUS property to the list of requested
  properties when querying the unit status

- Add support to use the "requested" unit name (which could be
  an alias) as the real handle even if the underlying unit Id
  is different.

- Change the unit name verification code to check against the
  'Names' property which will include the id and aliases.

- Fix unit tests affected by the extra property and add a test
  case for aliased names.

Signed-off-by: Fred Lotter <fred.lotter@canonical.com>
2021-11-30 18:08:58 +02:00
Alberto Mardegan
31cae3c30e tests: mock systemctl output for listing mount units 2021-09-10 12:12:19 +03:00
Alberto Mardegan
256dafa6c2 many: address review comments 2021-06-11 11:36:40 +03:00
Alberto Mardegan
8d7b3e68be systemd: create a systemdtest package 2021-06-09 16:53:03 +03:00