summaryrefslogtreecommitdiff
diff options
authorPhilip Meulengracht <philip.meulengracht@canonical.com>2023-09-06 22:19:14 +0200
committerMichael Vogt <michael.vogt@gmail.com>2023-09-19 14:53:12 +0200
commit619489d6ea9d4bb9a864b6bfbde5ba2fa77e7af8 (patch)
tree8b362397ea8d02945bd9c70f91c5ac15540bb08a
parent1a58d996f994d82209946e2a3e118227d95638cf (diff)
wrappers: support activated services in QueryDisabledServices/ServicesEnableState
-rw-r--r--overlord/configstate/configcore/corecfg_test.go9
-rw-r--r--overlord/configstate/configcore/vitality_test.go4
-rw-r--r--overlord/managers_test.go20
-rw-r--r--overlord/servicestate/servicemgr_test.go63
-rw-r--r--overlord/snapstate/backend/link.go1
-rw-r--r--tests/main/snap-service-install-mode/svc.v1/meta/snap.yaml6
-rw-r--r--tests/main/snap-service-install-mode/task.yaml12
-rw-r--r--wrappers/services.go174
-rw-r--r--wrappers/services_test.go243
9 files changed, 428 insertions, 104 deletions
diff --git a/overlord/configstate/configcore/corecfg_test.go b/overlord/configstate/configcore/corecfg_test.go
index 9f2fbee937..59466ed7b7 100644
--- a/overlord/configstate/configcore/corecfg_test.go
+++ b/overlord/configstate/configcore/corecfg_test.go
@@ -149,6 +149,15 @@ func (s *configcoreSuite) SetUpTest(c *C) {
s.AddCleanup(osutil.MockMountInfo(""))
s.systemctlOutput = func(args ...string) []byte {
+ if args[0] == "show" {
+ return []byte(fmt.Sprintf(`Type=notify
+Id=%[1]s
+Names=%[1]s
+ActiveState=inactive
+UnitFileState=enabled
+NeedDaemonReload=no
+`, args[len(args)-1]))
+ }
return []byte("ActiveState=inactive")
}
diff --git a/overlord/configstate/configcore/vitality_test.go b/overlord/configstate/configcore/vitality_test.go
index b1ce9a95f9..d532b838a1 100644
--- a/overlord/configstate/configcore/vitality_test.go
+++ b/overlord/configstate/configcore/vitality_test.go
@@ -151,7 +151,7 @@ func (s *vitalitySuite) testConfigureVitalityWithValidSnap(c *C, uc18 bool) {
svcName := "snap.test-snap.foo.service"
c.Check(s.systemctlArgs, DeepEquals, [][]string{
{"daemon-reload"},
- {"is-enabled", "snap.test-snap.foo.service"},
+ {"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", "snap.test-snap.foo.service"},
{"--no-reload", "enable", "snap.test-snap.foo.service"},
{"daemon-reload"},
{"start", "snap.test-snap.foo.service"},
@@ -211,7 +211,7 @@ func (s *vitalitySuite) TestConfigureVitalityWithQuotaGroup(c *C) {
svcName := "snap.test-snap.foo.service"
c.Check(s.systemctlArgs, DeepEquals, [][]string{
{"daemon-reload"},
- {"is-enabled", "snap.test-snap.foo.service"},
+ {"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", "snap.test-snap.foo.service"},
{"--no-reload", "enable", "snap.test-snap.foo.service"},
{"daemon-reload"},
{"start", "snap.test-snap.foo.service"},
diff --git a/overlord/managers_test.go b/overlord/managers_test.go
index e8ca7a04bc..eada1bb338 100644
--- a/overlord/managers_test.go
+++ b/overlord/managers_test.go
@@ -9032,8 +9032,14 @@ WantedBy=multi-user.target
c.Check(cmd, DeepEquals, []string{"show", "--property", "InactiveEnterTimestamp", "snap.test-snap.svc1.service"})
return []byte("InactiveEnterTimestamp=" + t1.Format("Mon 2006-01-02 15:04:05 MST")), nil
case 12:
- c.Check(cmd, DeepEquals, []string{"is-enabled", "snap.test-snap.svc1.service"})
- return []byte("enabled"), nil
+ c.Check(cmd, DeepEquals, []string{"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", "snap.test-snap.svc1.service"})
+ return []byte(`Type=notify
+Id=snap.test-snap.svc1.service
+Names=snap.test-snap.svc1.service
+ActiveState=active
+UnitFileState=enabled
+NeedDaemonReload=no
+`), nil
case 13:
c.Check(cmd, DeepEquals, []string{"start", "snap.test-snap.svc1.service"})
return nil, nil
@@ -9262,8 +9268,14 @@ WantedBy=multi-user.target
c.Check(cmd, DeepEquals, []string{"show", "--property", "InactiveEnterTimestamp", "snap.test-snap.svc1.service"})
return []byte("InactiveEnterTimestamp=" + t1.Format("Mon 2006-01-02 15:04:05 MST")), nil
case 12:
- c.Check(cmd, DeepEquals, []string{"is-enabled", "snap.test-snap.svc1.service"})
- return []byte("enabled"), nil
+ c.Check(cmd, DeepEquals, []string{"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", "snap.test-snap.svc1.service"})
+ return []byte(`Type=notify
+Id=snap.test-snap.svc1.service
+Names=snap.test-snap.svc1.service
+ActiveState=active
+UnitFileState=enabled
+NeedDaemonReload=no
+`), nil
case 13:
// starting the snap fails
c.Check(cmd, DeepEquals, []string{"start", "snap.test-snap.svc1.service"})
diff --git a/overlord/servicestate/servicemgr_test.go b/overlord/servicestate/servicemgr_test.go
index d119cc9e6b..098baeba33 100644
--- a/overlord/servicestate/servicemgr_test.go
+++ b/overlord/servicestate/servicemgr_test.go
@@ -654,8 +654,14 @@ func (s *ensureSnapServiceSuite) TestEnsureSnapServicesWritesServicesFilesAndRes
output: fmt.Sprintf("InactiveEnterTimestamp=%s", slightFuture),
},
{
- expArgs: []string{"is-enabled", "snap.test-snap.svc1.service"},
- output: "enabled",
+ expArgs: []string{"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", "snap.test-snap.svc1.service"},
+ output: `Type=notify
+Id=snap.test-snap.svc1.service
+Names=snap.test-snap.svc1.service
+ActiveState=active
+UnitFileState=enabled
+NeedDaemonReload=no
+`,
},
{
expArgs: []string{"start", "snap.test-snap.svc1.service"},
@@ -735,9 +741,14 @@ func (s *ensureSnapServiceSuite) TestEnsureSnapServicesWritesServicesFilesButDoe
},
// the service is disabled
{
- expArgs: []string{"is-enabled", "snap.test-snap.svc1.service"},
- output: "disabled",
- err: systemctlDisabledServiceError{},
+ expArgs: []string{"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", "snap.test-snap.svc1.service"},
+ output: `Type=notify
+Id=snap.test-snap.svc1.service
+Names=snap.test-snap.svc1.service
+ActiveState=active
+UnitFileState=disabled
+NeedDaemonReload=no
+`,
},
// then we don't restart the service even though it was killed
})
@@ -953,8 +964,14 @@ func (s *ensureSnapServiceSuite) TestEnsureSnapServicesSimpleRewritesServicesFil
output: fmt.Sprintf("InactiveEnterTimestamp=%s", t1Str),
},
{
- expArgs: []string{"is-enabled", "snap.test-snap.svc1.service"},
- output: "enabled",
+ expArgs: []string{"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", "snap.test-snap.svc1.service"},
+ output: `Type=notify
+Id=snap.test-snap.svc1.service
+Names=snap.test-snap.svc1.service
+ActiveState=active
+UnitFileState=enabled
+NeedDaemonReload=no
+`,
},
{
expArgs: []string{"start", "snap.test-snap.svc1.service"},
@@ -1040,8 +1057,14 @@ func (s *ensureSnapServiceSuite) TestEnsureSnapServicesSimpleRewritesServicesFil
output: fmt.Sprintf("InactiveEnterTimestamp=%s", t1Str),
},
{
- expArgs: []string{"is-enabled", "snap.test-snap.svc1.service"},
- output: "enabled",
+ expArgs: []string{"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", "snap.test-snap.svc1.service"},
+ output: `Type=notify
+Id=snap.test-snap.svc1.service
+Names=snap.test-snap.svc1.service
+ActiveState=active
+UnitFileState=enabled
+NeedDaemonReload=no
+`,
},
{
expArgs: []string{"start", "snap.test-snap.svc1.service"},
@@ -1113,8 +1136,14 @@ func (s *ensureSnapServiceSuite) TestEnsureSnapServicesSimpleRewritesServicesFil
output: fmt.Sprintf("InactiveEnterTimestamp=%s", slightFuture),
},
{
- expArgs: []string{"is-enabled", "snap.test-snap.svc1.service"},
- output: "enabled",
+ expArgs: []string{"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", "snap.test-snap.svc1.service"},
+ output: `Type=notify
+Id=snap.test-snap.svc1.service
+Names=snap.test-snap.svc1.service
+ActiveState=active
+UnitFileState=enabled
+NeedDaemonReload=no
+`,
},
{
expArgs: []string{"start", "snap.test-snap.svc1.service"},
@@ -1287,8 +1316,14 @@ func (s *ensureSnapServiceSuite) TestEnsureSnapServicesWritesServicesFilesAndRes
output: fmt.Sprintf("InactiveEnterTimestamp=%s", slightFuture),
},
{
- expArgs: []string{"is-enabled", "snap.test-snap.svc1.service"},
- output: "enabled",
+ expArgs: []string{"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", "snap.test-snap.svc1.service"},
+ output: `Type=notify
+Id=snap.test-snap.svc1.service
+Names=snap.test-snap.svc1.service
+ActiveState=active
+UnitFileState=enabled
+NeedDaemonReload=no
+`,
},
{
expArgs: []string{"start", "snap.test-snap.svc1.service"},
@@ -1369,7 +1404,7 @@ func (s *ensureSnapServiceSuite) TestEnsureSnapServicesWritesServicesFilesAndTri
output: fmt.Sprintf("InactiveEnterTimestamp=%s", slightFuture),
},
{
- expArgs: []string{"is-enabled", "snap.test-snap.svc1.service"},
+ expArgs: []string{"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", "snap.test-snap.svc1.service"},
err: fmt.Errorf("systemd is having a bad day"),
},
})
diff --git a/overlord/snapstate/backend/link.go b/overlord/snapstate/backend/link.go
index f0ebfd504e..f615462bd1 100644
--- a/overlord/snapstate/backend/link.go
+++ b/overlord/snapstate/backend/link.go
@@ -319,6 +319,7 @@ func (b Backend) UnlinkSnap(info *snap.Info, linkCtx LinkContext, meter progress
// ServicesEnableState returns the current enabled/disabled states of a snap's
// services, primarily for committing before snap removal/disable/revert.
+// XXX: Not able to find where this is actually used?
func (b Backend) ServicesEnableState(info *snap.Info, meter progress.Meter) (map[string]bool, error) {
return wrappers.ServicesEnableState(info, meter)
}
diff --git a/tests/main/snap-service-install-mode/svc.v1/meta/snap.yaml b/tests/main/snap-service-install-mode/svc.v1/meta/snap.yaml
index 561ee57e4e..f9b4dfb9d2 100644
--- a/tests/main/snap-service-install-mode/svc.v1/meta/snap.yaml
+++ b/tests/main/snap-service-install-mode/svc.v1/meta/snap.yaml
@@ -8,8 +8,12 @@ apps:
command: sleep infinity
daemon: simple
install-mode: disable
- timer: 0:00~24:00/96 # every 15m
svc-enabled-by-hook:
command: sleep infinity
daemon: simple
install-mode: disable
+ svc-enabled-by-timer:
+ command: sleep infinity
+ daemon: simple
+ install-mode: disable
+ timer: 0:00~24:00/96 # every 15m
diff --git a/tests/main/snap-service-install-mode/task.yaml b/tests/main/snap-service-install-mode/task.yaml
index ab6e37924f..b80cd0ba66 100644
--- a/tests/main/snap-service-install-mode/task.yaml
+++ b/tests/main/snap-service-install-mode/task.yaml
@@ -14,9 +14,10 @@ execute: |
snap services | MATCH 'svc.svc1\s+enabled\s+active'
snap services | MATCH 'svc.svc2\s+disabled\s+inactive'
snap services | MATCH 'svc.svc-enabled-by-hook\s+enabled\s+active'
+ snap services | MATCH 'svc.svc-enabled-by-timer\s+disabled\s+inactive'
# ensure that the timer service unit is also disabled by poking systemd
- systemctl show --property=UnitFileState snap.svc.svc2.timer | grep "disabled"
+ systemctl show --property=UnitFileState snap.svc.svc-enabled-by-timer.timer | grep "disabled"
echo "And after a refresh nothing changes"
"$TESTSTOOLS"/snaps-state install-local ./svc.v1
@@ -25,15 +26,20 @@ execute: |
snap services | MATCH 'svc.svc-enabled-by-hook\s+enabled\s+active'
# ensure again that the timer service unit is still disabled by poking systemd
- systemctl show --property=UnitFileState snap.svc.svc2.timer | grep "disabled"
+ systemctl show --property=UnitFileState snap.svc.svc-enabled-by-timer.timer | grep "disabled"
echo "But install-mode: disable services can be enabled"
snap start --enable svc.svc2
snap services | MATCH 'svc.svc2\s+enabled\s+active'
- echo "And after a refresh the service stays enabled"
+ echo "And install-mode: disable activated services can be enabled"
+ snap start --enable svc.svc-enabled-by-timer
+ snap services | MATCH 'svc.svc-enabled-by-timer\s+enabled\s+inactive'
+
+ echo "And after a refresh the services stays enabled"
"$TESTSTOOLS"/snaps-state install-local ./svc.v1
snap services | MATCH 'svc.svc2\s+enabled\s+active'
+ snap services | MATCH 'svc.svc-enabled-by-timer\s+enabled\s+inactive'
# Now test with a refresh from svc.v1 to svc.v2
# svc.v2 has:
diff --git a/wrappers/services.go b/wrappers/services.go
index db59e10f87..0af94114f3 100644
--- a/wrappers/services.go
+++ b/wrappers/services.go
@@ -1016,31 +1016,6 @@ func StopServices(apps []*snap.AppInfo, flags *StopServicesFlags, reason snap.Se
return nil
}
-// ServicesEnableState returns a map of service names from the given snap,
-// together with their enable/disable status.
-func ServicesEnableState(s *snap.Info, inter Interacter) (map[string]bool, error) {
- sysd := systemd.New(systemd.SystemMode, inter)
-
- // loop over all services in the snap, querying systemd for the current
- // systemd state of the snaps
- snapSvcsState := make(map[string]bool, len(s.Apps))
- for name, app := range s.Apps {
- if !app.IsService() {
- continue
- }
- // FIXME: handle user daemons
- if app.DaemonScope != snap.SystemDaemon {
- continue
- }
- state, err := sysd.IsEnabled(app.ServiceName())
- if err != nil {
- return nil, err
- }
- snapSvcsState[name] = state
- }
- return snapSvcsState, nil
-}
-
// RemoveQuotaGroup ensures that the slice file for a quota group is removed. It
// assumes that the slice corresponding to the group is not in use anymore by
// any services or sub-groups of the group when it is invoked. To remove a group
@@ -1813,6 +1788,91 @@ func generateOnCalendarSchedules(schedule []*timeutil.Schedule) []string {
return calendarEvents
}
+// serviceStatus represents the status of a service, and any of its activation
+// service units. It also provides a method isEnabled which can determine the true
+// enable status for services that are activated.
+type serviceStatus struct {
+ name string
+ service *systemd.UnitStatus
+ activators []*systemd.UnitStatus
+ slotEnabled bool
+}
+
+func (s *serviceStatus) isEnabled() bool {
+ // If the service is slot activated, it cannot be disabled and thus always
+ // is enabled.
+ if s.slotEnabled {
+ return true
+ }
+
+ // If there are no activator units, then return status of the
+ // primary service.
+ if len(s.activators) == 0 {
+ return s.service.Enabled
+ }
+
+ // Just a single of those activators need to be enabled for us
+ // to report the service as enabled.
+ for _, a := range s.activators {
+ if a.Enabled {
+ return true
+ }
+ }
+ return false
+}
+
+func appServiceUnitsMany(apps []*snap.AppInfo) []string {
+ var allUnits []string
+ for _, app := range apps {
+ if !app.IsService() {
+ continue
+ }
+ // TODO: handle user daemons
+ if app.DaemonScope != snap.SystemDaemon {
+ continue
+ }
+ svc, activators := serviceUnits(app)
+ allUnits = append(allUnits, svc)
+ allUnits = append(allUnits, activators...)
+ }
+ return allUnits
+}
+
+func queryServiceStatusMany(sysd systemd.Systemd, apps []*snap.AppInfo) ([]*serviceStatus, error) {
+ allUnits := appServiceUnitsMany(apps)
+ unitStatuses, err := sysd.Status(allUnits)
+ if err != nil {
+ return nil, err
+ }
+
+ var appStatuses []*serviceStatus
+ var statusIndex int
+ for _, app := range apps {
+ if !app.IsService() {
+ continue
+ }
+ // TODO: handle user daemons
+ if app.DaemonScope != snap.SystemDaemon {
+ continue
+ }
+
+ // This builds on the principle that sysd.Status returns service unit statuses
+ // in the exact same order we requested them in.
+ _, activators := serviceUnits(app)
+ svcSt := &serviceStatus{
+ name: app.Name,
+ service: unitStatuses[statusIndex],
+ slotEnabled: serviceIsSlotActivated(app),
+ }
+ if len(activators) > 0 {
+ svcSt.activators = unitStatuses[statusIndex+1 : statusIndex+1+len(activators)]
+ }
+ appStatuses = append(appStatuses, svcSt)
+ statusIndex += 1 + len(activators)
+ }
+ return appStatuses, nil
+}
+
type RestartServicesFlags struct {
// Reload set if we might need to reload the service definitions.
Reload bool
@@ -1831,49 +1891,45 @@ type RestartServicesFlags struct {
// The list of explicitServices needs to use systemd unit names.
// TODO: change explicitServices format to be less unusual, more consistent
// (introduce AppRef?)
-func RestartServices(svcs []*snap.AppInfo, explicitServices []string,
+func RestartServices(apps []*snap.AppInfo, explicitServices []string,
flags *RestartServicesFlags, inter Interacter, tm timings.Measurer) error {
if flags == nil {
flags = &RestartServicesFlags{}
}
sysd := systemd.New(systemd.SystemMode, inter)
- unitNames := make([]string, 0, len(svcs))
- for _, srv := range svcs {
- // they're *supposed* to be all services, but checking doesn't hurt
- if !srv.IsService() {
- continue
- }
- unitNames = append(unitNames, srv.ServiceName())
- }
-
- unitStatuses, err := sysd.Status(unitNames)
+ // Get service statuses for each of the apps
+ sts, err := queryServiceStatusMany(sysd, apps)
if err != nil {
return err
}
- for _, unit := range unitStatuses {
+ for _, st := range sts {
+ unitName := st.service.Name
+ unitActive := st.service.Active
+ unitEnabled := st.isEnabled()
+
// If the unit was explicitly mentioned in the command line, restart it
// even if it is disabled; otherwise, we only restart units which are
// currently enabled or running. Reference:
// https://forum.snapcraft.io/t/command-line-interface-to-manipulate-services/262/47
- if !unit.Active && !strutil.ListContains(explicitServices, unit.Name) {
+ if !unitActive && !strutil.ListContains(explicitServices, unitName) {
if !flags.AlsoEnabledNonActive {
- logger.Noticef("not restarting inactive unit %s", unit.Name)
+ logger.Noticef("not restarting inactive unit %s", unitName)
continue
- } else if !unit.Enabled {
- logger.Noticef("not restarting disabled and inactive unit %s", unit.Name)
+ } else if !unitEnabled {
+ logger.Noticef("not restarting disabled and inactive unit %s", unitName)
continue
}
}
var err error
- timings.Run(tm, "restart-service", fmt.Sprintf("restart service %s", unit.Name), func(nested timings.Measurer) {
+ timings.Run(tm, "restart-service", fmt.Sprintf("restart service %s", unitName), func(nested timings.Measurer) {
if flags.Reload {
- err = sysd.ReloadOrRestart([]string{unit.Name})
+ err = sysd.ReloadOrRestart([]string{unitName})
} else {
// note: stop followed by start, not just 'restart'
- err = sysd.Restart([]string{unit.Name})
+ err = sysd.Restart([]string{unitName})
}
})
if err != nil {
@@ -1884,21 +1940,37 @@ func RestartServices(svcs []*snap.AppInfo, explicitServices []string,
return nil
}
+// ServicesEnableState returns a map of service names from the given snap,
+// together with their enable/disable status.
+func ServicesEnableState(s *snap.Info, inter Interacter) (map[string]bool, error) {
+ sysd := systemd.New(systemd.SystemMode, inter)
+ sts, err := queryServiceStatusMany(sysd, s.Services())
+ if err != nil {
+ return nil, err
+ }
+
+ // loop over all services in the snap, storing the current enable status
+ snapSvcsState := make(map[string]bool, len(sts))
+ for _, st := range sts {
+ snapSvcsState[st.name] = st.isEnabled()
+ }
+ return snapSvcsState, nil
+}
+
// QueryDisabledServices returns a list of all currently disabled snap services
// in the snap.
func QueryDisabledServices(info *snap.Info, pb progress.Meter) ([]string, error) {
- // save the list of services that are in the disabled state before unlinking
- // and thus removing the snap services
- snapSvcStates, err := ServicesEnableState(info, pb)
+ sysd := systemd.New(systemd.SystemMode, pb)
+ sts, err := queryServiceStatusMany(sysd, info.Services())
if err != nil {
return nil, err
}
- disabledSnapSvcs := []string{}
// add all disabled services to the list
- for svc, isEnabled := range snapSvcStates {
- if !isEnabled {
- disabledSnapSvcs = append(disabledSnapSvcs, svc)
+ disabledSnapSvcs := []string{}
+ for _, st := range sts {
+ if !st.isEnabled() {
+ disabledSnapSvcs = append(disabledSnapSvcs, st.name)
}
}
diff --git a/wrappers/services_test.go b/wrappers/services_test.go
index 6858698b03..16afe95875 100644
--- a/wrappers/services_test.go
+++ b/wrappers/services_test.go
@@ -2865,13 +2865,12 @@ apps:
func (s *servicesTestSuite) TestServicesEnableState(c *C) {
info := snaptest.MockSnap(c, packageHello+`
- svc2:
+ svc1:
command: bin/hello
daemon: forking
- svc3:
+ svc2:
command: bin/hello
daemon: simple
- daemon-scope: user
`, &snap.SideInfo{Revision: snap.R(12)})
svc1File := "snap.hello-snap.svc1.service"
svc2File := "snap.hello-snap.svc2.service"
@@ -2884,18 +2883,31 @@ func (s *servicesTestSuite) TestServicesEnableState(c *C) {
fi
case "$1" in
- is-enabled)
- case "$2" in
- "snap.hello-snap.svc1.service")
- echo "disabled"
- exit 1
- ;;
- "snap.hello-snap.svc2.service")
- echo "enabled"
+ show)
+ case "$3" in
+ "snap.hello-snap.svc1.service"|snap.hello-snap.svc2.service)
+ for SVC in $3 $4
+ do
+ echo "Type=notify"
+ echo "Id=$SVC"
+ echo "Names=$SVC"
+ echo "NeedDaemonReload=no"
+ if [ "$SVC" = "snap.hello-snap.svc1.service" ]; then
+ echo "ActiveState=active"
+ echo "UnitFileState=enabled"
+ elif [ "$SVC" = "snap.hello-snap.svc2.service" ]; then
+ echo "ActiveState=inactive"
+ echo "UnitFileState=disabled"
+ fi
+ if [ "$SVC" != "$4" ]; then
+ echo ""
+ fi
+ done
exit 0
;;
*)
- echo "unexpected is-enabled of service $2"
+ shift 2
+ echo "unexpected show of service $*"
exit 2
;;
esac
@@ -2911,17 +2923,17 @@ func (s *servicesTestSuite) TestServicesEnableState(c *C) {
c.Assert(err, IsNil)
c.Assert(states, DeepEquals, map[string]bool{
- "svc1": false,
- "svc2": true,
+ "svc1": true,
+ "svc2": false,
})
// the calls could be out of order in the list, since iterating over a map
// is non-deterministic, so manually check each call
- c.Assert(r.Calls(), HasLen, 2)
+ c.Assert(r.Calls(), HasLen, 1)
for _, call := range r.Calls() {
- c.Assert(call, HasLen, 3)
- c.Assert(call[:2], DeepEquals, []string{"systemctl", "is-enabled"})
- switch call[2] {
+ c.Assert(call, HasLen, 5)
+ c.Assert(call[:2], DeepEquals, []string{"systemctl", "show"})
+ switch call[3] {
case svc1File, svc2File:
default:
c.Errorf("unknown service for systemctl call: %s", call[2])
@@ -2941,14 +2953,14 @@ func (s *servicesTestSuite) TestServicesEnableStateFail(c *C) {
fi
case "$1" in
- is-enabled)
- case "$2" in
+ show)
+ case "$3" in
"snap.hello-snap.svc1.service")
echo "whoops"
exit 1
;;
*)
- echo "unexpected is-enabled of service $2"
+ echo "unexpected is-enabled of service $3"
exit 2
;;
esac
@@ -2961,13 +2973,189 @@ func (s *servicesTestSuite) TestServicesEnableStateFail(c *C) {
defer r.Restore()
_, err := wrappers.ServicesEnableState(info, progress.Null)
- c.Assert(err, ErrorMatches, ".*is-enabled snap.hello-snap.svc1.service\\] failed with exit status 1: whoops\n.*")
+ c.Assert(err, ErrorMatches, ".*show --property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload snap.hello-snap.svc1.service\\] failed with exit status 1: whoops\n.*")
c.Assert(r.Calls(), DeepEquals, [][]string{
- {"systemctl", "is-enabled", svc1File},
+ {"systemctl", "show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", svc1File},
})
}
+func (s *servicesTestSuite) TestQueryDisabledServices(c *C) {
+ info := snaptest.MockSnap(c, packageHelloNoSrv+`
+ svc1:
+ daemon: simple
+ command: bin/hello
+ svc2:
+ daemon: simple
+ command: bin/hello
+`, &snap.SideInfo{Revision: snap.R(12)})
+ s.systemctlRestorer()
+ // This will mock the following:
+ // svc 1 will be reported as disabled
+ // svc 2 will be reported as enabled
+ r := testutil.MockCommand(c, "systemctl", `#!/bin/sh
+ if [ "$1" = "--root" ]; then
+ # shifting by 2 also drops the temp dir arg to --root
+ shift 2
+ fi
+
+ case "$1" in
+ show)
+ case "$3" in
+ "snap.hello-snap.svc1.service"|"snap.hello-snap.svc2.service")
+ for SVC in $3 $4
+ do
+ echo "Type=notify"
+ echo "Id=$SVC"
+ echo "Names=$SVC"
+ echo "NeedDaemonReload=no"
+ if [ "$SVC" = "snap.hello-snap.svc1.service" ]; then
+ echo "ActiveState=inactive"
+ echo "UnitFileState=disabled"
+ elif [ "$SVC" = "snap.hello-snap.svc2.service" ]; then
+ echo "ActiveState=inactive"
+ echo "UnitFileState=enabled"
+ fi
+ if [ "$SVC" != "$4" ]; then
+ echo ""
+ fi
+ done
+ exit 0
+ ;;
+ *)
+ shift 2
+ echo "unexpected show of service $*"
+ exit 2
+ ;;
+ esac
+ ;;
+ *)
+ echo "unexpected op $*"
+ exit 2
+ esac
+ `)
+ defer r.Restore()
+
+ disabledSvcs, err := wrappers.QueryDisabledServices(info, progress.Null)
+ c.Assert(err, IsNil)
+
+ // ensure svc1 was reported as disabled
+ c.Assert(disabledSvcs, DeepEquals, []string{"svc1"})
+
+ // the calls could be out of order in the list, since iterating over a map
+ // is non-deterministic, so manually check each call
+ c.Assert(r.Calls(), HasLen, 1)
+ for _, call := range r.Calls() {
+ c.Assert(call, HasLen, 5)
+ c.Assert(call[:2], DeepEquals, []string{"systemctl", "show"})
+ switch call[3] {
+ case "snap.hello-snap.svc1.service", "snap.hello-snap.svc2.service":
+ default:
+ c.Errorf("unknown service for systemctl call: %s", call[2])
+ }
+ }
+}
+
+func (s *servicesTestSuite) TestQueryDisabledServicesActivatedServices(c *C) {
+ info := snaptest.MockSnap(c, packageHelloNoSrv+`
+ svc1:
+ daemon: simple
+ plugs: [network-bind]
+ sockets:
+ sock1:
+ listen-stream: $SNAP_COMMON/sock1.socket
+ socket-mode: 0666
+ sock2:
+ listen-stream: $SNAP_DATA/sock2.socket
+ svc2:
+ daemon: simple
+ command: bin/hello
+`, &snap.SideInfo{Revision: snap.R(12)})
+ s.systemctlRestorer()
+ // This will mock the following:
+ // svc 1 will be reported as static
+ // svc 2 will be reported as enabled
+ // svc 1 has two socket activations that both will be reported as disabled
+ r := testutil.MockCommand(c, "systemctl", `#!/bin/sh
+ if [ "$1" = "--root" ]; then
+ # shifting by 2 also drops the temp dir arg to --root
+ shift 2
+ fi
+
+ case "$1" in
+ show)
+ case "$3" in
+ "snap.hello-snap.svc1.service"|"snap.hello-snap.svc2.service")
+ for SVC in $3 $4
+ do
+ echo "Type=notify"
+ echo "Id=$SVC"
+ echo "Names=$SVC"
+ echo "NeedDaemonReload=no"
+ if [ "$SVC" = "snap.hello-snap.svc1.service" ]; then
+ echo "ActiveState=inactive"
+ echo "UnitFileState=static"
+ elif [ "$SVC" = "snap.hello-snap.svc2.service" ]; then
+ echo "ActiveState=inactive"
+ echo "UnitFileState=enabled"
+ fi
+ if [ "$SVC" != "$4" ]; then
+ echo ""
+ fi
+ done
+ exit 0
+ ;;
+ "snap.hello-snap.svc1.sock1.socket"|"snap.hello-snap.svc1.sock2.socket")
+ echo "Type=notify"
+ echo "Id=snap.hello-snap.svc1.sock1.socket"
+ echo "Names=snap.hello-snap.svc1.sock1.socket"
+ echo "ActiveState=inactive"
+ echo "UnitFileState=disabled"
+ echo "NeedDaemonReload=no"
+ echo ""
+ echo "Type=notify"
+ echo "Id=snap.hello-snap.svc1.sock2.socket"
+ echo "Names=snap.hello-snap.svc1.sock2.socket"
+ echo "ActiveState=inactive"
+ echo "UnitFileState=disabled"
+ echo "NeedDaemonReload=no"
+ exit 0
+ ;;
+ *)
+ shift 2
+ echo "unexpected show of service $*"
+ exit 2
+ ;;
+ esac
+ ;;
+ *)
+ echo "unexpected op $*"
+ exit 2
+ esac
+ `)
+ defer r.Restore()
+
+ disabledSvcs, err := wrappers.QueryDisabledServices(info, progress.Null)
+ c.Assert(err, IsNil)
+
+ // ensure svc1 were reported as disabled
+ c.Assert(disabledSvcs, DeepEquals, []string{"svc1"})
+
+ // the calls could be out of order in the list, since iterating over a map
+ // is non-deterministic, so manually check each call
+ c.Assert(r.Calls(), HasLen, 2)
+ for _, call := range r.Calls() {
+ c.Assert(call, HasLen, 5)
+ c.Assert(call[:2], DeepEquals, []string{"systemctl", "show"})
+ switch call[3] {
+ case "snap.hello-snap.svc1.service", "snap.hello-snap.svc2.service", "snap.hello-snap.svc3.service":
+ case "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket":
+ default:
+ c.Errorf("unknown service for systemctl call: %s", call[2])
+ }
+ }
+}
+
func (s *servicesTestSuite) TestAddSnapServicesWithDisabledServices(c *C) {
info := snaptest.MockSnap(c, packageHello+`
svc2:
@@ -4667,8 +4855,7 @@ apps:
sort.Sort(snap.AppInfoBySnapApp(services))
c.Assert(wrappers.RestartServices(services, nil, &wrappers.RestartServicesFlags{AlsoEnabledNonActive: true}, progress.Null, s.perfTimings), IsNil)
c.Check(s.sysdLog, DeepEquals, [][]string{
- {"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload",
- srvFile1, srvFile2, srvFile3, srvFile4},
+ {"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", srvFile1, srvFile2, srvFile3, srvFile4},
{"stop", srvFile1},
{"show", "--property=ActiveState", srvFile1},
{"start", srvFile1},
@@ -4685,8 +4872,7 @@ apps:
s.sysdLog = nil
c.Assert(wrappers.RestartServices(services, []string{srvFile4}, &wrappers.RestartServicesFlags{AlsoEnabledNonActive: true}, progress.Null, s.perfTimings), IsNil)
c.Check(s.sysdLog, DeepEquals, [][]string{
- {"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload",
- srvFile1, srvFile2, srvFile3, srvFile4},
+ {"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", srvFile1, srvFile2, srvFile3, srvFile4},
{"stop", srvFile1},
{"show", "--property=ActiveState", srvFile1},
{"start", srvFile1},
@@ -4705,8 +4891,7 @@ apps:
s.sysdLog = nil
c.Assert(wrappers.RestartServices(services, nil, &wrappers.RestartServicesFlags{AlsoEnabledNonActive: false}, progress.Null, s.perfTimings), IsNil)
c.Check(s.sysdLog, DeepEquals, [][]string{
- {"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload",
- srvFile1, srvFile2, srvFile3, srvFile4},
+ {"show", "--property=Id,ActiveState,UnitFileState,Type,Names,NeedDaemonReload", srvFile1, srvFile2, srvFile3, srvFile4},
{"stop", srvFile1},
{"show", "--property=ActiveState", srvFile1},
{"start", srvFile1},