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