diff options
| author | Michael Vogt <mvo@ubuntu.com> | 2017-05-23 19:36:37 +0200 |
|---|---|---|
| committer | Michael Vogt <mvo@ubuntu.com> | 2017-05-23 19:36:37 +0200 |
| commit | 8d3baef0b870208aa94a286c1eb64b978e930ee8 (patch) | |
| tree | 067274a898d13a5438a5aaf0993b0052863f65c3 | |
| parent | 27619cf438088e23232a6e86f27fbf0c98f473dd (diff) | |
| parent | d331e4fb1b9cf9fb86a5966b220be4c5009ebc15 (diff) | |
Merge remote-tracking branch 'upstream/master' into flock-non-lockingflock-non-locking
| -rw-r--r-- | cmd/snap/cmd_auto_import.go | 2 | ||||
| -rw-r--r-- | overlord/patch/patch5.go | 9 | ||||
| -rw-r--r-- | overlord/snapstate/aliasesv2.go | 9 | ||||
| -rw-r--r-- | overlord/snapstate/backend.go | 4 | ||||
| -rw-r--r-- | overlord/snapstate/backend/link.go | 8 | ||||
| -rw-r--r-- | overlord/snapstate/backend_test.go | 48 | ||||
| -rw-r--r-- | overlord/snapstate/handlers.go | 12 | ||||
| -rw-r--r-- | overlord/snapstate/snapmgr.go | 11 | ||||
| -rw-r--r-- | overlord/snapstate/snapstate_test.go | 449 | ||||
| -rw-r--r-- | snap/info.go | 38 | ||||
| -rw-r--r-- | snap/info_test.go | 126 | ||||
| -rw-r--r-- | tests/main/snap-multi-service-failing/task.yaml | 2 | ||||
| -rw-r--r-- | wrappers/binaries.go | 7 | ||||
| -rw-r--r-- | wrappers/services.go | 21 | ||||
| -rw-r--r-- | wrappers/services_test.go | 12 |
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) |
