* 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>
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>