summaryrefslogtreecommitdiff
diff options
authorMichael Vogt <mvo@ubuntu.com>2017-05-23 19:36:37 +0200
committerMichael Vogt <mvo@ubuntu.com>2017-05-23 19:36:37 +0200
commit8d3baef0b870208aa94a286c1eb64b978e930ee8 (patch)
tree067274a898d13a5438a5aaf0993b0052863f65c3
parent27619cf438088e23232a6e86f27fbf0c98f473dd (diff)
parentd331e4fb1b9cf9fb86a5966b220be4c5009ebc15 (diff)
Merge remote-tracking branch 'upstream/master' into flock-non-lockingflock-non-locking
-rw-r--r--cmd/snap/cmd_auto_import.go2
-rw-r--r--overlord/patch/patch5.go9
-rw-r--r--overlord/snapstate/aliasesv2.go9
-rw-r--r--overlord/snapstate/backend.go4
-rw-r--r--overlord/snapstate/backend/link.go8
-rw-r--r--overlord/snapstate/backend_test.go48
-rw-r--r--overlord/snapstate/handlers.go12
-rw-r--r--overlord/snapstate/snapmgr.go11
-rw-r--r--overlord/snapstate/snapstate_test.go449
-rw-r--r--snap/info.go38
-rw-r--r--snap/info_test.go126
-rw-r--r--tests/main/snap-multi-service-failing/task.yaml2
-rw-r--r--wrappers/binaries.go7
-rw-r--r--wrappers/services.go21
-rw-r--r--wrappers/services_test.go12
15 files changed, 474 insertions, 284 deletions
diff --git a/cmd/snap/cmd_auto_import.go b/cmd/snap/cmd_auto_import.go
index cac935f57c..3712024ad8 100644
--- a/cmd/snap/cmd_auto_import.go
+++ b/cmd/snap/cmd_auto_import.go
@@ -195,7 +195,7 @@ func tryMount(deviceName string) (string, error) {
os.Setenv("PATH", "/usr/sbin:/usr/bin:/sbin:/bin")
}
// not using syscall.Mount() because we don't know the fs type in advance
- cmd := exec.Command("mount", "-o", "ro", "--make-private", deviceName, tmpMountTarget)
+ cmd := exec.Command("mount", "-t", "ext4,vfat", "-o", "ro", "--make-private", deviceName, tmpMountTarget)
if output, err := cmd.CombinedOutput(); err != nil {
os.Remove(tmpMountTarget)
err = fmt.Errorf("cannot mount %s: %s", deviceName, osutil.OutputErr(output, err))
diff --git a/overlord/patch/patch5.go b/overlord/patch/patch5.go
index cf4df82505..419d3fb000 100644
--- a/overlord/patch/patch5.go
+++ b/overlord/patch/patch5.go
@@ -56,12 +56,13 @@ func patch5(st *state.State) error {
return err
}
- if len(info.Apps) == 0 {
- logger.Debugf("patch 5: skipping for %q: no apps", snapName)
+ svcs := info.Services()
+ if len(svcs) == 0 {
+ logger.Debugf("patch 5: skipping for %q: no services", snapName)
continue
}
- err = wrappers.StopSnapServices(info, log)
+ err = wrappers.StopServices(svcs, log)
if err != nil {
return err
}
@@ -71,7 +72,7 @@ func patch5(st *state.State) error {
return err
}
- err = wrappers.StartSnapServices(info, log)
+ err = wrappers.StartServices(svcs, log)
if err != nil {
return err
}
diff --git a/overlord/snapstate/aliasesv2.go b/overlord/snapstate/aliasesv2.go
index 30ab3d74cb..348acbcd15 100644
--- a/overlord/snapstate/aliasesv2.go
+++ b/overlord/snapstate/aliasesv2.go
@@ -30,7 +30,6 @@ import (
"github.com/snapcore/snapd/overlord/state"
"github.com/snapcore/snapd/snap"
"github.com/snapcore/snapd/strutil"
- "github.com/snapcore/snapd/wrappers"
)
// AliasTarget carries the targets of an alias in the context of snap.
@@ -214,7 +213,7 @@ func refreshAliases(st *state.State, info *snap.Info, curAliases map[string]*Ali
newAliases = make(map[string]*AliasTarget, len(autoAliases))
// apply the current auto-aliases
for alias, target := range autoAliases {
- if app := info.Apps[target]; app == nil || wrappers.IsService(app) {
+ if app := info.Apps[target]; app == nil || app.IsService() {
// non-existing app or a daemon
continue
}
@@ -226,7 +225,7 @@ func refreshAliases(st *state.State, info *snap.Info, curAliases map[string]*Ali
if curTarget.Manual == "" {
continue
}
- if app := info.Apps[curTarget.Manual]; app == nil || wrappers.IsService(app) {
+ if app := info.Apps[curTarget.Manual]; app == nil || app.IsService() {
// non-existing app or daemon
continue
}
@@ -402,7 +401,7 @@ func reenableAliases(info *snap.Info, curAliases map[string]*AliasTarget, disabl
}
for alias, manual := range disabledManual {
- if app := info.Apps[manual]; app == nil || wrappers.IsService(app) {
+ if app := info.Apps[manual]; app == nil || app.IsService() {
// not a non-daemon app presently
continue
}
@@ -562,7 +561,7 @@ func Alias(st *state.State, snapName, app, alias string) (*state.TaskSet, error)
// manualAliases returns newAliases with a manual alias to target setup over
// curAliases.
func manualAlias(info *snap.Info, curAliases map[string]*AliasTarget, target, alias string) (newAliases map[string]*AliasTarget, err error) {
- if app := info.Apps[target]; app == nil || wrappers.IsService(app) {
+ if app := info.Apps[target]; app == nil || app.IsService() {
var reason string
if app == nil {
reason = fmt.Sprintf("target application %q does not exist", target)
diff --git a/overlord/snapstate/backend.go b/overlord/snapstate/backend.go
index 0d86a320b2..6fcd9fb500 100644
--- a/overlord/snapstate/backend.go
+++ b/overlord/snapstate/backend.go
@@ -50,8 +50,8 @@ type managerBackend interface {
SetupSnap(snapFilePath string, si *snap.SideInfo, meter progress.Meter) error
CopySnapData(newSnap, oldSnap *snap.Info, meter progress.Meter) error
LinkSnap(info *snap.Info) error
- StartSnapServices(info *snap.Info, meter progress.Meter) error
- StopSnapServices(info *snap.Info, meter progress.Meter) error
+ StartServices(svcs []*snap.AppInfo, meter progress.Meter) error
+ StopServices(svcs []*snap.AppInfo, meter progress.Meter) error
// the undoers for install
UndoSetupSnap(s snap.PlaceInfo, typ snap.Type, meter progress.Meter) error
diff --git a/overlord/snapstate/backend/link.go b/overlord/snapstate/backend/link.go
index 3560f0ae36..d254664eda 100644
--- a/overlord/snapstate/backend/link.go
+++ b/overlord/snapstate/backend/link.go
@@ -74,12 +74,12 @@ func (b Backend) LinkSnap(info *snap.Info) error {
return updateCurrentSymlinks(info)
}
-func (b Backend) StartSnapServices(info *snap.Info, meter progress.Meter) error {
- return wrappers.StartSnapServices(info, meter)
+func (b Backend) StartServices(apps []*snap.AppInfo, meter progress.Meter) error {
+ return wrappers.StartServices(apps, meter)
}
-func (b Backend) StopSnapServices(info *snap.Info, meter progress.Meter) error {
- return wrappers.StopSnapServices(info, meter)
+func (b Backend) StopServices(apps []*snap.AppInfo, meter progress.Meter) error {
+ return wrappers.StopServices(apps, meter)
}
func generateWrappers(s *snap.Info) error {
diff --git a/overlord/snapstate/backend_test.go b/overlord/snapstate/backend_test.go
index 99da079ae3..112b063938 100644
--- a/overlord/snapstate/backend_test.go
+++ b/overlord/snapstate/backend_test.go
@@ -175,6 +175,8 @@ func (f *fakeStore) ListRefresh(cands []*store.RefreshCandidate, _ *auth.UserSta
var name string
switch snapID {
+ case "services-snap-id":
+ name = "services-snap"
case "some-snap-id":
name = "some-snap"
case "core-snap-id":
@@ -315,15 +317,30 @@ func (f *fakeSnappyBackend) ReadInfo(name string, si *snap.SideInfo) (*snap.Info
SuggestedName: name,
SideInfo: *si,
Architectures: []string{"all"},
+ Type: snap.TypeApp,
}
- info.Type = snap.TypeApp
- if name == "gadget" {
- info.Type = snap.TypeGadget
+ if strings.Contains(name, "alias-snap") {
+ name = "alias-snap"
}
- if name == "core" {
+ switch name {
+ case "gadget":
+ info.Type = snap.TypeGadget
+ case "core":
info.Type = snap.TypeOS
- }
- if strings.Contains(name, "alias-snap") {
+ case "services-snap":
+ var err error
+ info, err = snap.InfoFromSnapYaml([]byte(`name: services-snap
+apps:
+ svc1:
+ daemon: simple
+ svc2:
+ daemon: simple
+`))
+ if err != nil {
+ panic(err)
+ }
+ info.SideInfo = *si
+ case "alias-snap":
var err error
info, err = snap.InfoFromSnapYaml([]byte(`name: alias-snap
apps:
@@ -340,6 +357,7 @@ apps:
}
info.SideInfo = *si
}
+
return info, nil
}
@@ -397,18 +415,28 @@ func (f *fakeSnappyBackend) LinkSnap(info *snap.Info) error {
return nil
}
-func (f *fakeSnappyBackend) StartSnapServices(info *snap.Info, meter progress.Meter) error {
+func svcSnapMountDir(svcs []*snap.AppInfo) string {
+ if len(svcs) == 0 {
+ return "<no services>"
+ }
+ if svcs[0].Snap == nil {
+ return "<snapless service>"
+ }
+ return svcs[0].Snap.MountDir()
+}
+
+func (f *fakeSnappyBackend) StartServices(svcs []*snap.AppInfo, meter progress.Meter) error {
f.ops = append(f.ops, fakeOp{
op: "start-snap-services",
- name: info.MountDir(),
+ name: svcSnapMountDir(svcs),
})
return nil
}
-func (f *fakeSnappyBackend) StopSnapServices(info *snap.Info, meter progress.Meter) error {
+func (f *fakeSnappyBackend) StopServices(svcs []*snap.AppInfo, meter progress.Meter) error {
f.ops = append(f.ops, fakeOp{
op: "stop-snap-services",
- name: info.MountDir(),
+ name: svcSnapMountDir(svcs),
})
return nil
}
diff --git a/overlord/snapstate/handlers.go b/overlord/snapstate/handlers.go
index 20a0fe8d23..c72e1d7122 100644
--- a/overlord/snapstate/handlers.go
+++ b/overlord/snapstate/handlers.go
@@ -690,10 +690,14 @@ func (m *SnapManager) startSnapServices(t *state.Task, _ *tomb.Tomb) error {
if err != nil {
return err
}
+ svcs := currentInfo.Services()
+ if len(svcs) == 0 {
+ return nil
+ }
pb := NewTaskProgressAdapterUnlocked(t)
st.Unlock()
- err = m.backend.StartSnapServices(currentInfo, pb)
+ err = m.backend.StartServices(svcs, pb)
st.Lock()
return err
}
@@ -712,10 +716,14 @@ func (m *SnapManager) stopSnapServices(t *state.Task, _ *tomb.Tomb) error {
if err != nil {
return err
}
+ svcs := currentInfo.Services()
+ if len(svcs) == 0 {
+ return nil
+ }
pb := NewTaskProgressAdapterUnlocked(t)
st.Unlock()
- err = m.backend.StopSnapServices(currentInfo, pb)
+ err = m.backend.StopServices(svcs, pb)
st.Lock()
return err
}
diff --git a/overlord/snapstate/snapmgr.go b/overlord/snapstate/snapmgr.go
index 66d1196726..93905b27ec 100644
--- a/overlord/snapstate/snapmgr.go
+++ b/overlord/snapstate/snapmgr.go
@@ -499,8 +499,13 @@ func (m *SnapManager) ensureRefreshes() error {
// compute next refresh attempt time (if needed)
if m.nextRefresh.IsZero() {
// store attempts in memory so that we can backoff
- delta := timeutil.Next(refreshSchedule, lastRefresh)
- m.nextRefresh = time.Now().Add(delta)
+ if !lastRefresh.IsZero() {
+ delta := timeutil.Next(refreshSchedule, lastRefresh)
+ m.nextRefresh = time.Now().Add(delta)
+ } else {
+ // immediate
+ m.nextRefresh = time.Now()
+ }
logger.Debugf("Next refresh scheduled for %s.", m.nextRefresh)
}
@@ -512,7 +517,7 @@ func (m *SnapManager) ensureRefreshes() error {
}
// do refresh attempt (if needed)
- if m.nextRefresh.Before(time.Now()) {
+ if !m.nextRefresh.After(time.Now()) {
err = m.launchAutoRefresh()
// clear nextRefresh only if the refresh worked. There is
// still the lastRefreshAttempt rate limit so things will
diff --git a/overlord/snapstate/snapstate_test.go b/overlord/snapstate/snapstate_test.go
index 25c407bfc7..cf12727ef1 100644
--- a/overlord/snapstate/snapstate_test.go
+++ b/overlord/snapstate/snapstate_test.go
@@ -1226,10 +1226,6 @@ func (s *snapmgrTestSuite) TestInstallRunThrough(c *C) {
op: "update-aliases",
},
{
- op: "start-snap-services",
- name: "/snap/some-snap/42",
- },
- {
op: "cleanup-trash",
name: "some-snap",
revno: snap.R(42),
@@ -1291,16 +1287,17 @@ func (s *snapmgrTestSuite) TestInstallRunThrough(c *C) {
}
func (s *snapmgrTestSuite) TestUpdateRunThrough(c *C) {
+ // use services-snap here to make sure services would be stopped/started appropriately
si := snap.SideInfo{
- RealName: "some-snap",
+ RealName: "services-snap",
Revision: snap.R(7),
- SnapID: "some-snap-id",
+ SnapID: "services-snap-id",
}
s.state.Lock()
defer s.state.Unlock()
- snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
+ snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
Active: true,
Sequence: []*snap.SideInfo{&si},
Current: si.Revision,
@@ -1308,7 +1305,7 @@ func (s *snapmgrTestSuite) TestUpdateRunThrough(c *C) {
})
chg := s.state.NewChange("refresh", "refresh a snap")
- ts, err := snapstate.Update(s.state, "some-snap", "some-channel", snap.R(0), s.user.ID, snapstate.Flags{})
+ ts, err := snapstate.Update(s.state, "services-snap", "some-channel", snap.R(0), s.user.ID, snapstate.Flags{})
c.Assert(err, IsNil)
chg.AddAll(ts)
@@ -1322,85 +1319,85 @@ func (s *snapmgrTestSuite) TestUpdateRunThrough(c *C) {
op: "storesvc-list-refresh",
cand: store.RefreshCandidate{
Channel: "some-channel",
- SnapID: "some-snap-id",
+ SnapID: "services-snap-id",
Revision: snap.R(7),
- Epoch: "",
+ Epoch: "0",
},
revno: snap.R(11),
},
{
op: "storesvc-download",
- name: "some-snap",
+ name: "services-snap",
},
{
op: "validate-snap:Doing",
- name: "some-snap",
+ name: "services-snap",
revno: snap.R(11),
},
{
op: "current",
- old: "/snap/some-snap/7",
+ old: "/snap/services-snap/7",
},
{
op: "open-snap-file",
- name: "/var/lib/snapd/snaps/some-snap_11.snap",
+ name: "/var/lib/snapd/snaps/services-snap_11.snap",
sinfo: snap.SideInfo{
- RealName: "some-snap",
- SnapID: "some-snap-id",
+ RealName: "services-snap",
+ SnapID: "services-snap-id",
Channel: "some-channel",
Revision: snap.R(11),
},
},
{
op: "setup-snap",
- name: "/var/lib/snapd/snaps/some-snap_11.snap",
+ name: "/var/lib/snapd/snaps/services-snap_11.snap",
revno: snap.R(11),
},
{
op: "stop-snap-services",
- name: "/snap/some-snap/7",
+ name: "/snap/services-snap/7",
},
{
op: "remove-snap-aliases",
- name: "some-snap",
+ name: "services-snap",
},
{
op: "unlink-snap",
- name: "/snap/some-snap/7",
+ name: "/snap/services-snap/7",
},
{
op: "copy-data",
- name: "/snap/some-snap/11",
- old: "/snap/some-snap/7",
+ name: "/snap/services-snap/11",
+ old: "/snap/services-snap/7",
},
{
op: "setup-profiles:Doing",
- name: "some-snap",
+ name: "services-snap",
revno: snap.R(11),
},
{
op: "candidate",
sinfo: snap.SideInfo{
- RealName: "some-snap",
- SnapID: "some-snap-id",
+ RealName: "services-snap",
+ SnapID: "services-snap-id",
Channel: "some-channel",
Revision: snap.R(11),
},
},
{
op: "link-snap",
- name: "/snap/some-snap/11",
+ name: "/snap/services-snap/11",
},
{
op: "update-aliases",
},
{
op: "start-snap-services",
- name: "/snap/some-snap/11",
+ name: "/snap/services-snap/11",
},
{
op: "cleanup-trash",
- name: "some-snap",
+ name: "services-snap",
revno: snap.R(11),
},
}
@@ -1408,7 +1405,7 @@ func (s *snapmgrTestSuite) TestUpdateRunThrough(c *C) {
// ensure all our tasks ran
c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
macaroon: s.user.StoreMacaroon,
- name: "some-snap",
+ name: "services-snap",
}})
// start with an easier-to-read error if this fails:
c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
@@ -1428,36 +1425,36 @@ func (s *snapmgrTestSuite) TestUpdateRunThrough(c *C) {
Channel: "some-channel",
UserID: s.user.ID,
- SnapPath: "/var/lib/snapd/snaps/some-snap_11.snap",
+ SnapPath: "/var/lib/snapd/snaps/services-snap_11.snap",
DownloadInfo: &snap.DownloadInfo{
DownloadURL: "https://some-server.com/some/path.snap",
},
SideInfo: snapsup.SideInfo,
})
c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
- RealName: "some-snap",
+ RealName: "services-snap",
Revision: snap.R(11),
Channel: "some-channel",
- SnapID: "some-snap-id",
+ SnapID: "services-snap-id",
})
// verify snaps in the system state
var snapst snapstate.SnapState
- err = snapstate.Get(s.state, "some-snap", &snapst)
+ err = snapstate.Get(s.state, "services-snap", &snapst)
c.Assert(err, IsNil)
c.Assert(snapst.Active, Equals, true)
c.Assert(snapst.Sequence, HasLen, 2)
c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
- RealName: "some-snap",
- SnapID: "some-snap-id",
+ RealName: "services-snap",
+ SnapID: "services-snap-id",
Channel: "",
Revision: snap.R(7),
})
c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
- RealName: "some-snap",
+ RealName: "services-snap",
Channel: "some-channel",
- SnapID: "some-snap-id",
+ SnapID: "services-snap-id",
Revision: snap.R(11),
})
}
@@ -1531,10 +1528,6 @@ func (s *snapmgrTestSuite) TestUpdateUndoRunThrough(c *C) {
revno: snap.R(11),
},
{
- op: "stop-snap-services",
- name: "/snap/some-snap/7",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -1587,10 +1580,6 @@ func (s *snapmgrTestSuite) TestUpdateUndoRunThrough(c *C) {
op: "update-aliases",
},
{
- op: "start-snap-services",
- name: "/snap/some-snap/7",
- },
- {
op: "undo-setup-snap",
name: "/snap/some-snap/11",
stype: "app",
@@ -1699,10 +1688,6 @@ func (s *snapmgrTestSuite) TestUpdateTotalUndoRunThrough(c *C) {
revno: snap.R(11),
},
{
- op: "stop-snap-services",
- name: "/snap/some-snap/7",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -1737,16 +1722,8 @@ func (s *snapmgrTestSuite) TestUpdateTotalUndoRunThrough(c *C) {
op: "update-aliases",
},
- {
- op: "start-snap-services",
- name: "/snap/some-snap/11",
- },
// undoing everything from here down...
{
- op: "stop-snap-services",
- name: "/snap/some-snap/11",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -1772,10 +1749,6 @@ func (s *snapmgrTestSuite) TestUpdateTotalUndoRunThrough(c *C) {
op: "update-aliases",
},
{
- op: "start-snap-services",
- name: "/snap/some-snap/7",
- },
- {
op: "undo-setup-snap",
name: "/snap/some-snap/11",
stype: "app",
@@ -2475,25 +2448,56 @@ version: 1.0`)
s.settle()
s.state.Lock()
- // ensure only local install was run, i.e. first actions are pseudo-action current
- c.Assert(s.fakeBackend.ops.Ops(), HasLen, 10)
- c.Check(s.fakeBackend.ops[0].op, Equals, "current")
- c.Check(s.fakeBackend.ops[0].old, Equals, "<no-current>")
- // and setup-snap
- c.Check(s.fakeBackend.ops[1].op, Equals, "setup-snap")
- c.Check(s.fakeBackend.ops[1].name, Matches, `.*/mock_1.0_all.snap`)
- c.Check(s.fakeBackend.ops[1].revno, Equals, snap.R("x1"))
+ expected := fakeOps{
+ {
+ // only local install was run, i.e. first actions are pseudo-action current
+ op: "current",
+ old: "<no-current>",
+ },
+ {
+ // and setup-snap
+ op: "setup-snap",
+ name: mockSnap,
+ revno: snap.R("x1"),
+ },
+ {
+ op: "copy-data",
+ name: "/snap/mock/x1",
+ old: "<no-old>",
+ },
+ {
+ op: "setup-profiles:Doing",
+ name: "mock",
+ revno: snap.R("x1"),
+ },
+ {
+ op: "candidate",
+ sinfo: snap.SideInfo{
+ RealName: "mock",
+ Revision: snap.R("x1"),
+ },
+ },
+ {
+ op: "link-snap",
+ name: "/snap/mock/x1",
+ },
+ {
+ op: "setup-profiles:Doing", // core phase 2
+ name: "mock",
+ revno: snap.R("x1"),
+ },
+ {
+ op: "update-aliases",
+ },
+ {
+ op: "cleanup-trash",
+ name: "mock",
+ revno: snap.R("x1"),
+ },
+ }
- c.Check(s.fakeBackend.ops[4].op, Equals, "candidate")
- c.Check(s.fakeBackend.ops[4].sinfo, DeepEquals, snap.SideInfo{
- RealName: "mock",
- Revision: snap.R(-1),
- })
- c.Check(s.fakeBackend.ops[5].op, Equals, "link-snap")
- c.Check(s.fakeBackend.ops[5].name, Equals, "/snap/mock/x1")
- c.Check(s.fakeBackend.ops[6].op, Equals, "setup-profiles:Doing") // core phase 2
- c.Check(s.fakeBackend.ops[8].op, Equals, "start-snap-services")
- c.Check(s.fakeBackend.ops[8].name, Equals, "/snap/mock/x1")
+ c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
+ c.Check(s.fakeBackend.ops, DeepEquals, expected)
// verify snapSetup info
var snapsup snapstate.SnapSetup
@@ -2552,7 +2556,7 @@ version: 1.0`)
ops := s.fakeBackend.ops
// ensure only local install was run, i.e. first action is pseudo-action current
- c.Assert(ops, HasLen, 13)
+ c.Assert(ops.Ops(), HasLen, 11)
c.Check(ops[0].op, Equals, "current")
c.Check(ops[0].old, Equals, "/snap/mock/x2")
// and setup-snap
@@ -2566,30 +2570,25 @@ version: 1.0`)
revno: snap.R("x3"),
})
- c.Check(ops[2].op, Equals, "stop-snap-services")
- c.Check(ops[2].name, Equals, "/snap/mock/x2")
+ c.Check(ops[3].op, Equals, "unlink-snap")
+ c.Check(ops[3].name, Equals, "/snap/mock/x2")
- c.Check(ops[4].op, Equals, "unlink-snap")
- c.Check(ops[4].name, Equals, "/snap/mock/x2")
+ c.Check(ops[4].op, Equals, "copy-data")
+ c.Check(ops[4].name, Equals, "/snap/mock/x3")
+ c.Check(ops[4].old, Equals, "/snap/mock/x2")
- c.Check(ops[5].op, Equals, "copy-data")
- c.Check(ops[5].name, Equals, "/snap/mock/x3")
- c.Check(ops[5].old, Equals, "/snap/mock/x2")
+ c.Check(ops[5].op, Equals, "setup-profiles:Doing")
+ c.Check(ops[5].name, Equals, "mock")
+ c.Check(ops[5].revno, Equals, snap.R(-3))
- c.Check(ops[6].op, Equals, "setup-profiles:Doing")
- c.Check(ops[6].name, Equals, "mock")
- c.Check(ops[6].revno, Equals, snap.R(-3))
-
- c.Check(ops[7].op, Equals, "candidate")
- c.Check(ops[7].sinfo, DeepEquals, snap.SideInfo{
+ c.Check(ops[6].op, Equals, "candidate")
+ c.Check(ops[6].sinfo, DeepEquals, snap.SideInfo{
RealName: "mock",
Revision: snap.R(-3),
})
- c.Check(ops[8].op, Equals, "link-snap")
- c.Check(ops[8].name, Equals, "/snap/mock/x3")
- c.Check(ops[9].op, Equals, "setup-profiles:Doing") // core phase 2
- c.Check(ops[11].op, Equals, "start-snap-services")
- c.Check(ops[11].name, Equals, "/snap/mock/x3")
+ c.Check(ops[7].op, Equals, "link-snap")
+ c.Check(ops[7].name, Equals, "/snap/mock/x3")
+ c.Check(ops[8].op, Equals, "setup-profiles:Doing") // core phase 2
// verify snapSetup info
var snapsup snapstate.SnapSetup
@@ -2647,21 +2646,64 @@ version: 1.0`)
s.settle()
s.state.Lock()
- // ensure only local install was run, i.e. first action is pseudo-action current
- ops := s.fakeBackend.ops
- c.Assert(ops, HasLen, 13)
- c.Check(ops[0].op, Equals, "current")
- c.Check(ops[0].old, Equals, "/snap/mock/100001")
- // and setup-snap
- c.Check(ops[1].op, Equals, "setup-snap")
- c.Check(ops[1].name, Matches, `.*/mock_1.0_all.snap`)
- c.Check(ops[1].revno, Equals, snap.R("x1"))
- // and cleanup
- c.Check(ops[len(ops)-1], DeepEquals, fakeOp{
- op: "cleanup-trash",
- name: "mock",
- revno: snap.R("x1"),
- })
+ expected := fakeOps{
+ {
+ // ensure only local install was run, i.e. first action is pseudo-action current
+ op: "current",
+ old: "/snap/mock/100001",
+ },
+ {
+ // and setup-snap
+ op: "setup-snap",
+ name: mockSnap,
+ revno: snap.R("x1"),
+ },
+ {
+ op: "remove-snap-aliases",
+ name: "mock",
+ },
+ {
+ op: "unlink-snap",
+ name: "/snap/mock/100001",
+ },
+ {
+ op: "copy-data",
+ name: "/snap/mock/x1",
+ old: "/snap/mock/100001",
+ },
+ {
+ op: "setup-profiles:Doing",
+ name: "mock",
+ revno: snap.R("x1"),
+ },
+ {
+ op: "candidate",
+ sinfo: snap.SideInfo{
+ RealName: "mock",
+ Revision: snap.R("x1"),
+ },
+ },
+ {
+ op: "link-snap",
+ name: "/snap/mock/x1",
+ },
+ {
+ op: "setup-profiles:Doing",
+ name: "mock",
+ revno: snap.R("x1"),
+ },
+ {
+ op: "update-aliases",
+ },
+ {
+ // and cleanup
+ op: "cleanup-trash",
+ name: "mock",
+ revno: snap.R("x1"),
+ },
+ }
+ c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
+ c.Check(s.fakeBackend.ops, DeepEquals, expected)
var snapst snapstate.SnapState
err = snapstate.Get(s.state, "mock", &snapst)
@@ -2703,7 +2745,7 @@ version: 1.0`)
s.state.Lock()
// ensure only local install was run, i.e. first actions are pseudo-action current
- c.Assert(s.fakeBackend.ops.Ops(), HasLen, 10)
+ c.Assert(s.fakeBackend.ops.Ops(), HasLen, 9)
c.Check(s.fakeBackend.ops[0].op, Equals, "current")
c.Check(s.fakeBackend.ops[0].old, Equals, "<no-current>")
// and setup-snap
@@ -2715,8 +2757,6 @@ version: 1.0`)
c.Check(s.fakeBackend.ops[4].sinfo, DeepEquals, *si)
c.Check(s.fakeBackend.ops[5].op, Equals, "link-snap")
c.Check(s.fakeBackend.ops[5].name, Equals, "/snap/some-snap/42")
- c.Check(s.fakeBackend.ops[8].op, Equals, "start-snap-services")
- c.Check(s.fakeBackend.ops[8].name, Equals, "/snap/some-snap/42")
// verify snapSetup info
var snapsup snapstate.SnapSetup
@@ -2770,13 +2810,8 @@ func (s *snapmgrTestSuite) TestRemoveRunThrough(c *C) {
s.settle()
s.state.Lock()
- c.Check(len(s.fakeBackend.ops), Equals, 9)
expected := fakeOps{
{
- op: "stop-snap-services",
- name: "/snap/some-snap/7",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -2812,6 +2847,7 @@ func (s *snapmgrTestSuite) TestRemoveRunThrough(c *C) {
},
}
// start with an easier-to-read error if this fails:
+ c.Check(len(s.fakeBackend.ops), Equals, len(expected))
c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
c.Check(s.fakeBackend.ops, DeepEquals, expected)
@@ -2883,10 +2919,6 @@ func (s *snapmgrTestSuite) TestRemoveWithManyRevisionsRunThrough(c *C) {
expected := fakeOps{
{
- op: "stop-snap-services",
- name: "/snap/some-snap/7",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -3468,39 +3500,42 @@ func (s *snapmgrTestSuite) TestUpdateDoesGC(c *C) {
s.state.Lock()
// ensure garbage collection runs as the last tasks
- ops := s.fakeBackend.ops
- c.Assert(ops[len(ops)-8], DeepEquals, fakeOp{
- op: "link-snap",
- name: "/snap/some-snap/11",
- })
- c.Assert(ops[len(ops)-6], DeepEquals, fakeOp{
- op: "start-snap-services",
- name: "/snap/some-snap/11",
- })
- c.Assert(ops[len(ops)-5], DeepEquals, fakeOp{
- op: "remove-snap-data",
- name: "/snap/some-snap/1",
- })
- c.Assert(ops[len(ops)-4], DeepEquals, fakeOp{
- op: "remove-snap-files",
- name: "/snap/some-snap/1",
- stype: "app",
- })
- c.Assert(ops[len(ops)-3], DeepEquals, fakeOp{
- op: "remove-snap-data",
- name: "/snap/some-snap/2",
- })
- c.Assert(ops[len(ops)-2], DeepEquals, fakeOp{
- op: "remove-snap-files",
- name: "/snap/some-snap/2",
- stype: "app",
- })
- c.Assert(ops[len(ops)-1], DeepEquals, fakeOp{
- op: "cleanup-trash",
- name: "some-snap",
- revno: snap.R(11),
- })
+ expectedTail := fakeOps{
+ {
+ op: "link-snap",
+ name: "/snap/some-snap/11",
+ },
+ {
+ op: "update-aliases",
+ },
+ {
+ op: "remove-snap-data",
+ name: "/snap/some-snap/1",
+ },
+ {
+ op: "remove-snap-files",
+ name: "/snap/some-snap/1",
+ stype: "app",
+ },
+ {
+ op: "remove-snap-data",
+ name: "/snap/some-snap/2",
+ },
+ {
+ op: "remove-snap-files",
+ name: "/snap/some-snap/2",
+ stype: "app",
+ },
+ {
+ op: "cleanup-trash",
+ name: "some-snap",
+ revno: snap.R(11),
+ },
+ }
+ opsTail := s.fakeBackend.ops[len(s.fakeBackend.ops)-len(expectedTail):]
+ c.Assert(opsTail.Ops(), DeepEquals, expectedTail.Ops())
+ c.Check(opsTail, DeepEquals, expectedTail)
}
func (s *snapmgrTestSuite) TestRevertNoRevertAgain(c *C) {
@@ -3628,10 +3663,6 @@ func (s *snapmgrTestSuite) TestRevertRunThrough(c *C) {
expected := fakeOps{
{
- op: "stop-snap-services",
- name: "/snap/some-snap/7",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -3658,10 +3689,6 @@ func (s *snapmgrTestSuite) TestRevertRunThrough(c *C) {
{
op: "update-aliases",
},
- {
- op: "start-snap-services",
- name: "/snap/some-snap/2",
- },
}
// start with an easier-to-read error if this fails:
c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
@@ -3718,7 +3745,7 @@ func (s *snapmgrTestSuite) TestRevertWithLocalRevisionRunThrough(c *C) {
s.settle()
s.state.Lock()
- c.Assert(s.fakeBackend.ops, HasLen, 8)
+ c.Assert(s.fakeBackend.ops.Ops(), HasLen, 6)
// verify that LocalRevision is still -7
var snapst snapstate.SnapState
@@ -3764,10 +3791,6 @@ func (s *snapmgrTestSuite) TestRevertToRevisionNewVersion(c *C) {
expected := fakeOps{
{
- op: "stop-snap-services",
- name: "/snap/some-snap/2",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -3791,10 +3814,6 @@ func (s *snapmgrTestSuite) TestRevertToRevisionNewVersion(c *C) {
{
op: "update-aliases",
},
- {
- op: "start-snap-services",
- name: "/snap/some-snap/7",
- },
}
// start with an easier-to-read error if this fails:
c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
@@ -3853,10 +3872,6 @@ func (s *snapmgrTestSuite) TestRevertTotalUndoRunThrough(c *C) {
expected := fakeOps{
{
- op: "stop-snap-services",
- name: "/snap/some-snap/2",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -3883,16 +3898,8 @@ func (s *snapmgrTestSuite) TestRevertTotalUndoRunThrough(c *C) {
{
op: "update-aliases",
},
- {
- op: "start-snap-services",
- name: "/snap/some-snap/1",
- },
// undoing everything from here down...
{
- op: "stop-snap-services",
- name: "/snap/some-snap/1",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -3912,10 +3919,6 @@ func (s *snapmgrTestSuite) TestRevertTotalUndoRunThrough(c *C) {
{
op: "update-aliases",
},
- {
- op: "start-snap-services",
- name: "/snap/some-snap/2",
- },
}
// start with an easier-to-read error if this fails:
c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
@@ -3965,10 +3968,6 @@ func (s *snapmgrTestSuite) TestRevertUndoRunThrough(c *C) {
expected := fakeOps{
{
- op: "stop-snap-services",
- name: "/snap/some-snap/2",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -4009,10 +4008,6 @@ func (s *snapmgrTestSuite) TestRevertUndoRunThrough(c *C) {
{
op: "update-aliases",
},
- {
- op: "start-snap-services",
- name: "/snap/some-snap/2",
- },
}
// ensure all our tasks ran
@@ -4103,10 +4098,6 @@ func (s *snapmgrTestSuite) TestEnableRunThrough(c *C) {
{
op: "update-aliases",
},
- {
- op: "start-snap-services",
- name: "/snap/some-snap/7",
- },
}
// start with an easier-to-read error if this fails:
c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
@@ -4151,10 +4142,6 @@ func (s *snapmgrTestSuite) TestDisableRunThrough(c *C) {
expected := fakeOps{
{
- op: "stop-snap-services",
- name: "/snap/some-snap/7",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -4494,6 +4481,36 @@ func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdate(c *C) {
s.verifyRefreshLast(c)
}
+func (s *snapmgrTestSuite) TestEnsureRefreshesImmediateWithUpdate(c *C) {
+ s.state.Lock()
+ defer s.state.Unlock()
+ snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
+
+ // lastRefresh is unset/zero => immediate refresh try
+
+ snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
+ Active: true,
+ Sequence: []*snap.SideInfo{
+ {RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
+ },
+ Current: snap.R(1),
+ SnapType: "app",
+ })
+
+ // Ensure() also runs ensureRefreshes() and our test setup has an
+ // update for the "some-snap" in our fake store
+ s.state.Unlock()
+ s.snapmgr.Ensure()
+ s.state.Lock()
+
+ // verify we have an auto-refresh change scheduled now
+ c.Assert(s.state.Changes(), HasLen, 1)
+ chg := s.state.Changes()[0]
+ c.Check(chg.Kind(), Equals, "auto-refresh")
+ c.Check(chg.IsReady(), Equals, false)
+ s.verifyRefreshLast(c)
+}
+
func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdateError(c *C) {
s.state.Lock()
defer s.state.Unlock()
@@ -5532,10 +5549,6 @@ func (s *snapmgrTestSuite) TestUpdateCanDoBackwards(c *C) {
s.state.Lock()
expected := fakeOps{
{
- op: "stop-snap-services",
- name: "/snap/some-snap/11",
- },
- {
op: "remove-snap-aliases",
name: "some-snap",
},
@@ -5570,10 +5583,6 @@ func (s *snapmgrTestSuite) TestUpdateCanDoBackwards(c *C) {
op: "update-aliases",
},
{
- op: "start-snap-services",
- name: "/snap/some-snap/7",
- },
- {
op: "cleanup-trash",
name: "some-snap",
revno: snap.R(7),
@@ -6050,18 +6059,10 @@ func (s *snapmgrTestSuite) TestTransitionCoreRunThrough(c *C) {
op: "update-aliases",
},
{
- op: "start-snap-services",
- name: "/snap/core/11",
- },
- {
op: "transition-ubuntu-core:Doing",
name: "ubuntu-core",
},
{
- op: "stop-snap-services",
- name: "/snap/ubuntu-core/1",
- },
- {
op: "remove-snap-aliases",
name: "ubuntu-core",
},
@@ -6149,10 +6150,6 @@ func (s *snapmgrTestSuite) TestTransitionCoreRunThroughWithCore(c *C) {
name: "ubuntu-core",
},
{
- op: "stop-snap-services",
- name: "/snap/ubuntu-core/1",
- },
- {
op: "remove-snap-aliases",
name: "ubuntu-core",
},
diff --git a/snap/info.go b/snap/info.go
index c47921d926..5b379c2ac4 100644
--- a/snap/info.go
+++ b/snap/info.go
@@ -50,16 +50,28 @@ type PlaceInfo interface {
// DataDir returns the data directory of the snap.
DataDir() string
+ // HomeDirBase returns the user-specific home directory base of the snap.
+ HomeDirBase(home string) string
+
+ // UserDataDir returns the per user data directory of the snap.
+ UserDataDir(home string) string
+
// CommonDataDir returns the data directory common across revisions of the snap.
CommonDataDir() string
- // DataHomeDir returns the per user data directory of the snap.
+ // UserCommonDataDir returns the per user data directory common across revisions of the snap.
+ UserCommonDataDir(home string) string
+
+ // UserXdgRuntimeDir returns the per user XDG_RUNTIME_DIR directory
+ UserXdgRuntimeDir(userID int) string
+
+ // DataHomeDir returns the a glob that matches all per user data directories of a snap.
DataHomeDir() string
- // CommonDataHomeDir returns the per user data directory common across revisions of the snap.
+ // CommonDataHomeDir returns a glob that matches all per user data directories common across revisions of the snap.
CommonDataHomeDir() string
- // XdgRuntimeDirs returns the XDG_RUNTIME_DIR directories for all users of the snap.
+ // XdgRuntimeDirs returns a glob that matches all XDG_RUNTIME_DIR directories for all users of the snap.
XdgRuntimeDirs() string
}
@@ -280,6 +292,19 @@ func (s *Info) NeedsClassic() bool {
return s.Confinement == ClassicConfinement
}
+// Services returns a list of the apps that have "daemon" set.
+func (s *Info) Services() []*AppInfo {
+ svcs := make([]*AppInfo, 0, len(s.Apps))
+ for _, app := range s.Apps {
+ if !app.IsService() {
+ continue
+ }
+ svcs = append(svcs, app)
+ }
+
+ return svcs
+}
+
// DownloadInfo contains the information to download a snap.
// It can be marshalled.
type DownloadInfo struct {
@@ -454,7 +479,7 @@ func (app *AppInfo) LauncherPostStopCommand() string {
return app.launcherCommand("--command=post-stop")
}
-// Servicename returns the systemd service name for the daemon app.
+// ServiceName returns the systemd service name for the daemon app.
func (app *AppInfo) ServiceName() string {
return app.SecurityTag() + ".service"
}
@@ -482,6 +507,11 @@ func (app *AppInfo) Env() []string {
return env
}
+// IsService returns whether app represents a daemon/service.
+func (app *AppInfo) IsService() bool {
+ return app.Daemon != ""
+}
+
// SecurityTag returns the hook-specific security tag.
//
// Security tags are used by various security subsystems as "profile names" and
diff --git a/snap/info_test.go b/snap/info_test.go
index 125e6b4b63..346d5eadcd 100644
--- a/snap/info_test.go
+++ b/snap/info_test.go
@@ -123,6 +123,9 @@ apps:
c.Assert(err, IsNil)
info.Revision = snap.R(42)
c.Check(info.Apps["bar"].LauncherCommand(), Equals, "/usr/bin/snap run foo.bar")
+ c.Check(info.Apps["bar"].LauncherStopCommand(), Equals, "/usr/bin/snap run --command=stop foo.bar")
+ c.Check(info.Apps["bar"].LauncherReloadCommand(), Equals, "/usr/bin/snap run --command=reload foo.bar")
+ c.Check(info.Apps["bar"].LauncherPostStopCommand(), Equals, "/usr/bin/snap run --command=post-stop foo.bar")
c.Check(info.Apps["foo"].LauncherCommand(), Equals, "/usr/bin/snap run foo")
}
@@ -157,6 +160,47 @@ func (s *infoSuite) TestReadInfo(c *C) {
c.Check(snapInfo2, DeepEquals, snapInfo1)
}
+func (s *infoSuite) TestReadInfoNotFound(c *C) {
+ si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"}
+ info, err := snap.ReadInfo("sample", si)
+ c.Check(info, IsNil)
+ c.Check(err, ErrorMatches, `cannot find installed snap "sample" at revision 42`)
+}
+
+func (s *infoSuite) TestReadInfoUnreadable(c *C) {
+ si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"}
+ c.Assert(os.MkdirAll(filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml"), 0755), IsNil)
+
+ info, err := snap.ReadInfo("sample", si)
+ c.Check(info, IsNil)
+ // TODO: maybe improve this error message
+ c.Check(err, ErrorMatches, ".* is a directory")
+}
+
+func (s *infoSuite) TestReadInfoUnparsable(c *C) {
+ si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"}
+ p := filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml")
+ c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil)
+ c.Assert(ioutil.WriteFile(p, []byte(`- :`), 0644), IsNil)
+
+ info, err := snap.ReadInfo("sample", si)
+ c.Check(info, IsNil)
+ // TODO: maybe improve this error message
+ c.Check(err, ErrorMatches, ".* failed to parse.*")
+}
+
+func (s *infoSuite) TestReadInfoUnfindable(c *C) {
+ si := &snap.SideInfo{Revision: snap.R(42), EditedSummary: "esummary"}
+ p := filepath.Join(snap.MinimalPlaceInfo("sample", si.Revision).MountDir(), "meta", "snap.yaml")
+ c.Assert(os.MkdirAll(filepath.Dir(p), 0755), IsNil)
+ c.Assert(ioutil.WriteFile(p, []byte(``), 0644), IsNil)
+
+ info, err := snap.ReadInfo("sample", si)
+ c.Check(info, IsNil)
+ // TODO: maybe improve this error message
+ c.Check(err, ErrorMatches, ".* no such file or directory")
+}
+
// makeTestSnap here can also be used to produce broken snaps (differently from snaptest.MakeTestSnapWithFiles)!
func makeTestSnap(c *C, yaml string) string {
tmp := c.MkDir()
@@ -204,6 +248,29 @@ confinement: devmode`
c.Check(info.Revision, Equals, snap.R(0))
c.Check(info.Epoch, Equals, "1*")
c.Check(info.Confinement, Equals, snap.DevModeConfinement)
+ c.Check(info.NeedsDevMode(), Equals, true)
+ c.Check(info.NeedsClassic(), Equals, false)
+}
+
+func (s *infoSuite) TestReadInfoFromClassicSnapFile(c *C) {
+ yaml := `name: foo
+version: 1.0
+type: app
+confinement: classic`
+ snapPath := snaptest.MakeTestSnapWithFiles(c, yaml, nil)
+
+ snapf, err := snap.Open(snapPath)
+ c.Assert(err, IsNil)
+
+ info, err := snap.ReadInfoFromSnapFile(snapf, nil)
+ c.Assert(err, IsNil)
+ c.Check(info.Name(), Equals, "foo")
+ c.Check(info.Version, Equals, "1.0")
+ c.Check(info.Type, Equals, snap.TypeApp)
+ c.Check(info.Revision, Equals, snap.R(0))
+ c.Check(info.Confinement, Equals, snap.ClassicConfinement)
+ c.Check(info.NeedsDevMode(), Equals, false)
+ c.Check(info.NeedsClassic(), Equals, true)
}
func (s *infoSuite) TestReadInfoFromSnapFileMissingEpoch(c *C) {
@@ -222,6 +289,8 @@ type: app`
c.Check(info.Type, Equals, snap.TypeApp)
c.Check(info.Revision, Equals, snap.R(0))
c.Check(info.Epoch, Equals, "0") // Defaults to 0
+ c.Check(info.Confinement, Equals, snap.StrictConfinement)
+ c.Check(info.NeedsDevMode(), Equals, false)
}
func (s *infoSuite) TestReadInfoFromSnapFileWithSideInfo(c *C) {
@@ -505,14 +574,25 @@ func verifyExplicitHook(c *C, info *snap.Info, hookName string, plugNames []stri
}
}
+func (s *infoSuite) TestMinimalInfoDirAndFileMethods(c *C) {
+ dirs.SetRootDir("")
+ info := snap.MinimalPlaceInfo("name", snap.R("1"))
+ s.testDirAndFileMethods(c, info)
+}
+
func (s *infoSuite) TestDirAndFileMethods(c *C) {
dirs.SetRootDir("")
info := &snap.Info{SuggestedName: "name", SideInfo: snap.SideInfo{Revision: snap.R(1)}}
+ s.testDirAndFileMethods(c, info)
+}
+
+func (s *infoSuite) testDirAndFileMethods(c *C, info snap.PlaceInfo) {
c.Check(info.MountDir(), Equals, fmt.Sprintf("%s/name/1", dirs.SnapMountDir))
c.Check(info.MountFile(), Equals, "/var/lib/snapd/snaps/name_1.snap")
c.Check(info.HooksDir(), Equals, fmt.Sprintf("%s/name/1/meta/hooks", dirs.SnapMountDir))
c.Check(info.DataDir(), Equals, "/var/snap/name/1")
c.Check(info.UserDataDir("/home/bob"), Equals, "/home/bob/snap/name/1")
+ c.Check(info.HomeDirBase("/home/bob"), Equals, "/home/bob/snap/name")
c.Check(info.UserCommonDataDir("/home/bob"), Equals, "/home/bob/snap/name/common")
c.Check(info.CommonDataDir(), Equals, "/var/snap/name/common")
c.Check(info.UserXdgRuntimeDir(12345), Equals, "/run/user/12345/snap.name")
@@ -584,3 +664,49 @@ func (s *infoSuite) TestInfoFromSnapYamlRenamesCorePlugs(c *C) {
c.Check(info.Plugs["network-bind-plug"], NotNil)
c.Check(info.Plugs["core-support-plug"], NotNil)
}
+
+func (s *infoSuite) TestInfoServices(c *C) {
+ info, err := snap.InfoFromSnapYaml([]byte(`name: pans
+apps:
+ svc1:
+ daemon: potato
+ svc2:
+ daemon: no
+ app1:
+ app2:
+`))
+ c.Assert(err, IsNil)
+ svcNames := []string{}
+ svcs := info.Services()
+ for i := range svcs {
+ svcNames = append(svcNames, svcs[i].ServiceName())
+ }
+ sort.Strings(svcNames)
+ c.Check(svcNames, DeepEquals, []string{
+ "snap.pans.svc1.service",
+ "snap.pans.svc2.service",
+ })
+}
+
+func (s *infoSuite) TestAppInfoIsService(c *C) {
+ info, err := snap.InfoFromSnapYaml([]byte(`name: pans
+apps:
+ svc1:
+ daemon: potato
+ svc2:
+ daemon: no
+ app1:
+ app2:
+`))
+ c.Assert(err, IsNil)
+
+ svc := info.Apps["svc1"]
+ c.Check(svc.IsService(), Equals, true)
+ c.Check(svc.ServiceName(), Equals, "snap.pans.svc1.service")
+ c.Check(svc.ServiceFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.svc1.service")
+ c.Check(svc.ServiceSocketFile(), Equals, dirs.GlobalRootDir+"/etc/systemd/system/snap.pans.svc1.socket")
+
+ c.Check(info.Apps["svc2"].IsService(), Equals, true)
+ c.Check(info.Apps["app1"].IsService(), Equals, false)
+ c.Check(info.Apps["app1"].IsService(), Equals, false)
+}
diff --git a/tests/main/snap-multi-service-failing/task.yaml b/tests/main/snap-multi-service-failing/task.yaml
index eb427e0b91..c631bf46e6 100644
--- a/tests/main/snap-multi-service-failing/task.yaml
+++ b/tests/main/snap-multi-service-failing/task.yaml
@@ -1,4 +1,4 @@
-summary: >
+summary: |
Check that `snap install` doesn't leave a service running when the install fails.
execute: |
diff --git a/wrappers/binaries.go b/wrappers/binaries.go
index 636631a24f..fceb3c698f 100644
--- a/wrappers/binaries.go
+++ b/wrappers/binaries.go
@@ -27,11 +27,6 @@ import (
"github.com/snapcore/snapd/snap"
)
-// IsService returns whether app represents a daemon/service.
-func IsService(app *snap.AppInfo) bool {
- return app.Daemon != ""
-}
-
// AddSnapBinaries writes the wrapper binaries for the applications from the snap which aren't services.
func AddSnapBinaries(s *snap.Info) error {
if err := os.MkdirAll(dirs.SnapBinariesDir, 0755); err != nil {
@@ -39,7 +34,7 @@ func AddSnapBinaries(s *snap.Info) error {
}
for _, app := range s.Apps {
- if IsService(app) {
+ if app.IsService() {
continue
}
diff --git a/wrappers/services.go b/wrappers/services.go
index 684cdadbfd..9b3dfd6f28 100644
--- a/wrappers/services.go
+++ b/wrappers/services.go
@@ -75,12 +75,13 @@ func stopService(sysd systemd.Systemd, app *snap.AppInfo, inter interacter) erro
return nil
}
-// StartSnapServices starts service units for the applications from the snap which are services.
-func StartSnapServices(s *snap.Info, inter interacter) (err error) {
+// StartServices starts service units for the applications from the snap which are services.
+func StartServices(apps []*snap.AppInfo, inter interacter) (err error) {
sysd := systemd.New(dirs.GlobalRootDir, inter)
- for _, app := range s.Apps {
- if app.Daemon == "" {
+ for _, app := range apps {
+ // they're *supposed* to be all services, but checking doesn't hurt
+ if !app.IsService() {
continue
}
if err := sysd.Start(app.ServiceName()); err != nil {
@@ -105,7 +106,7 @@ func AddSnapServices(s *snap.Info, inter interacter) error {
nservices := 0
for _, app := range s.Apps {
- if app.Daemon == "" {
+ if !app.IsService() {
continue
}
nservices++
@@ -133,14 +134,14 @@ func AddSnapServices(s *snap.Info, inter interacter) error {
return nil
}
-// StopSnapServices stops service units for the applications from the snap which are services.
-func StopSnapServices(s *snap.Info, inter interacter) error {
+// StopServices stops service units for the applications from the snap which are services.
+func StopServices(apps []*snap.AppInfo, inter interacter) error {
sysd := systemd.New(dirs.GlobalRootDir, inter)
- for _, app := range s.Apps {
+ for _, app := range apps {
// Handle the case where service file doesn't exist and don't try to stop it as it will fail.
// This can happen with snap try when snap.yaml is modified on the fly and a daemon line is added.
- if app.Daemon == "" || !osutil.FileExists(app.ServiceFile()) {
+ if !app.IsService() || !osutil.FileExists(app.ServiceFile()) {
continue
}
if err := stopService(sysd, app, inter); err != nil {
@@ -158,7 +159,7 @@ func RemoveSnapServices(s *snap.Info, inter interacter) error {
nservices := 0
for _, app := range s.Apps {
- if app.Daemon == "" || !osutil.FileExists(app.ServiceFile()) {
+ if !app.IsService() || !osutil.FileExists(app.ServiceFile()) {
continue
}
nservices++
diff --git a/wrappers/services_test.go b/wrappers/services_test.go
index e53bc21755..c70ae56a3d 100644
--- a/wrappers/services_test.go
+++ b/wrappers/services_test.go
@@ -87,7 +87,7 @@ func (s *servicesTestSuite) TestAddSnapServicesAndRemove(c *C) {
}
sysdLog = nil
- err = wrappers.StopSnapServices(info, &progress.NullProgress{})
+ err = wrappers.StopServices(info.Services(), &progress.NullProgress{})
c.Assert(err, IsNil)
c.Assert(sysdLog, HasLen, 2)
c.Check(sysdLog, DeepEquals, [][]string{
@@ -111,7 +111,7 @@ func (s *servicesTestSuite) TestRemoveSnapPackageFallbackToKill(c *C) {
var sysdLog [][]string
systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) {
// filter out the "systemctl show" that
- // StopSnapServicesGenerates
+ // StopServices generates
if cmd[0] != "show" {
sysdLog = append(sysdLog, cmd)
}
@@ -134,7 +134,7 @@ apps:
svcFName := "snap.wat.wat.service"
- err = wrappers.StopSnapServices(info, &progress.NullProgress{})
+ err = wrappers.StopServices(info.Services(), &progress.NullProgress{})
c.Assert(err, IsNil)
c.Check(sysdLog, DeepEquals, [][]string{
@@ -145,7 +145,7 @@ apps:
})
}
-func (s *servicesTestSuite) TestStartSnapServices(c *C) {
+func (s *servicesTestSuite) TestStartServices(c *C) {
var sysdLog [][]string
systemd.SystemctlCmd = func(cmd ...string) ([]byte, error) {
sysdLog = append(sysdLog, cmd)
@@ -155,7 +155,7 @@ func (s *servicesTestSuite) TestStartSnapServices(c *C) {
info := snaptest.MockSnap(c, packageHello, contentsHello, &snap.SideInfo{Revision: snap.R(12)})
svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service")
- err := wrappers.StartSnapServices(info, nil)
+ err := wrappers.StartServices(info.Services(), nil)
c.Assert(err, IsNil)
c.Assert(sysdLog, DeepEquals, [][]string{{"start", filepath.Base(svcFile)}})
@@ -189,7 +189,7 @@ func (s *servicesTestSuite) TestStartSnapMultiServicesFailStartCleanup(c *C) {
daemon: simple
`, contentsHello, &snap.SideInfo{Revision: snap.R(12)})
- err := wrappers.StartSnapServices(info, nil)
+ err := wrappers.StartServices(info.Services(), nil)
c.Assert(err, ErrorMatches, "failed")
c.Assert(sysdLog, HasLen, 4)