github.com/stulluk/snapd@v0.0.0-20210611110309-f6d5d5bd24b0/overlord/snapstate/snapstate_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2018 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package snapstate_test
    21  
    22  import (
    23  	"context"
    24  	"encoding/json"
    25  	"errors"
    26  	"fmt"
    27  	"io/ioutil"
    28  	"os"
    29  	"path/filepath"
    30  	"sort"
    31  	"strings"
    32  	"testing"
    33  	"time"
    34  
    35  	. "gopkg.in/check.v1"
    36  	"gopkg.in/tomb.v2"
    37  
    38  	"github.com/snapcore/snapd/asserts"
    39  	"github.com/snapcore/snapd/bootloader"
    40  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    41  	"github.com/snapcore/snapd/dirs"
    42  	"github.com/snapcore/snapd/gadget"
    43  	"github.com/snapcore/snapd/interfaces"
    44  	"github.com/snapcore/snapd/logger"
    45  	"github.com/snapcore/snapd/overlord"
    46  	"github.com/snapcore/snapd/overlord/auth"
    47  	"github.com/snapcore/snapd/overlord/configstate/config"
    48  	"github.com/snapcore/snapd/overlord/hookstate"
    49  	"github.com/snapcore/snapd/overlord/ifacestate/ifacerepo"
    50  	"github.com/snapcore/snapd/overlord/servicestate"
    51  	"github.com/snapcore/snapd/overlord/snapstate"
    52  	"github.com/snapcore/snapd/overlord/snapstate/backend"
    53  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    54  	"github.com/snapcore/snapd/overlord/state"
    55  	"github.com/snapcore/snapd/release"
    56  	"github.com/snapcore/snapd/sandbox"
    57  	"github.com/snapcore/snapd/snap"
    58  	"github.com/snapcore/snapd/snap/snaptest"
    59  	"github.com/snapcore/snapd/store"
    60  	"github.com/snapcore/snapd/testutil"
    61  	"github.com/snapcore/snapd/timeutil"
    62  
    63  	// So it registers Configure.
    64  	_ "github.com/snapcore/snapd/overlord/configstate"
    65  )
    66  
    67  func TestSnapManager(t *testing.T) { TestingT(t) }
    68  
    69  type snapmgrTestSuite struct {
    70  	testutil.BaseTest
    71  	o       *overlord.Overlord
    72  	state   *state.State
    73  	se      *overlord.StateEngine
    74  	snapmgr *snapstate.SnapManager
    75  
    76  	fakeBackend *fakeSnappyBackend
    77  	fakeStore   *fakeStore
    78  
    79  	bl *bootloadertest.MockBootloader
    80  
    81  	user  *auth.UserState
    82  	user2 *auth.UserState
    83  	user3 *auth.UserState
    84  }
    85  
    86  func (s *snapmgrTestSuite) settle(c *C) {
    87  	err := s.o.Settle(testutil.HostScaledTimeout(5 * time.Second))
    88  	c.Assert(err, IsNil)
    89  }
    90  
    91  var _ = Suite(&snapmgrTestSuite{})
    92  
    93  var fakeRevDateEpoch = time.Date(2018, 1, 0, 0, 0, 0, 0, time.UTC)
    94  
    95  func (s *snapmgrTestSuite) SetUpTest(c *C) {
    96  	s.BaseTest.SetUpTest(c)
    97  	dirs.SetRootDir(c.MkDir())
    98  
    99  	s.o = overlord.Mock()
   100  	s.state = s.o.State()
   101  
   102  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
   103  
   104  	restoreCheckFreeSpace := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return nil })
   105  	s.AddCleanup(restoreCheckFreeSpace)
   106  
   107  	s.fakeBackend = &fakeSnappyBackend{}
   108  	s.fakeBackend.emptyContainer = emptyContainer(c)
   109  	s.fakeStore = &fakeStore{
   110  		fakeCurrentProgress: 75,
   111  		fakeTotalProgress:   100,
   112  		fakeBackend:         s.fakeBackend,
   113  		state:               s.state,
   114  	}
   115  
   116  	// setup a bootloader for policy and boot
   117  	s.bl = bootloadertest.Mock("mock", c.MkDir())
   118  	bootloader.Force(s.bl)
   119  	s.AddCleanup(func() { bootloader.Force(nil) })
   120  
   121  	oldSetupInstallHook := snapstate.SetupInstallHook
   122  	oldSetupPreRefreshHook := snapstate.SetupPreRefreshHook
   123  	oldSetupPostRefreshHook := snapstate.SetupPostRefreshHook
   124  	oldSetupRemoveHook := snapstate.SetupRemoveHook
   125  	oldSnapServiceOptions := snapstate.SnapServiceOptions
   126  	oldEnsureSnapAbsentFromQuotaGroup := snapstate.EnsureSnapAbsentFromQuotaGroup
   127  	snapstate.SetupInstallHook = hookstate.SetupInstallHook
   128  	snapstate.SetupPreRefreshHook = hookstate.SetupPreRefreshHook
   129  	snapstate.SetupPostRefreshHook = hookstate.SetupPostRefreshHook
   130  	snapstate.SetupRemoveHook = hookstate.SetupRemoveHook
   131  	snapstate.SnapServiceOptions = servicestate.SnapServiceOptions
   132  	snapstate.EnsureSnapAbsentFromQuotaGroup = servicestate.EnsureSnapAbsentFromQuota
   133  
   134  	var err error
   135  	s.snapmgr, err = snapstate.Manager(s.state, s.o.TaskRunner())
   136  	c.Assert(err, IsNil)
   137  
   138  	AddForeignTaskHandlers(s.o.TaskRunner(), s.fakeBackend)
   139  
   140  	snapstate.SetSnapManagerBackend(s.snapmgr, s.fakeBackend)
   141  
   142  	s.o.AddManager(s.snapmgr)
   143  	s.o.AddManager(s.o.TaskRunner())
   144  	s.se = s.o.StateEngine()
   145  	c.Assert(s.o.StartUp(), IsNil)
   146  
   147  	s.BaseTest.AddCleanup(snapstate.MockSnapReadInfo(s.fakeBackend.ReadInfo))
   148  	s.BaseTest.AddCleanup(snapstate.MockOpenSnapFile(s.fakeBackend.OpenSnapFile))
   149  	revDate := func(info *snap.Info) time.Time {
   150  		if info.Revision.Local() {
   151  			panic("no local revision should reach revisionDate")
   152  		}
   153  		// for convenience a date derived from the revision
   154  		return fakeRevDateEpoch.AddDate(0, 0, info.Revision.N)
   155  	}
   156  	s.BaseTest.AddCleanup(snapstate.MockRevisionDate(revDate))
   157  
   158  	s.BaseTest.AddCleanup(func() {
   159  		snapstate.SetupInstallHook = oldSetupInstallHook
   160  		snapstate.SetupPreRefreshHook = oldSetupPreRefreshHook
   161  		snapstate.SetupPostRefreshHook = oldSetupPostRefreshHook
   162  		snapstate.SetupRemoveHook = oldSetupRemoveHook
   163  		snapstate.SnapServiceOptions = oldSnapServiceOptions
   164  		snapstate.EnsureSnapAbsentFromQuotaGroup = oldEnsureSnapAbsentFromQuotaGroup
   165  
   166  		dirs.SetRootDir("/")
   167  	})
   168  
   169  	s.BaseTest.AddCleanup(snapstate.MockReRefreshRetryTimeout(time.Second / 200))
   170  	s.BaseTest.AddCleanup(snapstate.MockReRefreshUpdateMany(func(context.Context, *state.State, []string, int, snapstate.UpdateFilter, *snapstate.Flags, string) ([]string, []*state.TaskSet, error) {
   171  		return nil, nil, nil
   172  	}))
   173  
   174  	oldEstimateSnapshotSize := snapstate.EstimateSnapshotSize
   175  	snapstate.EstimateSnapshotSize = func(st *state.State, instanceName string, users []string) (uint64, error) {
   176  		return 1, nil
   177  	}
   178  	restoreInstallSize := snapstate.MockInstallSize(func(st *state.State, snaps []snapstate.MinimalInstallInfo, userID int) (uint64, error) {
   179  		return 0, nil
   180  	})
   181  	s.AddCleanup(restoreInstallSize)
   182  
   183  	oldAutomaticSnapshot := snapstate.AutomaticSnapshot
   184  	snapstate.AutomaticSnapshot = func(st *state.State, instanceName string) (ts *state.TaskSet, err error) {
   185  		task := st.NewTask("save-snapshot", "...")
   186  		ts = state.NewTaskSet(task)
   187  		return ts, nil
   188  	}
   189  
   190  	oldAutomaticSnapshotExpiration := snapstate.AutomaticSnapshotExpiration
   191  	snapstate.AutomaticSnapshotExpiration = func(st *state.State) (time.Duration, error) { return 1, nil }
   192  	s.BaseTest.AddCleanup(func() {
   193  		snapstate.EstimateSnapshotSize = oldEstimateSnapshotSize
   194  		snapstate.AutomaticSnapshot = oldAutomaticSnapshot
   195  		snapstate.AutomaticSnapshotExpiration = oldAutomaticSnapshotExpiration
   196  	})
   197  
   198  	s.state.Lock()
   199  	snapstate.ReplaceStore(s.state, s.fakeStore)
   200  	s.user, err = auth.NewUser(s.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   201  	c.Assert(err, IsNil)
   202  	s.user2, err = auth.NewUser(s.state, "username2", "email2@test.com", "macaroon2", []string{"discharge2"})
   203  	c.Assert(err, IsNil)
   204  	// 3 has no store auth
   205  	s.user3, err = auth.NewUser(s.state, "username3", "email2@test.com", "", nil)
   206  	c.Assert(err, IsNil)
   207  
   208  	s.state.Set("seeded", true)
   209  	s.state.Set("seed-time", time.Now())
   210  
   211  	r := snapstatetest.MockDeviceModel(DefaultModel())
   212  	s.BaseTest.AddCleanup(r)
   213  
   214  	s.state.Set("refresh-privacy-key", "privacy-key")
   215  	snapstate.Set(s.state, "core", &snapstate.SnapState{
   216  		Active: true,
   217  		Sequence: []*snap.SideInfo{
   218  			{RealName: "core", Revision: snap.R(1)},
   219  		},
   220  		Current:  snap.R(1),
   221  		SnapType: "os",
   222  	})
   223  
   224  	repo := interfaces.NewRepository()
   225  	ifacerepo.Replace(s.state, repo)
   226  	s.state.Unlock()
   227  
   228  	snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) {
   229  		return nil, nil
   230  	}
   231  
   232  	s.AddCleanup(snapstate.MockSecurityProfilesDiscardLate(func(snapName string, rev snap.Revision, typ snap.Type) error {
   233  		return nil
   234  	}))
   235  }
   236  
   237  func (s *snapmgrTestSuite) TearDownTest(c *C) {
   238  	s.BaseTest.TearDownTest(c)
   239  	snapstate.ValidateRefreshes = nil
   240  	snapstate.AutoAliases = nil
   241  	snapstate.CanAutoRefresh = nil
   242  }
   243  
   244  type ForeignTaskTracker interface {
   245  	ForeignTask(kind string, status state.Status, snapsup *snapstate.SnapSetup)
   246  }
   247  
   248  func AddForeignTaskHandlers(runner *state.TaskRunner, tracker ForeignTaskTracker) {
   249  	// Add fake handlers for tasks handled by interfaces manager
   250  	fakeHandler := func(task *state.Task, _ *tomb.Tomb) error {
   251  		task.State().Lock()
   252  		kind := task.Kind()
   253  		status := task.Status()
   254  		snapsup, err := snapstate.TaskSnapSetup(task)
   255  		task.State().Unlock()
   256  		if err != nil {
   257  			return err
   258  		}
   259  
   260  		tracker.ForeignTask(kind, status, snapsup)
   261  
   262  		return nil
   263  	}
   264  	runner.AddHandler("setup-profiles", fakeHandler, fakeHandler)
   265  	runner.AddHandler("auto-connect", fakeHandler, nil)
   266  	runner.AddHandler("auto-disconnect", fakeHandler, nil)
   267  	runner.AddHandler("remove-profiles", fakeHandler, fakeHandler)
   268  	runner.AddHandler("discard-conns", fakeHandler, fakeHandler)
   269  	runner.AddHandler("validate-snap", fakeHandler, nil)
   270  	runner.AddHandler("transition-ubuntu-core", fakeHandler, nil)
   271  	runner.AddHandler("transition-to-snapd-snap", fakeHandler, nil)
   272  
   273  	// Add handler to test full aborting of changes
   274  	erroringHandler := func(task *state.Task, _ *tomb.Tomb) error {
   275  		return errors.New("error out")
   276  	}
   277  	runner.AddHandler("error-trigger", erroringHandler, nil)
   278  
   279  	runner.AddHandler("save-snapshot", func(task *state.Task, _ *tomb.Tomb) error {
   280  		return nil
   281  	}, nil)
   282  	runner.AddHandler("run-hook", func(task *state.Task, _ *tomb.Tomb) error {
   283  		return nil
   284  	}, nil)
   285  	runner.AddHandler("configure-snapd", func(t *state.Task, _ *tomb.Tomb) error {
   286  		return nil
   287  	}, nil)
   288  
   289  }
   290  
   291  func (s *snapmgrTestSuite) TestCleanSnapStateGet(c *C) {
   292  	snapst := snapstate.SnapState{
   293  		Sequence: []*snap.SideInfo{
   294  			{RealName: "foo", Revision: snap.R(1)},
   295  		},
   296  		Current:         snap.R(1),
   297  		SnapType:        "os",
   298  		TrackingChannel: "foo/stable",
   299  		InstanceKey:     "bar",
   300  	}
   301  
   302  	s.state.Lock()
   303  
   304  	defer s.state.Unlock()
   305  	snapstate.Set(s.state, "no-instance-key", &snapstate.SnapState{
   306  		Sequence: []*snap.SideInfo{
   307  			{RealName: "core", Revision: snap.R(1)},
   308  		},
   309  		Current:  snap.R(1),
   310  		SnapType: "app",
   311  	})
   312  
   313  	err := snapstate.Get(s.state, "bar", nil)
   314  	c.Assert(err, ErrorMatches, "internal error: snapst is nil")
   315  
   316  	err = snapstate.Get(s.state, "no-instance-key", &snapst)
   317  	c.Assert(err, IsNil)
   318  	c.Assert(snapst, DeepEquals, snapstate.SnapState{
   319  		Sequence: []*snap.SideInfo{
   320  			{RealName: "core", Revision: snap.R(1)},
   321  		},
   322  		Current:  snap.R(1),
   323  		SnapType: "app",
   324  	})
   325  }
   326  
   327  func (s *snapmgrTestSuite) TestStore(c *C) {
   328  	s.state.Lock()
   329  	defer s.state.Unlock()
   330  
   331  	sto := &store.Store{}
   332  	snapstate.ReplaceStore(s.state, sto)
   333  	store1 := snapstate.Store(s.state, nil)
   334  	c.Check(store1, Equals, sto)
   335  
   336  	// cached
   337  	store2 := snapstate.Store(s.state, nil)
   338  	c.Check(store2, Equals, sto)
   339  }
   340  
   341  func (s *snapmgrTestSuite) TestStoreWithDeviceContext(c *C) {
   342  	s.state.Lock()
   343  	defer s.state.Unlock()
   344  
   345  	stoA := &store.Store{}
   346  	snapstate.ReplaceStore(s.state, stoA)
   347  	store1 := snapstate.Store(s.state, nil)
   348  	c.Check(store1, Equals, stoA)
   349  
   350  	stoB := &store.Store{}
   351  
   352  	// cached
   353  	store2 := snapstate.Store(s.state, &snapstatetest.TrivialDeviceContext{})
   354  	c.Check(store2, Equals, stoA)
   355  
   356  	// from context
   357  	store3 := snapstate.Store(s.state, &snapstatetest.TrivialDeviceContext{CtxStore: stoB})
   358  	c.Check(store3, Equals, stoB)
   359  }
   360  
   361  func (s *snapmgrTestSuite) TestUserFromUserID(c *C) {
   362  	s.state.Lock()
   363  	defer s.state.Unlock()
   364  
   365  	tests := []struct {
   366  		ids     []int
   367  		u       *auth.UserState
   368  		invalid bool
   369  	}{
   370  		{[]int{0}, nil, false},
   371  		{[]int{2}, s.user2, false},
   372  		{[]int{99}, nil, true},
   373  		{[]int{1, 99}, s.user, false},
   374  		{[]int{99, 0}, nil, false},
   375  		{[]int{99, 2}, s.user2, false},
   376  		{[]int{99, 100}, nil, true},
   377  	}
   378  
   379  	for _, t := range tests {
   380  		u, err := snapstate.UserFromUserID(s.state, t.ids...)
   381  		c.Check(u, DeepEquals, t.u)
   382  		if t.invalid {
   383  			c.Check(err, Equals, auth.ErrInvalidUser)
   384  		} else {
   385  			c.Check(err, IsNil)
   386  		}
   387  	}
   388  }
   389  
   390  const (
   391  	unlinkBefore = 1 << iota
   392  	cleanupAfter
   393  	maybeCore
   394  	runCoreConfigure
   395  	doesReRefresh
   396  	updatesGadget
   397  	updatesBootConfig
   398  	noConfigure
   399  )
   400  
   401  func taskKinds(tasks []*state.Task) []string {
   402  	kinds := make([]string, len(tasks))
   403  	for i, task := range tasks {
   404  		k := task.Kind()
   405  		if k == "run-hook" {
   406  			var hooksup hookstate.HookSetup
   407  			if err := task.Get("hook-setup", &hooksup); err != nil {
   408  				panic(err)
   409  			}
   410  			k = fmt.Sprintf("%s[%s]", k, hooksup.Hook)
   411  		}
   412  		kinds[i] = k
   413  	}
   414  	return kinds
   415  }
   416  
   417  func verifyLastTasksetIsReRefresh(c *C, tts []*state.TaskSet) {
   418  	ts := tts[len(tts)-1]
   419  	c.Assert(ts.Tasks(), HasLen, 1)
   420  	reRefresh := ts.Tasks()[0]
   421  	c.Check(reRefresh.Kind(), Equals, "check-rerefresh")
   422  	// nothing should wait on it
   423  	c.Check(reRefresh.NumHaltTasks(), Equals, 0)
   424  }
   425  
   426  func verifyRemoveTasks(c *C, ts *state.TaskSet) {
   427  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
   428  		"stop-snap-services",
   429  		"run-hook[remove]",
   430  		"auto-disconnect",
   431  		"save-snapshot",
   432  		"remove-aliases",
   433  		"unlink-snap",
   434  		"remove-profiles",
   435  		"clear-snap",
   436  		"discard-snap",
   437  	})
   438  	verifyStopReason(c, ts, "remove")
   439  }
   440  
   441  func verifyCoreRemoveTasks(c *C, ts *state.TaskSet) {
   442  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
   443  		"stop-snap-services",
   444  		"run-hook[remove]",
   445  		"auto-disconnect",
   446  		"remove-aliases",
   447  		"unlink-snap",
   448  		"remove-profiles",
   449  		"clear-snap",
   450  		"discard-snap",
   451  	})
   452  	verifyStopReason(c, ts, "remove")
   453  }
   454  
   455  func checkIsAutoRefresh(c *C, tasks []*state.Task, expected bool) {
   456  	for _, t := range tasks {
   457  		if t.Kind() == "download-snap" {
   458  			var snapsup snapstate.SnapSetup
   459  			err := t.Get("snap-setup", &snapsup)
   460  			c.Assert(err, IsNil)
   461  			c.Check(snapsup.IsAutoRefresh, Equals, expected)
   462  			return
   463  		}
   464  	}
   465  	c.Fatalf("cannot find download-snap task in %v", tasks)
   466  }
   467  
   468  func (s *snapmgrTestSuite) TestLastIndexFindsLast(c *C) {
   469  	snapst := &snapstate.SnapState{Sequence: []*snap.SideInfo{
   470  		{Revision: snap.R(7)},
   471  		{Revision: snap.R(11)},
   472  		{Revision: snap.R(11)},
   473  	}}
   474  	c.Check(snapst.LastIndex(snap.R(11)), Equals, 2)
   475  }
   476  
   477  func maybeMockClassicSupport(c *C) (restore func()) {
   478  	if dirs.SupportsClassicConfinement() {
   479  		return func() {}
   480  	}
   481  
   482  	d := filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd/snap")
   483  	err := os.MkdirAll(d, 0755)
   484  	c.Assert(err, IsNil)
   485  	snapSymlink := filepath.Join(dirs.GlobalRootDir, "snap")
   486  	err = os.Symlink(d, snapSymlink)
   487  	c.Assert(err, IsNil)
   488  
   489  	return func() { os.Remove(snapSymlink) }
   490  }
   491  
   492  type fullFlags struct{ before, change, after, setup snapstate.Flags }
   493  
   494  func (s *snapmgrTestSuite) testRevertTasksFullFlags(flags fullFlags, c *C) {
   495  	s.state.Lock()
   496  	defer s.state.Unlock()
   497  
   498  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   499  		Active: true,
   500  		Sequence: []*snap.SideInfo{
   501  			{RealName: "some-snap", Revision: snap.R(7)},
   502  			{RealName: "some-snap", Revision: snap.R(11)},
   503  		},
   504  		Flags:    flags.before,
   505  		Current:  snap.R(11),
   506  		SnapType: "app",
   507  	})
   508  
   509  	ts, err := snapstate.Revert(s.state, "some-snap", flags.change)
   510  	c.Assert(err, IsNil)
   511  
   512  	tasks := ts.Tasks()
   513  	c.Assert(s.state.TaskCount(), Equals, len(tasks))
   514  	c.Assert(taskKinds(tasks), DeepEquals, []string{
   515  		"prerequisites",
   516  		"prepare-snap",
   517  		"stop-snap-services",
   518  		"remove-aliases",
   519  		"unlink-current-snap",
   520  		"setup-profiles",
   521  		"link-snap",
   522  		"auto-connect",
   523  		"set-auto-aliases",
   524  		"setup-aliases",
   525  		"start-snap-services",
   526  		"run-hook[configure]",
   527  		"run-hook[check-health]",
   528  	})
   529  	// a revert is a special refresh
   530  	verifyStopReason(c, ts, "refresh")
   531  
   532  	snapsup, err := snapstate.TaskSnapSetup(tasks[0])
   533  	c.Assert(err, IsNil)
   534  	flags.setup.Revert = true
   535  	c.Check(snapsup.Flags, Equals, flags.setup)
   536  	c.Check(snapsup.Type, Equals, snap.TypeApp)
   537  
   538  	chg := s.state.NewChange("revert", "revert snap")
   539  	chg.AddAll(ts)
   540  
   541  	s.state.Unlock()
   542  	defer s.se.Stop()
   543  	s.settle(c)
   544  	s.state.Lock()
   545  
   546  	var snapst snapstate.SnapState
   547  	err = snapstate.Get(s.state, "some-snap", &snapst)
   548  	c.Assert(err, IsNil)
   549  	c.Check(snapst.Flags, Equals, flags.after)
   550  }
   551  
   552  func (s *snapmgrTestSuite) testRevertTasks(flags snapstate.Flags, c *C) {
   553  	s.testRevertTasksFullFlags(fullFlags{before: flags, change: flags, after: flags, setup: flags}, c)
   554  }
   555  
   556  func (s *snapmgrTestSuite) TestRevertTasks(c *C) {
   557  	s.testRevertTasks(snapstate.Flags{}, c)
   558  }
   559  
   560  func (s *snapmgrTestSuite) TestRevertTasksFromDevMode(c *C) {
   561  	// the snap is installed in devmode, but the request to revert does not specify devmode
   562  	s.testRevertTasksFullFlags(fullFlags{
   563  		before: snapstate.Flags{DevMode: true}, // the snap is installed in devmode
   564  		change: snapstate.Flags{},              // the request to revert does not specify devmode
   565  		after:  snapstate.Flags{DevMode: true}, // the reverted snap is installed in devmode
   566  		setup:  snapstate.Flags{DevMode: true}, // because setup said so
   567  	}, c)
   568  }
   569  
   570  func (s *snapmgrTestSuite) TestRevertTasksFromJailMode(c *C) {
   571  	// the snap is installed in jailmode, but the request to revert does not specify jailmode
   572  	s.testRevertTasksFullFlags(fullFlags{
   573  		before: snapstate.Flags{JailMode: true}, // the snap is installed in jailmode
   574  		change: snapstate.Flags{},               // the request to revert does not specify jailmode
   575  		after:  snapstate.Flags{JailMode: true}, // the reverted snap is installed in jailmode
   576  		setup:  snapstate.Flags{JailMode: true}, // because setup said so
   577  	}, c)
   578  }
   579  
   580  func (s *snapmgrTestSuite) TestRevertTasksFromClassic(c *C) {
   581  	restore := maybeMockClassicSupport(c)
   582  	defer restore()
   583  
   584  	// the snap is installed in classic, but the request to revert does not specify classic
   585  	s.testRevertTasksFullFlags(fullFlags{
   586  		before: snapstate.Flags{Classic: true}, // the snap is installed in classic
   587  		change: snapstate.Flags{},              // the request to revert does not specify classic
   588  		after:  snapstate.Flags{Classic: true}, // the reverted snap is installed in classic
   589  		setup:  snapstate.Flags{Classic: true}, // because setup said so
   590  	}, c)
   591  }
   592  
   593  func (s *snapmgrTestSuite) TestRevertTasksDevMode(c *C) {
   594  	s.testRevertTasks(snapstate.Flags{DevMode: true}, c)
   595  }
   596  
   597  func (s *snapmgrTestSuite) TestRevertTasksJailMode(c *C) {
   598  	s.testRevertTasks(snapstate.Flags{JailMode: true}, c)
   599  }
   600  
   601  func (s *snapmgrTestSuite) TestRevertTasksClassic(c *C) {
   602  	restore := maybeMockClassicSupport(c)
   603  	defer restore()
   604  
   605  	s.testRevertTasks(snapstate.Flags{Classic: true}, c)
   606  }
   607  
   608  func (s *snapmgrTestSuite) TestRevertCreatesNoGCTasks(c *C) {
   609  	s.state.Lock()
   610  	defer s.state.Unlock()
   611  
   612  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   613  		Active:   true,
   614  		SnapType: "app",
   615  		Sequence: []*snap.SideInfo{
   616  			{RealName: "some-snap", Revision: snap.R(1)},
   617  			{RealName: "some-snap", Revision: snap.R(2)},
   618  			{RealName: "some-snap", Revision: snap.R(3)},
   619  			{RealName: "some-snap", Revision: snap.R(4)},
   620  		},
   621  		Current: snap.R(2),
   622  	})
   623  
   624  	ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(4), snapstate.Flags{})
   625  	c.Assert(err, IsNil)
   626  
   627  	// ensure that we do not run any form of garbage-collection
   628  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   629  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
   630  		"prerequisites",
   631  		"prepare-snap",
   632  		"stop-snap-services",
   633  		"remove-aliases",
   634  		"unlink-current-snap",
   635  		"setup-profiles",
   636  		"link-snap",
   637  		"auto-connect",
   638  		"set-auto-aliases",
   639  		"setup-aliases",
   640  		"start-snap-services",
   641  		"run-hook[configure]",
   642  		"run-hook[check-health]",
   643  	})
   644  }
   645  
   646  func (s *snapmgrTestSuite) TestEnableTasks(c *C) {
   647  	s.state.Lock()
   648  	defer s.state.Unlock()
   649  
   650  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   651  		Sequence: []*snap.SideInfo{
   652  			{RealName: "some-snap", Revision: snap.R(11)},
   653  		},
   654  		Current: snap.R(11),
   655  		Active:  false,
   656  	})
   657  
   658  	ts, err := snapstate.Enable(s.state, "some-snap")
   659  	c.Assert(err, IsNil)
   660  
   661  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   662  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
   663  		"prepare-snap",
   664  		"setup-profiles",
   665  		"link-snap",
   666  		"setup-aliases",
   667  		"start-snap-services",
   668  	})
   669  }
   670  
   671  func (s *snapmgrTestSuite) TestSwitchTasks(c *C) {
   672  	s.state.Lock()
   673  	defer s.state.Unlock()
   674  
   675  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   676  		Sequence: []*snap.SideInfo{
   677  			{RealName: "some-snap", Revision: snap.R(11)},
   678  		},
   679  		Current: snap.R(11),
   680  		Active:  false,
   681  	})
   682  
   683  	ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"})
   684  	c.Assert(err, IsNil)
   685  
   686  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   687  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{"switch-snap"})
   688  }
   689  
   690  func (s *snapmgrTestSuite) TestSwitchConflict(c *C) {
   691  	s.state.Lock()
   692  	defer s.state.Unlock()
   693  
   694  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   695  		Sequence: []*snap.SideInfo{
   696  			{RealName: "some-snap", Revision: snap.R(11)},
   697  		},
   698  		Current: snap.R(11),
   699  		Active:  false,
   700  	})
   701  
   702  	ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"})
   703  	c.Assert(err, IsNil)
   704  	// need a change to make the tasks visible
   705  	s.state.NewChange("switch-snap", "...").AddAll(ts)
   706  
   707  	_, err = snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "other-channel"})
   708  	c.Check(err, ErrorMatches, `snap "some-snap" has "switch-snap" change in progress`)
   709  }
   710  
   711  func (s *snapmgrTestSuite) TestSwitchUnhappy(c *C) {
   712  	s.state.Lock()
   713  	defer s.state.Unlock()
   714  
   715  	_, err := snapstate.Switch(s.state, "non-existing-snap", &snapstate.RevisionOptions{Channel: "some-channel"})
   716  	c.Assert(err, ErrorMatches, `snap "non-existing-snap" is not installed`)
   717  }
   718  
   719  func (s *snapmgrTestSuite) TestSwitchRevision(c *C) {
   720  	s.state.Lock()
   721  	defer s.state.Unlock()
   722  
   723  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   724  		Sequence: []*snap.SideInfo{
   725  			{RealName: "some-snap", Revision: snap.R(11)},
   726  		},
   727  		Current: snap.R(11),
   728  	})
   729  
   730  	_, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(42)})
   731  	c.Assert(err, ErrorMatches, "cannot switch revision")
   732  }
   733  
   734  func (s *snapmgrTestSuite) TestSwitchKernelTrackForbidden(c *C) {
   735  	s.state.Lock()
   736  	defer s.state.Unlock()
   737  
   738  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
   739  	defer r()
   740  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
   741  		Sequence: []*snap.SideInfo{
   742  			{RealName: "kernel", Revision: snap.R(11)},
   743  		},
   744  		TrackingChannel: "18/stable",
   745  		Current:         snap.R(11),
   746  		Active:          true,
   747  	})
   748  
   749  	_, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"})
   750  	c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`)
   751  }
   752  
   753  func (s *snapmgrTestSuite) TestSwitchKernelTrackRiskOnlyIsOK(c *C) {
   754  	s.state.Lock()
   755  	defer s.state.Unlock()
   756  
   757  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
   758  	defer r()
   759  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
   760  		Sequence: []*snap.SideInfo{
   761  			{RealName: "kernel", Revision: snap.R(11)},
   762  		},
   763  		TrackingChannel: "18/stable",
   764  		Current:         snap.R(11),
   765  		Active:          true,
   766  	})
   767  
   768  	_, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "18/beta"})
   769  	c.Assert(err, IsNil)
   770  }
   771  
   772  func (s *snapmgrTestSuite) TestSwitchKernelTrackRiskOnlyDefaultTrackIsOK(c *C) {
   773  	s.state.Lock()
   774  	defer s.state.Unlock()
   775  
   776  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
   777  	defer r()
   778  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
   779  		Sequence: []*snap.SideInfo{
   780  			{RealName: "kernel", Revision: snap.R(11)},
   781  		},
   782  		TrackingChannel: "18/stable",
   783  		Current:         snap.R(11),
   784  		Active:          true,
   785  	})
   786  
   787  	_, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "beta"})
   788  	c.Assert(err, IsNil)
   789  }
   790  
   791  func (s *snapmgrTestSuite) TestSwitchGadgetTrackForbidden(c *C) {
   792  	s.state.Lock()
   793  	defer s.state.Unlock()
   794  
   795  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
   796  	defer r()
   797  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
   798  		Sequence: []*snap.SideInfo{
   799  			{RealName: "brand-gadget", Revision: snap.R(11)},
   800  		},
   801  		TrackingChannel: "18/stable",
   802  		Current:         snap.R(11),
   803  		Active:          true,
   804  	})
   805  
   806  	_, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"})
   807  	c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`)
   808  }
   809  
   810  func (s *snapmgrTestSuite) TestSwitchGadgetTrackRiskOnlyIsOK(c *C) {
   811  	s.state.Lock()
   812  	defer s.state.Unlock()
   813  
   814  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
   815  	defer r()
   816  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
   817  		Sequence: []*snap.SideInfo{
   818  			{RealName: "brand-gadget", Revision: snap.R(11)},
   819  		},
   820  		TrackingChannel: "18/stable",
   821  		Current:         snap.R(11),
   822  		Active:          true,
   823  	})
   824  
   825  	_, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "18/beta"})
   826  	c.Assert(err, IsNil)
   827  }
   828  
   829  func (s *snapmgrTestSuite) TestSwitchGadgetTrackRiskOnlyDefaultTrackIsOK(c *C) {
   830  	s.state.Lock()
   831  	defer s.state.Unlock()
   832  
   833  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
   834  	defer r()
   835  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
   836  		Sequence: []*snap.SideInfo{
   837  			{RealName: "brand-gadget", Revision: snap.R(11)},
   838  		},
   839  		TrackingChannel: "18/stable",
   840  		Current:         snap.R(11),
   841  		Active:          true,
   842  	})
   843  
   844  	_, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "beta"})
   845  	c.Assert(err, IsNil)
   846  }
   847  
   848  func (s *snapmgrTestSuite) TestDisableTasks(c *C) {
   849  	s.state.Lock()
   850  	defer s.state.Unlock()
   851  
   852  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   853  		Sequence: []*snap.SideInfo{
   854  			{RealName: "some-snap", Revision: snap.R(11)},
   855  		},
   856  		Current: snap.R(11),
   857  		Active:  true,
   858  	})
   859  
   860  	ts, err := snapstate.Disable(s.state, "some-snap")
   861  	c.Assert(err, IsNil)
   862  
   863  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   864  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
   865  		"stop-snap-services",
   866  		"remove-aliases",
   867  		"unlink-snap",
   868  		"remove-profiles",
   869  	})
   870  	verifyStopReason(c, ts, "disable")
   871  }
   872  
   873  func (s *snapmgrTestSuite) TestEnableConflict(c *C) {
   874  	s.state.Lock()
   875  	defer s.state.Unlock()
   876  
   877  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   878  		Sequence: []*snap.SideInfo{
   879  			{RealName: "some-snap", Revision: snap.R(11)},
   880  		},
   881  		Current: snap.R(11),
   882  		Active:  false,
   883  	})
   884  
   885  	ts, err := snapstate.Enable(s.state, "some-snap")
   886  	c.Assert(err, IsNil)
   887  	// need a change to make the tasks visible
   888  	s.state.NewChange("enable", "...").AddAll(ts)
   889  
   890  	_, err = snapstate.Enable(s.state, "some-snap")
   891  	c.Assert(err, ErrorMatches, `snap "some-snap" has "enable" change in progress`)
   892  }
   893  
   894  func (s *snapmgrTestSuite) TestDisableConflict(c *C) {
   895  	s.state.Lock()
   896  	defer s.state.Unlock()
   897  
   898  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   899  		Sequence: []*snap.SideInfo{
   900  			{RealName: "some-snap", Revision: snap.R(11)},
   901  		},
   902  		Current: snap.R(11),
   903  		Active:  true,
   904  	})
   905  
   906  	ts, err := snapstate.Disable(s.state, "some-snap")
   907  	c.Assert(err, IsNil)
   908  	// need a change to make the tasks visible
   909  	s.state.NewChange("install", "...").AddAll(ts)
   910  
   911  	_, err = snapstate.Disable(s.state, "some-snap")
   912  	c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`)
   913  }
   914  
   915  func (s *snapmgrTestSuite) TestDoInstallWithSlots(c *C) {
   916  	s.state.Lock()
   917  	defer s.state.Unlock()
   918  
   919  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
   920  
   921  	ts, err := snapstate.Install(context.Background(), s.state, "snap-content-slot", nil, 0, snapstate.Flags{})
   922  	c.Assert(err, IsNil)
   923  
   924  	var snapsup snapstate.SnapSetup
   925  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
   926  	c.Assert(err, IsNil)
   927  
   928  	c.Check(snapsup.PlugsOnly, Equals, false)
   929  }
   930  
   931  func (s *snapmgrTestSuite) TestDoUpdateHadSlots(c *C) {
   932  	s.state.Lock()
   933  	defer s.state.Unlock()
   934  
   935  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   936  		Active: true,
   937  		Sequence: []*snap.SideInfo{
   938  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
   939  		},
   940  		Current:  snap.R(4),
   941  		SnapType: "app",
   942  	})
   943  
   944  	snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
   945  		if name != "some-snap" {
   946  			return s.fakeBackend.ReadInfo(name, si)
   947  		}
   948  
   949  		info := &snap.Info{
   950  			SideInfo: *si,
   951  			SnapType: snap.TypeApp,
   952  		}
   953  		info.Slots = map[string]*snap.SlotInfo{
   954  			"some-slot": {
   955  				Snap:      info,
   956  				Name:      "shared-content",
   957  				Interface: "content",
   958  				Attrs: map[string]interface{}{
   959  					"content": "shared-content",
   960  				},
   961  			},
   962  		}
   963  		return info, nil
   964  	})
   965  
   966  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{})
   967  	c.Assert(err, IsNil)
   968  
   969  	var snapsup snapstate.SnapSetup
   970  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
   971  	c.Assert(err, IsNil)
   972  
   973  	c.Check(snapsup.PlugsOnly, Equals, false)
   974  }
   975  
   976  func (s *snapmgrTestSuite) TestDisableSnapDisabledServicesSaved(c *C) {
   977  	s.state.Lock()
   978  	defer s.state.Unlock()
   979  
   980  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
   981  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
   982  
   983  	// reset the services to what they were before after the test is done
   984  	defer func() {
   985  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
   986  	}()
   987  
   988  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
   989  		Sequence: []*snap.SideInfo{
   990  			{RealName: "services-snap", Revision: snap.R(11)},
   991  		},
   992  		Current: snap.R(11),
   993  		Active:  true,
   994  	})
   995  
   996  	disableChg := s.state.NewChange("disable", "disable a snap")
   997  	ts, err := snapstate.Disable(s.state, "services-snap")
   998  	c.Assert(err, IsNil)
   999  	disableChg.AddAll(ts)
  1000  
  1001  	s.state.Unlock()
  1002  	defer s.se.Stop()
  1003  	s.settle(c)
  1004  	s.state.Lock()
  1005  
  1006  	c.Assert(disableChg.Err(), IsNil)
  1007  	c.Assert(disableChg.IsReady(), Equals, true)
  1008  
  1009  	// get the snap state
  1010  	var snapst snapstate.SnapState
  1011  	err = snapstate.Get(s.state, "services-snap", &snapst)
  1012  	c.Assert(err, IsNil)
  1013  
  1014  	// make sure that the disabled services in this snap's state is what we
  1015  	// provided
  1016  	sort.Strings(snapst.LastActiveDisabledServices)
  1017  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  1018  }
  1019  func (s *snapmgrTestSuite) TestEnableSnapDisabledServicesPassedAroundHappy(c *C) {
  1020  	s.state.Lock()
  1021  	defer s.state.Unlock()
  1022  
  1023  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  1024  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  1025  
  1026  	// reset the services to what they were before after the test is done
  1027  	defer func() {
  1028  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  1029  	}()
  1030  
  1031  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1032  		Sequence: []*snap.SideInfo{
  1033  			{RealName: "services-snap", Revision: snap.R(11)},
  1034  		},
  1035  		Current: snap.R(11),
  1036  		Active:  true,
  1037  	})
  1038  
  1039  	disableChg := s.state.NewChange("disable", "disable a snap")
  1040  	disableTs, err := snapstate.Disable(s.state, "services-snap")
  1041  	c.Assert(err, IsNil)
  1042  	disableChg.AddAll(disableTs)
  1043  
  1044  	s.state.Unlock()
  1045  	defer s.se.Stop()
  1046  	s.settle(c)
  1047  	s.state.Lock()
  1048  
  1049  	c.Assert(disableChg.Err(), IsNil)
  1050  	c.Assert(disableChg.IsReady(), Equals, true)
  1051  
  1052  	// get the snap state
  1053  	var snapst snapstate.SnapState
  1054  	err = snapstate.Get(s.state, "services-snap", &snapst)
  1055  	c.Assert(err, IsNil)
  1056  
  1057  	// make sure that the disabled services in this snap's state is what we
  1058  	// provided
  1059  	sort.Strings(snapst.LastActiveDisabledServices)
  1060  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  1061  
  1062  	enableChg := s.state.NewChange("enable", "disable a snap")
  1063  	enableTs, err := snapstate.Enable(s.state, "services-snap")
  1064  	c.Assert(err, IsNil)
  1065  	enableChg.AddAll(enableTs)
  1066  
  1067  	s.state.Unlock()
  1068  	defer s.se.Stop()
  1069  	s.settle(c)
  1070  	s.state.Lock()
  1071  
  1072  	c.Assert(enableChg.Err(), IsNil)
  1073  	c.Assert(enableChg.IsReady(), Equals, true)
  1074  
  1075  	// check the ops that will be provided disabledServices
  1076  	svcStateOp := s.fakeBackend.ops.First("current-snap-service-states")
  1077  	c.Assert(svcStateOp, Not(IsNil))
  1078  	c.Assert(svcStateOp.disabledServices, DeepEquals, []string{"svc1", "svc2"})
  1079  
  1080  	linkStateOp := s.fakeBackend.ops.First("start-snap-services")
  1081  	c.Assert(linkStateOp, Not(IsNil))
  1082  	c.Assert(linkStateOp.disabledServices, DeepEquals, []string{"svc1", "svc2"})
  1083  }
  1084  
  1085  func (s *snapmgrTestSuite) TestEnableSnapDisabledServicesNotSaved(c *C) {
  1086  	s.state.Lock()
  1087  	defer s.state.Unlock()
  1088  
  1089  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  1090  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  1091  
  1092  	// reset the services to what they were before after the test is done
  1093  	defer func() {
  1094  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  1095  	}()
  1096  
  1097  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1098  		Sequence: []*snap.SideInfo{
  1099  			{RealName: "services-snap", Revision: snap.R(11)},
  1100  		},
  1101  		Current: snap.R(11),
  1102  		Active:  true,
  1103  	})
  1104  
  1105  	disableChg := s.state.NewChange("disable", "disable a snap")
  1106  	disableTs, err := snapstate.Disable(s.state, "services-snap")
  1107  	c.Assert(err, IsNil)
  1108  	disableChg.AddAll(disableTs)
  1109  
  1110  	s.state.Unlock()
  1111  	defer s.se.Stop()
  1112  	s.settle(c)
  1113  	s.state.Lock()
  1114  
  1115  	c.Assert(disableChg.Err(), IsNil)
  1116  	c.Assert(disableChg.IsReady(), Equals, true)
  1117  
  1118  	// get the snap state
  1119  	var snapst snapstate.SnapState
  1120  	err = snapstate.Get(s.state, "services-snap", &snapst)
  1121  	c.Assert(err, IsNil)
  1122  
  1123  	// make sure that the disabled services in this snap's state is what we
  1124  	// provided
  1125  	sort.Strings(snapst.LastActiveDisabledServices)
  1126  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  1127  
  1128  	enableChg := s.state.NewChange("enable", "disable a snap")
  1129  	enableTs, err := snapstate.Enable(s.state, "services-snap")
  1130  	c.Assert(err, IsNil)
  1131  	enableChg.AddAll(enableTs)
  1132  
  1133  	s.state.Unlock()
  1134  	defer s.se.Stop()
  1135  	s.settle(c)
  1136  	s.state.Lock()
  1137  
  1138  	c.Assert(enableChg.Err(), IsNil)
  1139  	c.Assert(enableChg.IsReady(), Equals, true)
  1140  
  1141  	// get the snap state again
  1142  	err = snapstate.Get(s.state, "services-snap", &snapst)
  1143  	c.Assert(err, IsNil)
  1144  
  1145  	// make sure that there is nothing in the last active disabled services list
  1146  	// because we re-enabled the snap and there should be nothing we have to
  1147  	// keep track of in the state anymore
  1148  	c.Assert(snapst.LastActiveDisabledServices, HasLen, 0)
  1149  }
  1150  
  1151  func (s *snapmgrTestSuite) TestEnableSnapMissingDisabledServicesMergedAndSaved(c *C) {
  1152  	s.state.Lock()
  1153  	defer s.state.Unlock()
  1154  
  1155  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  1156  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  1157  
  1158  	// reset the services to what they were before after the test is done
  1159  	defer func() {
  1160  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  1161  	}()
  1162  
  1163  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1164  		Sequence: []*snap.SideInfo{
  1165  			{RealName: "services-snap", Revision: snap.R(11)},
  1166  		},
  1167  		Current: snap.R(11),
  1168  		Active:  true,
  1169  		// keep this to make gofmt 1.10 happy
  1170  		LastActiveDisabledServices: []string{"missing-svc3"},
  1171  	})
  1172  
  1173  	disableChg := s.state.NewChange("disable", "disable a snap")
  1174  	disableTs, err := snapstate.Disable(s.state, "services-snap")
  1175  	c.Assert(err, IsNil)
  1176  	disableChg.AddAll(disableTs)
  1177  
  1178  	s.state.Unlock()
  1179  	defer s.se.Stop()
  1180  	s.settle(c)
  1181  	s.state.Lock()
  1182  
  1183  	c.Assert(disableChg.Err(), IsNil)
  1184  	c.Assert(disableChg.IsReady(), Equals, true)
  1185  
  1186  	// get the snap state
  1187  	var snapst snapstate.SnapState
  1188  	err = snapstate.Get(s.state, "services-snap", &snapst)
  1189  	c.Assert(err, IsNil)
  1190  
  1191  	// make sure that the disabled services in this snap's state is what we
  1192  	// provided
  1193  	sort.Strings(snapst.LastActiveDisabledServices)
  1194  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3", "svc1", "svc2"})
  1195  
  1196  	enableChg := s.state.NewChange("enable", "disable a snap")
  1197  	enableTs, err := snapstate.Enable(s.state, "services-snap")
  1198  	c.Assert(err, IsNil)
  1199  	enableChg.AddAll(enableTs)
  1200  
  1201  	s.state.Unlock()
  1202  	defer s.se.Stop()
  1203  	s.settle(c)
  1204  	s.state.Lock()
  1205  
  1206  	c.Assert(enableChg.Err(), IsNil)
  1207  	c.Assert(enableChg.IsReady(), Equals, true)
  1208  
  1209  	// get the snap state again
  1210  	err = snapstate.Get(s.state, "services-snap", &snapst)
  1211  	c.Assert(err, IsNil)
  1212  
  1213  	// make sure that there is nothing in the last active disabled services list
  1214  	// because we re-enabled the snap and there should be nothing we have to
  1215  	// keep track of in the state anymore
  1216  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3"})
  1217  }
  1218  
  1219  func (s *snapmgrTestSuite) TestEnableSnapMissingDisabledServicesSaved(c *C) {
  1220  	s.state.Lock()
  1221  	defer s.state.Unlock()
  1222  
  1223  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1224  		Sequence: []*snap.SideInfo{
  1225  			{RealName: "services-snap", Revision: snap.R(11)},
  1226  		},
  1227  		Current: snap.R(11),
  1228  		Active:  true,
  1229  		// keep this to make gofmt 1.10 happy
  1230  		LastActiveDisabledServices: []string{"missing-svc3"},
  1231  	})
  1232  
  1233  	disableChg := s.state.NewChange("disable", "disable a snap")
  1234  	disableTs, err := snapstate.Disable(s.state, "services-snap")
  1235  	c.Assert(err, IsNil)
  1236  	disableChg.AddAll(disableTs)
  1237  
  1238  	s.state.Unlock()
  1239  	defer s.se.Stop()
  1240  	s.settle(c)
  1241  	s.state.Lock()
  1242  
  1243  	c.Assert(disableChg.Err(), IsNil)
  1244  	c.Assert(disableChg.IsReady(), Equals, true)
  1245  
  1246  	// get the snap state
  1247  	var snapst snapstate.SnapState
  1248  	err = snapstate.Get(s.state, "services-snap", &snapst)
  1249  	c.Assert(err, IsNil)
  1250  
  1251  	// make sure that the disabled services in this snap's state is what we
  1252  	// provided
  1253  	sort.Strings(snapst.LastActiveDisabledServices)
  1254  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3"})
  1255  
  1256  	enableChg := s.state.NewChange("enable", "disable a snap")
  1257  	enableTs, err := snapstate.Enable(s.state, "services-snap")
  1258  	c.Assert(err, IsNil)
  1259  	enableChg.AddAll(enableTs)
  1260  
  1261  	s.state.Unlock()
  1262  	defer s.se.Stop()
  1263  	s.settle(c)
  1264  	s.state.Lock()
  1265  
  1266  	c.Assert(enableChg.Err(), IsNil)
  1267  	c.Assert(enableChg.IsReady(), Equals, true)
  1268  
  1269  	// get the snap state again
  1270  	err = snapstate.Get(s.state, "services-snap", &snapst)
  1271  	c.Assert(err, IsNil)
  1272  
  1273  	// make sure that there is nothing in the last active disabled services list
  1274  	// because we re-enabled the snap and there should be nothing we have to
  1275  	// keep track of in the state anymore
  1276  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3"})
  1277  }
  1278  
  1279  func makeTestSnap(c *C, snapYamlContent string) (snapFilePath string) {
  1280  	return snaptest.MakeTestSnapWithFiles(c, snapYamlContent, nil)
  1281  }
  1282  
  1283  func (s *snapmgrTestSuite) TestRevertRestoresConfigSnapshot(c *C) {
  1284  	s.state.Lock()
  1285  	defer s.state.Unlock()
  1286  
  1287  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1288  		Active: true,
  1289  		Sequence: []*snap.SideInfo{
  1290  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1291  			{RealName: "some-snap", Revision: snap.R(2)},
  1292  		},
  1293  		Current:  snap.R(2),
  1294  		SnapType: "app",
  1295  	})
  1296  
  1297  	// set configuration for current snap
  1298  	tr := config.NewTransaction(s.state)
  1299  	tr.Set("some-snap", "foo", "100")
  1300  	tr.Commit()
  1301  
  1302  	// make config snapshot for rev.1
  1303  	config.SaveRevisionConfig(s.state, "some-snap", snap.R(1))
  1304  
  1305  	// modify for rev. 2
  1306  	tr = config.NewTransaction(s.state)
  1307  	tr.Set("some-snap", "foo", "200")
  1308  	tr.Commit()
  1309  
  1310  	chg := s.state.NewChange("revert", "revert snap")
  1311  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  1312  	c.Assert(err, IsNil)
  1313  	chg.AddAll(ts)
  1314  
  1315  	s.state.Unlock()
  1316  	defer s.se.Stop()
  1317  	s.settle(c)
  1318  
  1319  	s.state.Lock()
  1320  	// config snapshot of rev. 2 has been made by 'revert'
  1321  	var cfgs map[string]interface{}
  1322  	c.Assert(s.state.Get("revision-config", &cfgs), IsNil)
  1323  	c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{
  1324  		"1": map[string]interface{}{"foo": "100"},
  1325  		"2": map[string]interface{}{"foo": "200"},
  1326  	})
  1327  
  1328  	// current snap configuration has been restored from rev. 1 config snapshot
  1329  	tr = config.NewTransaction(s.state)
  1330  	var res string
  1331  	c.Assert(tr.Get("some-snap", "foo", &res), IsNil)
  1332  	c.Assert(res, Equals, "100")
  1333  }
  1334  
  1335  func (s *snapmgrTestSuite) TestRevertNoRevertAgain(c *C) {
  1336  	siNew := snap.SideInfo{
  1337  		RealName: "some-snap",
  1338  		Revision: snap.R(77),
  1339  	}
  1340  
  1341  	si := snap.SideInfo{
  1342  		RealName: "some-snap",
  1343  		Revision: snap.R(7),
  1344  	}
  1345  
  1346  	s.state.Lock()
  1347  	defer s.state.Unlock()
  1348  
  1349  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1350  		Active:   true,
  1351  		Sequence: []*snap.SideInfo{&si, &siNew},
  1352  		Current:  snap.R(7),
  1353  	})
  1354  
  1355  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  1356  	c.Assert(err, ErrorMatches, "no revision to revert to")
  1357  	c.Assert(ts, IsNil)
  1358  }
  1359  
  1360  func (s *snapmgrTestSuite) TestRevertNothingToRevertTo(c *C) {
  1361  	si := snap.SideInfo{
  1362  		RealName: "some-snap",
  1363  		Revision: snap.R(7),
  1364  	}
  1365  
  1366  	s.state.Lock()
  1367  	defer s.state.Unlock()
  1368  
  1369  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1370  		Active:   true,
  1371  		Sequence: []*snap.SideInfo{&si},
  1372  		Current:  si.Revision,
  1373  	})
  1374  
  1375  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  1376  	c.Assert(err, ErrorMatches, "no revision to revert to")
  1377  	c.Assert(ts, IsNil)
  1378  }
  1379  
  1380  func (s *snapmgrTestSuite) TestRevertToRevisionNoValidVersion(c *C) {
  1381  	si := snap.SideInfo{
  1382  		RealName: "some-snap",
  1383  		Revision: snap.R(7),
  1384  	}
  1385  	si2 := snap.SideInfo{
  1386  		RealName: "some-snap",
  1387  		Revision: snap.R(77),
  1388  	}
  1389  
  1390  	s.state.Lock()
  1391  	defer s.state.Unlock()
  1392  
  1393  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1394  		Active:   true,
  1395  		Sequence: []*snap.SideInfo{&si, &si2},
  1396  		Current:  snap.R(77),
  1397  	})
  1398  
  1399  	ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("99"), snapstate.Flags{})
  1400  	c.Assert(err, ErrorMatches, `cannot find revision 99 for snap "some-snap"`)
  1401  	c.Assert(ts, IsNil)
  1402  }
  1403  
  1404  func (s *snapmgrTestSuite) TestRevertToRevisionAlreadyCurrent(c *C) {
  1405  	si := snap.SideInfo{
  1406  		RealName: "some-snap",
  1407  		Revision: snap.R(7),
  1408  	}
  1409  	si2 := snap.SideInfo{
  1410  		RealName: "some-snap",
  1411  		Revision: snap.R(77),
  1412  	}
  1413  
  1414  	s.state.Lock()
  1415  	defer s.state.Unlock()
  1416  
  1417  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1418  		Active:   true,
  1419  		Sequence: []*snap.SideInfo{&si, &si2},
  1420  		Current:  snap.R(77),
  1421  	})
  1422  
  1423  	ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("77"), snapstate.Flags{})
  1424  	c.Assert(err, ErrorMatches, `already on requested revision`)
  1425  	c.Assert(ts, IsNil)
  1426  }
  1427  
  1428  func (s *snapmgrTestSuite) TestRevertRunThrough(c *C) {
  1429  	si := snap.SideInfo{
  1430  		RealName: "some-snap",
  1431  		Revision: snap.R(7),
  1432  	}
  1433  	siOld := snap.SideInfo{
  1434  		RealName: "some-snap",
  1435  		Revision: snap.R(2),
  1436  	}
  1437  
  1438  	s.state.Lock()
  1439  	defer s.state.Unlock()
  1440  
  1441  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1442  		Active:   true,
  1443  		SnapType: "app",
  1444  		Sequence: []*snap.SideInfo{&siOld, &si},
  1445  		Current:  si.Revision,
  1446  	})
  1447  
  1448  	chg := s.state.NewChange("revert", "revert a snap backwards")
  1449  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  1450  	c.Assert(err, IsNil)
  1451  	chg.AddAll(ts)
  1452  
  1453  	s.state.Unlock()
  1454  	defer s.se.Stop()
  1455  	s.settle(c)
  1456  	s.state.Lock()
  1457  
  1458  	expected := fakeOps{
  1459  		{
  1460  			op:   "remove-snap-aliases",
  1461  			name: "some-snap",
  1462  		},
  1463  		{
  1464  			op:   "unlink-snap",
  1465  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  1466  		},
  1467  		{
  1468  			op:    "setup-profiles:Doing",
  1469  			name:  "some-snap",
  1470  			revno: snap.R(2),
  1471  		},
  1472  		{
  1473  			op: "candidate",
  1474  			sinfo: snap.SideInfo{
  1475  				RealName: "some-snap",
  1476  				Revision: snap.R(2),
  1477  			},
  1478  		},
  1479  		{
  1480  			op:   "link-snap",
  1481  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  1482  		},
  1483  		{
  1484  			op:    "auto-connect:Doing",
  1485  			name:  "some-snap",
  1486  			revno: snap.R(2),
  1487  		},
  1488  		{
  1489  			op: "update-aliases",
  1490  		},
  1491  	}
  1492  	// start with an easier-to-read error if this fails:
  1493  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1494  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  1495  
  1496  	// verify that the R(2) version is active now and R(7) is still there
  1497  	var snapst snapstate.SnapState
  1498  	err = snapstate.Get(s.state, "some-snap", &snapst)
  1499  	c.Assert(err, IsNil)
  1500  
  1501  	c.Assert(snapst.Active, Equals, true)
  1502  	c.Assert(snapst.Current, Equals, snap.R(2))
  1503  	c.Assert(snapst.Sequence, HasLen, 2)
  1504  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1505  		RealName: "some-snap",
  1506  		Channel:  "",
  1507  		Revision: snap.R(2),
  1508  	})
  1509  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  1510  		RealName: "some-snap",
  1511  		Channel:  "",
  1512  		Revision: snap.R(7),
  1513  	})
  1514  	c.Assert(snapst.Block(), DeepEquals, []snap.Revision{snap.R(7)})
  1515  }
  1516  
  1517  func (s *snapmgrTestSuite) TestRevertWithBaseRunThrough(c *C) {
  1518  	si := snap.SideInfo{
  1519  		RealName: "some-snap-with-base",
  1520  		Revision: snap.R(7),
  1521  	}
  1522  	siOld := snap.SideInfo{
  1523  		RealName: "some-snap-with-base",
  1524  		Revision: snap.R(2),
  1525  	}
  1526  
  1527  	s.state.Lock()
  1528  	defer s.state.Unlock()
  1529  
  1530  	// core18 with snapd, no core snap
  1531  	snapstate.Set(s.state, "core", nil)
  1532  	snapstate.Set(s.state, "core18", &snapstate.SnapState{
  1533  		Active: true,
  1534  		Sequence: []*snap.SideInfo{
  1535  			{RealName: "core18", SnapID: "core18-snap-id", Revision: snap.R(1)},
  1536  		},
  1537  		Current:  snap.R(1),
  1538  		SnapType: "base",
  1539  	})
  1540  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
  1541  		Active: true,
  1542  		Sequence: []*snap.SideInfo{
  1543  			{RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)},
  1544  		},
  1545  		Current:  snap.R(1),
  1546  		SnapType: "app",
  1547  	})
  1548  
  1549  	// test snap to revert
  1550  	snapstate.Set(s.state, "some-snap-with-base", &snapstate.SnapState{
  1551  		Active:   true,
  1552  		SnapType: "app",
  1553  		Sequence: []*snap.SideInfo{&siOld, &si},
  1554  		Current:  si.Revision,
  1555  	})
  1556  
  1557  	chg := s.state.NewChange("revert", "revert a snap backwards")
  1558  	ts, err := snapstate.Revert(s.state, "some-snap-with-base", snapstate.Flags{})
  1559  	c.Assert(err, IsNil)
  1560  	chg.AddAll(ts)
  1561  
  1562  	s.state.Unlock()
  1563  	defer s.se.Stop()
  1564  	s.settle(c)
  1565  	s.state.Lock()
  1566  
  1567  	expected := fakeOps{
  1568  		{
  1569  			op:   "remove-snap-aliases",
  1570  			name: "some-snap-with-base",
  1571  		},
  1572  		{
  1573  			op:   "unlink-snap",
  1574  			path: filepath.Join(dirs.SnapMountDir, "some-snap-with-base/7"),
  1575  		},
  1576  		{
  1577  			op:    "setup-profiles:Doing",
  1578  			name:  "some-snap-with-base",
  1579  			revno: snap.R(2),
  1580  		},
  1581  		{
  1582  			op: "candidate",
  1583  			sinfo: snap.SideInfo{
  1584  				RealName: "some-snap-with-base",
  1585  				Revision: snap.R(2),
  1586  			},
  1587  		},
  1588  		{
  1589  			op:   "link-snap",
  1590  			path: filepath.Join(dirs.SnapMountDir, "some-snap-with-base/2"),
  1591  		},
  1592  		{
  1593  			op:    "auto-connect:Doing",
  1594  			name:  "some-snap-with-base",
  1595  			revno: snap.R(2),
  1596  		},
  1597  		{
  1598  			op: "update-aliases",
  1599  		},
  1600  	}
  1601  	// start with an easier-to-read error if this fails:
  1602  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1603  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  1604  
  1605  	// verify that the R(2) version is active now and R(7) is still there
  1606  	var snapst snapstate.SnapState
  1607  	err = snapstate.Get(s.state, "some-snap-with-base", &snapst)
  1608  	c.Assert(err, IsNil)
  1609  
  1610  	c.Assert(snapst.Active, Equals, true)
  1611  	c.Assert(snapst.Current, Equals, snap.R(2))
  1612  }
  1613  
  1614  func (s *snapmgrTestSuite) TestParallelInstanceRevertRunThrough(c *C) {
  1615  	si := snap.SideInfo{
  1616  		RealName: "some-snap",
  1617  		Revision: snap.R(7),
  1618  	}
  1619  	siOld := snap.SideInfo{
  1620  		RealName: "some-snap",
  1621  		Revision: snap.R(2),
  1622  	}
  1623  
  1624  	s.state.Lock()
  1625  	defer s.state.Unlock()
  1626  
  1627  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  1628  		Active:      true,
  1629  		SnapType:    "app",
  1630  		Sequence:    []*snap.SideInfo{&siOld, &si},
  1631  		Current:     si.Revision,
  1632  		InstanceKey: "instance",
  1633  	})
  1634  
  1635  	// another snap withouth instance key
  1636  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1637  		Active:   true,
  1638  		SnapType: "app",
  1639  		Sequence: []*snap.SideInfo{&siOld, &si},
  1640  		Current:  si.Revision,
  1641  	})
  1642  
  1643  	chg := s.state.NewChange("revert", "revert a snap backwards")
  1644  	ts, err := snapstate.Revert(s.state, "some-snap_instance", snapstate.Flags{})
  1645  	c.Assert(err, IsNil)
  1646  	chg.AddAll(ts)
  1647  
  1648  	s.state.Unlock()
  1649  	defer s.se.Stop()
  1650  	s.settle(c)
  1651  	s.state.Lock()
  1652  
  1653  	expected := fakeOps{
  1654  		{
  1655  			op:   "remove-snap-aliases",
  1656  			name: "some-snap_instance",
  1657  		},
  1658  		{
  1659  			op:   "unlink-snap",
  1660  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  1661  		},
  1662  		{
  1663  			op:    "setup-profiles:Doing",
  1664  			name:  "some-snap_instance",
  1665  			revno: snap.R(2),
  1666  		},
  1667  		{
  1668  			op: "candidate",
  1669  			sinfo: snap.SideInfo{
  1670  				RealName: "some-snap",
  1671  				Revision: snap.R(2),
  1672  			},
  1673  		},
  1674  		{
  1675  			op:   "link-snap",
  1676  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/2"),
  1677  		},
  1678  		{
  1679  			op:    "auto-connect:Doing",
  1680  			name:  "some-snap_instance",
  1681  			revno: snap.R(2),
  1682  		},
  1683  		{
  1684  			op: "update-aliases",
  1685  		},
  1686  	}
  1687  	// start with an easier-to-read error if this fails:
  1688  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1689  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  1690  
  1691  	// verify that the R(2) version is active now and R(7) is still there
  1692  	var snapst snapstate.SnapState
  1693  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  1694  	c.Assert(err, IsNil)
  1695  
  1696  	c.Assert(snapst.Active, Equals, true)
  1697  	c.Assert(snapst.Current, Equals, snap.R(2))
  1698  	c.Assert(snapst.InstanceKey, Equals, "instance")
  1699  	c.Assert(snapst.Sequence, HasLen, 2)
  1700  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1701  		RealName: "some-snap",
  1702  		Channel:  "",
  1703  		Revision: snap.R(2),
  1704  	})
  1705  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  1706  		RealName: "some-snap",
  1707  		Channel:  "",
  1708  		Revision: snap.R(7),
  1709  	})
  1710  	c.Assert(snapst.Block(), DeepEquals, []snap.Revision{snap.R(7)})
  1711  
  1712  	// non instance snap is not affected
  1713  	var nonInstanceSnapst snapstate.SnapState
  1714  	err = snapstate.Get(s.state, "some-snap", &nonInstanceSnapst)
  1715  	c.Assert(err, IsNil)
  1716  	c.Assert(nonInstanceSnapst.Current, Equals, snap.R(7))
  1717  
  1718  }
  1719  
  1720  func (s *snapmgrTestSuite) TestRevertWithLocalRevisionRunThrough(c *C) {
  1721  	si := snap.SideInfo{
  1722  		RealName: "some-snap",
  1723  		Revision: snap.R(-7),
  1724  	}
  1725  	siOld := snap.SideInfo{
  1726  		RealName: "some-snap",
  1727  		Revision: snap.R(-2),
  1728  	}
  1729  
  1730  	s.state.Lock()
  1731  	defer s.state.Unlock()
  1732  
  1733  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1734  		Active:   true,
  1735  		SnapType: "app",
  1736  		Sequence: []*snap.SideInfo{&siOld, &si},
  1737  		Current:  si.Revision,
  1738  	})
  1739  
  1740  	chg := s.state.NewChange("revert", "revert a snap backwards")
  1741  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  1742  	c.Assert(err, IsNil)
  1743  	chg.AddAll(ts)
  1744  
  1745  	s.state.Unlock()
  1746  	defer s.se.Stop()
  1747  	s.settle(c)
  1748  	s.state.Lock()
  1749  
  1750  	c.Assert(s.fakeBackend.ops.Ops(), HasLen, 7)
  1751  
  1752  	// verify that LocalRevision is still -7
  1753  	var snapst snapstate.SnapState
  1754  	err = snapstate.Get(s.state, "some-snap", &snapst)
  1755  	c.Assert(err, IsNil)
  1756  
  1757  	c.Assert(snapst.LocalRevision(), Equals, snap.R(-7))
  1758  }
  1759  
  1760  func (s *snapmgrTestSuite) TestRevertToRevisionNewVersion(c *C) {
  1761  	siNew := snap.SideInfo{
  1762  		RealName: "some-snap",
  1763  		Revision: snap.R(7),
  1764  		SnapID:   "october",
  1765  	}
  1766  
  1767  	si := snap.SideInfo{
  1768  		RealName: "some-snap",
  1769  		Revision: snap.R(2),
  1770  		SnapID:   "october",
  1771  	}
  1772  
  1773  	s.state.Lock()
  1774  	defer s.state.Unlock()
  1775  
  1776  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1777  		Active:          true,
  1778  		SnapType:        "app",
  1779  		Sequence:        []*snap.SideInfo{&si, &siNew},
  1780  		Current:         snap.R(2),
  1781  		TrackingChannel: "latest/edge",
  1782  	})
  1783  
  1784  	chg := s.state.NewChange("revert", "revert a snap forward")
  1785  	ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(7), snapstate.Flags{})
  1786  	c.Assert(err, IsNil)
  1787  	chg.AddAll(ts)
  1788  
  1789  	s.state.Unlock()
  1790  	defer s.se.Stop()
  1791  	s.settle(c)
  1792  	s.state.Lock()
  1793  
  1794  	expected := fakeOps{
  1795  		{
  1796  			op:   "remove-snap-aliases",
  1797  			name: "some-snap",
  1798  		},
  1799  		{
  1800  			op:   "unlink-snap",
  1801  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  1802  		},
  1803  		{
  1804  			op:    "setup-profiles:Doing",
  1805  			name:  "some-snap",
  1806  			revno: snap.R(7),
  1807  		},
  1808  		{
  1809  			op:    "candidate",
  1810  			sinfo: siNew,
  1811  		},
  1812  		{
  1813  			op:   "link-snap",
  1814  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  1815  		},
  1816  		{
  1817  			op:    "auto-connect:Doing",
  1818  			name:  "some-snap",
  1819  			revno: snap.R(7),
  1820  		},
  1821  		{
  1822  			op: "update-aliases",
  1823  		},
  1824  	}
  1825  	// start with an easier-to-read error if this fails:
  1826  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1827  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  1828  
  1829  	// verify that the R(7) version is active now
  1830  	var snapst snapstate.SnapState
  1831  	err = snapstate.Get(s.state, "some-snap", &snapst)
  1832  	c.Assert(err, IsNil)
  1833  
  1834  	c.Check(snapst.Active, Equals, true)
  1835  	c.Check(snapst.Current, Equals, snap.R(7))
  1836  	c.Check(snapst.Sequence, HasLen, 2)
  1837  	c.Check(snapst.TrackingChannel, Equals, "latest/edge")
  1838  	c.Check(snapst.CurrentSideInfo(), DeepEquals, &siNew)
  1839  
  1840  	c.Check(snapst.Block(), HasLen, 0)
  1841  }
  1842  
  1843  func (s *snapmgrTestSuite) TestRevertTotalUndoRunThrough(c *C) {
  1844  	si := snap.SideInfo{
  1845  		RealName: "some-snap",
  1846  		Revision: snap.R(1),
  1847  	}
  1848  	si2 := snap.SideInfo{
  1849  		RealName: "some-snap",
  1850  		Revision: snap.R(2),
  1851  	}
  1852  
  1853  	s.state.Lock()
  1854  	defer s.state.Unlock()
  1855  
  1856  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1857  		Active:   true,
  1858  		SnapType: "app",
  1859  		Sequence: []*snap.SideInfo{&si, &si2},
  1860  		Current:  si2.Revision,
  1861  	})
  1862  
  1863  	chg := s.state.NewChange("revert", "revert a snap")
  1864  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  1865  	c.Assert(err, IsNil)
  1866  	chg.AddAll(ts)
  1867  
  1868  	tasks := ts.Tasks()
  1869  	last := tasks[len(tasks)-1]
  1870  
  1871  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  1872  	terr.WaitFor(last)
  1873  	chg.AddTask(terr)
  1874  
  1875  	s.state.Unlock()
  1876  	defer s.se.Stop()
  1877  	s.settle(c)
  1878  	s.state.Lock()
  1879  
  1880  	expected := fakeOps{
  1881  		{
  1882  			op:   "remove-snap-aliases",
  1883  			name: "some-snap",
  1884  		},
  1885  		{
  1886  			op:   "unlink-snap",
  1887  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  1888  		},
  1889  		{
  1890  			op:    "setup-profiles:Doing",
  1891  			name:  "some-snap",
  1892  			revno: snap.R(1),
  1893  		},
  1894  		{
  1895  			op: "candidate",
  1896  			sinfo: snap.SideInfo{
  1897  				RealName: "some-snap",
  1898  				Revision: snap.R(1),
  1899  			},
  1900  		},
  1901  		{
  1902  			op:   "link-snap",
  1903  			path: filepath.Join(dirs.SnapMountDir, "some-snap/1"),
  1904  		},
  1905  		{
  1906  			op:    "auto-connect:Doing",
  1907  			name:  "some-snap",
  1908  			revno: snap.R(1),
  1909  		},
  1910  		{
  1911  			op: "update-aliases",
  1912  		},
  1913  		// undoing everything from here down...
  1914  		{
  1915  			op:   "remove-snap-aliases",
  1916  			name: "some-snap",
  1917  		},
  1918  		{
  1919  			op:   "unlink-snap",
  1920  			path: filepath.Join(dirs.SnapMountDir, "some-snap/1"),
  1921  		},
  1922  		{
  1923  			op:    "setup-profiles:Undoing",
  1924  			name:  "some-snap",
  1925  			revno: snap.R(1),
  1926  		},
  1927  		{
  1928  			op:   "link-snap",
  1929  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  1930  		},
  1931  		{
  1932  			op: "update-aliases",
  1933  		},
  1934  	}
  1935  	// start with an easier-to-read error if this fails:
  1936  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1937  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  1938  
  1939  	// verify snaps in the system state
  1940  	var snapst snapstate.SnapState
  1941  	err = snapstate.Get(s.state, "some-snap", &snapst)
  1942  	c.Assert(err, IsNil)
  1943  
  1944  	c.Assert(snapst.Active, Equals, true)
  1945  	c.Assert(snapst.Sequence, HasLen, 2)
  1946  	c.Assert(snapst.Current, Equals, si2.Revision)
  1947  }
  1948  
  1949  func (s *snapmgrTestSuite) TestRevertUndoRunThrough(c *C) {
  1950  	si := snap.SideInfo{
  1951  		RealName: "some-snap",
  1952  		Revision: snap.R(1),
  1953  	}
  1954  	si2 := snap.SideInfo{
  1955  		RealName: "some-snap",
  1956  		Revision: snap.R(2),
  1957  	}
  1958  
  1959  	s.state.Lock()
  1960  	defer s.state.Unlock()
  1961  
  1962  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1963  		Active:   true,
  1964  		SnapType: "app",
  1965  		Sequence: []*snap.SideInfo{&si, &si2},
  1966  		Current:  si2.Revision,
  1967  	})
  1968  
  1969  	chg := s.state.NewChange("revert", "install a revert")
  1970  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  1971  	c.Assert(err, IsNil)
  1972  	chg.AddAll(ts)
  1973  
  1974  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/1")
  1975  
  1976  	s.state.Unlock()
  1977  	defer s.se.Stop()
  1978  	s.settle(c)
  1979  	s.state.Lock()
  1980  
  1981  	expected := fakeOps{
  1982  		{
  1983  			op:   "remove-snap-aliases",
  1984  			name: "some-snap",
  1985  		},
  1986  		{
  1987  			op:   "unlink-snap",
  1988  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  1989  		},
  1990  		{
  1991  			op:    "setup-profiles:Doing",
  1992  			name:  "some-snap",
  1993  			revno: snap.R(1),
  1994  		},
  1995  		{
  1996  			op: "candidate",
  1997  			sinfo: snap.SideInfo{
  1998  				RealName: "some-snap",
  1999  				Revision: snap.R(1),
  2000  			},
  2001  		},
  2002  		{
  2003  			op:   "link-snap.failed",
  2004  			path: filepath.Join(dirs.SnapMountDir, "some-snap/1"),
  2005  		},
  2006  		// undo stuff here
  2007  		{
  2008  			op:   "unlink-snap",
  2009  			path: filepath.Join(dirs.SnapMountDir, "some-snap/1"),
  2010  		},
  2011  		{
  2012  			op:    "setup-profiles:Undoing",
  2013  			name:  "some-snap",
  2014  			revno: snap.R(1),
  2015  		},
  2016  		{
  2017  			op:   "link-snap",
  2018  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  2019  		},
  2020  		{
  2021  			op: "update-aliases",
  2022  		},
  2023  	}
  2024  
  2025  	// ensure all our tasks ran
  2026  	// start with an easier-to-read error if this fails:
  2027  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2028  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2029  
  2030  	// verify snaps in the system state
  2031  	var snapst snapstate.SnapState
  2032  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2033  	c.Assert(err, IsNil)
  2034  
  2035  	c.Assert(snapst.Active, Equals, true)
  2036  	c.Assert(snapst.Sequence, HasLen, 2)
  2037  	c.Assert(snapst.Current, Equals, snap.R(2))
  2038  }
  2039  
  2040  func (s *snapmgrTestSuite) TestEnableDoesNotEnableAgain(c *C) {
  2041  	si := snap.SideInfo{
  2042  		RealName: "some-snap",
  2043  		Revision: snap.R(7),
  2044  	}
  2045  
  2046  	s.state.Lock()
  2047  	defer s.state.Unlock()
  2048  
  2049  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2050  		Sequence: []*snap.SideInfo{&si},
  2051  		Current:  snap.R(7),
  2052  		Active:   true,
  2053  	})
  2054  
  2055  	ts, err := snapstate.Enable(s.state, "some-snap")
  2056  	c.Assert(err, ErrorMatches, `snap "some-snap" already enabled`)
  2057  	c.Assert(ts, IsNil)
  2058  }
  2059  
  2060  func (s *snapmgrTestSuite) TestEnableRunThrough(c *C) {
  2061  	si := snap.SideInfo{
  2062  		RealName: "some-snap",
  2063  		Revision: snap.R(7),
  2064  		Channel:  "edge",
  2065  		SnapID:   "foo",
  2066  	}
  2067  
  2068  	s.state.Lock()
  2069  	defer s.state.Unlock()
  2070  
  2071  	flags := snapstate.Flags{
  2072  		DevMode:  true,
  2073  		JailMode: true,
  2074  		Classic:  true,
  2075  		TryMode:  true,
  2076  		Required: true,
  2077  	}
  2078  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2079  		Sequence:            []*snap.SideInfo{&si},
  2080  		Current:             si.Revision,
  2081  		Active:              false,
  2082  		TrackingChannel:     "latest/edge",
  2083  		Flags:               flags,
  2084  		AliasesPending:      true,
  2085  		AutoAliasesDisabled: true,
  2086  	})
  2087  
  2088  	chg := s.state.NewChange("enable", "enable a snap")
  2089  	ts, err := snapstate.Enable(s.state, "some-snap")
  2090  	c.Assert(err, IsNil)
  2091  	chg.AddAll(ts)
  2092  
  2093  	s.state.Unlock()
  2094  	defer s.se.Stop()
  2095  	s.settle(c)
  2096  	s.state.Lock()
  2097  
  2098  	expected := fakeOps{
  2099  		{
  2100  			op:    "setup-profiles:Doing",
  2101  			name:  "some-snap",
  2102  			revno: snap.R(7),
  2103  		},
  2104  		{
  2105  			op:    "candidate",
  2106  			sinfo: si,
  2107  		},
  2108  		{
  2109  			op:   "link-snap",
  2110  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2111  		},
  2112  		{
  2113  			op:    "auto-connect:Doing",
  2114  			name:  "some-snap",
  2115  			revno: snap.R(7),
  2116  		},
  2117  		{
  2118  			op: "update-aliases",
  2119  		},
  2120  	}
  2121  	// start with an easier-to-read error if this fails:
  2122  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2123  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2124  
  2125  	var snapst snapstate.SnapState
  2126  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2127  	c.Assert(err, IsNil)
  2128  	c.Check(snapst.Flags, DeepEquals, flags)
  2129  
  2130  	c.Assert(snapst.Active, Equals, true)
  2131  	c.Assert(snapst.AliasesPending, Equals, false)
  2132  	c.Assert(snapst.AutoAliasesDisabled, Equals, true)
  2133  
  2134  	info, err := snapst.CurrentInfo()
  2135  	c.Assert(err, IsNil)
  2136  	c.Assert(info.Channel, Equals, "edge")
  2137  	c.Assert(info.SnapID, Equals, "foo")
  2138  
  2139  	first := ts.Tasks()[0]
  2140  	snapsup, err := snapstate.TaskSnapSetup(first)
  2141  	c.Assert(err, IsNil)
  2142  	c.Check(snapsup, DeepEquals, &snapstate.SnapSetup{
  2143  		SideInfo:  &si,
  2144  		Flags:     flags,
  2145  		Type:      snap.TypeApp,
  2146  		PlugsOnly: true,
  2147  	})
  2148  }
  2149  
  2150  func (s *snapmgrTestSuite) TestDisableRunThrough(c *C) {
  2151  	si := snap.SideInfo{
  2152  		RealName: "some-snap",
  2153  		Revision: snap.R(7),
  2154  	}
  2155  
  2156  	s.state.Lock()
  2157  	defer s.state.Unlock()
  2158  
  2159  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2160  		Sequence: []*snap.SideInfo{&si},
  2161  		Current:  si.Revision,
  2162  		Active:   true,
  2163  		SnapType: "app",
  2164  	})
  2165  
  2166  	chg := s.state.NewChange("disable", "disable a snap")
  2167  	ts, err := snapstate.Disable(s.state, "some-snap")
  2168  	c.Assert(err, IsNil)
  2169  	chg.AddAll(ts)
  2170  
  2171  	s.state.Unlock()
  2172  	defer s.se.Stop()
  2173  	s.settle(c)
  2174  	s.state.Lock()
  2175  
  2176  	expected := fakeOps{
  2177  		{
  2178  			op:   "remove-snap-aliases",
  2179  			name: "some-snap",
  2180  		},
  2181  		{
  2182  			op:   "unlink-snap",
  2183  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2184  		},
  2185  		{
  2186  			op:    "remove-profiles:Doing",
  2187  			name:  "some-snap",
  2188  			revno: snap.R(7),
  2189  		},
  2190  	}
  2191  	// start with an easier-to-read error if this fails:
  2192  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2193  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2194  
  2195  	var snapst snapstate.SnapState
  2196  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2197  	c.Assert(err, IsNil)
  2198  
  2199  	c.Assert(snapst.Active, Equals, false)
  2200  	c.Assert(snapst.AliasesPending, Equals, true)
  2201  
  2202  	first := ts.Tasks()[0]
  2203  	snapsup, err := snapstate.TaskSnapSetup(first)
  2204  	c.Assert(err, IsNil)
  2205  	c.Check(snapsup, DeepEquals, &snapstate.SnapSetup{
  2206  		SideInfo: &snap.SideInfo{
  2207  			RealName: "some-snap",
  2208  			Revision: snap.R(7),
  2209  		},
  2210  		Type:      snap.TypeApp,
  2211  		PlugsOnly: true,
  2212  	})
  2213  }
  2214  
  2215  func (s *snapmgrTestSuite) TestParallelInstanceEnableRunThrough(c *C) {
  2216  	si := snap.SideInfo{
  2217  		RealName: "some-snap",
  2218  		Revision: snap.R(7),
  2219  		Channel:  "edge",
  2220  		SnapID:   "foo",
  2221  	}
  2222  
  2223  	s.state.Lock()
  2224  	defer s.state.Unlock()
  2225  
  2226  	flags := snapstate.Flags{
  2227  		DevMode:  true,
  2228  		JailMode: true,
  2229  		Classic:  true,
  2230  		TryMode:  true,
  2231  		Required: true,
  2232  	}
  2233  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  2234  		Sequence:            []*snap.SideInfo{&si},
  2235  		Current:             si.Revision,
  2236  		Active:              false,
  2237  		TrackingChannel:     "latest/edge",
  2238  		Flags:               flags,
  2239  		AliasesPending:      true,
  2240  		AutoAliasesDisabled: true,
  2241  		InstanceKey:         "instance",
  2242  	})
  2243  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2244  		Sequence:            []*snap.SideInfo{&si},
  2245  		Current:             si.Revision,
  2246  		Active:              false,
  2247  		TrackingChannel:     "latest/edge",
  2248  		Flags:               flags,
  2249  		AliasesPending:      true,
  2250  		AutoAliasesDisabled: true,
  2251  	})
  2252  
  2253  	chg := s.state.NewChange("enable", "enable a snap")
  2254  	ts, err := snapstate.Enable(s.state, "some-snap_instance")
  2255  	c.Assert(err, IsNil)
  2256  	chg.AddAll(ts)
  2257  
  2258  	s.state.Unlock()
  2259  	s.settle(c)
  2260  	s.state.Lock()
  2261  
  2262  	expected := fakeOps{
  2263  		{
  2264  			op:    "setup-profiles:Doing",
  2265  			name:  "some-snap_instance",
  2266  			revno: snap.R(7),
  2267  		},
  2268  		{
  2269  			op:    "candidate",
  2270  			sinfo: si,
  2271  		},
  2272  		{
  2273  			op:   "link-snap",
  2274  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  2275  		},
  2276  		{
  2277  			op:    "auto-connect:Doing",
  2278  			name:  "some-snap_instance",
  2279  			revno: snap.R(7),
  2280  		},
  2281  		{
  2282  			op: "update-aliases",
  2283  		},
  2284  	}
  2285  	// start with an easier-to-read error if this fails:
  2286  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2287  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2288  
  2289  	var snapst snapstate.SnapState
  2290  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  2291  	c.Assert(err, IsNil)
  2292  	c.Check(snapst.Flags, DeepEquals, flags)
  2293  
  2294  	c.Assert(snapst.InstanceKey, Equals, "instance")
  2295  	c.Assert(snapst.Active, Equals, true)
  2296  	c.Assert(snapst.AliasesPending, Equals, false)
  2297  	c.Assert(snapst.AutoAliasesDisabled, Equals, true)
  2298  
  2299  	info, err := snapst.CurrentInfo()
  2300  	c.Assert(err, IsNil)
  2301  	c.Assert(info.Channel, Equals, "edge")
  2302  	c.Assert(info.SnapID, Equals, "foo")
  2303  
  2304  	// the non-parallel instance is still disabled
  2305  	snapst = snapstate.SnapState{}
  2306  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2307  	c.Assert(err, IsNil)
  2308  	c.Assert(snapst.InstanceKey, Equals, "")
  2309  	c.Assert(snapst.Active, Equals, false)
  2310  }
  2311  
  2312  func (s *snapmgrTestSuite) TestParallelInstanceDisableRunThrough(c *C) {
  2313  	si := snap.SideInfo{
  2314  		RealName: "some-snap",
  2315  		Revision: snap.R(7),
  2316  	}
  2317  
  2318  	s.state.Lock()
  2319  	defer s.state.Unlock()
  2320  
  2321  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2322  		Sequence: []*snap.SideInfo{&si},
  2323  		Current:  si.Revision,
  2324  		Active:   true,
  2325  	})
  2326  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  2327  		Sequence:    []*snap.SideInfo{&si},
  2328  		Current:     si.Revision,
  2329  		Active:      true,
  2330  		InstanceKey: "instance",
  2331  	})
  2332  
  2333  	chg := s.state.NewChange("disable", "disable a snap")
  2334  	ts, err := snapstate.Disable(s.state, "some-snap_instance")
  2335  	c.Assert(err, IsNil)
  2336  	chg.AddAll(ts)
  2337  
  2338  	s.state.Unlock()
  2339  	s.settle(c)
  2340  	s.state.Lock()
  2341  
  2342  	expected := fakeOps{
  2343  		{
  2344  			op:   "remove-snap-aliases",
  2345  			name: "some-snap_instance",
  2346  		},
  2347  		{
  2348  			op:   "unlink-snap",
  2349  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  2350  		},
  2351  		{
  2352  			op:    "remove-profiles:Doing",
  2353  			name:  "some-snap_instance",
  2354  			revno: snap.R(7),
  2355  		},
  2356  	}
  2357  	// start with an easier-to-read error if this fails:
  2358  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2359  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2360  
  2361  	var snapst snapstate.SnapState
  2362  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  2363  	c.Assert(err, IsNil)
  2364  	c.Assert(snapst.InstanceKey, Equals, "instance")
  2365  	c.Assert(snapst.Active, Equals, false)
  2366  	c.Assert(snapst.AliasesPending, Equals, true)
  2367  
  2368  	// the non-parallel instance is still active
  2369  	snapst = snapstate.SnapState{}
  2370  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2371  	c.Assert(err, IsNil)
  2372  	c.Assert(snapst.InstanceKey, Equals, "")
  2373  	c.Assert(snapst.Active, Equals, true)
  2374  }
  2375  
  2376  type switchScenario struct {
  2377  	chanFrom string
  2378  	chanTo   string
  2379  	cohFrom  string
  2380  	cohTo    string
  2381  	summary  string
  2382  }
  2383  
  2384  var switchScenarios = map[string]switchScenario{
  2385  	"no cohort at all": {
  2386  		chanFrom: "latest/stable",
  2387  		chanTo:   "some-channel/stable",
  2388  		cohFrom:  "",
  2389  		cohTo:    "",
  2390  		summary:  `Switch snap "some-snap" from channel "latest/stable" to "some-channel/stable"`,
  2391  	},
  2392  	"no cohort, from empty channel": {
  2393  		chanFrom: "",
  2394  		chanTo:   "some-channel/stable",
  2395  		cohFrom:  "",
  2396  		cohTo:    "",
  2397  		summary:  `Switch snap "some-snap" to channel "some-channel/stable"`,
  2398  	},
  2399  	"no cohort change requested": {
  2400  		chanFrom: "latest/stable",
  2401  		chanTo:   "some-channel/stable",
  2402  		cohFrom:  "some-cohort",
  2403  		cohTo:    "some-cohort",
  2404  		summary:  `Switch snap "some-snap" from channel "latest/stable" to "some-channel/stable"`,
  2405  	},
  2406  	"leave cohort": {
  2407  		chanFrom: "latest/stable",
  2408  		chanTo:   "latest/stable",
  2409  		cohFrom:  "some-cohort",
  2410  		cohTo:    "",
  2411  		summary:  `Switch snap "some-snap" away from cohort "…me-cohort"`,
  2412  	},
  2413  	"leave cohort, change channel": {
  2414  		chanFrom: "latest/stable",
  2415  		chanTo:   "latest/edge",
  2416  		cohFrom:  "some-cohort",
  2417  		cohTo:    "",
  2418  		summary:  `Switch snap "some-snap" from channel "latest/stable" to "latest/edge" and away from cohort "…me-cohort"`,
  2419  	},
  2420  	"leave cohort, change from empty channel": {
  2421  		chanFrom: "",
  2422  		chanTo:   "latest/stable",
  2423  		cohFrom:  "some-cohort",
  2424  		cohTo:    "",
  2425  		summary:  `Switch snap "some-snap" to channel "latest/stable" and away from cohort "…me-cohort"`,
  2426  	},
  2427  	"no channel at all": {
  2428  		chanFrom: "",
  2429  		chanTo:   "",
  2430  		cohFrom:  "some-cohort",
  2431  		cohTo:    "some-other-cohort",
  2432  		summary:  `Switch snap "some-snap" from cohort "…me-cohort" to "…er-cohort"`,
  2433  	},
  2434  	"no channel change requested": {
  2435  		chanFrom: "latest/stable",
  2436  		chanTo:   "latest/stable",
  2437  		cohFrom:  "some-cohort",
  2438  		cohTo:    "some-other-cohort",
  2439  		summary:  `Switch snap "some-snap" from cohort "…me-cohort" to "…er-cohort"`,
  2440  	},
  2441  	"no channel change requested, from empty cohort": {
  2442  		chanFrom: "latest/stable",
  2443  		chanTo:   "latest/stable",
  2444  		cohFrom:  "",
  2445  		cohTo:    "some-cohort",
  2446  		summary:  `Switch snap "some-snap" from no cohort to "…me-cohort"`,
  2447  	},
  2448  	"all change": {
  2449  		chanFrom: "latest/stable",
  2450  		chanTo:   "latest/edge",
  2451  		cohFrom:  "some-cohort",
  2452  		cohTo:    "some-other-cohort",
  2453  		summary:  `Switch snap "some-snap" from channel "latest/stable" to "latest/edge" and from cohort "…me-cohort" to "…er-cohort"`,
  2454  	},
  2455  	"all change, from empty channel": {
  2456  		chanFrom: "",
  2457  		chanTo:   "latest/stable",
  2458  		cohFrom:  "some-cohort",
  2459  		cohTo:    "some-other-cohort",
  2460  		summary:  `Switch snap "some-snap" to channel "latest/stable" and from cohort "…me-cohort" to "…er-cohort"`,
  2461  	},
  2462  	"all change, from empty cohort": {
  2463  		chanFrom: "latest/stable",
  2464  		chanTo:   "latest/edge",
  2465  		cohFrom:  "",
  2466  		cohTo:    "some-cohort",
  2467  		summary:  `Switch snap "some-snap" from channel "latest/stable" to "latest/edge" and from no cohort to "…me-cohort"`,
  2468  	},
  2469  	"all change, from empty channel and cohort": {
  2470  		chanFrom: "",
  2471  		chanTo:   "latest/stable",
  2472  		cohFrom:  "",
  2473  		cohTo:    "some-cohort",
  2474  		summary:  `Switch snap "some-snap" to channel "latest/stable" and from no cohort to "…me-cohort"`,
  2475  	},
  2476  	"no change": {
  2477  		chanFrom: "latest/stable",
  2478  		chanTo:   "latest/stable",
  2479  		cohFrom:  "some-cohort",
  2480  		cohTo:    "some-cohort",
  2481  		summary:  `No change switch (no-op)`,
  2482  	},
  2483  }
  2484  
  2485  func (s *snapmgrTestSuite) TestSwitchScenarios(c *C) {
  2486  	for k, t := range switchScenarios {
  2487  		s.testSwitchScenario(c, k, t)
  2488  	}
  2489  }
  2490  
  2491  func (s *snapmgrTestSuite) testSwitchScenario(c *C, desc string, t switchScenario) {
  2492  	comment := Commentf("%q (%+v)", desc, t)
  2493  	si := snap.SideInfo{
  2494  		RealName: "some-snap",
  2495  		Revision: snap.R(7),
  2496  		Channel:  t.chanFrom,
  2497  		SnapID:   "foo",
  2498  	}
  2499  
  2500  	s.state.Lock()
  2501  	defer s.state.Unlock()
  2502  
  2503  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2504  		Sequence:        []*snap.SideInfo{&si},
  2505  		Current:         si.Revision,
  2506  		TrackingChannel: t.chanFrom,
  2507  		CohortKey:       t.cohFrom,
  2508  	})
  2509  
  2510  	summary := snapstate.SwitchSummary("some-snap", t.chanFrom, t.chanTo, t.cohFrom, t.cohTo)
  2511  	c.Check(summary, Equals, t.summary, comment)
  2512  	chg := s.state.NewChange("switch-snap", summary)
  2513  	ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{
  2514  		Channel:     t.chanTo,
  2515  		CohortKey:   t.cohTo,
  2516  		LeaveCohort: t.cohFrom != "" && t.cohTo == "",
  2517  	})
  2518  	c.Assert(err, IsNil, comment)
  2519  	chg.AddAll(ts)
  2520  
  2521  	s.state.Unlock()
  2522  	s.settle(c)
  2523  	s.state.Lock()
  2524  
  2525  	// switch is not really really doing anything backend related
  2526  	c.Assert(s.fakeBackend.ops, HasLen, 0, comment)
  2527  
  2528  	expectedChanTo := t.chanTo
  2529  	if t.chanTo == "" {
  2530  		expectedChanTo = t.chanFrom
  2531  	}
  2532  	expectedCohTo := t.cohTo
  2533  
  2534  	// ensure the desired channel/cohort has changed
  2535  	var snapst snapstate.SnapState
  2536  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2537  	c.Assert(err, IsNil, comment)
  2538  	c.Assert(snapst.TrackingChannel, Equals, expectedChanTo, comment)
  2539  	c.Assert(snapst.CohortKey, Equals, expectedCohTo, comment)
  2540  
  2541  	// ensure the current info has not changed
  2542  	info, err := snapst.CurrentInfo()
  2543  	c.Assert(err, IsNil, comment)
  2544  	c.Assert(info.Channel, Equals, t.chanFrom, comment)
  2545  }
  2546  
  2547  func (s *snapmgrTestSuite) TestParallelInstallSwitchRunThrough(c *C) {
  2548  	si := snap.SideInfo{
  2549  		RealName: "some-snap",
  2550  		Revision: snap.R(7),
  2551  		Channel:  "edge",
  2552  		SnapID:   "foo",
  2553  	}
  2554  
  2555  	s.state.Lock()
  2556  	defer s.state.Unlock()
  2557  
  2558  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2559  		Sequence:        []*snap.SideInfo{&si},
  2560  		Current:         si.Revision,
  2561  		TrackingChannel: "latest/edge",
  2562  	})
  2563  
  2564  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  2565  		Sequence:        []*snap.SideInfo{&si},
  2566  		Current:         si.Revision,
  2567  		TrackingChannel: "latest/edge",
  2568  		InstanceKey:     "instance",
  2569  	})
  2570  
  2571  	chg := s.state.NewChange("switch-snap", "switch snap to some-channel")
  2572  	ts, err := snapstate.Switch(s.state, "some-snap_instance", &snapstate.RevisionOptions{Channel: "some-channel"})
  2573  	c.Assert(err, IsNil)
  2574  	chg.AddAll(ts)
  2575  
  2576  	s.state.Unlock()
  2577  	defer s.se.Stop()
  2578  	s.settle(c)
  2579  	s.state.Lock()
  2580  
  2581  	// switch is not really really doing anything backend related
  2582  	c.Assert(s.fakeBackend.ops, HasLen, 0)
  2583  
  2584  	// ensure the desired channel has changed
  2585  	var snapst snapstate.SnapState
  2586  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  2587  	c.Assert(err, IsNil)
  2588  	c.Assert(snapst.TrackingChannel, Equals, "some-channel/stable")
  2589  
  2590  	// ensure the current info has not changed
  2591  	info, err := snapst.CurrentInfo()
  2592  	c.Assert(err, IsNil)
  2593  	c.Assert(info.Channel, Equals, "edge")
  2594  
  2595  	// Ensure that the non-instance snap is unchanged
  2596  	var nonInstanceSnapst snapstate.SnapState
  2597  	err = snapstate.Get(s.state, "some-snap", &nonInstanceSnapst)
  2598  	c.Assert(err, IsNil)
  2599  	c.Assert(nonInstanceSnapst.TrackingChannel, Equals, "latest/edge")
  2600  }
  2601  
  2602  func (s *snapmgrTestSuite) TestDisableDoesNotEnableAgain(c *C) {
  2603  	si := snap.SideInfo{
  2604  		RealName: "some-snap",
  2605  		Revision: snap.R(7),
  2606  	}
  2607  
  2608  	s.state.Lock()
  2609  	defer s.state.Unlock()
  2610  
  2611  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2612  		Sequence: []*snap.SideInfo{&si},
  2613  		Current:  snap.R(7),
  2614  		Active:   false,
  2615  	})
  2616  
  2617  	ts, err := snapstate.Disable(s.state, "some-snap")
  2618  	c.Assert(err, ErrorMatches, `snap "some-snap" already disabled`)
  2619  	c.Assert(ts, IsNil)
  2620  }
  2621  
  2622  func (s *snapmgrTestSuite) TestAbortCausesNoErrReport(c *C) {
  2623  	errReported := 0
  2624  	restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) {
  2625  		errReported++
  2626  		return "oops-id", nil
  2627  	})
  2628  	defer restore()
  2629  
  2630  	s.state.Lock()
  2631  	defer s.state.Unlock()
  2632  
  2633  	chg := s.state.NewChange("install", "install a snap")
  2634  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  2635  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2636  	c.Assert(err, IsNil)
  2637  
  2638  	s.fakeBackend.linkSnapWaitCh = make(chan int)
  2639  	s.fakeBackend.linkSnapWaitTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11")
  2640  	go func() {
  2641  		<-s.fakeBackend.linkSnapWaitCh
  2642  		chg.Abort()
  2643  		s.fakeBackend.linkSnapWaitCh <- 1
  2644  	}()
  2645  
  2646  	chg.AddAll(ts)
  2647  
  2648  	s.state.Unlock()
  2649  	defer s.se.Stop()
  2650  	s.settle(c)
  2651  	s.state.Lock()
  2652  
  2653  	c.Check(chg.Status(), Equals, state.UndoneStatus)
  2654  	c.Assert(errReported, Equals, 0)
  2655  }
  2656  
  2657  func (s *snapmgrTestSuite) TestErrreportDisable(c *C) {
  2658  	s.state.Lock()
  2659  	defer s.state.Unlock()
  2660  
  2661  	tr := config.NewTransaction(s.state)
  2662  	tr.Set("core", "problem-reports.disabled", true)
  2663  	tr.Commit()
  2664  
  2665  	restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) {
  2666  		c.Fatalf("this should not be reached")
  2667  		return "", nil
  2668  	})
  2669  	defer restore()
  2670  
  2671  	chg := s.state.NewChange("install", "install a snap")
  2672  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  2673  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2674  	c.Assert(err, IsNil)
  2675  	chg.AddAll(ts)
  2676  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11")
  2677  
  2678  	s.state.Unlock()
  2679  	defer s.se.Stop()
  2680  	s.settle(c)
  2681  	s.state.Lock()
  2682  
  2683  	// no failure report was generated
  2684  }
  2685  
  2686  func (s *snapmgrTestSuite) TestEnsureRefreshesAtSeedPolicy(c *C) {
  2687  	// special policy only on classic
  2688  	r := release.MockOnClassic(true)
  2689  	defer r()
  2690  	// set at not seeded yet
  2691  	st := s.state
  2692  	st.Lock()
  2693  	st.Set("seeded", nil)
  2694  	st.Unlock()
  2695  
  2696  	s.snapmgr.Ensure()
  2697  
  2698  	st.Lock()
  2699  	defer st.Unlock()
  2700  
  2701  	// check that refresh policies have run in this case
  2702  	var t1 time.Time
  2703  	err := st.Get("last-refresh-hints", &t1)
  2704  	c.Check(err, IsNil)
  2705  	tr := config.NewTransaction(st)
  2706  	err = tr.Get("core", "refresh.hold", &t1)
  2707  	c.Check(err, IsNil)
  2708  }
  2709  
  2710  func (s *snapmgrTestSuite) TestEsnureCleansOldSideloads(c *C) {
  2711  	filenames := func() []string {
  2712  		filenames, _ := filepath.Glob(filepath.Join(dirs.SnapBlobDir, "*"))
  2713  		return filenames
  2714  	}
  2715  
  2716  	defer snapstate.MockLocalInstallCleanupWait(200 * time.Millisecond)()
  2717  	c.Assert(os.MkdirAll(dirs.SnapBlobDir, 0700), IsNil)
  2718  	// sanity check; note * in go glob matches .foo
  2719  	c.Assert(filenames(), HasLen, 0)
  2720  
  2721  	s0 := filepath.Join(dirs.SnapBlobDir, "some.snap")
  2722  	s1 := filepath.Join(dirs.SnapBlobDir, dirs.LocalInstallBlobTempPrefix+"-12345")
  2723  	s2 := filepath.Join(dirs.SnapBlobDir, dirs.LocalInstallBlobTempPrefix+"-67890")
  2724  
  2725  	c.Assert(ioutil.WriteFile(s0, nil, 0600), IsNil)
  2726  	c.Assert(ioutil.WriteFile(s1, nil, 0600), IsNil)
  2727  	c.Assert(ioutil.WriteFile(s2, nil, 0600), IsNil)
  2728  
  2729  	t1 := time.Now()
  2730  	t0 := t1.Add(-time.Hour)
  2731  
  2732  	c.Assert(os.Chtimes(s0, t0, t0), IsNil)
  2733  	c.Assert(os.Chtimes(s1, t0, t0), IsNil)
  2734  	c.Assert(os.Chtimes(s2, t1, t1), IsNil)
  2735  
  2736  	// all there
  2737  	c.Assert(filenames(), DeepEquals, []string{s1, s2, s0})
  2738  
  2739  	// set last cleanup in the future
  2740  	defer snapstate.MockLocalInstallLastCleanup(t1.Add(time.Minute))()
  2741  	s.snapmgr.Ensure()
  2742  	// all there ( -> cleanup not done)
  2743  	c.Assert(filenames(), DeepEquals, []string{s1, s2, s0})
  2744  
  2745  	// set last cleanup to epoch
  2746  	snapstate.MockLocalInstallLastCleanup(time.Time{})
  2747  
  2748  	s.snapmgr.Ensure()
  2749  	// oldest sideload gone
  2750  	c.Assert(filenames(), DeepEquals, []string{s2, s0})
  2751  
  2752  	time.Sleep(200 * time.Millisecond)
  2753  
  2754  	s.snapmgr.Ensure()
  2755  	// all sideloads gone
  2756  	c.Assert(filenames(), DeepEquals, []string{s0})
  2757  
  2758  }
  2759  
  2760  func (s *snapmgrTestSuite) verifyRefreshLast(c *C) {
  2761  	var lastRefresh time.Time
  2762  
  2763  	s.state.Get("last-refresh", &lastRefresh)
  2764  	c.Check(time.Now().Year(), Equals, lastRefresh.Year())
  2765  }
  2766  
  2767  func makeTestRefreshConfig(st *state.State) {
  2768  	// avoid special at seed policy
  2769  	now := time.Now()
  2770  	st.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, now.Location()))
  2771  
  2772  	tr := config.NewTransaction(st)
  2773  	tr.Set("core", "refresh.timer", "00:00-23:59")
  2774  	tr.Commit()
  2775  }
  2776  
  2777  func (s *snapmgrTestSuite) TestEnsureRefreshRefusesLegacyWeekdaySchedules(c *C) {
  2778  	s.state.Lock()
  2779  	defer s.state.Unlock()
  2780  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  2781  
  2782  	logbuf, restore := logger.MockLogger()
  2783  	defer restore()
  2784  
  2785  	s.state.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, time.UTC))
  2786  	tr := config.NewTransaction(s.state)
  2787  	tr.Set("core", "refresh.timer", "")
  2788  	tr.Set("core", "refresh.schedule", "00:00-23:59/mon@12:00-14:00")
  2789  	tr.Commit()
  2790  
  2791  	// Ensure() also runs ensureRefreshes()
  2792  	s.state.Unlock()
  2793  	s.se.Ensure()
  2794  	s.state.Lock()
  2795  
  2796  	c.Check(logbuf.String(), testutil.Contains, `cannot use refresh.schedule configuration: cannot parse "mon@12:00": not a valid time`)
  2797  	schedule, legacy, err := s.snapmgr.RefreshSchedule()
  2798  	c.Assert(err, IsNil)
  2799  	c.Check(schedule, Equals, "00:00~24:00/4")
  2800  	c.Check(legacy, Equals, false)
  2801  
  2802  	tr = config.NewTransaction(s.state)
  2803  	refreshTimer := "canary"
  2804  	refreshSchedule := "canary"
  2805  	c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil)
  2806  	c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil)
  2807  	c.Check(refreshTimer, Equals, "")
  2808  	c.Check(refreshSchedule, Equals, "00:00-23:59/mon@12:00-14:00")
  2809  }
  2810  
  2811  func (s *snapmgrTestSuite) TestEnsureRefreshLegacyScheduleIsLowerPriority(c *C) {
  2812  	s.state.Lock()
  2813  	defer s.state.Unlock()
  2814  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  2815  
  2816  	s.state.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, time.UTC))
  2817  	tr := config.NewTransaction(s.state)
  2818  	tr.Set("core", "refresh.timer", "00:00-23:59,,mon,12:00-14:00")
  2819  	// legacy schedule is invalid
  2820  	tr.Set("core", "refresh.schedule", "00:00-23:59/mon@12:00-14:00")
  2821  	tr.Commit()
  2822  
  2823  	// Ensure() also runs ensureRefreshes()
  2824  	s.state.Unlock()
  2825  	s.se.Ensure()
  2826  	s.state.Lock()
  2827  
  2828  	// expecting new refresh.timer to have been used, fallback to legacy was
  2829  	// not attempted otherwise it would get reset to the default due to
  2830  	// refresh.schedule being garbage
  2831  	schedule, legacy, err := s.snapmgr.RefreshSchedule()
  2832  	c.Assert(err, IsNil)
  2833  	c.Check(schedule, Equals, "00:00-23:59,,mon,12:00-14:00")
  2834  	c.Check(legacy, Equals, false)
  2835  }
  2836  
  2837  func (s *snapmgrTestSuite) TestEnsureRefreshFallbackToLegacySchedule(c *C) {
  2838  	s.state.Lock()
  2839  	defer s.state.Unlock()
  2840  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  2841  
  2842  	tr := config.NewTransaction(s.state)
  2843  	tr.Set("core", "refresh.timer", "")
  2844  	tr.Set("core", "refresh.schedule", "00:00-23:59")
  2845  	tr.Commit()
  2846  
  2847  	// Ensure() also runs ensureRefreshes()
  2848  	s.state.Unlock()
  2849  	s.se.Ensure()
  2850  	s.state.Lock()
  2851  
  2852  	// refresh.timer is unset, triggering automatic fallback to legacy
  2853  	// schedule if that was set
  2854  	schedule, legacy, err := s.snapmgr.RefreshSchedule()
  2855  	c.Assert(err, IsNil)
  2856  	c.Check(schedule, Equals, "00:00-23:59")
  2857  	c.Check(legacy, Equals, true)
  2858  }
  2859  
  2860  func (s *snapmgrTestSuite) TestEnsureRefreshFallbackToDefaultOnError(c *C) {
  2861  	s.state.Lock()
  2862  	defer s.state.Unlock()
  2863  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  2864  
  2865  	tr := config.NewTransaction(s.state)
  2866  	tr.Set("core", "refresh.timer", "garbage-in")
  2867  	tr.Set("core", "refresh.schedule", "00:00-23:59")
  2868  	tr.Commit()
  2869  
  2870  	// Ensure() also runs ensureRefreshes()
  2871  	s.state.Unlock()
  2872  	s.se.Ensure()
  2873  	s.state.Lock()
  2874  
  2875  	// automatic fallback to default schedule if refresh.timer is set but
  2876  	// cannot be parsed
  2877  	schedule, legacy, err := s.snapmgr.RefreshSchedule()
  2878  	c.Assert(err, IsNil)
  2879  	c.Check(schedule, Equals, "00:00~24:00/4")
  2880  	c.Check(legacy, Equals, false)
  2881  
  2882  	tr = config.NewTransaction(s.state)
  2883  	refreshTimer := "canary"
  2884  	refreshSchedule := "canary"
  2885  	c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil)
  2886  	c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil)
  2887  	c.Check(refreshTimer, Equals, "garbage-in")
  2888  	c.Check(refreshSchedule, Equals, "00:00-23:59")
  2889  }
  2890  
  2891  func (s *snapmgrTestSuite) TestEnsureRefreshFallbackOnEmptyToDefaultSchedule(c *C) {
  2892  	s.state.Lock()
  2893  	defer s.state.Unlock()
  2894  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  2895  
  2896  	tr := config.NewTransaction(s.state)
  2897  	tr.Set("core", "refresh.timer", "")
  2898  	tr.Set("core", "refresh.schedule", "")
  2899  	tr.Commit()
  2900  
  2901  	// Ensure() also runs ensureRefreshes()
  2902  	s.state.Unlock()
  2903  	s.se.Ensure()
  2904  	s.state.Lock()
  2905  
  2906  	// automatic fallback to default schedule if neither refresh.timer nor
  2907  	// refresh.schedule was set
  2908  	schedule, legacy, err := s.snapmgr.RefreshSchedule()
  2909  	c.Assert(err, IsNil)
  2910  	c.Check(schedule, Equals, "00:00~24:00/4")
  2911  	c.Check(legacy, Equals, false)
  2912  
  2913  	tr = config.NewTransaction(s.state)
  2914  	refreshTimer := "canary"
  2915  	refreshSchedule := "canary"
  2916  	c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil)
  2917  	c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil)
  2918  	c.Check(refreshTimer, Equals, "")
  2919  	c.Check(refreshSchedule, Equals, "")
  2920  }
  2921  
  2922  func (s *snapmgrTestSuite) TestEnsureRefreshesNoUpdate(c *C) {
  2923  	s.state.Lock()
  2924  	defer s.state.Unlock()
  2925  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  2926  
  2927  	makeTestRefreshConfig(s.state)
  2928  
  2929  	// Ensure() also runs ensureRefreshes()
  2930  	s.state.Unlock()
  2931  	s.snapmgr.Ensure()
  2932  	s.state.Lock()
  2933  
  2934  	// nothing needs to be done, but last-refresh got updated
  2935  	c.Check(s.state.Changes(), HasLen, 0)
  2936  	s.verifyRefreshLast(c)
  2937  
  2938  	// ensure the next-refresh time is reset and re-calculated
  2939  	c.Check(s.snapmgr.NextRefresh().IsZero(), Equals, true)
  2940  }
  2941  
  2942  func (s *snapmgrTestSuite) TestEnsureRefreshesAlreadyRanInThisInterval(c *C) {
  2943  	s.state.Lock()
  2944  	defer s.state.Unlock()
  2945  
  2946  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) {
  2947  		return true, nil
  2948  	}
  2949  	nextRefresh := s.snapmgr.NextRefresh()
  2950  	c.Check(nextRefresh.IsZero(), Equals, true)
  2951  
  2952  	now := time.Now()
  2953  	fakeLastRefresh := now.Add(-1 * time.Hour)
  2954  	s.state.Set("last-refresh", fakeLastRefresh)
  2955  
  2956  	tr := config.NewTransaction(s.state)
  2957  	tr.Set("core", "refresh.timer", fmt.Sprintf("00:00-%02d:%02d", now.Hour(), now.Minute()))
  2958  	tr.Commit()
  2959  
  2960  	// Ensure() also runs ensureRefreshes()
  2961  	s.state.Unlock()
  2962  	s.snapmgr.Ensure()
  2963  	s.state.Lock()
  2964  
  2965  	// nothing needs to be done and no refresh was run
  2966  	c.Check(s.state.Changes(), HasLen, 0)
  2967  
  2968  	var refreshLast time.Time
  2969  	s.state.Get("last-refresh", &refreshLast)
  2970  	c.Check(refreshLast.Equal(fakeLastRefresh), Equals, true)
  2971  
  2972  	// but a nextRefresh time got calculated
  2973  	nextRefresh = s.snapmgr.NextRefresh()
  2974  	c.Check(nextRefresh.IsZero(), Equals, false)
  2975  
  2976  	// run ensure again to test that nextRefresh again to ensure that
  2977  	// nextRefresh is not calculated again if nothing changes
  2978  	s.state.Unlock()
  2979  	s.snapmgr.Ensure()
  2980  	s.state.Lock()
  2981  	c.Check(s.snapmgr.NextRefresh(), Equals, nextRefresh)
  2982  }
  2983  
  2984  func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdate(c *C) {
  2985  	s.state.Lock()
  2986  	defer s.state.Unlock()
  2987  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  2988  
  2989  	makeTestRefreshConfig(s.state)
  2990  
  2991  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2992  		Active: true,
  2993  		Sequence: []*snap.SideInfo{
  2994  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  2995  		},
  2996  		Current:  snap.R(1),
  2997  		SnapType: "app",
  2998  	})
  2999  
  3000  	// Ensure() also runs ensureRefreshes() and our test setup has an
  3001  	// update for the "some-snap" in our fake store
  3002  	s.state.Unlock()
  3003  	s.snapmgr.Ensure()
  3004  	s.state.Lock()
  3005  
  3006  	// verify we have an auto-refresh change scheduled now
  3007  	c.Assert(s.state.Changes(), HasLen, 1)
  3008  	chg := s.state.Changes()[0]
  3009  	c.Check(chg.Kind(), Equals, "auto-refresh")
  3010  	c.Check(chg.IsReady(), Equals, false)
  3011  	s.verifyRefreshLast(c)
  3012  
  3013  	checkIsAutoRefresh(c, chg.Tasks(), true)
  3014  }
  3015  
  3016  func (s *snapmgrTestSuite) TestEnsureRefreshesImmediateWithUpdate(c *C) {
  3017  	r := release.MockOnClassic(false)
  3018  	defer r()
  3019  
  3020  	s.state.Lock()
  3021  	defer s.state.Unlock()
  3022  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  3023  
  3024  	// lastRefresh is unset/zero => immediate refresh try
  3025  
  3026  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3027  		Active: true,
  3028  		Sequence: []*snap.SideInfo{
  3029  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  3030  		},
  3031  		Current:  snap.R(1),
  3032  		SnapType: "app",
  3033  	})
  3034  
  3035  	// Ensure() also runs ensureRefreshes() and our test setup has an
  3036  	// update for the "some-snap" in our fake store
  3037  	s.state.Unlock()
  3038  	s.snapmgr.Ensure()
  3039  	s.state.Lock()
  3040  
  3041  	// verify we have an auto-refresh change scheduled now
  3042  	c.Assert(s.state.Changes(), HasLen, 1)
  3043  	chg := s.state.Changes()[0]
  3044  	c.Check(chg.Kind(), Equals, "auto-refresh")
  3045  	c.Check(chg.IsReady(), Equals, false)
  3046  	s.verifyRefreshLast(c)
  3047  }
  3048  
  3049  func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdateError(c *C) {
  3050  	s.state.Lock()
  3051  	defer s.state.Unlock()
  3052  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  3053  
  3054  	makeTestRefreshConfig(s.state)
  3055  
  3056  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3057  		Active: true,
  3058  		Sequence: []*snap.SideInfo{
  3059  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  3060  		},
  3061  		Current:  snap.R(1),
  3062  		SnapType: "app",
  3063  	})
  3064  
  3065  	// Ensure() also runs ensureRefreshes() and our test setup has an
  3066  	// update for the "some-snap" in our fake store
  3067  	s.state.Unlock()
  3068  	s.snapmgr.Ensure()
  3069  	s.state.Lock()
  3070  
  3071  	c.Check(s.state.Changes(), HasLen, 1)
  3072  	chg := s.state.Changes()[0]
  3073  	terr := s.state.NewTask("error-trigger", "simulate an error")
  3074  	tasks := chg.Tasks()
  3075  	for _, t := range tasks[:len(tasks)-2] {
  3076  		terr.WaitFor(t)
  3077  	}
  3078  	chg.AddTask(terr)
  3079  
  3080  	// run the changes
  3081  	s.state.Unlock()
  3082  	s.settle(c)
  3083  	s.state.Lock()
  3084  
  3085  	s.verifyRefreshLast(c)
  3086  }
  3087  
  3088  func (s *snapmgrTestSuite) TestEnsureRefreshesInFlight(c *C) {
  3089  	s.state.Lock()
  3090  	defer s.state.Unlock()
  3091  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  3092  
  3093  	makeTestRefreshConfig(s.state)
  3094  
  3095  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3096  		Active: true,
  3097  		Sequence: []*snap.SideInfo{
  3098  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  3099  		},
  3100  		Current:  snap.R(1),
  3101  		SnapType: "app",
  3102  	})
  3103  
  3104  	// simulate an in-flight change
  3105  	chg := s.state.NewChange("auto-refresh", "...")
  3106  	chg.SetStatus(state.DoStatus)
  3107  	c.Check(s.state.Changes(), HasLen, 1)
  3108  
  3109  	s.state.Unlock()
  3110  	s.snapmgr.Ensure()
  3111  	s.state.Lock()
  3112  
  3113  	// verify no additional change got generated
  3114  	c.Check(s.state.Changes(), HasLen, 1)
  3115  }
  3116  
  3117  func mockAutoRefreshAssertions(f func(st *state.State, userID int) error) func() {
  3118  	origAutoRefreshAssertions := snapstate.AutoRefreshAssertions
  3119  	snapstate.AutoRefreshAssertions = f
  3120  	return func() {
  3121  		snapstate.AutoRefreshAssertions = origAutoRefreshAssertions
  3122  	}
  3123  }
  3124  
  3125  func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdateStoreError(c *C) {
  3126  	s.state.Lock()
  3127  	defer s.state.Unlock()
  3128  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  3129  
  3130  	// avoid special at seed policy
  3131  	s.state.Set("last-refresh", time.Time{})
  3132  	autoRefreshAssertionsCalled := 0
  3133  	restore := mockAutoRefreshAssertions(func(st *state.State, userID int) error {
  3134  		// simulate failure in snapstate.AutoRefresh()
  3135  		autoRefreshAssertionsCalled++
  3136  		return fmt.Errorf("simulate store error")
  3137  	})
  3138  	defer restore()
  3139  
  3140  	// check that no change got created and that autoRefreshAssertins
  3141  	// got called once
  3142  	s.state.Unlock()
  3143  	s.snapmgr.Ensure()
  3144  	s.state.Lock()
  3145  	c.Check(s.state.Changes(), HasLen, 0)
  3146  	c.Check(autoRefreshAssertionsCalled, Equals, 1)
  3147  
  3148  	// run Ensure() again and check that AutoRefresh() did not run
  3149  	// again because to test that lastRefreshAttempt backoff is working
  3150  	s.state.Unlock()
  3151  	s.snapmgr.Ensure()
  3152  	s.state.Lock()
  3153  	c.Check(s.state.Changes(), HasLen, 0)
  3154  	c.Check(autoRefreshAssertionsCalled, Equals, 1)
  3155  }
  3156  
  3157  func (s *snapmgrTestSuite) testEnsureRefreshesDisabledViaSnapdControl(c *C, confSet func(*config.Transaction)) {
  3158  	st := s.state
  3159  	st.Lock()
  3160  	defer st.Unlock()
  3161  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  3162  
  3163  	makeTestRefreshConfig(st)
  3164  
  3165  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3166  		Active: true,
  3167  		Sequence: []*snap.SideInfo{
  3168  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  3169  		},
  3170  		Current:  snap.R(1),
  3171  		SnapType: "app",
  3172  	})
  3173  
  3174  	// snapstate.AutoRefresh is called from AutoRefresh()
  3175  	autoRefreshAssertionsCalled := 0
  3176  	restore := mockAutoRefreshAssertions(func(st *state.State, userID int) error {
  3177  		autoRefreshAssertionsCalled++
  3178  		return nil
  3179  	})
  3180  	defer restore()
  3181  
  3182  	// pretend the device is refresh-control: managed
  3183  	oldCanManageRefreshes := snapstate.CanManageRefreshes
  3184  	snapstate.CanManageRefreshes = func(*state.State) bool {
  3185  		return true
  3186  	}
  3187  	defer func() { snapstate.CanManageRefreshes = oldCanManageRefreshes }()
  3188  
  3189  	tr := config.NewTransaction(st)
  3190  	confSet(tr)
  3191  	tr.Commit()
  3192  
  3193  	// Ensure() also runs ensureRefreshes()
  3194  	st.Unlock()
  3195  	s.snapmgr.Ensure()
  3196  	st.Lock()
  3197  
  3198  	// no refresh was called (i.e. no update to last-refresh)
  3199  	var lastRefresh time.Time
  3200  	st.Get("last-refresh", &lastRefresh)
  3201  	c.Check(lastRefresh.Year(), Equals, 2009)
  3202  
  3203  	// AutoRefresh was not called
  3204  	c.Check(autoRefreshAssertionsCalled, Equals, 0)
  3205  
  3206  	// The last refresh hints got updated
  3207  	var lastRefreshHints time.Time
  3208  	st.Get("last-refresh-hints", &lastRefreshHints)
  3209  	c.Check(lastRefreshHints.Year(), Equals, time.Now().Year())
  3210  }
  3211  
  3212  func (s *snapmgrTestSuite) TestEnsureRefreshDisableLegacy(c *C) {
  3213  	f := func(tr *config.Transaction) {
  3214  		tr.Set("core", "refresh.timer", "")
  3215  		tr.Set("core", "refresh.schedule", "managed")
  3216  	}
  3217  	s.testEnsureRefreshesDisabledViaSnapdControl(c, f)
  3218  }
  3219  
  3220  func (s *snapmgrTestSuite) TestEnsureRefreshDisableNew(c *C) {
  3221  	f := func(tr *config.Transaction) {
  3222  		tr.Set("core", "refresh.timer", "managed")
  3223  		tr.Set("core", "refresh.schedule", "")
  3224  	}
  3225  	s.testEnsureRefreshesDisabledViaSnapdControl(c, f)
  3226  }
  3227  
  3228  func (s *snapmgrTestSuite) TestEnsureRefreshDisableNewTrumpsOld(c *C) {
  3229  	f := func(tr *config.Transaction) {
  3230  		tr.Set("core", "refresh.timer", "managed")
  3231  		tr.Set("core", "refresh.schedule", "00:00-12:00")
  3232  	}
  3233  	s.testEnsureRefreshesDisabledViaSnapdControl(c, f)
  3234  }
  3235  
  3236  func (s *snapmgrTestSuite) TestDefaultRefreshScheduleParsing(c *C) {
  3237  	l, err := timeutil.ParseSchedule(snapstate.DefaultRefreshSchedule)
  3238  	c.Assert(err, IsNil)
  3239  	c.Assert(l, HasLen, 1)
  3240  }
  3241  
  3242  func (s *snapmgrTestSuite) TestFinishRestartBasics(c *C) {
  3243  	r := release.MockOnClassic(true)
  3244  	defer r()
  3245  
  3246  	st := s.state
  3247  	st.Lock()
  3248  	defer st.Unlock()
  3249  
  3250  	task := st.NewTask("auto-connect", "...")
  3251  
  3252  	// not restarting
  3253  	state.MockRestarting(st, state.RestartUnset)
  3254  	si := &snap.SideInfo{RealName: "some-app"}
  3255  	snaptest.MockSnap(c, "name: some-app\nversion: 1", si)
  3256  	snapsup := &snapstate.SnapSetup{SideInfo: si}
  3257  	err := snapstate.FinishRestart(task, snapsup)
  3258  	c.Check(err, IsNil)
  3259  
  3260  	// restarting ... we always wait
  3261  	state.MockRestarting(st, state.RestartDaemon)
  3262  	err = snapstate.FinishRestart(task, snapsup)
  3263  	c.Check(err, FitsTypeOf, &state.Retry{})
  3264  }
  3265  
  3266  func (s *snapmgrTestSuite) TestFinishRestartGeneratesSnapdWrappersOnCore(c *C) {
  3267  	r := release.MockOnClassic(false)
  3268  	defer r()
  3269  
  3270  	var generateWrappersCalled bool
  3271  	restore := snapstate.MockGenerateSnapdWrappers(func(snapInfo *snap.Info) error {
  3272  		c.Assert(snapInfo.SnapName(), Equals, "snapd")
  3273  		generateWrappersCalled = true
  3274  		return nil
  3275  	})
  3276  	defer restore()
  3277  
  3278  	st := s.state
  3279  	st.Lock()
  3280  	defer st.Unlock()
  3281  
  3282  	for i, tc := range []struct {
  3283  		onClassic            bool
  3284  		expectedWrappersCall bool
  3285  		snapName             string
  3286  		snapYaml             string
  3287  	}{
  3288  		{
  3289  			onClassic: false,
  3290  			snapName:  "snapd",
  3291  			snapYaml: `name: snapd
  3292  type: snapd
  3293  `,
  3294  			expectedWrappersCall: true,
  3295  		},
  3296  		{
  3297  			onClassic: true,
  3298  			snapName:  "snapd",
  3299  			snapYaml: `name: snapd
  3300  type: snapd
  3301  `,
  3302  			expectedWrappersCall: false,
  3303  		},
  3304  		{
  3305  			onClassic:            false,
  3306  			snapName:             "some-snap",
  3307  			snapYaml:             `name: some-snap`,
  3308  			expectedWrappersCall: false,
  3309  		},
  3310  	} {
  3311  		generateWrappersCalled = false
  3312  		release.MockOnClassic(tc.onClassic)
  3313  
  3314  		task := st.NewTask("auto-connect", "...")
  3315  		si := &snap.SideInfo{Revision: snap.R("x2"), RealName: tc.snapName}
  3316  		snapInfo := snaptest.MockSnapCurrent(c, string(tc.snapYaml), si)
  3317  		snapsup := &snapstate.SnapSetup{SideInfo: si, Type: snapInfo.SnapType}
  3318  
  3319  		// restarting
  3320  		state.MockRestarting(st, state.RestartUnset)
  3321  		c.Assert(snapstate.FinishRestart(task, snapsup), IsNil)
  3322  		c.Check(generateWrappersCalled, Equals, tc.expectedWrappersCall, Commentf("#%d: %v", i, tc))
  3323  
  3324  		c.Assert(os.RemoveAll(filepath.Join(snap.BaseDir(snapInfo.SnapName()), "current")), IsNil)
  3325  	}
  3326  }
  3327  
  3328  type snapmgrQuerySuite struct {
  3329  	st      *state.State
  3330  	restore func()
  3331  }
  3332  
  3333  var _ = Suite(&snapmgrQuerySuite{})
  3334  
  3335  func (s *snapmgrQuerySuite) SetUpTest(c *C) {
  3336  	st := state.New(nil)
  3337  	st.Lock()
  3338  	defer st.Unlock()
  3339  
  3340  	restoreSanitize := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
  3341  	s.restore = func() {
  3342  		restoreSanitize()
  3343  	}
  3344  
  3345  	s.st = st
  3346  
  3347  	dirs.SetRootDir(c.MkDir())
  3348  
  3349  	// Write a snap.yaml with fake name
  3350  	sideInfo11 := &snap.SideInfo{RealName: "name1", Revision: snap.R(11), EditedSummary: "s11", SnapID: "123123123"}
  3351  	sideInfo12 := &snap.SideInfo{RealName: "name1", Revision: snap.R(12), EditedSummary: "s12", SnapID: "123123123"}
  3352  	instanceSideInfo13 := &snap.SideInfo{RealName: "name1", Revision: snap.R(13), EditedSummary: "s13 instance", SnapID: "123123123"}
  3353  	snaptest.MockSnap(c, `
  3354  name: name0
  3355  version: 1.1
  3356  description: |
  3357      Lots of text`, sideInfo11)
  3358  	snaptest.MockSnap(c, `
  3359  name: name0
  3360  version: 1.2
  3361  description: |
  3362      Lots of text`, sideInfo12)
  3363  	snaptest.MockSnapInstance(c, "name1_instance", `
  3364  name: name0
  3365  version: 1.3
  3366  description: |
  3367      Lots of text`, instanceSideInfo13)
  3368  	snapstate.Set(st, "name1", &snapstate.SnapState{
  3369  		Active:   true,
  3370  		Sequence: []*snap.SideInfo{sideInfo11, sideInfo12},
  3371  		Current:  sideInfo12.Revision,
  3372  		SnapType: "app",
  3373  	})
  3374  	snapstate.Set(st, "name1_instance", &snapstate.SnapState{
  3375  		Active:      true,
  3376  		Sequence:    []*snap.SideInfo{instanceSideInfo13},
  3377  		Current:     instanceSideInfo13.Revision,
  3378  		SnapType:    "app",
  3379  		InstanceKey: "instance",
  3380  	})
  3381  
  3382  	// have also a snap being installed
  3383  	/*
  3384  		snapstate.Set(st, "installing", &snapstate.SnapState{
  3385  			Candidate: &snap.SideInfo{RealName: "installing", Revision: snap.R(1)},
  3386  		})
  3387  	*/
  3388  }
  3389  
  3390  func (s *snapmgrQuerySuite) TearDownTest(c *C) {
  3391  	dirs.SetRootDir("")
  3392  	s.restore()
  3393  }
  3394  
  3395  func (s *snapmgrQuerySuite) TestInfo(c *C) {
  3396  	st := s.st
  3397  	st.Lock()
  3398  	defer st.Unlock()
  3399  
  3400  	info, err := snapstate.Info(st, "name1", snap.R(11))
  3401  	c.Assert(err, IsNil)
  3402  
  3403  	c.Check(info.InstanceName(), Equals, "name1")
  3404  	c.Check(info.Revision, Equals, snap.R(11))
  3405  	c.Check(info.Summary(), Equals, "s11")
  3406  	c.Check(info.Version, Equals, "1.1")
  3407  	c.Check(info.Description(), Equals, "Lots of text")
  3408  }
  3409  
  3410  func (s *snapmgrQuerySuite) TestSnapStateCurrentInfo(c *C) {
  3411  	st := s.st
  3412  	st.Lock()
  3413  	defer st.Unlock()
  3414  
  3415  	var snapst snapstate.SnapState
  3416  	err := snapstate.Get(st, "name1", &snapst)
  3417  	c.Assert(err, IsNil)
  3418  
  3419  	info, err := snapst.CurrentInfo()
  3420  	c.Assert(err, IsNil)
  3421  
  3422  	c.Check(info.InstanceName(), Equals, "name1")
  3423  	c.Check(info.Revision, Equals, snap.R(12))
  3424  	c.Check(info.Summary(), Equals, "s12")
  3425  	c.Check(info.Version, Equals, "1.2")
  3426  	c.Check(info.Description(), Equals, "Lots of text")
  3427  	c.Check(info.Media, IsNil)
  3428  	c.Check(info.Website, Equals, "")
  3429  }
  3430  
  3431  func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoLoadsAuxiliaryStoreInfo(c *C) {
  3432  	storeInfo := &snapstate.AuxStoreInfo{
  3433  		Media: snap.MediaInfos{{
  3434  			Type: "icon",
  3435  			URL:  "http://example.com/favicon.ico",
  3436  		}},
  3437  		Website: "http://example.com/",
  3438  	}
  3439  
  3440  	c.Assert(snapstate.KeepAuxStoreInfo("123123123", storeInfo), IsNil)
  3441  
  3442  	st := s.st
  3443  	st.Lock()
  3444  	defer st.Unlock()
  3445  
  3446  	var snapst snapstate.SnapState
  3447  	err := snapstate.Get(st, "name1", &snapst)
  3448  	c.Assert(err, IsNil)
  3449  
  3450  	info, err := snapst.CurrentInfo()
  3451  	c.Assert(err, IsNil)
  3452  
  3453  	c.Check(info.InstanceName(), Equals, "name1")
  3454  	c.Check(info.Revision, Equals, snap.R(12))
  3455  	c.Check(info.Summary(), Equals, "s12")
  3456  	c.Check(info.Version, Equals, "1.2")
  3457  	c.Check(info.Description(), Equals, "Lots of text")
  3458  	c.Check(info.Media, DeepEquals, storeInfo.Media)
  3459  	c.Check(info.Website, Equals, storeInfo.Website)
  3460  }
  3461  
  3462  func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoParallelInstall(c *C) {
  3463  	st := s.st
  3464  	st.Lock()
  3465  	defer st.Unlock()
  3466  
  3467  	var snapst snapstate.SnapState
  3468  	err := snapstate.Get(st, "name1_instance", &snapst)
  3469  	c.Assert(err, IsNil)
  3470  
  3471  	info, err := snapst.CurrentInfo()
  3472  	c.Assert(err, IsNil)
  3473  
  3474  	c.Check(info.InstanceName(), Equals, "name1_instance")
  3475  	c.Check(info.Revision, Equals, snap.R(13))
  3476  	c.Check(info.Summary(), Equals, "s13 instance")
  3477  	c.Check(info.Version, Equals, "1.3")
  3478  	c.Check(info.Description(), Equals, "Lots of text")
  3479  }
  3480  
  3481  func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoErrNoCurrent(c *C) {
  3482  	snapst := new(snapstate.SnapState)
  3483  	_, err := snapst.CurrentInfo()
  3484  	c.Assert(err, Equals, snapstate.ErrNoCurrent)
  3485  
  3486  }
  3487  
  3488  func (s *snapmgrQuerySuite) TestCurrentInfo(c *C) {
  3489  	st := s.st
  3490  	st.Lock()
  3491  	defer st.Unlock()
  3492  
  3493  	info, err := snapstate.CurrentInfo(st, "name1")
  3494  	c.Assert(err, IsNil)
  3495  
  3496  	c.Check(info.InstanceName(), Equals, "name1")
  3497  	c.Check(info.Revision, Equals, snap.R(12))
  3498  }
  3499  
  3500  func (s *snapmgrQuerySuite) TestCurrentInfoAbsent(c *C) {
  3501  	st := s.st
  3502  	st.Lock()
  3503  	defer st.Unlock()
  3504  
  3505  	_, err := snapstate.CurrentInfo(st, "absent")
  3506  	c.Assert(err, ErrorMatches, `snap "absent" is not installed`)
  3507  }
  3508  
  3509  func (s *snapmgrQuerySuite) TestActiveInfos(c *C) {
  3510  	st := s.st
  3511  	st.Lock()
  3512  	defer st.Unlock()
  3513  
  3514  	infos, err := snapstate.ActiveInfos(st)
  3515  	c.Assert(err, IsNil)
  3516  
  3517  	c.Check(infos, HasLen, 2)
  3518  
  3519  	instanceName := "name1_instance"
  3520  	if infos[0].InstanceName() != instanceName && infos[1].InstanceName() != instanceName {
  3521  		c.Fail()
  3522  	}
  3523  	// need stable ordering
  3524  	if infos[0].InstanceName() == instanceName {
  3525  		infos[1], infos[0] = infos[0], infos[1]
  3526  	}
  3527  
  3528  	c.Check(infos[0].InstanceName(), Equals, "name1")
  3529  	c.Check(infos[0].Revision, Equals, snap.R(12))
  3530  	c.Check(infos[0].Summary(), Equals, "s12")
  3531  	c.Check(infos[0].Version, Equals, "1.2")
  3532  	c.Check(infos[0].Description(), Equals, "Lots of text")
  3533  
  3534  	c.Check(infos[1].InstanceName(), Equals, "name1_instance")
  3535  	c.Check(infos[1].Revision, Equals, snap.R(13))
  3536  	c.Check(infos[1].Summary(), Equals, "s13 instance")
  3537  	c.Check(infos[1].Version, Equals, "1.3")
  3538  	c.Check(infos[1].Description(), Equals, "Lots of text")
  3539  }
  3540  
  3541  func (s *snapmgrQuerySuite) TestGadgetInfo(c *C) {
  3542  	st := s.st
  3543  	st.Lock()
  3544  	defer st.Unlock()
  3545  
  3546  	deviceCtxNoGadget := deviceWithoutGadgetContext()
  3547  	deviceCtx := deviceWithGadgetContext("gadget")
  3548  
  3549  	_, err := snapstate.GadgetInfo(st, deviceCtxNoGadget)
  3550  	c.Assert(err, Equals, state.ErrNoState)
  3551  
  3552  	_, err = snapstate.GadgetInfo(st, deviceCtx)
  3553  	c.Assert(err, Equals, state.ErrNoState)
  3554  
  3555  	sideInfo := &snap.SideInfo{
  3556  		RealName: "gadget",
  3557  		Revision: snap.R(2),
  3558  	}
  3559  	snaptest.MockSnap(c, `
  3560  name: gadget
  3561  type: gadget
  3562  version: v1
  3563  `, sideInfo)
  3564  	snapstate.Set(st, "gadget", &snapstate.SnapState{
  3565  		SnapType: "gadget",
  3566  		Active:   true,
  3567  		Sequence: []*snap.SideInfo{sideInfo},
  3568  		Current:  sideInfo.Revision,
  3569  	})
  3570  
  3571  	info, err := snapstate.GadgetInfo(st, deviceCtx)
  3572  	c.Assert(err, IsNil)
  3573  
  3574  	c.Check(info.InstanceName(), Equals, "gadget")
  3575  	c.Check(info.Revision, Equals, snap.R(2))
  3576  	c.Check(info.Version, Equals, "v1")
  3577  	c.Check(info.Type(), Equals, snap.TypeGadget)
  3578  }
  3579  
  3580  func (s *snapmgrQuerySuite) TestKernelInfo(c *C) {
  3581  	st := s.st
  3582  	st.Lock()
  3583  	defer st.Unlock()
  3584  
  3585  	deviceCtxNoKernel := &snapstatetest.TrivialDeviceContext{
  3586  		DeviceModel: ClassicModel(),
  3587  	}
  3588  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  3589  		DeviceModel: MakeModel(map[string]interface{}{
  3590  			"kernel": "pc-kernel",
  3591  		}),
  3592  	}
  3593  
  3594  	_, err := snapstate.KernelInfo(st, deviceCtxNoKernel)
  3595  	c.Assert(err, Equals, state.ErrNoState)
  3596  
  3597  	_, err = snapstate.KernelInfo(st, deviceCtx)
  3598  	c.Assert(err, Equals, state.ErrNoState)
  3599  
  3600  	sideInfo := &snap.SideInfo{
  3601  		RealName: "pc-kernel",
  3602  		Revision: snap.R(3),
  3603  	}
  3604  	snaptest.MockSnap(c, `
  3605  name: pc-kernel
  3606  type: kernel
  3607  version: v2
  3608  `, sideInfo)
  3609  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  3610  		SnapType: "kernel",
  3611  		Active:   true,
  3612  		Sequence: []*snap.SideInfo{sideInfo},
  3613  		Current:  sideInfo.Revision,
  3614  	})
  3615  
  3616  	info, err := snapstate.KernelInfo(st, deviceCtx)
  3617  	c.Assert(err, IsNil)
  3618  
  3619  	c.Check(info.InstanceName(), Equals, "pc-kernel")
  3620  	c.Check(info.Revision, Equals, snap.R(3))
  3621  	c.Check(info.Version, Equals, "v2")
  3622  	c.Check(info.Type(), Equals, snap.TypeKernel)
  3623  }
  3624  
  3625  func (s *snapmgrQuerySuite) TestBootBaseInfo(c *C) {
  3626  	st := s.st
  3627  	st.Lock()
  3628  	defer st.Unlock()
  3629  
  3630  	deviceCtxNoBootBase := &snapstatetest.TrivialDeviceContext{
  3631  		DeviceModel: ClassicModel(),
  3632  	}
  3633  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  3634  		DeviceModel: MakeModel20("gadget", map[string]interface{}{
  3635  			"base": "core20",
  3636  		}),
  3637  	}
  3638  
  3639  	// add core18 which is *not* used for booting
  3640  	si := &snap.SideInfo{RealName: "core18", Revision: snap.R(1)}
  3641  	snaptest.MockSnap(c, `
  3642  name: core18
  3643  type: base
  3644  version: v18
  3645  `, si)
  3646  	snapstate.Set(st, "core18", &snapstate.SnapState{
  3647  		SnapType: "base",
  3648  		Active:   true,
  3649  		Sequence: []*snap.SideInfo{si},
  3650  		Current:  si.Revision,
  3651  	})
  3652  
  3653  	_, err := snapstate.BootBaseInfo(st, deviceCtxNoBootBase)
  3654  	c.Assert(err, Equals, state.ErrNoState)
  3655  
  3656  	// no boot-base in the state so ErrNoState
  3657  	_, err = snapstate.BootBaseInfo(st, deviceCtx)
  3658  	c.Assert(err, Equals, state.ErrNoState)
  3659  
  3660  	sideInfo := &snap.SideInfo{RealName: "core20", Revision: snap.R(4)}
  3661  	snaptest.MockSnap(c, `
  3662  name: core20
  3663  type: base
  3664  version: v20
  3665  `, sideInfo)
  3666  	snapstate.Set(st, "core20", &snapstate.SnapState{
  3667  		SnapType: "base",
  3668  		Active:   true,
  3669  		Sequence: []*snap.SideInfo{sideInfo},
  3670  		Current:  sideInfo.Revision,
  3671  	})
  3672  
  3673  	info, err := snapstate.BootBaseInfo(st, deviceCtx)
  3674  	c.Assert(err, IsNil)
  3675  
  3676  	c.Check(info.InstanceName(), Equals, "core20")
  3677  	c.Check(info.Revision, Equals, snap.R(4))
  3678  	c.Check(info.Version, Equals, "v20")
  3679  	c.Check(info.Type(), Equals, snap.TypeBase)
  3680  }
  3681  
  3682  func (s *snapmgrQuerySuite) TestCoreInfoInternal(c *C) {
  3683  	st := s.st
  3684  	st.Lock()
  3685  	defer st.Unlock()
  3686  
  3687  	for testNr, t := range []struct {
  3688  		expectedSnap string
  3689  		snapNames    []string
  3690  		errMatcher   string
  3691  	}{
  3692  		// nothing
  3693  		{"", []string{}, state.ErrNoState.Error()},
  3694  		// single
  3695  		{"core", []string{"core"}, ""},
  3696  		{"ubuntu-core", []string{"ubuntu-core"}, ""},
  3697  		{"hard-core", []string{"hard-core"}, ""},
  3698  		// unrolled loop to ensure we don't pass because
  3699  		// the order is randomly right
  3700  		{"core", []string{"core", "ubuntu-core"}, ""},
  3701  		{"core", []string{"core", "ubuntu-core"}, ""},
  3702  		{"core", []string{"core", "ubuntu-core"}, ""},
  3703  		{"core", []string{"core", "ubuntu-core"}, ""},
  3704  		{"core", []string{"core", "ubuntu-core"}, ""},
  3705  		{"core", []string{"core", "ubuntu-core"}, ""},
  3706  		{"core", []string{"core", "ubuntu-core"}, ""},
  3707  		{"core", []string{"core", "ubuntu-core"}, ""},
  3708  		// unknown combination
  3709  		{"", []string{"duo-core", "single-core"}, `unexpected cores.*`},
  3710  		// multi-core is not supported
  3711  		{"", []string{"core", "ubuntu-core", "multi-core"}, `unexpected number of cores, got 3`},
  3712  	} {
  3713  		// clear snapstate
  3714  		st.Set("snaps", map[string]*json.RawMessage{})
  3715  
  3716  		for _, snapName := range t.snapNames {
  3717  			sideInfo := &snap.SideInfo{
  3718  				RealName: snapName,
  3719  				Revision: snap.R(1),
  3720  			}
  3721  			snaptest.MockSnap(c, fmt.Sprintf("name: %q\ntype: os\nversion: %q\n", snapName, snapName), sideInfo)
  3722  			snapstate.Set(st, snapName, &snapstate.SnapState{
  3723  				SnapType: string(snap.TypeOS),
  3724  				Active:   true,
  3725  				Sequence: []*snap.SideInfo{sideInfo},
  3726  				Current:  sideInfo.Revision,
  3727  			})
  3728  		}
  3729  
  3730  		info, err := snapstate.CoreInfoInternal(st)
  3731  		if t.errMatcher != "" {
  3732  			c.Assert(err, ErrorMatches, t.errMatcher)
  3733  		} else {
  3734  			c.Assert(info, NotNil)
  3735  			c.Check(info.InstanceName(), Equals, t.expectedSnap, Commentf("(%d) test %q %v", testNr, t.expectedSnap, t.snapNames))
  3736  			c.Check(info.Type(), Equals, snap.TypeOS)
  3737  		}
  3738  	}
  3739  }
  3740  
  3741  func (s *snapmgrQuerySuite) TestHasSnapOfType(c *C) {
  3742  	st := s.st
  3743  	st.Lock()
  3744  	defer st.Unlock()
  3745  
  3746  	// an app snap is already setup
  3747  	ok, err := snapstate.HasSnapOfType(st, snap.TypeApp)
  3748  	c.Assert(err, IsNil)
  3749  	c.Check(ok, Equals, true)
  3750  
  3751  	for _, x := range []struct {
  3752  		snapName string
  3753  		snapType snap.Type
  3754  	}{
  3755  		{
  3756  			snapName: "gadget",
  3757  			snapType: snap.TypeGadget,
  3758  		},
  3759  		{
  3760  			snapName: "core",
  3761  			snapType: snap.TypeOS,
  3762  		},
  3763  		{
  3764  			snapName: "kernel",
  3765  			snapType: snap.TypeKernel,
  3766  		},
  3767  		{
  3768  			snapName: "base",
  3769  			snapType: snap.TypeBase,
  3770  		},
  3771  	} {
  3772  		ok, err := snapstate.HasSnapOfType(st, x.snapType)
  3773  		c.Assert(err, IsNil)
  3774  		c.Check(ok, Equals, false, Commentf("%q", x.snapType))
  3775  
  3776  		sideInfo := &snap.SideInfo{
  3777  			RealName: x.snapName,
  3778  			Revision: snap.R(2),
  3779  		}
  3780  		snapstate.Set(st, x.snapName, &snapstate.SnapState{
  3781  			SnapType: string(x.snapType),
  3782  			Active:   true,
  3783  			Sequence: []*snap.SideInfo{sideInfo},
  3784  			Current:  sideInfo.Revision,
  3785  		})
  3786  
  3787  		ok, err = snapstate.HasSnapOfType(st, x.snapType)
  3788  		c.Assert(err, IsNil)
  3789  		c.Check(ok, Equals, true)
  3790  	}
  3791  }
  3792  
  3793  func (s *snapmgrQuerySuite) TestPreviousSideInfo(c *C) {
  3794  	st := s.st
  3795  	st.Lock()
  3796  	defer st.Unlock()
  3797  
  3798  	var snapst snapstate.SnapState
  3799  	err := snapstate.Get(st, "name1", &snapst)
  3800  	c.Assert(err, IsNil)
  3801  	c.Assert(snapst.CurrentSideInfo(), NotNil)
  3802  	c.Assert(snapst.CurrentSideInfo().Revision, Equals, snap.R(12))
  3803  	c.Assert(snapstate.PreviousSideInfo(&snapst), NotNil)
  3804  	c.Assert(snapstate.PreviousSideInfo(&snapst).Revision, Equals, snap.R(11))
  3805  }
  3806  
  3807  func (s *snapmgrQuerySuite) TestPreviousSideInfoNoCurrent(c *C) {
  3808  	st := s.st
  3809  	st.Lock()
  3810  	defer st.Unlock()
  3811  
  3812  	snapst := &snapstate.SnapState{}
  3813  	c.Assert(snapstate.PreviousSideInfo(snapst), IsNil)
  3814  }
  3815  
  3816  func (s *snapmgrQuerySuite) TestAll(c *C) {
  3817  	st := s.st
  3818  	st.Lock()
  3819  	defer st.Unlock()
  3820  
  3821  	snapStates, err := snapstate.All(st)
  3822  	c.Assert(err, IsNil)
  3823  	c.Assert(snapStates, HasLen, 2)
  3824  
  3825  	n, err := snapstate.NumSnaps(st)
  3826  	c.Assert(err, IsNil)
  3827  	c.Check(n, Equals, 2)
  3828  
  3829  	snapst := snapStates["name1"]
  3830  	c.Assert(snapst, NotNil)
  3831  
  3832  	c.Check(snapst.Active, Equals, true)
  3833  	c.Check(snapst.CurrentSideInfo(), NotNil)
  3834  
  3835  	info12, err := snap.ReadInfo("name1", snapst.CurrentSideInfo())
  3836  	c.Assert(err, IsNil)
  3837  
  3838  	c.Check(info12.InstanceName(), Equals, "name1")
  3839  	c.Check(info12.Revision, Equals, snap.R(12))
  3840  	c.Check(info12.Summary(), Equals, "s12")
  3841  	c.Check(info12.Version, Equals, "1.2")
  3842  	c.Check(info12.Description(), Equals, "Lots of text")
  3843  
  3844  	info11, err := snap.ReadInfo("name1", snapst.Sequence[0])
  3845  	c.Assert(err, IsNil)
  3846  
  3847  	c.Check(info11.InstanceName(), Equals, "name1")
  3848  	c.Check(info11.Revision, Equals, snap.R(11))
  3849  	c.Check(info11.Version, Equals, "1.1")
  3850  
  3851  	instance := snapStates["name1_instance"]
  3852  	c.Assert(instance, NotNil)
  3853  
  3854  	c.Check(instance.Active, Equals, true)
  3855  	c.Check(instance.CurrentSideInfo(), NotNil)
  3856  
  3857  	info13, err := snap.ReadInfo("name1_instance", instance.CurrentSideInfo())
  3858  	c.Assert(err, IsNil)
  3859  
  3860  	c.Check(info13.InstanceName(), Equals, "name1_instance")
  3861  	c.Check(info13.SnapName(), Equals, "name1")
  3862  	c.Check(info13.Revision, Equals, snap.R(13))
  3863  	c.Check(info13.Summary(), Equals, "s13 instance")
  3864  	c.Check(info13.Version, Equals, "1.3")
  3865  	c.Check(info13.Description(), Equals, "Lots of text")
  3866  
  3867  	info13other, err := snap.ReadInfo("name1_instance", instance.Sequence[0])
  3868  	c.Assert(err, IsNil)
  3869  	c.Check(info13, DeepEquals, info13other)
  3870  }
  3871  
  3872  func (s *snapmgrQuerySuite) TestAllEmptyAndEmptyNormalisation(c *C) {
  3873  	st := state.New(nil)
  3874  	st.Lock()
  3875  	defer st.Unlock()
  3876  
  3877  	snapStates, err := snapstate.All(st)
  3878  	c.Assert(err, IsNil)
  3879  	c.Check(snapStates, HasLen, 0)
  3880  
  3881  	n, err := snapstate.NumSnaps(st)
  3882  	c.Assert(err, IsNil)
  3883  	c.Check(n, Equals, 0)
  3884  
  3885  	snapstate.Set(st, "foo", nil)
  3886  
  3887  	snapStates, err = snapstate.All(st)
  3888  	c.Assert(err, IsNil)
  3889  	c.Check(snapStates, HasLen, 0)
  3890  
  3891  	n, err = snapstate.NumSnaps(st)
  3892  	c.Assert(err, IsNil)
  3893  	c.Check(n, Equals, 0)
  3894  
  3895  	snapstate.Set(st, "foo", &snapstate.SnapState{})
  3896  
  3897  	snapStates, err = snapstate.All(st)
  3898  	c.Assert(err, IsNil)
  3899  	c.Check(snapStates, HasLen, 0)
  3900  
  3901  	n, err = snapstate.NumSnaps(st)
  3902  	c.Assert(err, IsNil)
  3903  	c.Check(n, Equals, 0)
  3904  }
  3905  
  3906  type snapStateSuite struct{}
  3907  
  3908  var _ = Suite(&snapStateSuite{})
  3909  
  3910  func (s *snapStateSuite) TestSnapStateDevMode(c *C) {
  3911  	snapst := &snapstate.SnapState{}
  3912  	c.Check(snapst.DevMode, Equals, false)
  3913  	snapst.Flags.DevMode = true
  3914  	c.Check(snapst.DevMode, Equals, true)
  3915  }
  3916  
  3917  func (s *snapStateSuite) TestSnapStateType(c *C) {
  3918  	snapst := &snapstate.SnapState{}
  3919  	_, err := snapst.Type()
  3920  	c.Check(err, ErrorMatches, "snap type unset")
  3921  
  3922  	snapst.SetType(snap.TypeKernel)
  3923  	typ, err := snapst.Type()
  3924  	c.Assert(err, IsNil)
  3925  	c.Check(typ, Equals, snap.TypeKernel)
  3926  }
  3927  
  3928  func (s *snapStateSuite) TestCurrentSideInfoEmpty(c *C) {
  3929  	var snapst snapstate.SnapState
  3930  	c.Check(snapst.CurrentSideInfo(), IsNil)
  3931  	c.Check(snapst.Current.Unset(), Equals, true)
  3932  }
  3933  
  3934  func (s *snapStateSuite) TestCurrentSideInfoSimple(c *C) {
  3935  	si1 := &snap.SideInfo{Revision: snap.R(1)}
  3936  	snapst := snapstate.SnapState{
  3937  		Sequence: []*snap.SideInfo{si1},
  3938  		Current:  snap.R(1),
  3939  	}
  3940  	c.Check(snapst.CurrentSideInfo(), DeepEquals, si1)
  3941  }
  3942  
  3943  func (s *snapStateSuite) TestCurrentSideInfoInOrder(c *C) {
  3944  	si1 := &snap.SideInfo{Revision: snap.R(1)}
  3945  	si2 := &snap.SideInfo{Revision: snap.R(2)}
  3946  	snapst := snapstate.SnapState{
  3947  		Sequence: []*snap.SideInfo{si1, si2},
  3948  		Current:  snap.R(2),
  3949  	}
  3950  	c.Check(snapst.CurrentSideInfo(), DeepEquals, si2)
  3951  }
  3952  
  3953  func (s *snapStateSuite) TestCurrentSideInfoOutOfOrder(c *C) {
  3954  	si1 := &snap.SideInfo{Revision: snap.R(1)}
  3955  	si2 := &snap.SideInfo{Revision: snap.R(2)}
  3956  	snapst := snapstate.SnapState{
  3957  		Sequence: []*snap.SideInfo{si1, si2},
  3958  		Current:  snap.R(1),
  3959  	}
  3960  	c.Check(snapst.CurrentSideInfo(), DeepEquals, si1)
  3961  }
  3962  
  3963  func (s *snapStateSuite) TestCurrentSideInfoInconsistent(c *C) {
  3964  	snapst := snapstate.SnapState{
  3965  		Sequence: []*snap.SideInfo{
  3966  			{Revision: snap.R(1)},
  3967  		},
  3968  	}
  3969  	c.Check(func() { snapst.CurrentSideInfo() }, PanicMatches, `snapst.Current and snapst.Sequence out of sync:.*`)
  3970  }
  3971  
  3972  func (s *snapStateSuite) TestCurrentSideInfoInconsistentWithCurrent(c *C) {
  3973  	snapst := snapstate.SnapState{Current: snap.R(17)}
  3974  	c.Check(func() { snapst.CurrentSideInfo() }, PanicMatches, `cannot find snapst.Current in the snapst.Sequence`)
  3975  }
  3976  
  3977  func (snapStateSuite) TestDefaultContentPlugProviders(c *C) {
  3978  	info := &snap.Info{
  3979  		Plugs: map[string]*snap.PlugInfo{},
  3980  	}
  3981  
  3982  	info.Plugs["foo"] = &snap.PlugInfo{
  3983  		Snap:      info,
  3984  		Name:      "sound-themes",
  3985  		Interface: "content",
  3986  		Attrs:     map[string]interface{}{"default-provider": "common-themes", "content": "foo"},
  3987  	}
  3988  	info.Plugs["bar"] = &snap.PlugInfo{
  3989  		Snap:      info,
  3990  		Name:      "visual-themes",
  3991  		Interface: "content",
  3992  		Attrs:     map[string]interface{}{"default-provider": "common-themes", "content": "bar"},
  3993  	}
  3994  	info.Plugs["baz"] = &snap.PlugInfo{
  3995  		Snap:      info,
  3996  		Name:      "not-themes",
  3997  		Interface: "content",
  3998  		Attrs:     map[string]interface{}{"default-provider": "some-snap", "content": "baz"},
  3999  	}
  4000  	info.Plugs["qux"] = &snap.PlugInfo{Snap: info, Interface: "not-content"}
  4001  
  4002  	st := state.New(nil)
  4003  	st.Lock()
  4004  	defer st.Unlock()
  4005  
  4006  	repo := interfaces.NewRepository()
  4007  	ifacerepo.Replace(st, repo)
  4008  
  4009  	providers := snapstate.DefaultContentPlugProviders(st, info)
  4010  	sort.Strings(providers)
  4011  	c.Check(providers, DeepEquals, []string{"common-themes", "some-snap"})
  4012  }
  4013  
  4014  func (s *snapmgrTestSuite) testRevertSequence(c *C, opts *opSeqOpts) *state.TaskSet {
  4015  	opts.revert = true
  4016  	opts.after = opts.before
  4017  	snapst, ts := s.testOpSequence(c, opts)
  4018  	// successful revert leaves current == via
  4019  	c.Check(snapst.Current.N, Equals, opts.via)
  4020  
  4021  	c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0)
  4022  
  4023  	return ts
  4024  }
  4025  
  4026  func (s *snapmgrTestSuite) testRevertFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet {
  4027  	opts.revert = true
  4028  	opts.after = opts.before
  4029  	s.fakeBackend.linkSnapFailTrigger = fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via)
  4030  	snapst, ts := s.testOpSequence(c, opts)
  4031  	// a failed revert will always end with current unchanged
  4032  	c.Check(snapst.Current.N, Equals, opts.current)
  4033  
  4034  	c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0)
  4035  	c.Check(s.fakeBackend.ops.Count("undo-copy-snap-data"), Equals, 0)
  4036  
  4037  	return ts
  4038  }
  4039  
  4040  func (s *snapmgrTestSuite) testTotalRevertFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet {
  4041  	opts.revert = true
  4042  	opts.fail = true
  4043  	opts.after = opts.before
  4044  	snapst, ts := s.testOpSequence(c, opts)
  4045  	// a failed revert will always end with current unchanged
  4046  	c.Check(snapst.Current.N, Equals, opts.current)
  4047  
  4048  	c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0)
  4049  	c.Check(s.fakeBackend.ops.Count("undo-copy-snap-data"), Equals, 0)
  4050  
  4051  	return ts
  4052  }
  4053  
  4054  // *** sequence tests ***
  4055  
  4056  // 1. a boring update
  4057  // 1a. ... that works
  4058  func (s *snapmgrTestSuite) TestSeqNormal(c *C) {
  4059  	s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4, after: []int{2, 3, 4}})
  4060  }
  4061  
  4062  // 1b. that fails during link
  4063  func (s *snapmgrTestSuite) TestSeqNormalFailure(c *C) {
  4064  	s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4})
  4065  }
  4066  
  4067  // 1c. that fails after link
  4068  func (s *snapmgrTestSuite) TestSeqTotalNormalFailure(c *C) {
  4069  	// total updates are failures after sequence trimming => we lose a rev
  4070  	s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4, after: []int{2, 3}})
  4071  }
  4072  
  4073  // 2. a boring revert
  4074  // 2a. that works
  4075  func (s *snapmgrTestSuite) TestSeqRevert(c *C) {
  4076  	s.testRevertSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2})
  4077  }
  4078  
  4079  // 2b. that fails during link
  4080  func (s *snapmgrTestSuite) TestSeqRevertFailure(c *C) {
  4081  	s.testRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2})
  4082  }
  4083  
  4084  // 2c. that fails after link
  4085  func (s *snapmgrTestSuite) TestSeqTotalRevertFailure(c *C) {
  4086  	s.testTotalRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2})
  4087  }
  4088  
  4089  // 3. a post-revert update
  4090  // 3a. that works
  4091  func (s *snapmgrTestSuite) TestSeqPostRevert(c *C) {
  4092  	s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4, after: []int{1, 2, 4}})
  4093  }
  4094  
  4095  // 3b. that fails during link
  4096  func (s *snapmgrTestSuite) TestSeqPostRevertFailure(c *C) {
  4097  	s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4})
  4098  }
  4099  
  4100  // 3c. that fails after link
  4101  func (s *snapmgrTestSuite) TestSeqTotalPostRevertFailure(c *C) {
  4102  	// lose a rev here as well
  4103  	s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4, after: []int{1, 2}})
  4104  }
  4105  
  4106  // 3d. manually requesting the one reverted away from
  4107  func (s *snapmgrTestSuite) TestSeqRefreshPostRevertSameRevno(c *C) {
  4108  	s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 3, after: []int{1, 2, 3}})
  4109  }
  4110  
  4111  // 4. a post-revert revert
  4112  // 4a. that works
  4113  func (s *snapmgrTestSuite) TestSeqRevertPostRevert(c *C) {
  4114  	s.testRevertSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1})
  4115  }
  4116  
  4117  // 4b. that fails during link
  4118  func (s *snapmgrTestSuite) TestSeqRevertPostRevertFailure(c *C) {
  4119  	s.testRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1})
  4120  }
  4121  
  4122  // 4c. that fails after link
  4123  func (s *snapmgrTestSuite) TestSeqTotalRevertPostRevertFailure(c *C) {
  4124  	s.testTotalRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1})
  4125  }
  4126  
  4127  // 5. an update that missed a rev
  4128  // 5a. that works
  4129  func (s *snapmgrTestSuite) TestSeqMissedOne(c *C) {
  4130  	s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4, after: []int{1, 2, 4}})
  4131  }
  4132  
  4133  // 5b. that fails during link
  4134  func (s *snapmgrTestSuite) TestSeqMissedOneFailure(c *C) {
  4135  	s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4})
  4136  }
  4137  
  4138  // 5c. that fails after link
  4139  func (s *snapmgrTestSuite) TestSeqTotalMissedOneFailure(c *C) {
  4140  	// we don't lose a rev here because len(Seq) < 3 going in
  4141  	s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4, after: []int{1, 2}})
  4142  }
  4143  
  4144  // 6. an update that updates to a revision we already have ("ABA update")
  4145  // 6a. that works
  4146  func (s *snapmgrTestSuite) TestSeqABA(c *C) {
  4147  	s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2, after: []int{1, 3, 2}})
  4148  	c.Check(s.fakeBackend.ops[len(s.fakeBackend.ops)-1], DeepEquals, fakeOp{
  4149  		op:    "cleanup-trash",
  4150  		name:  "some-snap",
  4151  		revno: snap.R(2),
  4152  	})
  4153  }
  4154  
  4155  // 6b. that fails during link
  4156  func (s *snapmgrTestSuite) TestSeqABAFailure(c *C) {
  4157  	s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2})
  4158  	c.Check(s.fakeBackend.ops.First("cleanup-trash"), IsNil)
  4159  }
  4160  
  4161  // 6c that fails after link
  4162  func (s *snapmgrTestSuite) TestSeqTotalABAFailure(c *C) {
  4163  	// we don't lose a rev here because ABA
  4164  	s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2, after: []int{1, 2, 3}})
  4165  	// XXX: TODO: NOTE!! WARNING!! etc
  4166  	//
  4167  	// if this happens in real life, things will be weird. revno 2 will
  4168  	// have data that has been copied from 3, instead of old 2's data,
  4169  	// because the failure occurred *after* nuking the trash. This can
  4170  	// happen when things are chained. Because of this, if it were to
  4171  	// *actually* happen the correct end sequence would be [1, 3] and not
  4172  	// [1, 2, 3]. IRL this scenario can happen if an update that works is
  4173  	// chained to an update that fails. Detecting this case is rather hard,
  4174  	// and the end result is not nice, and we want to move cleanup to a
  4175  	// separate handler & status that will cope with this better (so trash
  4176  	// gets nuked after all tasks succeeded).
  4177  }
  4178  
  4179  func (s *snapmgrTestSuite) TestSeqRetainConf(c *C) {
  4180  	revseq := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
  4181  
  4182  	for i := 2; i <= 10; i++ {
  4183  		// wot, me, hacky?
  4184  		s.TearDownTest(c)
  4185  		s.SetUpTest(c)
  4186  		s.state.Lock()
  4187  		tr := config.NewTransaction(s.state)
  4188  		tr.Set("core", "refresh.retain", i)
  4189  		tr.Commit()
  4190  		s.state.Unlock()
  4191  
  4192  		s.testUpdateSequence(c, &opSeqOpts{before: revseq[:9], current: 9, via: 10, after: revseq[10-i:]})
  4193  	}
  4194  }
  4195  
  4196  func (s *snapmgrTestSuite) TestSnapStateNoLocalRevision(c *C) {
  4197  	si7 := snap.SideInfo{
  4198  		RealName: "some-snap",
  4199  		Revision: snap.R(-7),
  4200  	}
  4201  	si11 := snap.SideInfo{
  4202  		RealName: "some-snap",
  4203  		Revision: snap.R(-11),
  4204  	}
  4205  	snapst := &snapstate.SnapState{
  4206  		Sequence: []*snap.SideInfo{&si7, &si11},
  4207  		Current:  si7.Revision,
  4208  	}
  4209  	c.Assert(snapst.LocalRevision(), Equals, snap.R(-11))
  4210  }
  4211  
  4212  func (s *snapmgrTestSuite) TestSnapStateLocalRevision(c *C) {
  4213  	si7 := snap.SideInfo{
  4214  		RealName: "some-snap",
  4215  		Revision: snap.R(7),
  4216  	}
  4217  	snapst := &snapstate.SnapState{
  4218  		Sequence: []*snap.SideInfo{&si7},
  4219  		Current:  si7.Revision,
  4220  	}
  4221  	c.Assert(snapst.LocalRevision().Unset(), Equals, true)
  4222  }
  4223  
  4224  func tasksWithKind(ts *state.TaskSet, kind string) []*state.Task {
  4225  	var tasks []*state.Task
  4226  	for _, task := range ts.Tasks() {
  4227  		if task.Kind() == kind {
  4228  			tasks = append(tasks, task)
  4229  		}
  4230  	}
  4231  	return tasks
  4232  }
  4233  
  4234  var gadgetYaml = `
  4235  defaults:
  4236      somesnapidididididididididididid:
  4237          key: value
  4238  
  4239  volumes:
  4240      volume-id:
  4241          bootloader: grub
  4242  `
  4243  
  4244  func (s *snapmgrTestSuite) prepareGadget(c *C, extraGadgetYaml ...string) {
  4245  	gadgetSideInfo := &snap.SideInfo{RealName: "the-gadget", SnapID: "the-gadget-id", Revision: snap.R(1)}
  4246  	gadgetInfo := snaptest.MockSnap(c, `
  4247  name: the-gadget
  4248  type: gadget
  4249  version: 1.0
  4250  `, gadgetSideInfo)
  4251  
  4252  	gadgetYamlWhole := strings.Join(append([]string{gadgetYaml}, extraGadgetYaml...), "")
  4253  	err := ioutil.WriteFile(filepath.Join(gadgetInfo.MountDir(), "meta/gadget.yaml"), []byte(gadgetYamlWhole), 0600)
  4254  	c.Assert(err, IsNil)
  4255  
  4256  	snapstate.Set(s.state, "the-gadget", &snapstate.SnapState{
  4257  		Active:   true,
  4258  		Sequence: []*snap.SideInfo{&gadgetInfo.SideInfo},
  4259  		Current:  snap.R(1),
  4260  		SnapType: "gadget",
  4261  	})
  4262  }
  4263  
  4264  func deviceWithGadgetContext(gadgetName string) snapstate.DeviceContext {
  4265  	return &snapstatetest.TrivialDeviceContext{
  4266  		DeviceModel: MakeModel(map[string]interface{}{
  4267  			"gadget": gadgetName,
  4268  		}),
  4269  	}
  4270  }
  4271  
  4272  func deviceWithGadgetContext20(gadgetName string) snapstate.DeviceContext {
  4273  	return &snapstatetest.TrivialDeviceContext{
  4274  		DeviceModel: MakeModel20(gadgetName, nil),
  4275  	}
  4276  }
  4277  
  4278  func deviceWithoutGadgetContext() snapstate.DeviceContext {
  4279  	return &snapstatetest.TrivialDeviceContext{
  4280  		DeviceModel: ClassicModel(),
  4281  	}
  4282  }
  4283  
  4284  func verifyTransitionConnectionsTasks(c *C, ts *state.TaskSet) {
  4285  	c.Check(taskKinds(ts.Tasks()), DeepEquals, []string{
  4286  		"transition-ubuntu-core",
  4287  	})
  4288  
  4289  	transIf := ts.Tasks()[0]
  4290  	var oldName, newName string
  4291  	err := transIf.Get("old-name", &oldName)
  4292  	c.Assert(err, IsNil)
  4293  	c.Check(oldName, Equals, "ubuntu-core")
  4294  
  4295  	err = transIf.Get("new-name", &newName)
  4296  	c.Assert(err, IsNil)
  4297  	c.Check(newName, Equals, "core")
  4298  }
  4299  
  4300  func (s *snapmgrTestSuite) TestTransitionCoreTasks(c *C) {
  4301  	s.state.Lock()
  4302  	defer s.state.Unlock()
  4303  
  4304  	snapstate.Set(s.state, "core", nil)
  4305  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
  4306  		Active:   true,
  4307  		Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}},
  4308  		Current:  snap.R(1),
  4309  		SnapType: "os",
  4310  	})
  4311  
  4312  	tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core")
  4313  	c.Assert(err, IsNil)
  4314  
  4315  	c.Assert(tsl, HasLen, 3)
  4316  	// 1. install core
  4317  	verifyInstallTasks(c, runCoreConfigure|maybeCore, 0, tsl[0], s.state)
  4318  	// 2 transition-connections
  4319  	verifyTransitionConnectionsTasks(c, tsl[1])
  4320  	// 3 remove-ubuntu-core
  4321  	verifyCoreRemoveTasks(c, tsl[2])
  4322  }
  4323  
  4324  func (s *snapmgrTestSuite) TestTransitionCoreTasksWithUbuntuCoreAndCore(c *C) {
  4325  	s.state.Lock()
  4326  	defer s.state.Unlock()
  4327  
  4328  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
  4329  		Active:   true,
  4330  		Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}},
  4331  		Current:  snap.R(1),
  4332  		SnapType: "os",
  4333  	})
  4334  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4335  		Active:   true,
  4336  		Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}},
  4337  		Current:  snap.R(1),
  4338  		SnapType: "os",
  4339  	})
  4340  
  4341  	tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core")
  4342  	c.Assert(err, IsNil)
  4343  
  4344  	c.Assert(tsl, HasLen, 2)
  4345  	// 1. transition connections
  4346  	verifyTransitionConnectionsTasks(c, tsl[0])
  4347  	// 2. remove ubuntu-core
  4348  	verifyCoreRemoveTasks(c, tsl[1])
  4349  }
  4350  
  4351  func (s *snapmgrTestSuite) TestTransitionCoreRunThrough(c *C) {
  4352  	s.state.Lock()
  4353  	defer s.state.Unlock()
  4354  
  4355  	snapstate.Set(s.state, "core", nil)
  4356  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
  4357  		Active:          true,
  4358  		Sequence:        []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}},
  4359  		Current:         snap.R(1),
  4360  		SnapType:        "os",
  4361  		TrackingChannel: "latest/beta",
  4362  	})
  4363  
  4364  	chg := s.state.NewChange("transition-ubuntu-core", "...")
  4365  	tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core")
  4366  	c.Assert(err, IsNil)
  4367  	for _, ts := range tsl {
  4368  		chg.AddAll(ts)
  4369  	}
  4370  
  4371  	s.state.Unlock()
  4372  	defer s.se.Stop()
  4373  	s.settle(c)
  4374  	s.state.Lock()
  4375  
  4376  	// ensure all our tasks ran
  4377  	c.Assert(chg.Err(), IsNil)
  4378  	c.Assert(chg.IsReady(), Equals, true)
  4379  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  4380  		name: "core",
  4381  		// the transition has no user associcated with it
  4382  		macaroon: "",
  4383  		target:   filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
  4384  	}})
  4385  	expected := fakeOps{
  4386  		{
  4387  			op: "storesvc-snap-action",
  4388  			curSnaps: []store.CurrentSnap{
  4389  				{
  4390  					InstanceName:    "ubuntu-core",
  4391  					SnapID:          "ubuntu-core-snap-id",
  4392  					Revision:        snap.R(1),
  4393  					TrackingChannel: "latest/beta",
  4394  					RefreshedDate:   fakeRevDateEpoch.AddDate(0, 0, 1),
  4395  					Epoch:           snap.E("1*"),
  4396  				},
  4397  			},
  4398  		},
  4399  		{
  4400  			op: "storesvc-snap-action:action",
  4401  			action: store.SnapAction{
  4402  				Action:       "install",
  4403  				InstanceName: "core",
  4404  				Channel:      "latest/beta",
  4405  			},
  4406  			revno: snap.R(11),
  4407  		},
  4408  		{
  4409  			op:   "storesvc-download",
  4410  			name: "core",
  4411  		},
  4412  		{
  4413  			op:    "validate-snap:Doing",
  4414  			name:  "core",
  4415  			revno: snap.R(11),
  4416  		},
  4417  		{
  4418  			op:  "current",
  4419  			old: "<no-current>",
  4420  		},
  4421  		{
  4422  			op:   "open-snap-file",
  4423  			path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
  4424  			sinfo: snap.SideInfo{
  4425  				RealName: "core",
  4426  				SnapID:   "core-id",
  4427  				Channel:  "latest/beta",
  4428  				Revision: snap.R(11),
  4429  			},
  4430  		},
  4431  		{
  4432  			op:    "setup-snap",
  4433  			name:  "core",
  4434  			path:  filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
  4435  			revno: snap.R(11),
  4436  		},
  4437  		{
  4438  			op:   "copy-data",
  4439  			path: filepath.Join(dirs.SnapMountDir, "core/11"),
  4440  			old:  "<no-old>",
  4441  		},
  4442  		{
  4443  			op:    "setup-profiles:Doing",
  4444  			name:  "core",
  4445  			revno: snap.R(11),
  4446  		},
  4447  		{
  4448  			op: "candidate",
  4449  			sinfo: snap.SideInfo{
  4450  				RealName: "core",
  4451  				SnapID:   "core-id",
  4452  				Channel:  "latest/beta",
  4453  				Revision: snap.R(11),
  4454  			},
  4455  		},
  4456  		{
  4457  			op:   "link-snap",
  4458  			path: filepath.Join(dirs.SnapMountDir, "core/11"),
  4459  		},
  4460  		{
  4461  			op:    "auto-connect:Doing",
  4462  			name:  "core",
  4463  			revno: snap.R(11),
  4464  		},
  4465  		{
  4466  			op: "update-aliases",
  4467  		},
  4468  		{
  4469  			op:   "transition-ubuntu-core:Doing",
  4470  			name: "ubuntu-core",
  4471  		},
  4472  		{
  4473  			op:    "auto-disconnect:Doing",
  4474  			name:  "ubuntu-core",
  4475  			revno: snap.R(1),
  4476  		},
  4477  		{
  4478  			op:   "remove-snap-aliases",
  4479  			name: "ubuntu-core",
  4480  		},
  4481  		{
  4482  			op:   "unlink-snap",
  4483  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
  4484  		},
  4485  		{
  4486  			op:    "remove-profiles:Doing",
  4487  			name:  "ubuntu-core",
  4488  			revno: snap.R(1),
  4489  		},
  4490  		{
  4491  			op:   "remove-snap-data",
  4492  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
  4493  		},
  4494  		{
  4495  			op:   "remove-snap-common-data",
  4496  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
  4497  		},
  4498  		{
  4499  			op:   "remove-snap-data-dir",
  4500  			name: "ubuntu-core",
  4501  			path: filepath.Join(dirs.SnapDataDir, "ubuntu-core"),
  4502  		},
  4503  		{
  4504  			op:    "remove-snap-files",
  4505  			path:  filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
  4506  			stype: "os",
  4507  		},
  4508  		{
  4509  			op:   "discard-namespace",
  4510  			name: "ubuntu-core",
  4511  		},
  4512  		{
  4513  			op:   "remove-inhibit-lock",
  4514  			name: "ubuntu-core",
  4515  		},
  4516  		{
  4517  			op:   "remove-snap-dir",
  4518  			name: "ubuntu-core",
  4519  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core"),
  4520  		},
  4521  		{
  4522  			op:    "cleanup-trash",
  4523  			name:  "core",
  4524  			revno: snap.R(11),
  4525  		},
  4526  	}
  4527  	// start with an easier-to-read error if this fails:
  4528  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  4529  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  4530  }
  4531  
  4532  func (s *snapmgrTestSuite) TestTransitionCoreRunThroughWithCore(c *C) {
  4533  	s.state.Lock()
  4534  	defer s.state.Unlock()
  4535  
  4536  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
  4537  		Active:          true,
  4538  		Sequence:        []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}},
  4539  		Current:         snap.R(1),
  4540  		SnapType:        "os",
  4541  		TrackingChannel: "latest/stable",
  4542  	})
  4543  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4544  		Active:          true,
  4545  		Sequence:        []*snap.SideInfo{{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(1)}},
  4546  		Current:         snap.R(1),
  4547  		SnapType:        "os",
  4548  		TrackingChannel: "latest/stable",
  4549  	})
  4550  
  4551  	chg := s.state.NewChange("transition-ubuntu-core", "...")
  4552  	tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core")
  4553  	c.Assert(err, IsNil)
  4554  	for _, ts := range tsl {
  4555  		chg.AddAll(ts)
  4556  	}
  4557  
  4558  	s.state.Unlock()
  4559  	defer s.se.Stop()
  4560  	s.settle(c)
  4561  	s.state.Lock()
  4562  
  4563  	// ensure all our tasks ran
  4564  	c.Assert(chg.Err(), IsNil)
  4565  	c.Assert(chg.IsReady(), Equals, true)
  4566  	c.Check(s.fakeStore.downloads, HasLen, 0)
  4567  	expected := fakeOps{
  4568  		{
  4569  			op:   "transition-ubuntu-core:Doing",
  4570  			name: "ubuntu-core",
  4571  		},
  4572  		{
  4573  			op:    "auto-disconnect:Doing",
  4574  			name:  "ubuntu-core",
  4575  			revno: snap.R(1),
  4576  		},
  4577  		{
  4578  			op:   "remove-snap-aliases",
  4579  			name: "ubuntu-core",
  4580  		},
  4581  		{
  4582  			op:   "unlink-snap",
  4583  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
  4584  		},
  4585  		{
  4586  			op:    "remove-profiles:Doing",
  4587  			name:  "ubuntu-core",
  4588  			revno: snap.R(1),
  4589  		},
  4590  		{
  4591  			op:   "remove-snap-data",
  4592  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
  4593  		},
  4594  		{
  4595  			op:   "remove-snap-common-data",
  4596  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
  4597  		},
  4598  		{
  4599  			op:   "remove-snap-data-dir",
  4600  			name: "ubuntu-core",
  4601  			path: filepath.Join(dirs.SnapDataDir, "ubuntu-core"),
  4602  		},
  4603  		{
  4604  			op:    "remove-snap-files",
  4605  			path:  filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
  4606  			stype: "os",
  4607  		},
  4608  		{
  4609  			op:   "discard-namespace",
  4610  			name: "ubuntu-core",
  4611  		},
  4612  		{
  4613  			op:   "remove-inhibit-lock",
  4614  			name: "ubuntu-core",
  4615  		},
  4616  		{
  4617  			op:   "remove-snap-dir",
  4618  			name: "ubuntu-core",
  4619  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core"),
  4620  		},
  4621  	}
  4622  	// start with an easier-to-read error if this fails:
  4623  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  4624  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  4625  }
  4626  
  4627  func (s *snapmgrTestSuite) TestTransitionCoreStartsAutomatically(c *C) {
  4628  	s.state.Lock()
  4629  	defer s.state.Unlock()
  4630  
  4631  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
  4632  		Active:   true,
  4633  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}},
  4634  		Current:  snap.R(1),
  4635  		SnapType: "os",
  4636  	})
  4637  
  4638  	s.state.Unlock()
  4639  	defer s.se.Stop()
  4640  	s.settle(c)
  4641  	s.state.Lock()
  4642  
  4643  	c.Check(s.state.Changes(), HasLen, 1)
  4644  	c.Check(s.state.Changes()[0].Kind(), Equals, "transition-ubuntu-core")
  4645  }
  4646  
  4647  func (s *snapmgrTestSuite) TestTransitionCoreTooEarly(c *C) {
  4648  	s.state.Lock()
  4649  	defer s.state.Unlock()
  4650  
  4651  	r := snapstatetest.MockDeviceModel(nil)
  4652  	defer r()
  4653  
  4654  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
  4655  		Active:   true,
  4656  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}},
  4657  		Current:  snap.R(1),
  4658  		SnapType: "os",
  4659  	})
  4660  
  4661  	s.state.Unlock()
  4662  	defer s.se.Stop()
  4663  	s.settle(c)
  4664  	s.state.Lock()
  4665  
  4666  	c.Check(s.state.Changes(), HasLen, 0)
  4667  	// not counted as a try
  4668  	var t time.Time
  4669  	err := s.state.Get("ubuntu-core-transition-last-retry-time", &t)
  4670  	c.Assert(err, Equals, state.ErrNoState)
  4671  }
  4672  
  4673  func (s *snapmgrTestSuite) TestTransitionCoreTimeLimitWorks(c *C) {
  4674  	s.state.Lock()
  4675  	defer s.state.Unlock()
  4676  
  4677  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
  4678  		Active:   true,
  4679  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}},
  4680  		Current:  snap.R(1),
  4681  		SnapType: "os",
  4682  	})
  4683  
  4684  	// tried 3h ago, no retry
  4685  	s.state.Set("ubuntu-core-transition-last-retry-time", time.Now().Add(-3*time.Hour))
  4686  
  4687  	s.state.Unlock()
  4688  	defer s.se.Stop()
  4689  	s.settle(c)
  4690  	s.state.Lock()
  4691  
  4692  	c.Check(s.state.Changes(), HasLen, 0)
  4693  
  4694  	// tried 7h ago, retry
  4695  	s.state.Set("ubuntu-core-transition-last-retry-time", time.Now().Add(-7*time.Hour))
  4696  
  4697  	s.state.Unlock()
  4698  	defer s.se.Stop()
  4699  	s.settle(c)
  4700  	s.state.Lock()
  4701  	c.Check(s.state.Changes(), HasLen, 1)
  4702  
  4703  	var t time.Time
  4704  	s.state.Get("ubuntu-core-transition-last-retry-time", &t)
  4705  	c.Assert(time.Since(t) < 2*time.Minute, Equals, true)
  4706  }
  4707  
  4708  func (s *snapmgrTestSuite) TestTransitionCoreNoOtherChanges(c *C) {
  4709  	s.state.Lock()
  4710  	defer s.state.Unlock()
  4711  
  4712  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
  4713  		Active:   true,
  4714  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}},
  4715  		Current:  snap.R(1),
  4716  		SnapType: "os",
  4717  	})
  4718  	chg := s.state.NewChange("unrelated-change", "unfinished change blocks core transition")
  4719  	chg.SetStatus(state.DoStatus)
  4720  
  4721  	s.state.Unlock()
  4722  	defer s.se.Stop()
  4723  	s.settle(c)
  4724  	s.state.Lock()
  4725  
  4726  	c.Check(s.state.Changes(), HasLen, 1)
  4727  	c.Check(s.state.Changes()[0].Kind(), Equals, "unrelated-change")
  4728  }
  4729  
  4730  func (s *snapmgrTestSuite) TestTransitionCoreBlocksOtherChanges(c *C) {
  4731  	s.state.Lock()
  4732  	defer s.state.Unlock()
  4733  
  4734  	// if we have a ubuntu-core -> core transition
  4735  	chg := s.state.NewChange("transition-ubuntu-core", "...")
  4736  	chg.SetStatus(state.DoStatus)
  4737  
  4738  	// other tasks block until the transition is done
  4739  	opts := &snapstate.RevisionOptions{Channel: "stable"}
  4740  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  4741  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  4742  	c.Check(err, ErrorMatches, "ubuntu-core to core transition in progress, no other changes allowed until this is done")
  4743  
  4744  	// and when the transition is done, other tasks run
  4745  	chg.SetStatus(state.DoneStatus)
  4746  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  4747  	c.Check(err, IsNil)
  4748  	c.Check(ts, NotNil)
  4749  }
  4750  
  4751  func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesNotRunWithoutSnaps(c *C) {
  4752  	s.state.Lock()
  4753  	defer s.state.Unlock()
  4754  
  4755  	tr := config.NewTransaction(s.state)
  4756  	tr.Set("core", "experimental.snapd-snap", true)
  4757  	tr.Commit()
  4758  
  4759  	// no snaps installed on this system (e.g. fresh classic)
  4760  	snapstate.Set(s.state, "core", nil)
  4761  
  4762  	s.state.Unlock()
  4763  	defer s.se.Stop()
  4764  	s.settle(c)
  4765  	s.state.Lock()
  4766  
  4767  	c.Check(s.state.Changes(), HasLen, 0)
  4768  }
  4769  
  4770  func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesRunWithAnySnap(c *C) {
  4771  	s.state.Lock()
  4772  	defer s.state.Unlock()
  4773  
  4774  	tr := config.NewTransaction(s.state)
  4775  	tr.Set("core", "experimental.snapd-snap", true)
  4776  	tr.Commit()
  4777  
  4778  	// some snap installed on this system but no core
  4779  	snapstate.Set(s.state, "core", nil)
  4780  	snapstate.Set(s.state, "foo", &snapstate.SnapState{
  4781  		Active:   true,
  4782  		Sequence: []*snap.SideInfo{{RealName: "foo", SnapID: "foo-id", Revision: snap.R(1), Channel: "beta"}},
  4783  		Current:  snap.R(1),
  4784  	})
  4785  
  4786  	s.state.Unlock()
  4787  	defer s.se.Stop()
  4788  	s.settle(c)
  4789  	s.state.Lock()
  4790  
  4791  	c.Check(s.state.Changes(), HasLen, 1)
  4792  }
  4793  
  4794  func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesNotRunWhenNotEnabled(c *C) {
  4795  	s.state.Lock()
  4796  	defer s.state.Unlock()
  4797  
  4798  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4799  		Active:   true,
  4800  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "beta"}},
  4801  		Current:  snap.R(1),
  4802  		SnapType: "os",
  4803  	})
  4804  
  4805  	s.state.Unlock()
  4806  	defer s.se.Stop()
  4807  	s.settle(c)
  4808  	s.state.Lock()
  4809  
  4810  	c.Check(s.state.Changes(), HasLen, 0)
  4811  }
  4812  
  4813  func (s *snapmgrTestSuite) TestTransitionSnapdSnapStartsAutomaticallyWhenEnabled(c *C) {
  4814  	s.state.Lock()
  4815  	defer s.state.Unlock()
  4816  
  4817  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4818  		Active:   true,
  4819  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "beta"}},
  4820  		Current:  snap.R(1),
  4821  		SnapType: "os",
  4822  	})
  4823  	tr := config.NewTransaction(s.state)
  4824  	tr.Set("core", "experimental.snapd-snap", true)
  4825  	tr.Commit()
  4826  
  4827  	s.state.Unlock()
  4828  	defer s.se.Stop()
  4829  	s.settle(c)
  4830  	s.state.Lock()
  4831  
  4832  	c.Check(s.state.Changes(), HasLen, 1)
  4833  	chg := s.state.Changes()[0]
  4834  	c.Check(chg.Kind(), Equals, "transition-to-snapd-snap")
  4835  	c.Assert(chg.Err(), IsNil)
  4836  	c.Assert(chg.IsReady(), Equals, true)
  4837  
  4838  	// snapd snap is instaleld from the default channel
  4839  	var snapst snapstate.SnapState
  4840  	snapstate.Get(s.state, "snapd", &snapst)
  4841  	c.Assert(snapst.TrackingChannel, Equals, "latest/stable")
  4842  }
  4843  
  4844  func (s *snapmgrTestSuite) TestTransitionSnapdSnapWithCoreRunthrough(c *C) {
  4845  	s.state.Lock()
  4846  	defer s.state.Unlock()
  4847  
  4848  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4849  		Active:   true,
  4850  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "edge"}},
  4851  		Current:  snap.R(1),
  4852  		SnapType: "os",
  4853  		// TrackingChannel
  4854  		TrackingChannel: "latest/beta",
  4855  	})
  4856  	tr := config.NewTransaction(s.state)
  4857  	tr.Set("core", "experimental.snapd-snap", true)
  4858  	tr.Commit()
  4859  
  4860  	s.state.Unlock()
  4861  	defer s.se.Stop()
  4862  	s.settle(c)
  4863  	s.state.Lock()
  4864  
  4865  	c.Assert(s.state.Changes(), HasLen, 1)
  4866  	chg := s.state.Changes()[0]
  4867  	c.Assert(chg.Kind(), Equals, "transition-to-snapd-snap")
  4868  	c.Assert(chg.Err(), IsNil)
  4869  	c.Assert(chg.IsReady(), Equals, true)
  4870  	c.Check(s.fakeStore.downloads, HasLen, 1)
  4871  	ts := state.NewTaskSet(chg.Tasks()...)
  4872  	verifyInstallTasks(c, noConfigure, 0, ts, s.state)
  4873  
  4874  	// ensure preferences from the core snap got transferred over
  4875  	var snapst snapstate.SnapState
  4876  	snapstate.Get(s.state, "snapd", &snapst)
  4877  	c.Assert(snapst.TrackingChannel, Equals, "latest/beta")
  4878  }
  4879  
  4880  func (s *snapmgrTestSuite) TestTransitionSnapdSnapTimeLimitWorks(c *C) {
  4881  	s.state.Lock()
  4882  	defer s.state.Unlock()
  4883  
  4884  	tr := config.NewTransaction(s.state)
  4885  	tr.Set("core", "experimental.snapd-snap", true)
  4886  	tr.Commit()
  4887  
  4888  	// tried 3h ago, no retry
  4889  	s.state.Set("snapd-transition-last-retry-time", time.Now().Add(-3*time.Hour))
  4890  
  4891  	s.state.Unlock()
  4892  	defer s.se.Stop()
  4893  	s.settle(c)
  4894  	s.state.Lock()
  4895  
  4896  	c.Check(s.state.Changes(), HasLen, 0)
  4897  
  4898  	// tried 7h ago, retry
  4899  	s.state.Set("snapd-transition-last-retry-time", time.Now().Add(-7*time.Hour))
  4900  
  4901  	s.state.Unlock()
  4902  	defer s.se.Stop()
  4903  	s.settle(c)
  4904  	s.state.Lock()
  4905  	c.Check(s.state.Changes(), HasLen, 1)
  4906  
  4907  	var t time.Time
  4908  	s.state.Get("snapd-transition-last-retry-time", &t)
  4909  	c.Assert(time.Since(t) < 2*time.Minute, Equals, true)
  4910  }
  4911  
  4912  type unhappyStore struct {
  4913  	*fakeStore
  4914  }
  4915  
  4916  func (s unhappyStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) {
  4917  	if assertQuery != nil {
  4918  		panic("no assertion query support")
  4919  	}
  4920  
  4921  	return nil, nil, fmt.Errorf("a grumpy store")
  4922  }
  4923  
  4924  func (s *snapmgrTestSuite) TestTransitionSnapdSnapError(c *C) {
  4925  	s.state.Lock()
  4926  	defer s.state.Unlock()
  4927  
  4928  	snapstate.ReplaceStore(s.state, unhappyStore{fakeStore: s.fakeStore})
  4929  
  4930  	tr := config.NewTransaction(s.state)
  4931  	tr.Set("core", "experimental.snapd-snap", true)
  4932  	tr.Commit()
  4933  
  4934  	s.state.Unlock()
  4935  	defer s.se.Stop()
  4936  	err := s.o.Settle(5 * time.Second)
  4937  	c.Assert(err, ErrorMatches, `state ensure errors: \[a grumpy store\]`)
  4938  
  4939  	s.state.Lock()
  4940  	c.Check(s.state.Changes(), HasLen, 0)
  4941  
  4942  	// all the attempts were recorded
  4943  	var t time.Time
  4944  	s.state.Get("snapd-transition-last-retry-time", &t)
  4945  	c.Assert(time.Since(t) < 2*time.Minute, Equals, true)
  4946  
  4947  	var cnt int
  4948  	s.state.Get("snapd-transition-retry", &cnt)
  4949  	c.Assert(cnt, Equals, 1)
  4950  
  4951  	// the transition is not tried again (because of retry time)
  4952  	s.state.Unlock()
  4953  	err = s.o.Settle(5 * time.Second)
  4954  	c.Assert(err, IsNil)
  4955  	s.state.Lock()
  4956  
  4957  	s.state.Get("snapd-transition-retry", &cnt)
  4958  	c.Assert(cnt, Equals, 1)
  4959  }
  4960  
  4961  func (s *snapmgrTestSuite) TestTransitionSnapdSnapBlocksOtherChanges(c *C) {
  4962  	s.state.Lock()
  4963  	defer s.state.Unlock()
  4964  
  4965  	// if we have a snapd transition
  4966  	chg := s.state.NewChange("transition-to-snapd-snap", "...")
  4967  	chg.SetStatus(state.DoStatus)
  4968  
  4969  	// other tasks block until the transition is done
  4970  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
  4971  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  4972  	c.Check(err, ErrorMatches, "transition to snapd snap in progress, no other changes allowed until this is done")
  4973  
  4974  	// and when the transition is done, other tasks run
  4975  	chg.SetStatus(state.DoneStatus)
  4976  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
  4977  	c.Check(err, IsNil)
  4978  	c.Check(ts, NotNil)
  4979  }
  4980  
  4981  func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsForUbuntuCore(c *C) {
  4982  	s.checkForceDevModeCleanupRuns(c, "ubuntu-core", true)
  4983  }
  4984  
  4985  func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsForCore(c *C) {
  4986  	s.checkForceDevModeCleanupRuns(c, "core", true)
  4987  }
  4988  
  4989  func (s *snapmgrTestSuite) TestForceDevModeCleanupSkipsRando(c *C) {
  4990  	s.checkForceDevModeCleanupRuns(c, "rando", false)
  4991  }
  4992  
  4993  func (s *snapmgrTestSuite) checkForceDevModeCleanupRuns(c *C, name string, shouldBeReset bool) {
  4994  	r := sandbox.MockForceDevMode(true)
  4995  	defer r()
  4996  	c.Assert(sandbox.ForceDevMode(), Equals, true)
  4997  
  4998  	s.state.Lock()
  4999  	defer s.state.Unlock()
  5000  
  5001  	snapstate.Set(s.state, name, &snapstate.SnapState{
  5002  		Active: true,
  5003  		Sequence: []*snap.SideInfo{{
  5004  			RealName: name,
  5005  			SnapID:   "id-id-id",
  5006  			Revision: snap.R(1)}},
  5007  		Current:  snap.R(1),
  5008  		SnapType: "os",
  5009  		Flags:    snapstate.Flags{DevMode: true},
  5010  	})
  5011  
  5012  	var snapst1 snapstate.SnapState
  5013  	// sanity check
  5014  	snapstate.Get(s.state, name, &snapst1)
  5015  	c.Assert(snapst1.DevMode, Equals, true)
  5016  
  5017  	s.state.Unlock()
  5018  	defer s.se.Stop()
  5019  	s.settle(c)
  5020  	s.state.Lock()
  5021  
  5022  	var snapst2 snapstate.SnapState
  5023  	snapstate.Get(s.state, name, &snapst2)
  5024  
  5025  	c.Check(snapst2.DevMode, Equals, !shouldBeReset)
  5026  
  5027  	var n int
  5028  	s.state.Get("fix-forced-devmode", &n)
  5029  	c.Check(n, Equals, 1)
  5030  }
  5031  
  5032  func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsNoSnaps(c *C) {
  5033  	r := sandbox.MockForceDevMode(true)
  5034  	defer r()
  5035  	c.Assert(sandbox.ForceDevMode(), Equals, true)
  5036  
  5037  	defer s.se.Stop()
  5038  	s.settle(c)
  5039  	s.state.Lock()
  5040  	defer s.state.Unlock()
  5041  
  5042  	var n int
  5043  	s.state.Get("fix-forced-devmode", &n)
  5044  	c.Check(n, Equals, 1)
  5045  }
  5046  
  5047  func (s *snapmgrTestSuite) TestForceDevModeCleanupSkipsNonForcedOS(c *C) {
  5048  	r := sandbox.MockForceDevMode(false)
  5049  	defer r()
  5050  	c.Assert(sandbox.ForceDevMode(), Equals, false)
  5051  
  5052  	s.state.Lock()
  5053  	defer s.state.Unlock()
  5054  
  5055  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  5056  		Active: true,
  5057  		Sequence: []*snap.SideInfo{{
  5058  			RealName: "core",
  5059  			SnapID:   "id-id-id",
  5060  			Revision: snap.R(1)}},
  5061  		Current:  snap.R(1),
  5062  		SnapType: "os",
  5063  		Flags:    snapstate.Flags{DevMode: true},
  5064  	})
  5065  
  5066  	var snapst1 snapstate.SnapState
  5067  	// sanity check
  5068  	snapstate.Get(s.state, "core", &snapst1)
  5069  	c.Assert(snapst1.DevMode, Equals, true)
  5070  
  5071  	s.state.Unlock()
  5072  	defer s.se.Stop()
  5073  	s.settle(c)
  5074  	s.state.Lock()
  5075  
  5076  	var snapst2 snapstate.SnapState
  5077  	snapstate.Get(s.state, "core", &snapst2)
  5078  
  5079  	// no change
  5080  	c.Check(snapst2.DevMode, Equals, true)
  5081  
  5082  	// not really run at all in fact
  5083  	var n int
  5084  	s.state.Get("fix-forced-devmode", &n)
  5085  	c.Check(n, Equals, 0)
  5086  }
  5087  
  5088  func (s *snapmgrTestSuite) TestEnsureAliasesV2(c *C) {
  5089  	s.state.Lock()
  5090  	defer s.state.Unlock()
  5091  
  5092  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  5093  		switch info.InstanceName() {
  5094  		case "alias-snap":
  5095  			return map[string]string{
  5096  				"alias1": "cmd1",
  5097  				"alias2": "cmd2",
  5098  			}, nil
  5099  		}
  5100  		return nil, nil
  5101  	}
  5102  
  5103  	snapstate.Set(s.state, "core", nil)
  5104  	snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{
  5105  		Sequence: []*snap.SideInfo{
  5106  			{RealName: "alias-snap", Revision: snap.R(11)},
  5107  		},
  5108  		Current: snap.R(11),
  5109  		Active:  true,
  5110  	})
  5111  
  5112  	s.state.Set("aliases", map[string]map[string]string{
  5113  		"alias-snap": {
  5114  			"alias1": "auto",
  5115  		},
  5116  	})
  5117  
  5118  	s.state.Unlock()
  5119  	err := s.snapmgr.Ensure()
  5120  	s.state.Lock()
  5121  	c.Assert(err, IsNil)
  5122  
  5123  	var gone interface{}
  5124  	err = s.state.Get("aliases", &gone)
  5125  	c.Assert(err, Equals, state.ErrNoState)
  5126  
  5127  	var snapst snapstate.SnapState
  5128  	err = snapstate.Get(s.state, "alias-snap", &snapst)
  5129  	c.Assert(err, IsNil)
  5130  
  5131  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  5132  	c.Check(snapst.AliasesPending, Equals, false)
  5133  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  5134  		"alias1": {Auto: "cmd1"},
  5135  		"alias2": {Auto: "cmd2"},
  5136  	})
  5137  
  5138  	expected := fakeOps{
  5139  		{
  5140  			op:   "remove-snap-aliases",
  5141  			name: "alias-snap",
  5142  		},
  5143  		{
  5144  			op: "update-aliases",
  5145  			aliases: []*backend.Alias{
  5146  				{Name: "alias1", Target: "alias-snap.cmd1"},
  5147  				{Name: "alias2", Target: "alias-snap.cmd2"},
  5148  			},
  5149  		},
  5150  	}
  5151  	// start with an easier-to-read error if this fails:
  5152  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  5153  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  5154  }
  5155  
  5156  func (s *snapmgrTestSuite) TestEnsureAliasesV2SnapDisabled(c *C) {
  5157  	s.state.Lock()
  5158  	defer s.state.Unlock()
  5159  
  5160  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  5161  		switch info.InstanceName() {
  5162  		case "alias-snap":
  5163  			return map[string]string{
  5164  				"alias1": "cmd1",
  5165  				"alias2": "cmd2",
  5166  			}, nil
  5167  		}
  5168  		return nil, nil
  5169  	}
  5170  
  5171  	snapstate.Set(s.state, "core", nil)
  5172  	snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{
  5173  		Sequence: []*snap.SideInfo{
  5174  			{RealName: "alias-snap", Revision: snap.R(11)},
  5175  		},
  5176  		Current: snap.R(11),
  5177  		Active:  false,
  5178  	})
  5179  
  5180  	s.state.Set("aliases", map[string]map[string]string{
  5181  		"alias-snap": {
  5182  			"alias1": "auto",
  5183  		},
  5184  	})
  5185  
  5186  	s.state.Unlock()
  5187  	err := s.snapmgr.Ensure()
  5188  	s.state.Lock()
  5189  	c.Assert(err, IsNil)
  5190  
  5191  	var gone interface{}
  5192  	err = s.state.Get("aliases", &gone)
  5193  	c.Assert(err, Equals, state.ErrNoState)
  5194  
  5195  	var snapst snapstate.SnapState
  5196  	err = snapstate.Get(s.state, "alias-snap", &snapst)
  5197  	c.Assert(err, IsNil)
  5198  
  5199  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  5200  	c.Check(snapst.AliasesPending, Equals, true)
  5201  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  5202  		"alias1": {Auto: "cmd1"},
  5203  		"alias2": {Auto: "cmd2"},
  5204  	})
  5205  
  5206  	expected := fakeOps{
  5207  		{
  5208  			op:   "remove-snap-aliases",
  5209  			name: "alias-snap",
  5210  		},
  5211  	}
  5212  	// start with an easier-to-read error if this fails:
  5213  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  5214  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  5215  }
  5216  
  5217  func (s *snapmgrTestSuite) TestEnsureAliasesV2MarkAliasTasksInError(c *C) {
  5218  	s.state.Lock()
  5219  	defer s.state.Unlock()
  5220  
  5221  	s.state.Set("aliases", map[string]map[string]string{
  5222  		"alias-snap": {
  5223  			"alias1": "auto",
  5224  		},
  5225  	})
  5226  
  5227  	// pending old alias task
  5228  	t := s.state.NewTask("alias", "...")
  5229  	t.Set("aliases", map[string]string{})
  5230  	chg := s.state.NewChange("alias chg", "...")
  5231  	chg.AddTask(t)
  5232  
  5233  	s.state.Unlock()
  5234  	err := s.snapmgr.Ensure()
  5235  	s.state.Lock()
  5236  	c.Assert(err, IsNil)
  5237  
  5238  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  5239  	c.Check(chg.IsReady(), Equals, true)
  5240  	c.Check(t.Status(), Equals, state.ErrorStatus)
  5241  }
  5242  
  5243  func (s *snapmgrTestSuite) TestConflictMany(c *C) {
  5244  	s.state.Lock()
  5245  	defer s.state.Unlock()
  5246  
  5247  	for _, instanceName := range []string{"a-snap", "b-snap"} {
  5248  		snapstate.Set(s.state, instanceName, &snapstate.SnapState{
  5249  			Sequence: []*snap.SideInfo{
  5250  				{RealName: instanceName, Revision: snap.R(11)},
  5251  			},
  5252  			Current: snap.R(11),
  5253  			Active:  false,
  5254  		})
  5255  
  5256  		ts, err := snapstate.Enable(s.state, instanceName)
  5257  		c.Assert(err, IsNil)
  5258  		// need a change to make the tasks visible
  5259  		s.state.NewChange("enable", "...").AddAll(ts)
  5260  	}
  5261  
  5262  	// things that should be ok:
  5263  	for _, m := range [][]string{
  5264  		{}, //nothing
  5265  		{"c-snap"},
  5266  		{"c-snap", "d-snap", "e-snap", "f-snap"},
  5267  	} {
  5268  		c.Check(snapstate.CheckChangeConflictMany(s.state, m, ""), IsNil)
  5269  	}
  5270  
  5271  	// things that should not be ok:
  5272  	for _, m := range [][]string{
  5273  		{"a-snap"},
  5274  		{"a-snap", "b-snap"},
  5275  		{"a-snap", "c-snap"},
  5276  		{"b-snap", "c-snap"},
  5277  	} {
  5278  		err := snapstate.CheckChangeConflictMany(s.state, m, "")
  5279  		c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  5280  		c.Check(err, ErrorMatches, `snap "[^"]*" has "enable" change in progress`)
  5281  	}
  5282  }
  5283  
  5284  func (s *snapmgrTestSuite) TestConflictRemodeling(c *C) {
  5285  	s.state.Lock()
  5286  	defer s.state.Unlock()
  5287  
  5288  	chg := s.state.NewChange("remodel", "...")
  5289  	chg.SetStatus(state.DoingStatus)
  5290  
  5291  	err := snapstate.CheckChangeConflictMany(s.state, []string{"a-snap"}, "")
  5292  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  5293  	c.Check(err, ErrorMatches, `remodeling in progress, no other changes allowed until this is done`)
  5294  
  5295  	// a remodel conflicts with another remodel
  5296  	err = snapstate.CheckChangeConflictRunExclusively(s.state, "remodel")
  5297  	c.Check(err, ErrorMatches, `remodeling in progress, no other changes allowed until this is done`)
  5298  
  5299  	// we have a remodel change in state, a remodel change triggers are conflict
  5300  	err = snapstate.CheckChangeConflictRunExclusively(s.state, "create-recovery-system")
  5301  	c.Check(err, ErrorMatches, `remodeling in progress, no other changes allowed until this is done`)
  5302  }
  5303  
  5304  func (s *snapmgrTestSuite) TestConflictCreateRecovery(c *C) {
  5305  	s.state.Lock()
  5306  	defer s.state.Unlock()
  5307  
  5308  	chg := s.state.NewChange("create-recovery-system", "...")
  5309  	c.Check(chg.IsReady(), Equals, false)
  5310  	chg.SetStatus(state.DoingStatus)
  5311  
  5312  	err := snapstate.CheckChangeConflictMany(s.state, []string{"a-snap"}, "")
  5313  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  5314  	c.Check(err, ErrorMatches, `creating recovery system in progress, no other changes allowed until this is done`)
  5315  
  5316  	// remodeling conflicts with a change that creates a recovery system
  5317  	err = snapstate.CheckChangeConflictRunExclusively(s.state, "remodel")
  5318  	c.Check(err, ErrorMatches, `creating recovery system in progress, no other changes allowed until this is done`)
  5319  
  5320  	// so does another another create recovery system change
  5321  	err = snapstate.CheckChangeConflictRunExclusively(s.state, "create-recovery-system")
  5322  	c.Check(err, ErrorMatches, `creating recovery system in progress, no other changes allowed until this is done`)
  5323  }
  5324  
  5325  func (s *snapmgrTestSuite) TestConflictExclusive(c *C) {
  5326  	s.state.Lock()
  5327  	defer s.state.Unlock()
  5328  
  5329  	chg := s.state.NewChange("install-snap-a", "...")
  5330  	chg.SetStatus(state.DoingStatus)
  5331  
  5332  	// a remodel conflicts with any other change
  5333  	err := snapstate.CheckChangeConflictRunExclusively(s.state, "remodel")
  5334  	c.Check(err, ErrorMatches, `other changes in progress \(conflicting change "install-snap-a"\), change "remodel" not allowed until they are done`)
  5335  	// and so does the  remodel conflicts with any other change
  5336  	err = snapstate.CheckChangeConflictRunExclusively(s.state, "create-recovery-system")
  5337  	c.Check(err, ErrorMatches, `other changes in progress \(conflicting change "install-snap-a"\), change "create-recovery-system" not allowed until they are done`)
  5338  }
  5339  
  5340  type contentStore struct {
  5341  	*fakeStore
  5342  	state *state.State
  5343  }
  5344  
  5345  func (s contentStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) {
  5346  	sars, _, err := s.fakeStore.SnapAction(ctx, currentSnaps, actions, assertQuery, user, opts)
  5347  	if err != nil {
  5348  		return nil, nil, err
  5349  	}
  5350  	if len(sars) != 1 {
  5351  		panic("expected to be queried for install of only one snap at a time")
  5352  	}
  5353  	info := sars[0].Info
  5354  	switch info.InstanceName() {
  5355  	case "snap-content-plug":
  5356  		info.Plugs = map[string]*snap.PlugInfo{
  5357  			"some-plug": {
  5358  				Snap:      info,
  5359  				Name:      "shared-content",
  5360  				Interface: "content",
  5361  				Attrs: map[string]interface{}{
  5362  					"default-provider": "snap-content-slot",
  5363  					"content":          "shared-content",
  5364  				},
  5365  			},
  5366  		}
  5367  	case "snap-content-plug-compat":
  5368  		info.Plugs = map[string]*snap.PlugInfo{
  5369  			"some-plug": {
  5370  				Snap:      info,
  5371  				Name:      "shared-content",
  5372  				Interface: "content",
  5373  				Attrs: map[string]interface{}{
  5374  					"default-provider": "snap-content-slot:some-slot",
  5375  					"content":          "shared-content",
  5376  				},
  5377  			},
  5378  		}
  5379  	case "snap-content-slot":
  5380  		info.Slots = map[string]*snap.SlotInfo{
  5381  			"some-slot": {
  5382  				Snap:      info,
  5383  				Name:      "shared-content",
  5384  				Interface: "content",
  5385  				Attrs: map[string]interface{}{
  5386  					"content": "shared-content",
  5387  				},
  5388  			},
  5389  		}
  5390  	case "snap-content-circular1":
  5391  		info.Plugs = map[string]*snap.PlugInfo{
  5392  			"circular-plug1": {
  5393  				Snap:      info,
  5394  				Name:      "circular-plug1",
  5395  				Interface: "content",
  5396  				Attrs: map[string]interface{}{
  5397  					"default-provider": "snap-content-circular2",
  5398  					"content":          "circular2",
  5399  				},
  5400  			},
  5401  		}
  5402  		info.Slots = map[string]*snap.SlotInfo{
  5403  			"circular-slot1": {
  5404  				Snap:      info,
  5405  				Name:      "circular-slot1",
  5406  				Interface: "content",
  5407  				Attrs: map[string]interface{}{
  5408  					"content": "circular1",
  5409  				},
  5410  			},
  5411  		}
  5412  	case "snap-content-circular2":
  5413  		info.Plugs = map[string]*snap.PlugInfo{
  5414  			"circular-plug2": {
  5415  				Snap:      info,
  5416  				Name:      "circular-plug2",
  5417  				Interface: "content",
  5418  				Attrs: map[string]interface{}{
  5419  					"default-provider": "snap-content-circular1",
  5420  					"content":          "circular2",
  5421  				},
  5422  			},
  5423  		}
  5424  		info.Slots = map[string]*snap.SlotInfo{
  5425  			"circular-slot2": {
  5426  				Snap:      info,
  5427  				Name:      "circular-slot2",
  5428  				Interface: "content",
  5429  				Attrs: map[string]interface{}{
  5430  					"content": "circular1",
  5431  				},
  5432  			},
  5433  		}
  5434  	}
  5435  
  5436  	return []store.SnapActionResult{{Info: info}}, nil, err
  5437  }
  5438  
  5439  func (s *snapmgrTestSuite) TestSnapManagerLegacyRefreshSchedule(c *C) {
  5440  	s.state.Lock()
  5441  	defer s.state.Unlock()
  5442  
  5443  	for _, t := range []struct {
  5444  		in     string
  5445  		out    string
  5446  		legacy bool
  5447  	}{
  5448  		{"", snapstate.DefaultRefreshSchedule, false},
  5449  		{"invalid schedule", snapstate.DefaultRefreshSchedule, false},
  5450  		{"8:00-12:00", "8:00-12:00", true},
  5451  		// using the legacy configuration option with a new-style
  5452  		// refresh.timer string is rejected (i.e. the legacy parser is
  5453  		// used for the parsing)
  5454  		{"0:00~24:00/24", snapstate.DefaultRefreshSchedule, false},
  5455  	} {
  5456  		if t.in != "" {
  5457  			tr := config.NewTransaction(s.state)
  5458  			tr.Set("core", "refresh.timer", "")
  5459  			tr.Set("core", "refresh.schedule", t.in)
  5460  			tr.Commit()
  5461  		}
  5462  		scheduleStr, legacy, err := s.snapmgr.RefreshSchedule()
  5463  		c.Check(err, IsNil)
  5464  		c.Check(scheduleStr, Equals, t.out)
  5465  		c.Check(legacy, Equals, t.legacy)
  5466  	}
  5467  }
  5468  
  5469  func (s *snapmgrTestSuite) TestSnapManagerRefreshSchedule(c *C) {
  5470  	s.state.Lock()
  5471  	defer s.state.Unlock()
  5472  
  5473  	for _, t := range []struct {
  5474  		in  string
  5475  		out string
  5476  	}{
  5477  		{"", snapstate.DefaultRefreshSchedule},
  5478  		{"invalid schedule", snapstate.DefaultRefreshSchedule},
  5479  		{"8:00-12:00", "8:00-12:00"},
  5480  		// this is only valid under the new schedule parser
  5481  		{"9:00~15:00/2,,mon,20:00", "9:00~15:00/2,,mon,20:00"},
  5482  	} {
  5483  		if t.in != "" {
  5484  			tr := config.NewTransaction(s.state)
  5485  			tr.Set("core", "refresh.timer", t.in)
  5486  			tr.Commit()
  5487  		}
  5488  		scheduleStr, legacy, err := s.snapmgr.RefreshSchedule()
  5489  		c.Check(err, IsNil)
  5490  		c.Check(scheduleStr, Equals, t.out)
  5491  		c.Check(legacy, Equals, false)
  5492  	}
  5493  }
  5494  
  5495  func (s *snapmgrTestSuite) TestParallelInstallValidateFeatureFlag(c *C) {
  5496  	s.state.Lock()
  5497  	defer s.state.Unlock()
  5498  
  5499  	info := &snap.Info{
  5500  		InstanceKey: "foo",
  5501  	}
  5502  
  5503  	err := snapstate.ValidateFeatureFlags(s.state, info)
  5504  	c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`)
  5505  
  5506  	// various forms of disabling
  5507  	tr := config.NewTransaction(s.state)
  5508  	tr.Set("core", "experimental.parallel-instances", false)
  5509  	tr.Commit()
  5510  
  5511  	err = snapstate.ValidateFeatureFlags(s.state, info)
  5512  	c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`)
  5513  
  5514  	tr = config.NewTransaction(s.state)
  5515  	tr.Set("core", "experimental.parallel-instances", "")
  5516  	tr.Commit()
  5517  
  5518  	err = snapstate.ValidateFeatureFlags(s.state, info)
  5519  	c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`)
  5520  
  5521  	tr = config.NewTransaction(s.state)
  5522  	tr.Set("core", "experimental.parallel-instances", nil)
  5523  	tr.Commit()
  5524  
  5525  	err = snapstate.ValidateFeatureFlags(s.state, info)
  5526  	c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`)
  5527  
  5528  	tr = config.NewTransaction(s.state)
  5529  	tr.Set("core", "experimental.parallel-instances", "veryfalse")
  5530  	tr.Commit()
  5531  
  5532  	err = snapstate.ValidateFeatureFlags(s.state, info)
  5533  	c.Assert(err, ErrorMatches, `parallel-instances can only be set to 'true' or 'false', got "veryfalse"`)
  5534  
  5535  	// enable parallel instances
  5536  	tr = config.NewTransaction(s.state)
  5537  	tr.Set("core", "experimental.parallel-instances", true)
  5538  	tr.Commit()
  5539  
  5540  	err = snapstate.ValidateFeatureFlags(s.state, info)
  5541  	c.Assert(err, IsNil)
  5542  }
  5543  
  5544  func (s *snapmgrTestSuite) TestInjectTasks(c *C) {
  5545  	s.state.Lock()
  5546  	defer s.state.Unlock()
  5547  
  5548  	lane := s.state.NewLane()
  5549  
  5550  	// setup main task and two tasks waiting for it; all part of same change
  5551  	chg := s.state.NewChange("change", "")
  5552  	t0 := s.state.NewTask("task1", "")
  5553  	chg.AddTask(t0)
  5554  	t0.JoinLane(lane)
  5555  	t01 := s.state.NewTask("task1-1", "")
  5556  	t01.WaitFor(t0)
  5557  	chg.AddTask(t01)
  5558  	t02 := s.state.NewTask("task1-2", "")
  5559  	t02.WaitFor(t0)
  5560  	chg.AddTask(t02)
  5561  
  5562  	// setup extra tasks
  5563  	t1 := s.state.NewTask("task2", "")
  5564  	t2 := s.state.NewTask("task3", "")
  5565  	ts := state.NewTaskSet(t1, t2)
  5566  
  5567  	snapstate.InjectTasks(t0, ts)
  5568  
  5569  	// verify that extra tasks are now part of same change
  5570  	c.Assert(t1.Change().ID(), Equals, t0.Change().ID())
  5571  	c.Assert(t2.Change().ID(), Equals, t0.Change().ID())
  5572  	c.Assert(t1.Change().ID(), Equals, chg.ID())
  5573  
  5574  	c.Assert(t1.Lanes(), DeepEquals, []int{lane})
  5575  
  5576  	// verify that halt tasks of the main task now wait for extra tasks
  5577  	c.Assert(t1.HaltTasks(), HasLen, 2)
  5578  	c.Assert(t2.HaltTasks(), HasLen, 2)
  5579  	c.Assert(t1.HaltTasks(), DeepEquals, t2.HaltTasks())
  5580  
  5581  	ids := []string{t1.HaltTasks()[0].Kind(), t2.HaltTasks()[1].Kind()}
  5582  	sort.Strings(ids)
  5583  	c.Assert(ids, DeepEquals, []string{"task1-1", "task1-2"})
  5584  
  5585  	// verify that extra tasks wait for the main task
  5586  	c.Assert(t1.WaitTasks(), HasLen, 1)
  5587  	c.Assert(t1.WaitTasks()[0].Kind(), Equals, "task1")
  5588  	c.Assert(t2.WaitTasks(), HasLen, 1)
  5589  	c.Assert(t2.WaitTasks()[0].Kind(), Equals, "task1")
  5590  }
  5591  
  5592  func (s *snapmgrTestSuite) TestInjectTasksWithNullChange(c *C) {
  5593  	s.state.Lock()
  5594  	defer s.state.Unlock()
  5595  
  5596  	// setup main task
  5597  	t0 := s.state.NewTask("task1", "")
  5598  	t01 := s.state.NewTask("task1-1", "")
  5599  	t01.WaitFor(t0)
  5600  
  5601  	// setup extra task
  5602  	t1 := s.state.NewTask("task2", "")
  5603  	ts := state.NewTaskSet(t1)
  5604  
  5605  	snapstate.InjectTasks(t0, ts)
  5606  
  5607  	c.Assert(t1.Lanes(), DeepEquals, []int{0})
  5608  
  5609  	// verify that halt tasks of the main task now wait for extra tasks
  5610  	c.Assert(t1.HaltTasks(), HasLen, 1)
  5611  	c.Assert(t1.HaltTasks()[0].Kind(), Equals, "task1-1")
  5612  }
  5613  
  5614  func hasConfigureTask(ts *state.TaskSet) bool {
  5615  	for _, tk := range taskKinds(ts.Tasks()) {
  5616  		if tk == "run-hook[configure]" {
  5617  			return true
  5618  		}
  5619  	}
  5620  	return false
  5621  }
  5622  
  5623  func (s *snapmgrTestSuite) TestNoConfigureForBasesTask(c *C) {
  5624  	s.state.Lock()
  5625  	defer s.state.Unlock()
  5626  
  5627  	// normal snaps get a configure task
  5628  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  5629  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  5630  	c.Assert(err, IsNil)
  5631  	c.Check(hasConfigureTask(ts), Equals, true)
  5632  
  5633  	// but bases do not for install
  5634  	ts, err = snapstate.Install(context.Background(), s.state, "some-base", opts, s.user.ID, snapstate.Flags{})
  5635  	c.Assert(err, IsNil)
  5636  	c.Check(hasConfigureTask(ts), Equals, false)
  5637  
  5638  	// or for refresh
  5639  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  5640  		Active:          true,
  5641  		TrackingChannel: "latest/edge",
  5642  		Sequence:        []*snap.SideInfo{{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)}},
  5643  		Current:         snap.R(1),
  5644  		SnapType:        "base",
  5645  	})
  5646  	ts, err = snapstate.Update(s.state, "some-base", nil, s.user.ID, snapstate.Flags{})
  5647  	c.Assert(err, IsNil)
  5648  	c.Check(hasConfigureTask(ts), Equals, false)
  5649  }
  5650  
  5651  func (s *snapmgrTestSuite) TestSnapdSnapOnCoreWithoutBase(c *C) {
  5652  	s.state.Lock()
  5653  	defer s.state.Unlock()
  5654  	r := release.MockOnClassic(false)
  5655  	defer r()
  5656  
  5657  	// it is now possible to install snapd snap on a system with core
  5658  	_, err := snapstate.Install(context.Background(), s.state, "snapd", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  5659  	c.Assert(err, IsNil)
  5660  }
  5661  
  5662  func (s *snapmgrTestSuite) TestSnapdSnapOnSystemsWithoutBaseOnUbuntuCore(c *C) {
  5663  	s.state.Lock()
  5664  	defer s.state.Unlock()
  5665  	r := release.MockOnClassic(false)
  5666  	defer r()
  5667  
  5668  	// it is not possible to opt-into the snapd snap on core yet
  5669  	tr := config.NewTransaction(s.state)
  5670  	tr.Set("core", "experimental.snapd-snap", true)
  5671  	tr.Commit()
  5672  
  5673  	// it is now possible to install snapd snap on a system with core, experimental option has no effect
  5674  	_, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{})
  5675  	c.Assert(err, IsNil)
  5676  }
  5677  
  5678  func (s *snapmgrTestSuite) TestNoSnapdSnapOnSystemsWithoutBaseButOption(c *C) {
  5679  	s.state.Lock()
  5680  	defer s.state.Unlock()
  5681  
  5682  	tr := config.NewTransaction(s.state)
  5683  	tr.Set("core", "experimental.snapd-snap", true)
  5684  	tr.Commit()
  5685  
  5686  	_, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{})
  5687  	c.Assert(err, IsNil)
  5688  }
  5689  
  5690  func (s *snapmgrTestSuite) TestNoConfigureForSnapdSnap(c *C) {
  5691  	s.state.Lock()
  5692  	defer s.state.Unlock()
  5693  
  5694  	// snapd cannot be installed unless the model uses a base snap
  5695  	r := snapstatetest.MockDeviceModel(ModelWithBase("core18"))
  5696  	defer r()
  5697  
  5698  	// but snapd do not for install
  5699  	ts, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{})
  5700  	c.Assert(err, IsNil)
  5701  	c.Check(hasConfigureTask(ts), Equals, false)
  5702  
  5703  	// or for refresh
  5704  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
  5705  		Active:          true,
  5706  		TrackingChannel: "latest/edge",
  5707  		Sequence:        []*snap.SideInfo{{RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)}},
  5708  		Current:         snap.R(1),
  5709  		SnapType:        "app",
  5710  	})
  5711  	ts, err = snapstate.Update(s.state, "snapd", nil, s.user.ID, snapstate.Flags{})
  5712  	c.Assert(err, IsNil)
  5713  	c.Check(hasConfigureTask(ts), Equals, false)
  5714  
  5715  }
  5716  
  5717  func (s *snapmgrTestSuite) TestCanLoadOldSnapSetupWithoutType(c *C) {
  5718  	// ensure we don't crash when loading a SnapSetup json without
  5719  	// a type set
  5720  	oldSnapSetup := []byte(`{
  5721   "snap-path":"/some/path",
  5722   "side-info": {
  5723      "channel": "edge",
  5724      "name": "some-snap",
  5725      "revision": "1",
  5726      "snap-id": "some-snap-id"
  5727   }
  5728  }`)
  5729  	var snapsup snapstate.SnapSetup
  5730  	err := json.Unmarshal(oldSnapSetup, &snapsup)
  5731  	c.Assert(err, IsNil)
  5732  	c.Check(snapsup.SnapPath, Equals, "/some/path")
  5733  	c.Check(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  5734  		Channel:  "edge",
  5735  		RealName: "some-snap",
  5736  		Revision: snap.R(1),
  5737  		SnapID:   "some-snap-id",
  5738  	})
  5739  	c.Check(snapsup.Type, Equals, snap.Type(""))
  5740  }
  5741  
  5742  func (s *snapmgrTestSuite) TestHasOtherInstances(c *C) {
  5743  	s.state.Lock()
  5744  	defer s.state.Unlock()
  5745  
  5746  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5747  		Active: true,
  5748  		Sequence: []*snap.SideInfo{
  5749  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  5750  		},
  5751  		Current:  snap.R(1),
  5752  		SnapType: "app",
  5753  	})
  5754  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  5755  		Active: true,
  5756  		Sequence: []*snap.SideInfo{
  5757  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  5758  		},
  5759  		Current:     snap.R(3),
  5760  		SnapType:    "app",
  5761  		InstanceKey: "instance",
  5762  	})
  5763  	snapstate.Set(s.state, "some-other-snap", &snapstate.SnapState{
  5764  		Active: true,
  5765  		Sequence: []*snap.SideInfo{
  5766  			{RealName: "some-other-snap", SnapID: "some-other-snap-id", Revision: snap.R(1)},
  5767  		},
  5768  		Current:  snap.R(1),
  5769  		SnapType: "app",
  5770  	})
  5771  
  5772  	other, err := snapstate.HasOtherInstances(s.state, "some-snap")
  5773  	c.Assert(err, IsNil)
  5774  	c.Assert(other, Equals, true)
  5775  	other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance")
  5776  	c.Assert(err, IsNil)
  5777  	c.Assert(other, Equals, true)
  5778  	other, err = snapstate.HasOtherInstances(s.state, "some-other-snap")
  5779  	c.Assert(err, IsNil)
  5780  	c.Assert(other, Equals, false)
  5781  	// other snaps like only looks at the name of the refence snap
  5782  	other, err = snapstate.HasOtherInstances(s.state, "some-other-snap_instance")
  5783  	c.Assert(err, IsNil)
  5784  	c.Assert(other, Equals, true)
  5785  
  5786  	// remove the snap without instance key
  5787  	snapstate.Set(s.state, "some-snap", nil)
  5788  	// some-snap_instance is like some-snap
  5789  	other, err = snapstate.HasOtherInstances(s.state, "some-snap")
  5790  	c.Assert(err, IsNil)
  5791  	c.Assert(other, Equals, true)
  5792  	other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance")
  5793  	c.Assert(err, IsNil)
  5794  	c.Assert(other, Equals, false)
  5795  
  5796  	// add another snap with instance key
  5797  	snapstate.Set(s.state, "some-snap_other", &snapstate.SnapState{
  5798  		Active: true,
  5799  		Sequence: []*snap.SideInfo{
  5800  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  5801  		},
  5802  		Current:     snap.R(3),
  5803  		SnapType:    "app",
  5804  		InstanceKey: "other",
  5805  	})
  5806  	other, err = snapstate.HasOtherInstances(s.state, "some-snap")
  5807  	c.Assert(err, IsNil)
  5808  	c.Assert(other, Equals, true)
  5809  	other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance")
  5810  	c.Assert(err, IsNil)
  5811  	c.Assert(other, Equals, true)
  5812  }
  5813  
  5814  func (s *snapmgrTestSuite) TestRequestSalt(c *C) {
  5815  	si := snap.SideInfo{
  5816  		RealName: "other-snap",
  5817  		Revision: snap.R(7),
  5818  		SnapID:   "other-snap-id",
  5819  	}
  5820  	s.state.Lock()
  5821  	defer s.state.Unlock()
  5822  
  5823  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
  5824  		Active:   true,
  5825  		Sequence: []*snap.SideInfo{&si},
  5826  		Current:  si.Revision,
  5827  		SnapType: "app",
  5828  	})
  5829  	snapstate.Set(s.state, "other-snap_instance", &snapstate.SnapState{
  5830  		Active:      true,
  5831  		Sequence:    []*snap.SideInfo{&si},
  5832  		Current:     si.Revision,
  5833  		SnapType:    "app",
  5834  		InstanceKey: "instance",
  5835  	})
  5836  
  5837  	// clear request-salt to have it generated
  5838  	s.state.Set("refresh-privacy-key", nil)
  5839  
  5840  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  5841  	c.Assert(err, ErrorMatches, "internal error: request salt is unset")
  5842  
  5843  	s.state.Set("refresh-privacy-key", "privacy-key")
  5844  
  5845  	chg := s.state.NewChange("install", "install a snap")
  5846  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  5847  	c.Assert(err, IsNil)
  5848  	chg.AddAll(ts)
  5849  
  5850  	s.state.Unlock()
  5851  	defer s.se.Stop()
  5852  	s.settle(c)
  5853  	s.state.Lock()
  5854  
  5855  	c.Assert(len(s.fakeBackend.ops) >= 1, Equals, true)
  5856  	storeAction := s.fakeBackend.ops[0]
  5857  	c.Assert(storeAction.op, Equals, "storesvc-snap-action")
  5858  	c.Assert(storeAction.curSnaps, HasLen, 2)
  5859  	c.Assert(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true)
  5860  }
  5861  
  5862  type canDisableSuite struct{}
  5863  
  5864  var _ = Suite(&canDisableSuite{})
  5865  
  5866  func (s *canDisableSuite) TestCanDisable(c *C) {
  5867  	for _, tt := range []struct {
  5868  		typ        snap.Type
  5869  		canDisable bool
  5870  	}{
  5871  		{snap.TypeApp, true},
  5872  		{snap.TypeGadget, false},
  5873  		{snap.TypeKernel, false},
  5874  		{snap.TypeOS, false},
  5875  	} {
  5876  		info := &snap.Info{SnapType: tt.typ}
  5877  		c.Check(snapstate.CanDisable(info), Equals, tt.canDisable)
  5878  	}
  5879  }
  5880  
  5881  func (s *snapmgrTestSuite) TestGadgetConnections(c *C) {
  5882  	r := release.MockOnClassic(false)
  5883  	defer r()
  5884  
  5885  	// using MockSnap, we want to read the bits on disk
  5886  	snapstate.MockSnapReadInfo(snap.ReadInfo)
  5887  
  5888  	deviceCtxNoGadget := deviceWithoutGadgetContext()
  5889  	deviceCtx := deviceWithGadgetContext("the-gadget")
  5890  
  5891  	s.state.Lock()
  5892  	defer s.state.Unlock()
  5893  
  5894  	_, err := snapstate.GadgetConnections(s.state, deviceCtxNoGadget)
  5895  	c.Assert(err, Equals, state.ErrNoState)
  5896  
  5897  	_, err = snapstate.GadgetConnections(s.state, deviceCtx)
  5898  	c.Assert(err, Equals, state.ErrNoState)
  5899  
  5900  	s.prepareGadget(c, `
  5901  connections:
  5902    - plug: snap1idididididididididididididi:plug
  5903      slot: snap2idididididididididididididi:slot
  5904  `)
  5905  
  5906  	conns, err := snapstate.GadgetConnections(s.state, deviceCtx)
  5907  	c.Assert(err, IsNil)
  5908  	c.Check(conns, DeepEquals, []gadget.Connection{
  5909  		{Plug: gadget.ConnectionPlug{SnapID: "snap1idididididididididididididi", Plug: "plug"}, Slot: gadget.ConnectionSlot{SnapID: "snap2idididididididididididididi", Slot: "slot"}}})
  5910  }
  5911  
  5912  func (s *snapmgrTestSuite) TestGadgetConnectionsUC20(c *C) {
  5913  	r := release.MockOnClassic(false)
  5914  	defer r()
  5915  
  5916  	// using MockSnap, we want to read the bits on disk
  5917  	snapstate.MockSnapReadInfo(snap.ReadInfo)
  5918  
  5919  	// use a UC20 model context
  5920  	deviceCtx := deviceWithGadgetContext20("the-gadget")
  5921  
  5922  	s.state.Lock()
  5923  	defer s.state.Unlock()
  5924  
  5925  	// provide a uc20 gadget structure
  5926  	s.prepareGadget(c, `
  5927          bootloader: grub
  5928          structure:
  5929          - name: ubuntu-seed
  5930            role: system-seed
  5931            filesystem: vfat
  5932            type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B
  5933            size: 1200M
  5934          - name: ubuntu-boot
  5935            role: system-boot
  5936            filesystem: ext4
  5937            type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4
  5938            # whats the appropriate size?
  5939            size: 750M
  5940          - name: ubuntu-data
  5941            role: system-data
  5942            filesystem: ext4
  5943            type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4
  5944            size: 1G
  5945  connections:
  5946    - plug: snap1idididididididididididididi:plug
  5947      slot: snap2idididididididididididididi:slot
  5948  `)
  5949  
  5950  	conns, err := snapstate.GadgetConnections(s.state, deviceCtx)
  5951  	c.Assert(err, IsNil)
  5952  	c.Check(conns, DeepEquals, []gadget.Connection{
  5953  		{Plug: gadget.ConnectionPlug{SnapID: "snap1idididididididididididididi", Plug: "plug"}, Slot: gadget.ConnectionSlot{SnapID: "snap2idididididididididididididi", Slot: "slot"}}})
  5954  }
  5955  
  5956  func (s *snapmgrTestSuite) TestSnapManagerCanStandby(c *C) {
  5957  	s.state.Lock()
  5958  	defer s.state.Unlock()
  5959  
  5960  	// no snaps -> can standby
  5961  	s.state.Set("snaps", nil)
  5962  	c.Assert(s.snapmgr.CanStandby(), Equals, true)
  5963  
  5964  	// snaps installed -> can *not* standby
  5965  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  5966  		Active: true,
  5967  		Sequence: []*snap.SideInfo{
  5968  			{RealName: "core", Revision: snap.R(1)},
  5969  		},
  5970  		Current:  snap.R(1),
  5971  		SnapType: "os",
  5972  	})
  5973  	c.Assert(s.snapmgr.CanStandby(), Equals, false)
  5974  }
  5975  
  5976  func (s *snapmgrTestSuite) TestResolveChannelPinnedTrack(c *C) {
  5977  	type test struct {
  5978  		snap        string
  5979  		cur         string
  5980  		new         string
  5981  		exp         string
  5982  		kernelTrack string
  5983  		gadgetTrack string
  5984  		err         string
  5985  	}
  5986  
  5987  	for i, tc := range []test{
  5988  		// neither kernel nor gadget
  5989  		{snap: "some-snap"},
  5990  		{snap: "some-snap", new: "stable", exp: "stable"},
  5991  		{snap: "some-snap", new: "foo/stable", exp: "foo/stable"},
  5992  		{snap: "some-snap", new: "stable/with-branch", exp: "stable/with-branch"},
  5993  		{snap: "some-snap", new: "supertrack/stable", exp: "supertrack/stable"},
  5994  		{snap: "some-snap", new: "supertrack/stable/with-branch", exp: "supertrack/stable/with-branch"},
  5995  		// kernel or gadget snap set, but unrelated snap
  5996  		{snap: "some-snap", new: "stable", exp: "stable", kernelTrack: "18"},
  5997  		{snap: "some-snap", new: "foo/stable", exp: "foo/stable", kernelTrack: "18"},
  5998  		{snap: "some-snap", new: "foo/stable", exp: "foo/stable", gadgetTrack: "18"},
  5999  		// no pinned track
  6000  		{snap: "kernel", new: "latest/stable", exp: "latest/stable"},
  6001  		{snap: "kernel", new: "stable", exp: "stable"},
  6002  		{snap: "brand-gadget", new: "stable", exp: "stable"},
  6003  		// not a risk only request
  6004  		{snap: "kernel", new: "", kernelTrack: "18"},
  6005  		{snap: "brand-gadget", new: "", gadgetTrack: "18"},
  6006  		{snap: "kernel", new: "latest/stable", kernelTrack: "18", err: "cannot switch from kernel track.*"},
  6007  		{snap: "kernel", new: "latest/stable/hotfix-123", kernelTrack: "18", err: "cannot switch from kernel track.*"},
  6008  		{snap: "kernel", new: "foo/stable", kernelTrack: "18", err: "cannot switch from kernel track.*"},
  6009  		{snap: "brand-gadget", new: "foo/stable", exp: "18/stable", gadgetTrack: "18", err: "cannot switch from gadget track.*"},
  6010  		{snap: "kernel", new: "18/stable", exp: "18/stable", kernelTrack: "18"},
  6011  		{snap: "kernel", new: "18/stable", exp: "18/stable"},
  6012  		{snap: "brand-gadget", new: "18/stable", exp: "18/stable", gadgetTrack: "18"},
  6013  		{snap: "brand-gadget", new: "18/stable", exp: "18/stable"},
  6014  		// risk/branch within a track
  6015  		{snap: "kernel", new: "stable/hotfix-123", exp: "18/stable/hotfix-123", kernelTrack: "18"},
  6016  		{snap: "kernel", new: "18/stable/hotfix-123", exp: "18/stable/hotfix-123", kernelTrack: "18"},
  6017  		// risk only defaults to pinned gadget track
  6018  		{snap: "brand-gadget", new: "stable", exp: "17/stable", gadgetTrack: "17"},
  6019  		{snap: "brand-gadget", new: "edge", exp: "17/edge", gadgetTrack: "17"},
  6020  		// risk only defaults to pinned kernel track
  6021  		{snap: "kernel", new: "stable", exp: "17/stable", kernelTrack: "17"},
  6022  		{snap: "kernel", new: "edge", exp: "17/edge", kernelTrack: "17"},
  6023  		// risk only defaults to current track
  6024  		{snap: "some-snap", new: "stable", cur: "stable", exp: "stable"},
  6025  		{snap: "some-snap", new: "stable", cur: "latest/stable", exp: "latest/stable"},
  6026  		{snap: "some-snap", new: "stable", cur: "sometrack/edge", exp: "sometrack/stable"},
  6027  	} {
  6028  		if tc.kernelTrack != "" && tc.gadgetTrack != "" {
  6029  			c.Fatalf("%d: setting both kernel and gadget tracks is not supported by the test", i)
  6030  		}
  6031  		var model *asserts.Model
  6032  		switch {
  6033  		case tc.kernelTrack != "":
  6034  			model = ModelWithKernelTrack(tc.kernelTrack)
  6035  		case tc.gadgetTrack != "":
  6036  			model = ModelWithGadgetTrack(tc.gadgetTrack)
  6037  		default:
  6038  			model = DefaultModel()
  6039  		}
  6040  		deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
  6041  		s.state.Lock()
  6042  		ch, err := snapstate.ResolveChannel(s.state, tc.snap, tc.cur, tc.new, deviceCtx)
  6043  		s.state.Unlock()
  6044  		comment := Commentf("tc %d: %#v", i, tc)
  6045  		if tc.err != "" {
  6046  			c.Check(err, ErrorMatches, tc.err, comment)
  6047  		} else {
  6048  			c.Check(err, IsNil, comment)
  6049  			c.Check(ch, Equals, tc.exp, comment)
  6050  		}
  6051  	}
  6052  }
  6053  
  6054  func (s *snapmgrTestSuite) TestGadgetUpdateTaskAddedOnInstall(c *C) {
  6055  	restore := release.MockOnClassic(false)
  6056  	defer restore()
  6057  
  6058  	s.state.Lock()
  6059  	defer s.state.Unlock()
  6060  
  6061  	// task added on install
  6062  	ts, err := snapstate.Install(context.Background(), s.state, "brand-gadget", nil, 0, snapstate.Flags{})
  6063  	c.Assert(err, IsNil)
  6064  
  6065  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  6066  	verifyInstallTasks(c, updatesGadget, 0, ts, s.state)
  6067  }
  6068  
  6069  func (s *snapmgrTestSuite) TestGadgetUpdateTaskAddedOnRefresh(c *C) {
  6070  	restore := release.MockOnClassic(false)
  6071  	defer restore()
  6072  
  6073  	s.state.Lock()
  6074  	defer s.state.Unlock()
  6075  
  6076  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
  6077  		Active: true,
  6078  		Sequence: []*snap.SideInfo{
  6079  			{RealName: "brand-gadget", SnapID: "brand-gadget-id", Revision: snap.R(1)},
  6080  		},
  6081  		Current:  snap.R(1),
  6082  		SnapType: "gadget",
  6083  	})
  6084  
  6085  	// and on update
  6086  	ts, err := snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{}, 0, snapstate.Flags{})
  6087  	c.Assert(err, IsNil)
  6088  
  6089  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  6090  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh|updatesGadget, 0, ts, s.state)
  6091  
  6092  }
  6093  
  6094  func (s *snapmgrTestSuite) TestForSnapSetupResetsFlags(c *C) {
  6095  	flags := snapstate.Flags{
  6096  		DevMode:          true,
  6097  		JailMode:         true,
  6098  		Classic:          true,
  6099  		TryMode:          true,
  6100  		Revert:           true,
  6101  		RemoveSnapPath:   true,
  6102  		IgnoreValidation: true,
  6103  		Required:         true,
  6104  		SkipConfigure:    true,
  6105  		Unaliased:        true,
  6106  		Amend:            true,
  6107  		IsAutoRefresh:    true,
  6108  		NoReRefresh:      true,
  6109  		RequireTypeBase:  true,
  6110  	}
  6111  	flags = flags.ForSnapSetup()
  6112  
  6113  	// certain flags get reset, others are not touched
  6114  	c.Check(flags, DeepEquals, snapstate.Flags{
  6115  		DevMode:          true,
  6116  		JailMode:         true,
  6117  		Classic:          true,
  6118  		TryMode:          true,
  6119  		Revert:           true,
  6120  		RemoveSnapPath:   true,
  6121  		IgnoreValidation: true,
  6122  		Required:         true,
  6123  		SkipConfigure:    false,
  6124  		Unaliased:        true,
  6125  		Amend:            true,
  6126  		IsAutoRefresh:    true,
  6127  		NoReRefresh:      false,
  6128  		RequireTypeBase:  false,
  6129  	})
  6130  }
  6131  
  6132  const servicesSnap = `name: hello-snap
  6133  version: 1
  6134  apps:
  6135   hello:
  6136     command: bin/hello
  6137   svc1:
  6138    command: bin/hello
  6139    daemon: forking
  6140    before: [svc2]
  6141   svc2:
  6142    command: bin/hello
  6143    daemon: forking
  6144    after: [svc1]
  6145  `
  6146  
  6147  func (s *snapmgrTestSuite) runStartSnapServicesWithDisabledServices(c *C, disabled ...string) {
  6148  	s.state.Lock()
  6149  	defer s.state.Unlock()
  6150  
  6151  	si := &snap.SideInfo{RealName: "hello-snap", SnapID: "hello-snap-id", Revision: snap.R(1)}
  6152  	snaptest.MockSnap(c, servicesSnap, si)
  6153  
  6154  	snapstate.Set(s.state, "hello-snap", &snapstate.SnapState{
  6155  		Active:                     true,
  6156  		Sequence:                   []*snap.SideInfo{si},
  6157  		Current:                    si.Revision,
  6158  		SnapType:                   "app",
  6159  		LastActiveDisabledServices: disabled,
  6160  	})
  6161  
  6162  	// using MockSnap, we want to read the bits on disk
  6163  	snapstate.MockSnapReadInfo(snap.ReadInfo)
  6164  
  6165  	chg := s.state.NewChange("services..", "")
  6166  	t := s.state.NewTask("start-snap-services", "")
  6167  	sup := &snapstate.SnapSetup{SideInfo: si}
  6168  	t.Set("snap-setup", sup)
  6169  	chg.AddTask(t)
  6170  
  6171  	s.state.Unlock()
  6172  	defer s.se.Stop()
  6173  	s.settle(c)
  6174  	s.state.Lock()
  6175  
  6176  	c.Check(chg.Status(), Equals, state.DoneStatus)
  6177  
  6178  	expected := fakeOps{
  6179  		{
  6180  			op:       "start-snap-services",
  6181  			path:     filepath.Join(dirs.SnapMountDir, "hello-snap/1"),
  6182  			services: []string{"svc1", "svc2"},
  6183  		},
  6184  	}
  6185  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  6186  }
  6187  
  6188  func (s *snapmgrTestSuite) TestStartSnapServicesWithDisabledServicesNowApp(c *C) {
  6189  	// mock the logger
  6190  	buf, loggerRestore := logger.MockLogger()
  6191  	defer loggerRestore()
  6192  
  6193  	s.runStartSnapServicesWithDisabledServices(c, "hello")
  6194  
  6195  	// check the log for the notice
  6196  	c.Assert(buf.String(), Matches, `.*previously disabled service hello is now an app and not a service\n.*`)
  6197  }
  6198  
  6199  func (s *snapmgrTestSuite) TestStartSnapServicesWithDisabledServicesMissing(c *C) {
  6200  	// mock the logger
  6201  	buf, loggerRestore := logger.MockLogger()
  6202  	defer loggerRestore()
  6203  
  6204  	s.runStartSnapServicesWithDisabledServices(c, "old-disabled-svc")
  6205  
  6206  	// check the log for the notice
  6207  	c.Assert(buf.String(), Matches, `.*previously disabled service old-disabled-svc no longer exists\n.*`)
  6208  }
  6209  
  6210  func (s *snapmgrTestSuite) TestStartSnapServicesUndo(c *C) {
  6211  	s.state.Lock()
  6212  	defer s.state.Unlock()
  6213  
  6214  	si := &snap.SideInfo{RealName: "hello-snap", SnapID: "hello-snap-id", Revision: snap.R(1)}
  6215  	snaptest.MockSnap(c, servicesSnap, si)
  6216  
  6217  	snapstate.Set(s.state, "hello-snap", &snapstate.SnapState{
  6218  		Active:                     true,
  6219  		Sequence:                   []*snap.SideInfo{si},
  6220  		Current:                    si.Revision,
  6221  		SnapType:                   "app",
  6222  		LastActiveDisabledServices: []string{"old-svc"},
  6223  	})
  6224  
  6225  	// using MockSnap, we want to read the bits on disk
  6226  	snapstate.MockSnapReadInfo(snap.ReadInfo)
  6227  
  6228  	chg := s.state.NewChange("services..", "")
  6229  	t := s.state.NewTask("start-snap-services", "")
  6230  	sup := &snapstate.SnapSetup{SideInfo: si}
  6231  	t.Set("snap-setup", sup)
  6232  	chg.AddTask(t)
  6233  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  6234  	terr.WaitFor(t)
  6235  	terr.JoinLane(t.Lanes()[0])
  6236  	chg.AddTask(terr)
  6237  
  6238  	s.state.Unlock()
  6239  	defer s.se.Stop()
  6240  	s.settle(c)
  6241  	s.state.Lock()
  6242  
  6243  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  6244  	c.Check(t.Status(), Equals, state.UndoneStatus)
  6245  
  6246  	expected := fakeOps{
  6247  		{
  6248  			op:       "start-snap-services",
  6249  			path:     filepath.Join(dirs.SnapMountDir, "hello-snap/1"),
  6250  			services: []string{"svc1", "svc2"},
  6251  		},
  6252  		{
  6253  			op:   "stop-snap-services:",
  6254  			path: filepath.Join(dirs.SnapMountDir, "hello-snap/1"),
  6255  		},
  6256  	}
  6257  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  6258  
  6259  	var oldDisabledSvcs []string
  6260  	c.Check(t.Get("old-last-active-disabled-services", &oldDisabledSvcs), IsNil)
  6261  	c.Check(oldDisabledSvcs, DeepEquals, []string{"old-svc"})
  6262  }
  6263  
  6264  func (s *snapmgrTestSuite) TestStopSnapServicesUndo(c *C) {
  6265  	s.state.Lock()
  6266  	defer s.state.Unlock()
  6267  
  6268  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  6269  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1"}
  6270  
  6271  	// reset the services to what they were before after the test is done
  6272  	defer func() {
  6273  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  6274  	}()
  6275  
  6276  	si := &snap.SideInfo{RealName: "hello-snap", SnapID: "hello-snap-id", Revision: snap.R(1)}
  6277  	snaptest.MockSnap(c, servicesSnap, si)
  6278  
  6279  	snapstate.Set(s.state, "hello-snap", &snapstate.SnapState{
  6280  		Active:                     true,
  6281  		Sequence:                   []*snap.SideInfo{si},
  6282  		Current:                    si.Revision,
  6283  		SnapType:                   "app",
  6284  		LastActiveDisabledServices: []string{"old-svc"},
  6285  	})
  6286  
  6287  	// using MockSnap, we want to read the bits on disk
  6288  	snapstate.MockSnapReadInfo(snap.ReadInfo)
  6289  
  6290  	chg := s.state.NewChange("services..", "")
  6291  	t := s.state.NewTask("stop-snap-services", "")
  6292  	sup := &snapstate.SnapSetup{SideInfo: si}
  6293  	t.Set("snap-setup", sup)
  6294  	chg.AddTask(t)
  6295  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  6296  	terr.WaitFor(t)
  6297  	terr.JoinLane(t.Lanes()[0])
  6298  	chg.AddTask(terr)
  6299  
  6300  	s.state.Unlock()
  6301  	defer s.se.Stop()
  6302  	s.settle(c)
  6303  	s.state.Lock()
  6304  
  6305  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  6306  	c.Check(t.Status(), Equals, state.UndoneStatus)
  6307  
  6308  	expected := fakeOps{
  6309  		{
  6310  			op:   "stop-snap-services:",
  6311  			path: filepath.Join(dirs.SnapMountDir, "hello-snap/1"),
  6312  		},
  6313  		{
  6314  			op:               "current-snap-service-states",
  6315  			disabledServices: []string{"svc1"},
  6316  		},
  6317  		{
  6318  			op:               "start-snap-services",
  6319  			services:         []string{"svc1", "svc2"},
  6320  			disabledServices: []string{"svc1"},
  6321  			path:             filepath.Join(dirs.SnapMountDir, "hello-snap/1"),
  6322  		},
  6323  	}
  6324  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  6325  
  6326  	var oldDisabledSvcs []string
  6327  	c.Check(t.Get("old-last-active-disabled-services", &oldDisabledSvcs), IsNil)
  6328  	c.Check(oldDisabledSvcs, DeepEquals, []string{"old-svc"})
  6329  
  6330  	var disabled []string
  6331  	c.Check(t.Get("disabled-services", &disabled), IsNil)
  6332  	c.Check(disabled, DeepEquals, []string{"svc1"})
  6333  }
  6334  
  6335  func (s *snapmgrTestSuite) TestStopSnapServicesErrInUndo(c *C) {
  6336  	s.state.Lock()
  6337  	defer s.state.Unlock()
  6338  
  6339  	si := &snap.SideInfo{RealName: "hello-snap", SnapID: "hello-snap-id", Revision: snap.R(1)}
  6340  	snaptest.MockSnap(c, servicesSnap, si)
  6341  
  6342  	snapstate.Set(s.state, "hello-snap", &snapstate.SnapState{
  6343  		Active:   true,
  6344  		Sequence: []*snap.SideInfo{si},
  6345  		Current:  si.Revision,
  6346  		SnapType: "app",
  6347  	})
  6348  
  6349  	// using MockSnap, we want to read the bits on disk
  6350  	snapstate.MockSnapReadInfo(snap.ReadInfo)
  6351  
  6352  	chg := s.state.NewChange("services..", "")
  6353  	t := s.state.NewTask("stop-snap-services", "")
  6354  	sup := &snapstate.SnapSetup{SideInfo: si}
  6355  	t.Set("snap-setup", sup)
  6356  	chg.AddTask(t)
  6357  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  6358  	terr.WaitFor(t)
  6359  	terr.JoinLane(t.Lanes()[0])
  6360  	chg.AddTask(terr)
  6361  
  6362  	s.fakeBackend.maybeInjectErr = func(op *fakeOp) error {
  6363  		if op.op == "start-snap-services" {
  6364  			return fmt.Errorf("start-snap-services mock error")
  6365  		}
  6366  		return nil
  6367  	}
  6368  
  6369  	s.state.Unlock()
  6370  	defer s.se.Stop()
  6371  	s.settle(c)
  6372  	s.state.Lock()
  6373  
  6374  	c.Assert(chg.IsReady(), Equals, true)
  6375  	c.Assert(chg.Err(), ErrorMatches, `(?s)cannot perform the following tasks:.*- +\(start-snap-services mock error\).*`)
  6376  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  6377  	c.Check(t.Status(), Equals, state.ErrorStatus)
  6378  
  6379  	expected := fakeOps{
  6380  		{
  6381  			op:   "stop-snap-services:",
  6382  			path: filepath.Join(dirs.SnapMountDir, "hello-snap/1"),
  6383  		},
  6384  		{
  6385  			op: "current-snap-service-states",
  6386  		},
  6387  		{
  6388  			// failed after this op
  6389  			op:       "start-snap-services",
  6390  			services: []string{"svc1", "svc2"},
  6391  			path:     filepath.Join(dirs.SnapMountDir, "hello-snap/1"),
  6392  		},
  6393  	}
  6394  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  6395  }
  6396  
  6397  func (s *snapmgrTestSuite) TestEnsureAutoRefreshesAreDelayed(c *C) {
  6398  	s.state.Lock()
  6399  	defer s.state.Unlock()
  6400  
  6401  	t0 := time.Now()
  6402  	// with no changes in flight still works and we set the auto-refresh time as
  6403  	// at least one minute past the start of the test
  6404  	chgs, err := s.snapmgr.EnsureAutoRefreshesAreDelayed(time.Minute)
  6405  	c.Assert(err, IsNil)
  6406  	c.Assert(chgs, HasLen, 0)
  6407  
  6408  	var holdTime time.Time
  6409  	tr := config.NewTransaction(s.state)
  6410  	err = tr.Get("core", "refresh.hold", &holdTime)
  6411  	c.Assert(err, IsNil)
  6412  	// use After() == false in case holdTime is _exactly_ one minute later than
  6413  	// t0, in which case both After() and Before() will be false
  6414  	c.Assert(t0.Add(time.Minute).After(holdTime), Equals, false)
  6415  
  6416  	// now make some auto-refresh changes to make sure we get those figured out
  6417  	chg0 := s.state.NewChange("auto-refresh", "auto-refresh-the-things")
  6418  	chg0.AddTask(s.state.NewTask("nop", "do nothing"))
  6419  
  6420  	// make it in doing state
  6421  	chg0.SetStatus(state.DoingStatus)
  6422  
  6423  	// this one will be picked up too
  6424  	chg1 := s.state.NewChange("auto-refresh", "auto-refresh-the-things")
  6425  	chg1.AddTask(s.state.NewTask("nop", "do nothing"))
  6426  	chg1.SetStatus(state.DoStatus)
  6427  
  6428  	// this one won't, it's Done
  6429  	chg2 := s.state.NewChange("auto-refresh", "auto-refresh-the-things")
  6430  	chg2.AddTask(s.state.NewTask("nop", "do nothing"))
  6431  	chg2.SetStatus(state.DoneStatus)
  6432  
  6433  	// nor this one, it's Undone
  6434  	chg3 := s.state.NewChange("auto-refresh", "auto-refresh-the-things")
  6435  	chg3.AddTask(s.state.NewTask("nop", "do nothing"))
  6436  	chg3.SetStatus(state.UndoneStatus)
  6437  
  6438  	// now we get our change ID returned when calling EnsureAutoRefreshesAreDelayed
  6439  	chgs, err = s.snapmgr.EnsureAutoRefreshesAreDelayed(time.Minute)
  6440  	c.Assert(err, IsNil)
  6441  	// more helpful error message if we first compare the change ID's
  6442  	expids := []string{chg0.ID(), chg1.ID()}
  6443  	sort.Strings(expids)
  6444  	c.Assert(chgs, HasLen, len(expids))
  6445  	gotids := []string{chgs[0].ID(), chgs[1].ID()}
  6446  	sort.Strings(gotids)
  6447  	c.Assert(expids, DeepEquals, gotids)
  6448  
  6449  	sort.SliceStable(chgs, func(i, j int) bool {
  6450  		return chgs[i].ID() < chgs[j].ID()
  6451  	})
  6452  
  6453  	c.Assert(chgs, DeepEquals, []*state.Change{chg0, chg1})
  6454  }
  6455  
  6456  func (s *snapmgrTestSuite) TestInstallModeDisableFreshInstall(c *C) {
  6457  	s.state.Lock()
  6458  	defer s.state.Unlock()
  6459  
  6460  	oldServicesSnapYaml := servicesSnapYaml
  6461  	servicesSnapYaml += `
  6462    svcInstallModeDisable:
  6463      daemon: simple
  6464      install-mode: disable
  6465  `
  6466  	defer func() { servicesSnapYaml = oldServicesSnapYaml }()
  6467  
  6468  	installChg := s.state.NewChange("install", "...")
  6469  	installTs, err := snapstate.Install(context.Background(), s.state, "services-snap", nil, 0, snapstate.Flags{})
  6470  	c.Assert(err, IsNil)
  6471  	installChg.AddAll(installTs)
  6472  
  6473  	s.state.Unlock()
  6474  	defer s.se.Stop()
  6475  	s.settle(c)
  6476  	s.state.Lock()
  6477  
  6478  	c.Assert(installChg.Err(), IsNil)
  6479  	c.Assert(installChg.IsReady(), Equals, true)
  6480  
  6481  	op := s.fakeBackend.ops.First("start-snap-services")
  6482  	c.Assert(op, Not(IsNil))
  6483  	c.Check(op.disabledServices, DeepEquals, []string{"svcInstallModeDisable"})
  6484  }
  6485  
  6486  func (s *snapmgrTestSuite) TestInstallModeDisableUpdateServiceNotDisabled(c *C) {
  6487  	s.state.Lock()
  6488  	defer s.state.Unlock()
  6489  
  6490  	oldServicesSnapYaml := servicesSnapYaml
  6491  	servicesSnapYaml += `
  6492    svcInstallModeDisable:
  6493      daemon: simple
  6494      install-mode: disable
  6495  `
  6496  	defer func() { servicesSnapYaml = oldServicesSnapYaml }()
  6497  
  6498  	// pretent services-snap is installed and no service is disabled in
  6499  	// this install (i.e. svcInstallModeDisable is active)
  6500  	si := &snap.SideInfo{
  6501  		RealName: "services-snap", SnapID: "services-snap-id", Revision: snap.R(7),
  6502  	}
  6503  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  6504  		Sequence: []*snap.SideInfo{si},
  6505  		Current:  si.Revision,
  6506  		Active:   true,
  6507  	})
  6508  	snaptest.MockSnap(c, string(servicesSnapYaml), si)
  6509  
  6510  	updateChg := s.state.NewChange("refresh", "...")
  6511  	updateTs, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  6512  	c.Assert(err, IsNil)
  6513  	updateChg.AddAll(updateTs)
  6514  
  6515  	s.state.Unlock()
  6516  	defer s.se.Stop()
  6517  	s.settle(c)
  6518  	s.state.Lock()
  6519  
  6520  	c.Assert(updateChg.Err(), IsNil)
  6521  	c.Assert(updateChg.IsReady(), Equals, true)
  6522  
  6523  	op := s.fakeBackend.ops.First("start-snap-services")
  6524  	c.Assert(op, Not(IsNil))
  6525  	c.Check(op.disabledServices, HasLen, 0)
  6526  }
  6527  
  6528  func (s *snapmgrTestSuite) TestInstallModeDisableFreshInstallEnabledByHook(c *C) {
  6529  	st := s.state
  6530  	st.Lock()
  6531  	defer st.Unlock()
  6532  
  6533  	oldServicesSnapYaml := servicesSnapYaml
  6534  	servicesSnapYaml += `
  6535    svcInstallModeDisable:
  6536      daemon: simple
  6537      install-mode: disable
  6538  `
  6539  	defer func() { servicesSnapYaml = oldServicesSnapYaml }()
  6540  
  6541  	// XXX: should this become part of managers_test.go ?
  6542  	// pretent we have a hook that enables the service on install
  6543  	runner := s.o.TaskRunner()
  6544  	runner.AddHandler("run-hook", func(t *state.Task, _ *tomb.Tomb) error {
  6545  		var snapst snapstate.SnapState
  6546  		st.Lock()
  6547  		err := snapstate.Get(st, "services-snap", &snapst)
  6548  		st.Unlock()
  6549  		c.Assert(err, IsNil)
  6550  		snapst.ServicesEnabledByHooks = []string{"svcInstallModeDisable"}
  6551  		st.Lock()
  6552  		snapstate.Set(st, "services-snap", &snapst)
  6553  		st.Unlock()
  6554  		return nil
  6555  	}, nil)
  6556  
  6557  	installChg := s.state.NewChange("install", "...")
  6558  	installTs, err := snapstate.Install(context.Background(), s.state, "services-snap", nil, 0, snapstate.Flags{})
  6559  	c.Assert(err, IsNil)
  6560  	installChg.AddAll(installTs)
  6561  
  6562  	s.state.Unlock()
  6563  	defer s.se.Stop()
  6564  	s.settle(c)
  6565  	s.state.Lock()
  6566  
  6567  	c.Assert(installChg.Err(), IsNil)
  6568  	c.Assert(installChg.IsReady(), Equals, true)
  6569  
  6570  	op := s.fakeBackend.ops.First("start-snap-services")
  6571  	c.Assert(op, Not(IsNil))
  6572  	c.Check(op.disabledServices, HasLen, 0)
  6573  }
  6574  
  6575  func (s *snapmgrTestSuite) TestSnapdRefreshTasks(c *C) {
  6576  	s.state.Lock()
  6577  	defer s.state.Unlock()
  6578  
  6579  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
  6580  		Active: true,
  6581  		Sequence: []*snap.SideInfo{
  6582  			{RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)},
  6583  		},
  6584  		Current:  snap.R(1),
  6585  		SnapType: "snapd",
  6586  	})
  6587  
  6588  	chg := s.state.NewChange("snapd-refresh", "refresh snapd")
  6589  	ts, err := snapstate.Update(s.state, "snapd", nil, 0, snapstate.Flags{})
  6590  	c.Assert(err, IsNil)
  6591  	c.Assert(err, IsNil)
  6592  	chg.AddAll(ts)
  6593  
  6594  	s.state.Unlock()
  6595  	defer s.se.Stop()
  6596  	s.settle(c)
  6597  	s.state.Lock()
  6598  
  6599  	// various backend operations, but no unlink-current-snap
  6600  	expected := fakeOps{
  6601  		{
  6602  			op: "storesvc-snap-action",
  6603  			curSnaps: []store.CurrentSnap{
  6604  				{
  6605  					InstanceName:  "snapd",
  6606  					SnapID:        "snapd-snap-id",
  6607  					Revision:      snap.R(1),
  6608  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  6609  					Epoch:         snap.E("1*"),
  6610  				},
  6611  			},
  6612  		},
  6613  		{
  6614  			op: "storesvc-snap-action:action",
  6615  			action: store.SnapAction{
  6616  				Action:       "refresh",
  6617  				SnapID:       "snapd-snap-id",
  6618  				InstanceName: "snapd",
  6619  				Flags:        store.SnapActionEnforceValidation,
  6620  			},
  6621  			revno: snap.R(11),
  6622  		},
  6623  		{
  6624  			op:   "storesvc-download",
  6625  			name: "snapd",
  6626  		},
  6627  		{
  6628  			op:    "validate-snap:Doing",
  6629  			name:  "snapd",
  6630  			revno: snap.R(11),
  6631  		},
  6632  		{
  6633  			op:  "current",
  6634  			old: filepath.Join(dirs.SnapMountDir, "snapd/1"),
  6635  		},
  6636  		{
  6637  			op:   "open-snap-file",
  6638  			path: filepath.Join(dirs.SnapBlobDir, "snapd_11.snap"),
  6639  			sinfo: snap.SideInfo{
  6640  				RealName: "snapd",
  6641  				SnapID:   "snapd-snap-id",
  6642  				Revision: snap.R(11),
  6643  			},
  6644  		},
  6645  		{
  6646  			op:    "setup-snap",
  6647  			name:  "snapd",
  6648  			path:  filepath.Join(dirs.SnapBlobDir, "snapd_11.snap"),
  6649  			revno: snap.R(11),
  6650  		},
  6651  		{
  6652  			op:   "remove-snap-aliases",
  6653  			name: "snapd",
  6654  		},
  6655  		{
  6656  			op:   "copy-data",
  6657  			path: filepath.Join(dirs.SnapMountDir, "snapd/11"),
  6658  			old:  filepath.Join(dirs.SnapMountDir, "snapd/1"),
  6659  		},
  6660  		{
  6661  			op:    "setup-profiles:Doing",
  6662  			name:  "snapd",
  6663  			revno: snap.R(11),
  6664  		},
  6665  		{
  6666  			op: "candidate",
  6667  			sinfo: snap.SideInfo{
  6668  				RealName: "snapd",
  6669  				SnapID:   "snapd-snap-id",
  6670  				Revision: snap.R(11),
  6671  			},
  6672  		},
  6673  		{
  6674  			op:   "link-snap",
  6675  			path: filepath.Join(dirs.SnapMountDir, "snapd/11"),
  6676  		},
  6677  		{
  6678  			op:    "auto-connect:Doing",
  6679  			name:  "snapd",
  6680  			revno: snap.R(11),
  6681  		},
  6682  		{
  6683  			op: "update-aliases",
  6684  		},
  6685  		{
  6686  			op:    "cleanup-trash",
  6687  			name:  "snapd",
  6688  			revno: snap.R(11),
  6689  		},
  6690  	}
  6691  	// start with an easier-to-read error if this fails:
  6692  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  6693  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  6694  
  6695  	// verify that the R(2) version is active now and R(7) is still there
  6696  	var snapst snapstate.SnapState
  6697  	err = snapstate.Get(s.state, "snapd", &snapst)
  6698  	c.Assert(err, IsNil)
  6699  
  6700  	c.Assert(snapst.Active, Equals, true)
  6701  	c.Assert(snapst.Current, Equals, snap.R(11))
  6702  }
  6703  
  6704  type installTestType struct {
  6705  	t snap.Type
  6706  }
  6707  
  6708  func (t *installTestType) InstanceName() string {
  6709  	panic("not expected")
  6710  }
  6711  
  6712  func (t *installTestType) Type() snap.Type {
  6713  	return t.t
  6714  }
  6715  
  6716  func (t *installTestType) SnapBase() string {
  6717  	panic("not expected")
  6718  }
  6719  
  6720  func (t *installTestType) DownloadSize() int64 {
  6721  	panic("not expected")
  6722  }
  6723  
  6724  func (t *installTestType) Prereq(st *state.State) []string {
  6725  	panic("not expected")
  6726  }
  6727  
  6728  func (s *snapmgrTestSuite) TestMinimalInstallInfoSortByType(c *C) {
  6729  	snaps := []snapstate.MinimalInstallInfo{
  6730  		&installTestType{snap.TypeApp},
  6731  		&installTestType{snap.TypeBase},
  6732  		&installTestType{snap.TypeApp},
  6733  		&installTestType{snap.TypeSnapd},
  6734  		&installTestType{snap.TypeKernel},
  6735  		&installTestType{snap.TypeGadget},
  6736  	}
  6737  
  6738  	sort.Sort(snapstate.ByType(snaps))
  6739  	c.Check(snaps, DeepEquals, []snapstate.MinimalInstallInfo{
  6740  		&installTestType{snap.TypeSnapd},
  6741  		&installTestType{snap.TypeKernel},
  6742  		&installTestType{snap.TypeBase},
  6743  		&installTestType{snap.TypeGadget},
  6744  		&installTestType{snap.TypeApp},
  6745  		&installTestType{snap.TypeApp}})
  6746  }