github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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  	"bytes"
    24  	"context"
    25  	"encoding/json"
    26  	"errors"
    27  	"fmt"
    28  	"io/ioutil"
    29  	"os"
    30  	"path/filepath"
    31  	"sort"
    32  	"strings"
    33  	"testing"
    34  	"time"
    35  
    36  	. "gopkg.in/check.v1"
    37  	"gopkg.in/tomb.v2"
    38  
    39  	"github.com/snapcore/snapd/asserts"
    40  	"github.com/snapcore/snapd/bootloader"
    41  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    42  	"github.com/snapcore/snapd/dirs"
    43  	"github.com/snapcore/snapd/gadget"
    44  	"github.com/snapcore/snapd/interfaces"
    45  	"github.com/snapcore/snapd/logger"
    46  	"github.com/snapcore/snapd/overlord"
    47  	"github.com/snapcore/snapd/overlord/auth"
    48  	"github.com/snapcore/snapd/overlord/configstate/config"
    49  	"github.com/snapcore/snapd/overlord/hookstate"
    50  	"github.com/snapcore/snapd/overlord/ifacestate/ifacerepo"
    51  	"github.com/snapcore/snapd/overlord/snapstate"
    52  	"github.com/snapcore/snapd/overlord/snapstate/backend"
    53  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    54  	"github.com/snapcore/snapd/overlord/state"
    55  	"github.com/snapcore/snapd/release"
    56  	"github.com/snapcore/snapd/snap"
    57  	"github.com/snapcore/snapd/snap/snaptest"
    58  	"github.com/snapcore/snapd/store"
    59  	"github.com/snapcore/snapd/testutil"
    60  	"github.com/snapcore/snapd/timeutil"
    61  
    62  	// So it registers Configure.
    63  	_ "github.com/snapcore/snapd/overlord/configstate"
    64  )
    65  
    66  func TestSnapManager(t *testing.T) { TestingT(t) }
    67  
    68  type snapmgrTestSuite struct {
    69  	testutil.BaseTest
    70  	o       *overlord.Overlord
    71  	state   *state.State
    72  	se      *overlord.StateEngine
    73  	snapmgr *snapstate.SnapManager
    74  
    75  	fakeBackend *fakeSnappyBackend
    76  	fakeStore   *fakeStore
    77  
    78  	user  *auth.UserState
    79  	user2 *auth.UserState
    80  	user3 *auth.UserState
    81  }
    82  
    83  func (s *snapmgrTestSuite) settle(c *C) {
    84  	err := s.o.Settle(5 * time.Second)
    85  	c.Assert(err, IsNil)
    86  }
    87  
    88  var _ = Suite(&snapmgrTestSuite{})
    89  
    90  var fakeRevDateEpoch = time.Date(2018, 1, 0, 0, 0, 0, 0, time.UTC)
    91  
    92  func (s *snapmgrTestSuite) SetUpTest(c *C) {
    93  	s.BaseTest.SetUpTest(c)
    94  	dirs.SetRootDir(c.MkDir())
    95  
    96  	s.o = overlord.Mock()
    97  	s.state = s.o.State()
    98  
    99  	s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
   100  
   101  	s.fakeBackend = &fakeSnappyBackend{}
   102  	s.fakeBackend.emptyContainer = emptyContainer(c)
   103  	s.fakeStore = &fakeStore{
   104  		fakeCurrentProgress: 75,
   105  		fakeTotalProgress:   100,
   106  		fakeBackend:         s.fakeBackend,
   107  		state:               s.state,
   108  	}
   109  
   110  	oldSetupInstallHook := snapstate.SetupInstallHook
   111  	oldSetupPreRefreshHook := snapstate.SetupPreRefreshHook
   112  	oldSetupPostRefreshHook := snapstate.SetupPostRefreshHook
   113  	oldSetupRemoveHook := snapstate.SetupRemoveHook
   114  	snapstate.SetupInstallHook = hookstate.SetupInstallHook
   115  	snapstate.SetupPreRefreshHook = hookstate.SetupPreRefreshHook
   116  	snapstate.SetupPostRefreshHook = hookstate.SetupPostRefreshHook
   117  	snapstate.SetupRemoveHook = hookstate.SetupRemoveHook
   118  
   119  	var err error
   120  	s.snapmgr, err = snapstate.Manager(s.state, s.o.TaskRunner())
   121  	c.Assert(err, IsNil)
   122  
   123  	AddForeignTaskHandlers(s.o.TaskRunner(), s.fakeBackend)
   124  
   125  	snapstate.SetSnapManagerBackend(s.snapmgr, s.fakeBackend)
   126  
   127  	s.o.AddManager(s.snapmgr)
   128  	s.o.AddManager(s.o.TaskRunner())
   129  	s.se = s.o.StateEngine()
   130  	c.Assert(s.o.StartUp(), IsNil)
   131  
   132  	s.BaseTest.AddCleanup(snapstate.MockSnapReadInfo(s.fakeBackend.ReadInfo))
   133  	s.BaseTest.AddCleanup(snapstate.MockOpenSnapFile(s.fakeBackend.OpenSnapFile))
   134  	revDate := func(info *snap.Info) time.Time {
   135  		if info.Revision.Local() {
   136  			panic("no local revision should reach revisionDate")
   137  		}
   138  		// for convenience a date derived from the revision
   139  		return fakeRevDateEpoch.AddDate(0, 0, info.Revision.N)
   140  	}
   141  	s.BaseTest.AddCleanup(snapstate.MockRevisionDate(revDate))
   142  
   143  	s.BaseTest.AddCleanup(func() {
   144  		snapstate.SetupInstallHook = oldSetupInstallHook
   145  		snapstate.SetupPreRefreshHook = oldSetupPreRefreshHook
   146  		snapstate.SetupPostRefreshHook = oldSetupPostRefreshHook
   147  		snapstate.SetupRemoveHook = oldSetupRemoveHook
   148  
   149  		dirs.SetRootDir("/")
   150  	})
   151  
   152  	s.BaseTest.AddCleanup(snapstate.MockReRefreshRetryTimeout(time.Second / 200))
   153  	s.BaseTest.AddCleanup(snapstate.MockReRefreshUpdateMany(func(context.Context, *state.State, []string, int, snapstate.UpdateFilter, *snapstate.Flags, string) ([]string, []*state.TaskSet, error) {
   154  		return nil, nil, nil
   155  	}))
   156  
   157  	oldAutomaticSnapshot := snapstate.AutomaticSnapshot
   158  	snapstate.AutomaticSnapshot = func(st *state.State, instanceName string) (ts *state.TaskSet, err error) {
   159  		task := st.NewTask("save-snapshot", "...")
   160  		ts = state.NewTaskSet(task)
   161  		return ts, nil
   162  	}
   163  
   164  	oldAutomaticSnapshotExpiration := snapstate.AutomaticSnapshotExpiration
   165  	snapstate.AutomaticSnapshotExpiration = func(st *state.State) (time.Duration, error) { return 1, nil }
   166  	s.BaseTest.AddCleanup(func() {
   167  		snapstate.AutomaticSnapshot = oldAutomaticSnapshot
   168  		snapstate.AutomaticSnapshotExpiration = oldAutomaticSnapshotExpiration
   169  	})
   170  
   171  	s.state.Lock()
   172  	snapstate.ReplaceStore(s.state, s.fakeStore)
   173  	s.user, err = auth.NewUser(s.state, "username", "email@test.com", "macaroon", []string{"discharge"})
   174  	c.Assert(err, IsNil)
   175  	s.user2, err = auth.NewUser(s.state, "username2", "email2@test.com", "macaroon2", []string{"discharge2"})
   176  	c.Assert(err, IsNil)
   177  	// 3 has no store auth
   178  	s.user3, err = auth.NewUser(s.state, "username3", "email2@test.com", "", nil)
   179  	c.Assert(err, IsNil)
   180  
   181  	s.state.Set("seeded", true)
   182  	s.state.Set("seed-time", time.Now())
   183  
   184  	r := snapstatetest.MockDeviceModel(DefaultModel())
   185  	s.BaseTest.AddCleanup(r)
   186  
   187  	s.state.Set("refresh-privacy-key", "privacy-key")
   188  	snapstate.Set(s.state, "core", &snapstate.SnapState{
   189  		Active: true,
   190  		Sequence: []*snap.SideInfo{
   191  			{RealName: "core", Revision: snap.R(1)},
   192  		},
   193  		Current:  snap.R(1),
   194  		SnapType: "os",
   195  	})
   196  	s.state.Unlock()
   197  
   198  	snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) {
   199  		return nil, nil
   200  	}
   201  }
   202  
   203  func (s *snapmgrTestSuite) TearDownTest(c *C) {
   204  	s.BaseTest.TearDownTest(c)
   205  	snapstate.ValidateRefreshes = nil
   206  	snapstate.AutoAliases = nil
   207  	snapstate.CanAutoRefresh = nil
   208  }
   209  
   210  type ForeignTaskTracker interface {
   211  	ForeignTask(kind string, status state.Status, snapsup *snapstate.SnapSetup)
   212  }
   213  
   214  func AddForeignTaskHandlers(runner *state.TaskRunner, tracker ForeignTaskTracker) {
   215  	// Add fake handlers for tasks handled by interfaces manager
   216  	fakeHandler := func(task *state.Task, _ *tomb.Tomb) error {
   217  		task.State().Lock()
   218  		kind := task.Kind()
   219  		status := task.Status()
   220  		snapsup, err := snapstate.TaskSnapSetup(task)
   221  		task.State().Unlock()
   222  		if err != nil {
   223  			return err
   224  		}
   225  
   226  		tracker.ForeignTask(kind, status, snapsup)
   227  
   228  		return nil
   229  	}
   230  	runner.AddHandler("setup-profiles", fakeHandler, fakeHandler)
   231  	runner.AddHandler("auto-connect", fakeHandler, nil)
   232  	runner.AddHandler("auto-disconnect", fakeHandler, nil)
   233  	runner.AddHandler("remove-profiles", fakeHandler, fakeHandler)
   234  	runner.AddHandler("discard-conns", fakeHandler, fakeHandler)
   235  	runner.AddHandler("validate-snap", fakeHandler, nil)
   236  	runner.AddHandler("transition-ubuntu-core", fakeHandler, nil)
   237  	runner.AddHandler("transition-to-snapd-snap", fakeHandler, nil)
   238  
   239  	// Add handler to test full aborting of changes
   240  	erroringHandler := func(task *state.Task, _ *tomb.Tomb) error {
   241  		return errors.New("error out")
   242  	}
   243  	runner.AddHandler("error-trigger", erroringHandler, nil)
   244  
   245  	runner.AddHandler("save-snapshot", func(task *state.Task, _ *tomb.Tomb) error {
   246  		return nil
   247  	}, nil)
   248  	runner.AddHandler("run-hook", func(task *state.Task, _ *tomb.Tomb) error {
   249  		return nil
   250  	}, nil)
   251  	runner.AddHandler("configure-snapd", func(t *state.Task, _ *tomb.Tomb) error {
   252  		return nil
   253  	}, nil)
   254  
   255  }
   256  
   257  func (s *snapmgrTestSuite) TestCleanSnapStateGet(c *C) {
   258  	snapst := snapstate.SnapState{
   259  		Sequence: []*snap.SideInfo{
   260  			{RealName: "foo", Revision: snap.R(1)},
   261  		},
   262  		Current:     snap.R(1),
   263  		SnapType:    "os",
   264  		Channel:     "foo",
   265  		InstanceKey: "bar",
   266  	}
   267  
   268  	s.state.Lock()
   269  
   270  	defer s.state.Unlock()
   271  	snapstate.Set(s.state, "no-instance-key", &snapstate.SnapState{
   272  		Sequence: []*snap.SideInfo{
   273  			{RealName: "core", Revision: snap.R(1)},
   274  		},
   275  		Current:  snap.R(1),
   276  		SnapType: "app",
   277  	})
   278  
   279  	err := snapstate.Get(s.state, "bar", nil)
   280  	c.Assert(err, ErrorMatches, "internal error: snapst is nil")
   281  
   282  	err = snapstate.Get(s.state, "no-instance-key", &snapst)
   283  	c.Assert(err, IsNil)
   284  	c.Assert(snapst, DeepEquals, snapstate.SnapState{
   285  		Sequence: []*snap.SideInfo{
   286  			{RealName: "core", Revision: snap.R(1)},
   287  		},
   288  		Current:  snap.R(1),
   289  		SnapType: "app",
   290  	})
   291  }
   292  
   293  func (s *snapmgrTestSuite) TestStore(c *C) {
   294  	s.state.Lock()
   295  	defer s.state.Unlock()
   296  
   297  	sto := &store.Store{}
   298  	snapstate.ReplaceStore(s.state, sto)
   299  	store1 := snapstate.Store(s.state, nil)
   300  	c.Check(store1, Equals, sto)
   301  
   302  	// cached
   303  	store2 := snapstate.Store(s.state, nil)
   304  	c.Check(store2, Equals, sto)
   305  }
   306  
   307  func (s *snapmgrTestSuite) TestStoreWithDeviceContext(c *C) {
   308  	s.state.Lock()
   309  	defer s.state.Unlock()
   310  
   311  	stoA := &store.Store{}
   312  	snapstate.ReplaceStore(s.state, stoA)
   313  	store1 := snapstate.Store(s.state, nil)
   314  	c.Check(store1, Equals, stoA)
   315  
   316  	stoB := &store.Store{}
   317  
   318  	// cached
   319  	store2 := snapstate.Store(s.state, &snapstatetest.TrivialDeviceContext{})
   320  	c.Check(store2, Equals, stoA)
   321  
   322  	// from context
   323  	store3 := snapstate.Store(s.state, &snapstatetest.TrivialDeviceContext{CtxStore: stoB})
   324  	c.Check(store3, Equals, stoB)
   325  }
   326  
   327  func (s *snapmgrTestSuite) TestUserFromUserID(c *C) {
   328  	s.state.Lock()
   329  	defer s.state.Unlock()
   330  
   331  	tests := []struct {
   332  		ids     []int
   333  		u       *auth.UserState
   334  		invalid bool
   335  	}{
   336  		{[]int{0}, nil, false},
   337  		{[]int{2}, s.user2, false},
   338  		{[]int{99}, nil, true},
   339  		{[]int{1, 99}, s.user, false},
   340  		{[]int{99, 0}, nil, false},
   341  		{[]int{99, 2}, s.user2, false},
   342  		{[]int{99, 100}, nil, true},
   343  	}
   344  
   345  	for _, t := range tests {
   346  		u, err := snapstate.UserFromUserID(s.state, t.ids...)
   347  		c.Check(u, DeepEquals, t.u)
   348  		if t.invalid {
   349  			c.Check(err, Equals, auth.ErrInvalidUser)
   350  		} else {
   351  			c.Check(err, IsNil)
   352  		}
   353  	}
   354  }
   355  
   356  const (
   357  	unlinkBefore = 1 << iota
   358  	cleanupAfter
   359  	maybeCore
   360  	runCoreConfigure
   361  	doesReRefresh
   362  	updatesGadget
   363  	noConfigure
   364  )
   365  
   366  func taskKinds(tasks []*state.Task) []string {
   367  	kinds := make([]string, len(tasks))
   368  	for i, task := range tasks {
   369  		k := task.Kind()
   370  		if k == "run-hook" {
   371  			var hooksup hookstate.HookSetup
   372  			if err := task.Get("hook-setup", &hooksup); err != nil {
   373  				panic(err)
   374  			}
   375  			k = fmt.Sprintf("%s[%s]", k, hooksup.Hook)
   376  		}
   377  		kinds[i] = k
   378  	}
   379  	return kinds
   380  }
   381  
   382  func verifyInstallTasks(c *C, opts, discards int, ts *state.TaskSet, st *state.State) {
   383  	kinds := taskKinds(ts.Tasks())
   384  
   385  	expected := []string{
   386  		"prerequisites",
   387  		"download-snap",
   388  		"validate-snap",
   389  		"mount-snap",
   390  	}
   391  	if opts&unlinkBefore != 0 {
   392  		expected = append(expected,
   393  			"stop-snap-services",
   394  			"remove-aliases",
   395  			"unlink-current-snap",
   396  		)
   397  	}
   398  	if opts&updatesGadget != 0 {
   399  		expected = append(expected, "update-gadget-assets")
   400  	}
   401  	expected = append(expected,
   402  		"copy-snap-data",
   403  		"setup-profiles",
   404  		"link-snap",
   405  	)
   406  	expected = append(expected,
   407  		"auto-connect",
   408  		"set-auto-aliases",
   409  		"setup-aliases",
   410  		"run-hook[install]",
   411  		"start-snap-services")
   412  	for i := 0; i < discards; i++ {
   413  		expected = append(expected,
   414  			"clear-snap",
   415  			"discard-snap",
   416  		)
   417  	}
   418  	if opts&cleanupAfter != 0 {
   419  		expected = append(expected,
   420  			"cleanup",
   421  		)
   422  	}
   423  	if opts&noConfigure == 0 {
   424  		expected = append(expected,
   425  			"run-hook[configure]",
   426  		)
   427  	}
   428  	expected = append(expected,
   429  		"run-hook[check-health]",
   430  	)
   431  
   432  	c.Assert(kinds, DeepEquals, expected)
   433  }
   434  
   435  func verifyUpdateTasks(c *C, opts, discards int, ts *state.TaskSet, st *state.State) {
   436  	kinds := taskKinds(ts.Tasks())
   437  
   438  	expected := []string{
   439  		"prerequisites",
   440  		"download-snap",
   441  		"validate-snap",
   442  		"mount-snap",
   443  	}
   444  	expected = append(expected, "run-hook[pre-refresh]")
   445  	if opts&unlinkBefore != 0 {
   446  		expected = append(expected,
   447  			"stop-snap-services",
   448  		)
   449  	}
   450  	if opts&unlinkBefore != 0 {
   451  		expected = append(expected,
   452  			"remove-aliases",
   453  			"unlink-current-snap",
   454  		)
   455  	}
   456  	if opts&updatesGadget != 0 {
   457  		expected = append(expected, "update-gadget-assets")
   458  	}
   459  	expected = append(expected,
   460  		"copy-snap-data",
   461  		"setup-profiles",
   462  		"link-snap",
   463  	)
   464  	if opts&maybeCore != 0 {
   465  		expected = append(expected, "setup-profiles")
   466  	}
   467  	expected = append(expected,
   468  		"auto-connect",
   469  		"set-auto-aliases",
   470  		"setup-aliases",
   471  		"run-hook[post-refresh]",
   472  		"start-snap-services")
   473  
   474  	c.Assert(ts.Tasks()[len(expected)-2].Summary(), Matches, `Run post-refresh hook of .*`)
   475  	for i := 0; i < discards; i++ {
   476  		expected = append(expected,
   477  			"clear-snap",
   478  			"discard-snap",
   479  		)
   480  	}
   481  	if opts&cleanupAfter != 0 {
   482  		expected = append(expected,
   483  			"cleanup",
   484  		)
   485  	}
   486  	expected = append(expected,
   487  		"run-hook[configure]",
   488  		"run-hook[check-health]",
   489  	)
   490  	if opts&doesReRefresh != 0 {
   491  		expected = append(expected, "check-rerefresh")
   492  	}
   493  
   494  	c.Assert(kinds, DeepEquals, expected)
   495  }
   496  
   497  func verifyLastTasksetIsReRefresh(c *C, tts []*state.TaskSet) {
   498  	ts := tts[len(tts)-1]
   499  	c.Assert(ts.Tasks(), HasLen, 1)
   500  	c.Check(ts.Tasks()[0].Kind(), Equals, "check-rerefresh")
   501  }
   502  
   503  func verifyRemoveTasks(c *C, ts *state.TaskSet) {
   504  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
   505  		"stop-snap-services",
   506  		"run-hook[remove]",
   507  		"auto-disconnect",
   508  		"save-snapshot",
   509  		"remove-aliases",
   510  		"unlink-snap",
   511  		"remove-profiles",
   512  		"clear-snap",
   513  		"discard-snap",
   514  	})
   515  	verifyStopReason(c, ts, "remove")
   516  }
   517  
   518  func verifyCoreRemoveTasks(c *C, ts *state.TaskSet) {
   519  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
   520  		"stop-snap-services",
   521  		"run-hook[remove]",
   522  		"auto-disconnect",
   523  		"remove-aliases",
   524  		"unlink-snap",
   525  		"remove-profiles",
   526  		"clear-snap",
   527  		"discard-snap",
   528  	})
   529  	verifyStopReason(c, ts, "remove")
   530  }
   531  
   532  func checkIsAutoRefresh(c *C, tasks []*state.Task, expected bool) {
   533  	for _, t := range tasks {
   534  		if t.Kind() == "download-snap" {
   535  			var snapsup snapstate.SnapSetup
   536  			err := t.Get("snap-setup", &snapsup)
   537  			c.Assert(err, IsNil)
   538  			c.Check(snapsup.IsAutoRefresh, Equals, expected)
   539  			return
   540  		}
   541  	}
   542  	c.Fatalf("cannot find download-snap task in %v", tasks)
   543  }
   544  
   545  func (s *snapmgrTestSuite) TestLastIndexFindsLast(c *C) {
   546  	snapst := &snapstate.SnapState{Sequence: []*snap.SideInfo{
   547  		{Revision: snap.R(7)},
   548  		{Revision: snap.R(11)},
   549  		{Revision: snap.R(11)},
   550  	}}
   551  	c.Check(snapst.LastIndex(snap.R(11)), Equals, 2)
   552  }
   553  
   554  func (s *snapmgrTestSuite) TestInstallDevModeConfinementFiltering(c *C) {
   555  	s.state.Lock()
   556  	defer s.state.Unlock()
   557  
   558  	// if a snap is devmode, you can't install it without --devmode
   559  	opts := &snapstate.RevisionOptions{Channel: "channel-for-devmode"}
   560  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
   561  	c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`)
   562  
   563  	// if a snap is devmode, you *can* install it with --devmode
   564  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{DevMode: true})
   565  	c.Assert(err, IsNil)
   566  
   567  	// if a snap is *not* devmode, you can still install it with --devmode
   568  	opts.Channel = "channel-for-strict"
   569  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{DevMode: true})
   570  	c.Assert(err, IsNil)
   571  }
   572  
   573  func maybeMockClassicSupport(c *C) (restore func()) {
   574  	if dirs.SupportsClassicConfinement() {
   575  		return func() {}
   576  	}
   577  
   578  	d := filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd/snap")
   579  	err := os.MkdirAll(d, 0755)
   580  	c.Assert(err, IsNil)
   581  	snapSymlink := filepath.Join(dirs.GlobalRootDir, "snap")
   582  	err = os.Symlink(d, snapSymlink)
   583  	c.Assert(err, IsNil)
   584  
   585  	return func() { os.Remove(snapSymlink) }
   586  }
   587  
   588  func (s *snapmgrTestSuite) TestInstallClassicConfinementFiltering(c *C) {
   589  	restore := maybeMockClassicSupport(c)
   590  	defer restore()
   591  
   592  	s.state.Lock()
   593  	defer s.state.Unlock()
   594  
   595  	// if a snap is classic, you can't install it without --classic
   596  	opts := &snapstate.RevisionOptions{Channel: "channel-for-classic"}
   597  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
   598  	c.Assert(err, ErrorMatches, `.* requires classic confinement`)
   599  
   600  	// if a snap is classic, you *can* install it with --classic
   601  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true})
   602  	c.Assert(err, IsNil)
   603  
   604  	// if a snap is *not* classic, but can install it with --classic which gets ignored
   605  	opts.Channel = "channel-for-strict"
   606  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true})
   607  	c.Assert(err, IsNil)
   608  }
   609  
   610  func (s *snapmgrTestSuite) TestInstallFailsWhenClassicSnapsAreNotSupported(c *C) {
   611  	s.state.Lock()
   612  	defer s.state.Unlock()
   613  
   614  	reset := release.MockReleaseInfo(&release.OS{
   615  		ID: "fedora",
   616  	})
   617  	defer reset()
   618  
   619  	// this needs doing because dirs depends on the release info
   620  	dirs.SetRootDir(dirs.GlobalRootDir)
   621  
   622  	opts := &snapstate.RevisionOptions{Channel: "channel-for-classic"}
   623  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true})
   624  	c.Assert(err, ErrorMatches, "classic confinement requires snaps under /snap or symlink from /snap to "+dirs.SnapMountDir)
   625  }
   626  
   627  func (s *snapmgrTestSuite) TestInstallTasks(c *C) {
   628  	s.state.Lock()
   629  	defer s.state.Unlock()
   630  
   631  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
   632  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{})
   633  	c.Assert(err, IsNil)
   634  
   635  	verifyInstallTasks(c, 0, 0, ts, s.state)
   636  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   637  }
   638  
   639  func (s *snapmgrTestSuite) TestInstallSnapdSnapType(c *C) {
   640  	restore := snap.MockSnapdSnapID("snapd-id") // id provided by fakeStore
   641  	defer restore()
   642  
   643  	s.state.Lock()
   644  	defer s.state.Unlock()
   645  
   646  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
   647  	ts, err := snapstate.Install(context.Background(), s.state, "snapd", opts, 0, snapstate.Flags{})
   648  	c.Assert(err, IsNil)
   649  
   650  	verifyInstallTasks(c, noConfigure, 0, ts, s.state)
   651  
   652  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
   653  	c.Assert(err, IsNil)
   654  	c.Check(snapsup.Type, Equals, snap.TypeSnapd)
   655  }
   656  
   657  func (s *snapmgrTestSuite) TestInstallCohortTasks(c *C) {
   658  	s.state.Lock()
   659  	defer s.state.Unlock()
   660  
   661  	opts := &snapstate.RevisionOptions{Channel: "some-channel", CohortKey: "what"}
   662  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{})
   663  	c.Assert(err, IsNil)
   664  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
   665  	c.Assert(err, IsNil)
   666  	c.Check(snapsup.CohortKey, Equals, "what")
   667  
   668  	verifyInstallTasks(c, 0, 0, ts, s.state)
   669  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   670  }
   671  
   672  func (s *snapmgrTestSuite) TestInstallWithDeviceContext(c *C) {
   673  	s.state.Lock()
   674  	defer s.state.Unlock()
   675  
   676  	// unset the global store, it will need to come via the device context
   677  	snapstate.ReplaceStore(s.state, nil)
   678  
   679  	deviceCtx := &snapstatetest.TrivialDeviceContext{CtxStore: s.fakeStore}
   680  
   681  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
   682  	ts, err := snapstate.InstallWithDeviceContext(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}, deviceCtx, "")
   683  	c.Assert(err, IsNil)
   684  
   685  	verifyInstallTasks(c, 0, 0, ts, s.state)
   686  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   687  }
   688  
   689  func (s *snapmgrTestSuite) TestInstallHookNotRunForInstalledSnap(c *C) {
   690  	s.state.Lock()
   691  	defer s.state.Unlock()
   692  
   693  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   694  		Active: true,
   695  		Sequence: []*snap.SideInfo{
   696  			{RealName: "some-snap", Revision: snap.R(7)},
   697  		},
   698  		Current:  snap.R(7),
   699  		SnapType: "app",
   700  	})
   701  
   702  	mockSnap := makeTestSnap(c, `name: some-snap
   703  version: 1.0
   704  epoch: 1*
   705  `)
   706  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{})
   707  	c.Assert(err, IsNil)
   708  
   709  	runHooks := tasksWithKind(ts, "run-hook")
   710  	// no install hook task
   711  	c.Assert(taskKinds(runHooks), DeepEquals, []string{
   712  		"run-hook[pre-refresh]",
   713  		"run-hook[post-refresh]",
   714  		"run-hook[configure]",
   715  		"run-hook[check-health]",
   716  	})
   717  }
   718  
   719  type fullFlags struct{ before, change, after, setup snapstate.Flags }
   720  
   721  func (s *snapmgrTestSuite) testRevertTasksFullFlags(flags fullFlags, c *C) {
   722  	s.state.Lock()
   723  	defer s.state.Unlock()
   724  
   725  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   726  		Active: true,
   727  		Sequence: []*snap.SideInfo{
   728  			{RealName: "some-snap", Revision: snap.R(7)},
   729  			{RealName: "some-snap", Revision: snap.R(11)},
   730  		},
   731  		Flags:    flags.before,
   732  		Current:  snap.R(11),
   733  		SnapType: "app",
   734  	})
   735  
   736  	ts, err := snapstate.Revert(s.state, "some-snap", flags.change)
   737  	c.Assert(err, IsNil)
   738  
   739  	tasks := ts.Tasks()
   740  	c.Assert(s.state.TaskCount(), Equals, len(tasks))
   741  	c.Assert(taskKinds(tasks), DeepEquals, []string{
   742  		"prerequisites",
   743  		"prepare-snap",
   744  		"stop-snap-services",
   745  		"remove-aliases",
   746  		"unlink-current-snap",
   747  		"setup-profiles",
   748  		"link-snap",
   749  		"auto-connect",
   750  		"set-auto-aliases",
   751  		"setup-aliases",
   752  		"start-snap-services",
   753  		"run-hook[configure]",
   754  		"run-hook[check-health]",
   755  	})
   756  	// a revert is a special refresh
   757  	verifyStopReason(c, ts, "refresh")
   758  
   759  	snapsup, err := snapstate.TaskSnapSetup(tasks[0])
   760  	c.Assert(err, IsNil)
   761  	flags.setup.Revert = true
   762  	c.Check(snapsup.Flags, Equals, flags.setup)
   763  	c.Check(snapsup.Type, Equals, snap.TypeApp)
   764  
   765  	chg := s.state.NewChange("revert", "revert snap")
   766  	chg.AddAll(ts)
   767  
   768  	s.state.Unlock()
   769  	defer s.se.Stop()
   770  	s.settle(c)
   771  	s.state.Lock()
   772  
   773  	var snapst snapstate.SnapState
   774  	err = snapstate.Get(s.state, "some-snap", &snapst)
   775  	c.Assert(err, IsNil)
   776  	c.Check(snapst.Flags, Equals, flags.after)
   777  }
   778  
   779  func (s *snapmgrTestSuite) testRevertTasks(flags snapstate.Flags, c *C) {
   780  	s.testRevertTasksFullFlags(fullFlags{before: flags, change: flags, after: flags, setup: flags}, c)
   781  }
   782  
   783  func (s *snapmgrTestSuite) TestRevertTasks(c *C) {
   784  	s.testRevertTasks(snapstate.Flags{}, c)
   785  }
   786  
   787  func (s *snapmgrTestSuite) TestRevertTasksFromDevMode(c *C) {
   788  	// the snap is installed in devmode, but the request to revert does not specify devmode
   789  	s.testRevertTasksFullFlags(fullFlags{
   790  		before: snapstate.Flags{DevMode: true}, // the snap is installed in devmode
   791  		change: snapstate.Flags{},              // the request to revert does not specify devmode
   792  		after:  snapstate.Flags{DevMode: true}, // the reverted snap is installed in devmode
   793  		setup:  snapstate.Flags{DevMode: true}, // because setup said so
   794  	}, c)
   795  }
   796  
   797  func (s *snapmgrTestSuite) TestRevertTasksFromJailMode(c *C) {
   798  	// the snap is installed in jailmode, but the request to revert does not specify jailmode
   799  	s.testRevertTasksFullFlags(fullFlags{
   800  		before: snapstate.Flags{JailMode: true}, // the snap is installed in jailmode
   801  		change: snapstate.Flags{},               // the request to revert does not specify jailmode
   802  		after:  snapstate.Flags{JailMode: true}, // the reverted snap is installed in jailmode
   803  		setup:  snapstate.Flags{JailMode: true}, // because setup said so
   804  	}, c)
   805  }
   806  
   807  func (s *snapmgrTestSuite) TestRevertTasksFromClassic(c *C) {
   808  	restore := maybeMockClassicSupport(c)
   809  	defer restore()
   810  
   811  	// the snap is installed in classic, but the request to revert does not specify classic
   812  	s.testRevertTasksFullFlags(fullFlags{
   813  		before: snapstate.Flags{Classic: true}, // the snap is installed in classic
   814  		change: snapstate.Flags{},              // the request to revert does not specify classic
   815  		after:  snapstate.Flags{Classic: true}, // the reverted snap is installed in classic
   816  		setup:  snapstate.Flags{Classic: true}, // because setup said so
   817  	}, c)
   818  }
   819  
   820  func (s *snapmgrTestSuite) TestRevertTasksDevMode(c *C) {
   821  	s.testRevertTasks(snapstate.Flags{DevMode: true}, c)
   822  }
   823  
   824  func (s *snapmgrTestSuite) TestRevertTasksJailMode(c *C) {
   825  	s.testRevertTasks(snapstate.Flags{JailMode: true}, c)
   826  }
   827  
   828  func (s *snapmgrTestSuite) TestRevertTasksClassic(c *C) {
   829  	restore := maybeMockClassicSupport(c)
   830  	defer restore()
   831  
   832  	s.testRevertTasks(snapstate.Flags{Classic: true}, c)
   833  }
   834  
   835  func (s *snapmgrTestSuite) TestUpdateCreatesGCTasks(c *C) {
   836  	restore := release.MockOnClassic(false)
   837  	defer restore()
   838  
   839  	s.testUpdateCreatesGCTasks(c, 2)
   840  }
   841  
   842  func (s *snapmgrTestSuite) TestUpdateCreatesGCTasksOnClassic(c *C) {
   843  	restore := release.MockOnClassic(true)
   844  	defer restore()
   845  
   846  	s.testUpdateCreatesGCTasks(c, 3)
   847  }
   848  
   849  func (s *snapmgrTestSuite) testUpdateCreatesGCTasks(c *C, expectedDiscards int) {
   850  	s.state.Lock()
   851  	defer s.state.Unlock()
   852  
   853  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   854  		Active: true,
   855  		Sequence: []*snap.SideInfo{
   856  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   857  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
   858  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
   859  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
   860  		},
   861  		Current:  snap.R(4),
   862  		SnapType: "app",
   863  	})
   864  
   865  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{})
   866  	c.Assert(err, IsNil)
   867  
   868  	// ensure edges information is still there
   869  	te, err := ts.Edge(snapstate.DownloadAndChecksDoneEdge)
   870  	c.Assert(te, NotNil)
   871  	c.Assert(err, IsNil)
   872  
   873  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, expectedDiscards, ts, s.state)
   874  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   875  }
   876  
   877  func (s snapmgrTestSuite) TestInstallFailsOnDisabledSnap(c *C) {
   878  	s.state.Lock()
   879  	defer s.state.Unlock()
   880  
   881  	snapst := &snapstate.SnapState{
   882  		Active:   false,
   883  		Channel:  "channel",
   884  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}},
   885  		Current:  snap.R(2),
   886  		SnapType: "app",
   887  	}
   888  	snapsup := &snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}}
   889  	_, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "")
   890  	c.Assert(err, NotNil)
   891  	c.Assert(err, ErrorMatches, `cannot update disabled snap "some-snap"`)
   892  }
   893  
   894  func (s snapmgrTestSuite) TestInstallFailsOnBusySnap(c *C) {
   895  	s.state.Lock()
   896  	defer s.state.Unlock()
   897  
   898  	// With the refresh-app-awareness feature enabled.
   899  	tr := config.NewTransaction(s.state)
   900  	tr.Set("core", "experimental.refresh-app-awareness", true)
   901  	tr.Commit()
   902  
   903  	// With a snap state indicating a snap is already installed.
   904  	snapst := &snapstate.SnapState{
   905  		Active: true,
   906  		Sequence: []*snap.SideInfo{
   907  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   908  		},
   909  		Current:  snap.R(1),
   910  		SnapType: "app",
   911  	}
   912  	snapstate.Set(s.state, "some-snap", snapst)
   913  
   914  	// With a snap info indicating it has an application called "app"
   915  	snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
   916  		if name != "some-snap" {
   917  			return s.fakeBackend.ReadInfo(name, si)
   918  		}
   919  		info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp}
   920  		info.Apps = map[string]*snap.AppInfo{
   921  			"app": {Snap: info, Name: "app"},
   922  		}
   923  		return info, nil
   924  	})
   925  	// And with cgroup v1 information indicating the app has a process with pid 1234.
   926  	writePids(c, filepath.Join(dirs.PidsCgroupDir, "snap.some-snap.app"), []int{1234})
   927  
   928  	// Attempt to install revision 2 of the snap.
   929  	snapsup := &snapstate.SnapSetup{
   930  		SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
   931  	}
   932  
   933  	// And observe that we cannot refresh because the snap is busy.
   934  	_, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "")
   935  	c.Assert(err, ErrorMatches, `snap "some-snap" has running apps \(app\)`)
   936  
   937  	// The state records the time of the failed refresh operation.
   938  	err = snapstate.Get(s.state, "some-snap", snapst)
   939  	c.Assert(err, IsNil)
   940  	c.Check(snapst.RefreshInhibitedTime, NotNil)
   941  }
   942  
   943  func (s snapmgrTestSuite) TestInstallDespiteBusySnap(c *C) {
   944  	s.state.Lock()
   945  	defer s.state.Unlock()
   946  
   947  	// With the refresh-app-awareness feature enabled.
   948  	tr := config.NewTransaction(s.state)
   949  	tr.Set("core", "experimental.refresh-app-awareness", true)
   950  	tr.Commit()
   951  
   952  	// With a snap state indicating a snap is already installed and it failed
   953  	// to refresh over a week ago. Use UTC and Round to have predictable
   954  	// behaviour across time-zones and with enough precision loss to be
   955  	// compatible with the serialization format.
   956  	var longAgo = time.Now().UTC().Round(time.Second).Add(-time.Hour * 24 * 8)
   957  	snapst := &snapstate.SnapState{
   958  		Active: true,
   959  		Sequence: []*snap.SideInfo{
   960  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   961  		},
   962  		Current:              snap.R(1),
   963  		SnapType:             "app",
   964  		RefreshInhibitedTime: &longAgo,
   965  	}
   966  	snapstate.Set(s.state, "some-snap", snapst)
   967  
   968  	// With a snap info indicating it has an application called "app"
   969  	snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
   970  		if name != "some-snap" {
   971  			return s.fakeBackend.ReadInfo(name, si)
   972  		}
   973  		info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp}
   974  		info.Apps = map[string]*snap.AppInfo{
   975  			"app": {Snap: info, Name: "app"},
   976  		}
   977  		return info, nil
   978  	})
   979  	// And with cgroup v1 information indicating the app has a process with pid 1234.
   980  	writePids(c, filepath.Join(dirs.PidsCgroupDir, "snap.some-snap.app"), []int{1234})
   981  
   982  	// Attempt to install revision 2 of the snap.
   983  	snapsup := &snapstate.SnapSetup{
   984  		SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
   985  	}
   986  
   987  	// And observe that refresh occurred regardless of the running process.
   988  	_, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "")
   989  	c.Assert(err, IsNil)
   990  }
   991  
   992  func (s snapmgrTestSuite) TestInstallFailsOnSystem(c *C) {
   993  	s.state.Lock()
   994  	defer s.state.Unlock()
   995  
   996  	snapsup := &snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "system", SnapID: "some-snap-id", Revision: snap.R(1)}}
   997  	_, err := snapstate.DoInstall(s.state, nil, snapsup, 0, "")
   998  	c.Assert(err, NotNil)
   999  	c.Assert(err, ErrorMatches, `cannot install reserved snap name 'system'`)
  1000  }
  1001  
  1002  func (s *snapmgrTestSuite) TestUpdateCreatesDiscardAfterCurrentTasks(c *C) {
  1003  	s.state.Lock()
  1004  	defer s.state.Unlock()
  1005  
  1006  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1007  		Active: true,
  1008  		Sequence: []*snap.SideInfo{
  1009  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1010  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  1011  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  1012  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  1013  		},
  1014  		Current:  snap.R(1),
  1015  		SnapType: "app",
  1016  	})
  1017  
  1018  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{})
  1019  	c.Assert(err, IsNil)
  1020  
  1021  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 3, ts, s.state)
  1022  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  1023  }
  1024  
  1025  func (s *snapmgrTestSuite) TestUpdateManyTooEarly(c *C) {
  1026  	s.state.Lock()
  1027  	defer s.state.Unlock()
  1028  
  1029  	s.state.Set("seeded", nil)
  1030  
  1031  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1032  		Active:   true,
  1033  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  1034  		Current:  snap.R(7),
  1035  		SnapType: "app",
  1036  	})
  1037  
  1038  	_, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1039  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  1040  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
  1041  }
  1042  
  1043  func (s *snapmgrTestSuite) TestUpdateMany(c *C) {
  1044  	s.state.Lock()
  1045  	defer s.state.Unlock()
  1046  
  1047  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1048  		Active: true,
  1049  		Sequence: []*snap.SideInfo{
  1050  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1051  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  1052  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  1053  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  1054  		},
  1055  		Current:  snap.R(1),
  1056  		SnapType: "app",
  1057  	})
  1058  
  1059  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1060  	c.Assert(err, IsNil)
  1061  	c.Assert(tts, HasLen, 2)
  1062  	verifyLastTasksetIsReRefresh(c, tts)
  1063  	c.Check(updates, DeepEquals, []string{"some-snap"})
  1064  
  1065  	ts := tts[0]
  1066  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 3, ts, s.state)
  1067  
  1068  	// check that the tasks are in non-default lane
  1069  	for _, t := range ts.Tasks() {
  1070  		c.Assert(t.Lanes(), DeepEquals, []int{1})
  1071  	}
  1072  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())+1) // 1==rerefresh
  1073  
  1074  	// ensure edges information is still there
  1075  	te, err := ts.Edge(snapstate.DownloadAndChecksDoneEdge)
  1076  	c.Assert(te, NotNil)
  1077  	c.Assert(err, IsNil)
  1078  
  1079  	checkIsAutoRefresh(c, ts.Tasks(), false)
  1080  }
  1081  
  1082  func (s *snapmgrTestSuite) TestParallelInstanceUpdateMany(c *C) {
  1083  	restore := release.MockOnClassic(false)
  1084  	defer restore()
  1085  
  1086  	s.state.Lock()
  1087  	defer s.state.Unlock()
  1088  
  1089  	tr := config.NewTransaction(s.state)
  1090  	tr.Set("core", "experimental.parallel-instances", true)
  1091  	tr.Commit()
  1092  
  1093  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1094  		Active: true,
  1095  		Sequence: []*snap.SideInfo{
  1096  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1097  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  1098  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  1099  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  1100  		},
  1101  		Current:  snap.R(1),
  1102  		SnapType: "app",
  1103  	})
  1104  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  1105  		Active: true,
  1106  		Sequence: []*snap.SideInfo{
  1107  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1108  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  1109  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  1110  		},
  1111  		Current:     snap.R(3),
  1112  		SnapType:    "app",
  1113  		InstanceKey: "instance",
  1114  	})
  1115  
  1116  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1117  	c.Assert(err, IsNil)
  1118  	c.Assert(tts, HasLen, 3)
  1119  	verifyLastTasksetIsReRefresh(c, tts)
  1120  	// ensure stable ordering of updates list
  1121  	if updates[0] != "some-snap" {
  1122  		updates[1], updates[0] = updates[0], updates[1]
  1123  	}
  1124  
  1125  	c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"})
  1126  
  1127  	var snapsup, snapsupInstance *snapstate.SnapSetup
  1128  
  1129  	// ensure stable ordering of task sets list
  1130  	snapsup, err = snapstate.TaskSnapSetup(tts[0].Tasks()[0])
  1131  	c.Assert(err, IsNil)
  1132  	if snapsup.InstanceName() != "some-snap" {
  1133  		tts[0], tts[1] = tts[1], tts[0]
  1134  		snapsup, err = snapstate.TaskSnapSetup(tts[0].Tasks()[0])
  1135  		c.Assert(err, IsNil)
  1136  	}
  1137  	snapsupInstance, err = snapstate.TaskSnapSetup(tts[1].Tasks()[0])
  1138  	c.Assert(err, IsNil)
  1139  
  1140  	c.Assert(snapsup.InstanceName(), Equals, "some-snap")
  1141  	c.Assert(snapsupInstance.InstanceName(), Equals, "some-snap_instance")
  1142  
  1143  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 3, tts[0], s.state)
  1144  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 1, tts[1], s.state)
  1145  }
  1146  
  1147  func (s *snapmgrTestSuite) TestUpdateManyDevModeConfinementFiltering(c *C) {
  1148  	s.state.Lock()
  1149  	defer s.state.Unlock()
  1150  
  1151  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1152  		Active:   true,
  1153  		Channel:  "channel-for-devmode",
  1154  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  1155  		Current:  snap.R(7),
  1156  		SnapType: "app",
  1157  	})
  1158  
  1159  	// updated snap is devmode, updatemany doesn't update it
  1160  	_, tts, _ := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  1161  	// FIXME: UpdateMany will not error out in this case (daemon catches this case, with a weird error)
  1162  	c.Assert(tts, HasLen, 0)
  1163  }
  1164  
  1165  func (s *snapmgrTestSuite) TestUpdateManyClassicConfinementFiltering(c *C) {
  1166  	restore := maybeMockClassicSupport(c)
  1167  	defer restore()
  1168  
  1169  	s.state.Lock()
  1170  	defer s.state.Unlock()
  1171  
  1172  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1173  		Active:   true,
  1174  		Channel:  "channel-for-classic",
  1175  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  1176  		Current:  snap.R(7),
  1177  		SnapType: "app",
  1178  	})
  1179  
  1180  	// if a snap installed without --classic gets a classic update it isn't installed
  1181  	_, tts, _ := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  1182  	// FIXME: UpdateMany will not error out in this case (daemon catches this case, with a weird error)
  1183  	c.Assert(tts, HasLen, 0)
  1184  }
  1185  
  1186  func (s *snapmgrTestSuite) TestUpdateManyClassic(c *C) {
  1187  	restore := maybeMockClassicSupport(c)
  1188  	defer restore()
  1189  
  1190  	s.state.Lock()
  1191  	defer s.state.Unlock()
  1192  
  1193  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1194  		Active:   true,
  1195  		Channel:  "channel-for-classic",
  1196  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  1197  		Current:  snap.R(7),
  1198  		SnapType: "app",
  1199  		Flags:    snapstate.Flags{Classic: true},
  1200  	})
  1201  
  1202  	// snap installed with classic: refresh gets classic
  1203  	_, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  1204  	c.Assert(err, IsNil)
  1205  	c.Assert(tts, HasLen, 2)
  1206  	verifyLastTasksetIsReRefresh(c, tts)
  1207  }
  1208  
  1209  func (s *snapmgrTestSuite) TestUpdateManyDevMode(c *C) {
  1210  	s.state.Lock()
  1211  	defer s.state.Unlock()
  1212  
  1213  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1214  		Active: true,
  1215  		Flags:  snapstate.Flags{DevMode: true},
  1216  		Sequence: []*snap.SideInfo{
  1217  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1218  		},
  1219  		Current:  snap.R(1),
  1220  		SnapType: "app",
  1221  	})
  1222  
  1223  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, 0, nil)
  1224  	c.Assert(err, IsNil)
  1225  	c.Check(updates, HasLen, 1)
  1226  }
  1227  
  1228  func (s *snapmgrTestSuite) TestUpdateAllDevMode(c *C) {
  1229  	s.state.Lock()
  1230  	defer s.state.Unlock()
  1231  
  1232  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1233  		Active: true,
  1234  		Flags:  snapstate.Flags{DevMode: true},
  1235  		Sequence: []*snap.SideInfo{
  1236  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1237  		},
  1238  		Current:  snap.R(1),
  1239  		SnapType: "app",
  1240  	})
  1241  
  1242  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1243  	c.Assert(err, IsNil)
  1244  	c.Check(updates, HasLen, 0)
  1245  }
  1246  
  1247  func (s *snapmgrTestSuite) TestUpdateManyWaitForBasesUC16(c *C) {
  1248  	s.state.Lock()
  1249  	defer s.state.Unlock()
  1250  
  1251  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1252  		Active: true,
  1253  		Sequence: []*snap.SideInfo{
  1254  			{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(1)},
  1255  		},
  1256  		Current:  snap.R(1),
  1257  		SnapType: "os",
  1258  	})
  1259  
  1260  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  1261  		Active: true,
  1262  		Sequence: []*snap.SideInfo{
  1263  			{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)},
  1264  		},
  1265  		Current:  snap.R(1),
  1266  		SnapType: "base",
  1267  	})
  1268  
  1269  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1270  		Active: true,
  1271  		Sequence: []*snap.SideInfo{
  1272  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1273  		},
  1274  		Current:  snap.R(1),
  1275  		SnapType: "app",
  1276  		Channel:  "channel-for-base",
  1277  	})
  1278  
  1279  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "core", "some-base"}, 0, nil)
  1280  	c.Assert(err, IsNil)
  1281  	c.Assert(tts, HasLen, 4)
  1282  	verifyLastTasksetIsReRefresh(c, tts)
  1283  	c.Check(updates, HasLen, 3)
  1284  
  1285  	// to make TaskSnapSetup work
  1286  	chg := s.state.NewChange("refresh", "...")
  1287  	for _, ts := range tts {
  1288  		chg.AddAll(ts)
  1289  	}
  1290  
  1291  	prereqTotal := len(tts[0].Tasks()) + len(tts[1].Tasks())
  1292  	prereqs := map[string]bool{}
  1293  	for i, task := range tts[2].Tasks() {
  1294  		waitTasks := task.WaitTasks()
  1295  		if i == 0 {
  1296  			c.Check(len(waitTasks), Equals, prereqTotal)
  1297  		} else if task.Kind() == "link-snap" {
  1298  			c.Check(len(waitTasks), Equals, prereqTotal+1)
  1299  			for _, pre := range waitTasks {
  1300  				if pre.Kind() == "link-snap" {
  1301  					snapsup, err := snapstate.TaskSnapSetup(pre)
  1302  					c.Assert(err, IsNil)
  1303  					prereqs[snapsup.InstanceName()] = true
  1304  				}
  1305  			}
  1306  		}
  1307  	}
  1308  
  1309  	c.Check(prereqs, DeepEquals, map[string]bool{
  1310  		"core":      true,
  1311  		"some-base": true,
  1312  	})
  1313  }
  1314  
  1315  func (s *snapmgrTestSuite) TestUpdateManyWaitForBasesUC18(c *C) {
  1316  	r := snapstatetest.MockDeviceModel(ModelWithBase("core18"))
  1317  	defer r()
  1318  
  1319  	restore := snap.MockSnapdSnapID("snapd-id")
  1320  	defer restore()
  1321  
  1322  	s.state.Lock()
  1323  	defer s.state.Unlock()
  1324  
  1325  	snapstate.Set(s.state, "core18", &snapstate.SnapState{
  1326  		Active: true,
  1327  		Sequence: []*snap.SideInfo{
  1328  			{RealName: "core18", SnapID: "core18-snap-id", Revision: snap.R(1)},
  1329  		},
  1330  		Current:  snap.R(1),
  1331  		SnapType: "base",
  1332  	})
  1333  
  1334  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  1335  		Active: true,
  1336  		Sequence: []*snap.SideInfo{
  1337  			{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)},
  1338  		},
  1339  		Current:  snap.R(1),
  1340  		SnapType: "base",
  1341  	})
  1342  
  1343  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
  1344  		Active: true,
  1345  		Sequence: []*snap.SideInfo{
  1346  			{RealName: "snapd", SnapID: "snapd-id", Revision: snap.R(1)},
  1347  		},
  1348  		Current:  snap.R(1),
  1349  		SnapType: "app",
  1350  	})
  1351  
  1352  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1353  		Active: true,
  1354  		Sequence: []*snap.SideInfo{
  1355  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1356  		},
  1357  		Current:  snap.R(1),
  1358  		SnapType: "app",
  1359  		Channel:  "channel-for-base",
  1360  	})
  1361  
  1362  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "core18", "some-base", "snapd"}, 0, nil)
  1363  	c.Assert(err, IsNil)
  1364  	c.Assert(tts, HasLen, 5)
  1365  	verifyLastTasksetIsReRefresh(c, tts)
  1366  	c.Check(updates, HasLen, 4)
  1367  
  1368  	// to make TaskSnapSetup work
  1369  	chg := s.state.NewChange("refresh", "...")
  1370  	for _, ts := range tts {
  1371  		chg.AddAll(ts)
  1372  	}
  1373  
  1374  	// Note that some-app only waits for snapd+some-base. The core18
  1375  	// base is not special to this snap and not waited for
  1376  	prereqTotal := len(tts[0].Tasks()) + len(tts[1].Tasks())
  1377  	prereqs := map[string]bool{}
  1378  	for i, task := range tts[3].Tasks() {
  1379  		waitTasks := task.WaitTasks()
  1380  		if i == 0 {
  1381  			c.Check(len(waitTasks), Equals, prereqTotal)
  1382  		} else if task.Kind() == "link-snap" {
  1383  			c.Check(len(waitTasks), Equals, prereqTotal+1)
  1384  			for _, pre := range waitTasks {
  1385  				if pre.Kind() == "link-snap" {
  1386  					snapsup, err := snapstate.TaskSnapSetup(pre)
  1387  					c.Assert(err, IsNil)
  1388  					prereqs[snapsup.InstanceName()] = true
  1389  				}
  1390  			}
  1391  		}
  1392  	}
  1393  
  1394  	// Note that "core18" is not part of the prereqs for some-app
  1395  	// as it does not use this base.
  1396  	c.Check(prereqs, DeepEquals, map[string]bool{
  1397  		"some-base": true,
  1398  		"snapd":     true,
  1399  	})
  1400  }
  1401  
  1402  func (s *snapmgrTestSuite) TestUpdateManyValidateRefreshes(c *C) {
  1403  	s.state.Lock()
  1404  	defer s.state.Unlock()
  1405  
  1406  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1407  		Active: true,
  1408  		Sequence: []*snap.SideInfo{
  1409  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1410  		},
  1411  		Current:  snap.R(1),
  1412  		SnapType: "app",
  1413  	})
  1414  
  1415  	validateCalled := false
  1416  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  1417  		validateCalled = true
  1418  		c.Check(refreshes, HasLen, 1)
  1419  		c.Check(refreshes[0].InstanceName(), Equals, "some-snap")
  1420  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  1421  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  1422  		c.Check(ignoreValidation, HasLen, 0)
  1423  		return refreshes, nil
  1424  	}
  1425  	// hook it up
  1426  	snapstate.ValidateRefreshes = validateRefreshes
  1427  
  1428  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1429  	c.Assert(err, IsNil)
  1430  	c.Assert(tts, HasLen, 2)
  1431  	verifyLastTasksetIsReRefresh(c, tts)
  1432  	c.Check(updates, DeepEquals, []string{"some-snap"})
  1433  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[0], s.state)
  1434  
  1435  	c.Check(validateCalled, Equals, true)
  1436  }
  1437  
  1438  func (s *snapmgrTestSuite) TestParallelInstanceUpdateManyValidateRefreshes(c *C) {
  1439  	s.state.Lock()
  1440  	defer s.state.Unlock()
  1441  
  1442  	tr := config.NewTransaction(s.state)
  1443  	tr.Set("core", "experimental.parallel-instances", true)
  1444  	tr.Commit()
  1445  
  1446  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1447  		Active: true,
  1448  		Sequence: []*snap.SideInfo{
  1449  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1450  		},
  1451  		Current:  snap.R(1),
  1452  		SnapType: "app",
  1453  	})
  1454  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  1455  		Active: true,
  1456  		Sequence: []*snap.SideInfo{
  1457  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1458  		},
  1459  		Current:     snap.R(1),
  1460  		SnapType:    "app",
  1461  		InstanceKey: "instance",
  1462  	})
  1463  
  1464  	validateCalled := false
  1465  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  1466  		validateCalled = true
  1467  		c.Check(refreshes, HasLen, 2)
  1468  		instanceIdx := 0
  1469  		someIdx := 1
  1470  		if refreshes[0].InstanceName() != "some-snap_instance" {
  1471  			instanceIdx = 1
  1472  			someIdx = 0
  1473  		}
  1474  		c.Check(refreshes[someIdx].InstanceName(), Equals, "some-snap")
  1475  		c.Check(refreshes[instanceIdx].InstanceName(), Equals, "some-snap_instance")
  1476  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  1477  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  1478  		c.Check(refreshes[1].SnapID, Equals, "some-snap-id")
  1479  		c.Check(refreshes[1].Revision, Equals, snap.R(11))
  1480  		c.Check(ignoreValidation, HasLen, 0)
  1481  		return refreshes, nil
  1482  	}
  1483  	// hook it up
  1484  	snapstate.ValidateRefreshes = validateRefreshes
  1485  
  1486  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1487  	c.Assert(err, IsNil)
  1488  	c.Assert(tts, HasLen, 3)
  1489  	verifyLastTasksetIsReRefresh(c, tts)
  1490  	sort.Strings(updates)
  1491  	c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"})
  1492  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[0], s.state)
  1493  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[1], s.state)
  1494  
  1495  	c.Check(validateCalled, Equals, true)
  1496  }
  1497  
  1498  func (s *snapmgrTestSuite) TestUpdateManyValidateRefreshesUnhappy(c *C) {
  1499  	s.state.Lock()
  1500  	defer s.state.Unlock()
  1501  
  1502  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1503  		Active: true,
  1504  		Sequence: []*snap.SideInfo{
  1505  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  1506  		},
  1507  		Current: snap.R(1),
  1508  	})
  1509  
  1510  	validateErr := errors.New("refresh control error")
  1511  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  1512  		c.Check(refreshes, HasLen, 1)
  1513  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  1514  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  1515  		c.Check(ignoreValidation, HasLen, 0)
  1516  		return nil, validateErr
  1517  	}
  1518  	// hook it up
  1519  	snapstate.ValidateRefreshes = validateRefreshes
  1520  
  1521  	// refresh all => no error
  1522  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1523  	c.Assert(err, IsNil)
  1524  	c.Check(tts, HasLen, 0)
  1525  	c.Check(updates, HasLen, 0)
  1526  
  1527  	// refresh some-snap => report error
  1528  	updates, tts, err = snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, 0, nil)
  1529  	c.Assert(err, Equals, validateErr)
  1530  	c.Check(tts, HasLen, 0)
  1531  	c.Check(updates, HasLen, 0)
  1532  
  1533  }
  1534  
  1535  func (s *snapmgrTestSuite) TestRevertCreatesNoGCTasks(c *C) {
  1536  	s.state.Lock()
  1537  	defer s.state.Unlock()
  1538  
  1539  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1540  		Active:   true,
  1541  		SnapType: "app",
  1542  		Sequence: []*snap.SideInfo{
  1543  			{RealName: "some-snap", Revision: snap.R(1)},
  1544  			{RealName: "some-snap", Revision: snap.R(2)},
  1545  			{RealName: "some-snap", Revision: snap.R(3)},
  1546  			{RealName: "some-snap", Revision: snap.R(4)},
  1547  		},
  1548  		Current: snap.R(2),
  1549  	})
  1550  
  1551  	ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(4), snapstate.Flags{})
  1552  	c.Assert(err, IsNil)
  1553  
  1554  	// ensure that we do not run any form of garbage-collection
  1555  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  1556  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
  1557  		"prerequisites",
  1558  		"prepare-snap",
  1559  		"stop-snap-services",
  1560  		"remove-aliases",
  1561  		"unlink-current-snap",
  1562  		"setup-profiles",
  1563  		"link-snap",
  1564  		"auto-connect",
  1565  		"set-auto-aliases",
  1566  		"setup-aliases",
  1567  		"start-snap-services",
  1568  		"run-hook[configure]",
  1569  		"run-hook[check-health]",
  1570  	})
  1571  }
  1572  
  1573  func (s *snapmgrTestSuite) TestEnableTasks(c *C) {
  1574  	s.state.Lock()
  1575  	defer s.state.Unlock()
  1576  
  1577  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1578  		Sequence: []*snap.SideInfo{
  1579  			{RealName: "some-snap", Revision: snap.R(11)},
  1580  		},
  1581  		Current: snap.R(11),
  1582  		Active:  false,
  1583  	})
  1584  
  1585  	ts, err := snapstate.Enable(s.state, "some-snap")
  1586  	c.Assert(err, IsNil)
  1587  
  1588  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  1589  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
  1590  		"prepare-snap",
  1591  		"setup-profiles",
  1592  		"link-snap",
  1593  		"setup-aliases",
  1594  		"start-snap-services",
  1595  	})
  1596  }
  1597  
  1598  func (s *snapmgrTestSuite) TestSwitchTasks(c *C) {
  1599  	s.state.Lock()
  1600  	defer s.state.Unlock()
  1601  
  1602  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1603  		Sequence: []*snap.SideInfo{
  1604  			{RealName: "some-snap", Revision: snap.R(11)},
  1605  		},
  1606  		Current: snap.R(11),
  1607  		Active:  false,
  1608  	})
  1609  
  1610  	ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"})
  1611  	c.Assert(err, IsNil)
  1612  
  1613  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  1614  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{"switch-snap"})
  1615  }
  1616  
  1617  func (s *snapmgrTestSuite) TestSwitchConflict(c *C) {
  1618  	s.state.Lock()
  1619  	defer s.state.Unlock()
  1620  
  1621  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1622  		Sequence: []*snap.SideInfo{
  1623  			{RealName: "some-snap", Revision: snap.R(11)},
  1624  		},
  1625  		Current: snap.R(11),
  1626  		Active:  false,
  1627  	})
  1628  
  1629  	ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"})
  1630  	c.Assert(err, IsNil)
  1631  	// need a change to make the tasks visible
  1632  	s.state.NewChange("switch-snap", "...").AddAll(ts)
  1633  
  1634  	_, err = snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "other-channel"})
  1635  	c.Check(err, ErrorMatches, `snap "some-snap" has "switch-snap" change in progress`)
  1636  }
  1637  
  1638  func (s *snapmgrTestSuite) TestSwitchUnhappy(c *C) {
  1639  	s.state.Lock()
  1640  	defer s.state.Unlock()
  1641  
  1642  	_, err := snapstate.Switch(s.state, "non-existing-snap", &snapstate.RevisionOptions{Channel: "some-channel"})
  1643  	c.Assert(err, ErrorMatches, `snap "non-existing-snap" is not installed`)
  1644  }
  1645  
  1646  func (s *snapmgrTestSuite) TestSwitchRevision(c *C) {
  1647  	s.state.Lock()
  1648  	defer s.state.Unlock()
  1649  
  1650  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1651  		Sequence: []*snap.SideInfo{
  1652  			{RealName: "some-snap", Revision: snap.R(11)},
  1653  		},
  1654  		Current: snap.R(11),
  1655  	})
  1656  
  1657  	_, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(42)})
  1658  	c.Assert(err, ErrorMatches, "cannot switch revision")
  1659  }
  1660  
  1661  func (s *snapmgrTestSuite) TestSwitchKernelTrackForbidden(c *C) {
  1662  	s.state.Lock()
  1663  	defer s.state.Unlock()
  1664  
  1665  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  1666  	defer r()
  1667  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  1668  		Sequence: []*snap.SideInfo{
  1669  			{RealName: "kernel", Revision: snap.R(11)},
  1670  		},
  1671  		Channel: "18/stable",
  1672  		Current: snap.R(11),
  1673  		Active:  true,
  1674  	})
  1675  
  1676  	_, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"})
  1677  	c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`)
  1678  }
  1679  
  1680  func (s *snapmgrTestSuite) TestSwitchKernelTrackRiskOnlyIsOK(c *C) {
  1681  	s.state.Lock()
  1682  	defer s.state.Unlock()
  1683  
  1684  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  1685  	defer r()
  1686  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  1687  		Sequence: []*snap.SideInfo{
  1688  			{RealName: "kernel", Revision: snap.R(11)},
  1689  		},
  1690  		Channel: "18/stable",
  1691  		Current: snap.R(11),
  1692  		Active:  true,
  1693  	})
  1694  
  1695  	_, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "18/beta"})
  1696  	c.Assert(err, IsNil)
  1697  }
  1698  
  1699  func (s *snapmgrTestSuite) TestSwitchKernelTrackRiskOnlyDefaultTrackIsOK(c *C) {
  1700  	s.state.Lock()
  1701  	defer s.state.Unlock()
  1702  
  1703  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  1704  	defer r()
  1705  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  1706  		Sequence: []*snap.SideInfo{
  1707  			{RealName: "kernel", Revision: snap.R(11)},
  1708  		},
  1709  		Channel: "18/stable",
  1710  		Current: snap.R(11),
  1711  		Active:  true,
  1712  	})
  1713  
  1714  	_, err := snapstate.Switch(s.state, "kernel", &snapstate.RevisionOptions{Channel: "beta"})
  1715  	c.Assert(err, IsNil)
  1716  }
  1717  
  1718  func (s *snapmgrTestSuite) TestSwitchGadgetTrackForbidden(c *C) {
  1719  	s.state.Lock()
  1720  	defer s.state.Unlock()
  1721  
  1722  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
  1723  	defer r()
  1724  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
  1725  		Sequence: []*snap.SideInfo{
  1726  			{RealName: "brand-gadget", Revision: snap.R(11)},
  1727  		},
  1728  		Channel: "18/stable",
  1729  		Current: snap.R(11),
  1730  		Active:  true,
  1731  	})
  1732  
  1733  	_, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"})
  1734  	c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`)
  1735  }
  1736  
  1737  func (s *snapmgrTestSuite) TestSwitchGadgetTrackRiskOnlyIsOK(c *C) {
  1738  	s.state.Lock()
  1739  	defer s.state.Unlock()
  1740  
  1741  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
  1742  	defer r()
  1743  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
  1744  		Sequence: []*snap.SideInfo{
  1745  			{RealName: "brand-gadget", Revision: snap.R(11)},
  1746  		},
  1747  		Channel: "18/stable",
  1748  		Current: snap.R(11),
  1749  		Active:  true,
  1750  	})
  1751  
  1752  	_, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "18/beta"})
  1753  	c.Assert(err, IsNil)
  1754  }
  1755  
  1756  func (s *snapmgrTestSuite) TestSwitchGadgetTrackRiskOnlyDefaultTrackIsOK(c *C) {
  1757  	s.state.Lock()
  1758  	defer s.state.Unlock()
  1759  
  1760  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
  1761  	defer r()
  1762  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
  1763  		Sequence: []*snap.SideInfo{
  1764  			{RealName: "brand-gadget", Revision: snap.R(11)},
  1765  		},
  1766  		Channel: "18/stable",
  1767  		Current: snap.R(11),
  1768  		Active:  true,
  1769  	})
  1770  
  1771  	_, err := snapstate.Switch(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "beta"})
  1772  	c.Assert(err, IsNil)
  1773  }
  1774  
  1775  func (s *snapmgrTestSuite) TestDisableTasks(c *C) {
  1776  	s.state.Lock()
  1777  	defer s.state.Unlock()
  1778  
  1779  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1780  		Sequence: []*snap.SideInfo{
  1781  			{RealName: "some-snap", Revision: snap.R(11)},
  1782  		},
  1783  		Current: snap.R(11),
  1784  		Active:  true,
  1785  	})
  1786  
  1787  	ts, err := snapstate.Disable(s.state, "some-snap")
  1788  	c.Assert(err, IsNil)
  1789  
  1790  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  1791  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
  1792  		"stop-snap-services",
  1793  		"remove-aliases",
  1794  		"unlink-snap",
  1795  		"remove-profiles",
  1796  	})
  1797  	verifyStopReason(c, ts, "disable")
  1798  }
  1799  
  1800  func (s *snapmgrTestSuite) TestEnableConflict(c *C) {
  1801  	s.state.Lock()
  1802  	defer s.state.Unlock()
  1803  
  1804  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1805  		Sequence: []*snap.SideInfo{
  1806  			{RealName: "some-snap", Revision: snap.R(11)},
  1807  		},
  1808  		Current: snap.R(11),
  1809  		Active:  false,
  1810  	})
  1811  
  1812  	ts, err := snapstate.Enable(s.state, "some-snap")
  1813  	c.Assert(err, IsNil)
  1814  	// need a change to make the tasks visible
  1815  	s.state.NewChange("enable", "...").AddAll(ts)
  1816  
  1817  	_, err = snapstate.Enable(s.state, "some-snap")
  1818  	c.Assert(err, ErrorMatches, `snap "some-snap" has "enable" change in progress`)
  1819  }
  1820  
  1821  func (s *snapmgrTestSuite) TestDisableConflict(c *C) {
  1822  	s.state.Lock()
  1823  	defer s.state.Unlock()
  1824  
  1825  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1826  		Sequence: []*snap.SideInfo{
  1827  			{RealName: "some-snap", Revision: snap.R(11)},
  1828  		},
  1829  		Current: snap.R(11),
  1830  		Active:  true,
  1831  	})
  1832  
  1833  	ts, err := snapstate.Disable(s.state, "some-snap")
  1834  	c.Assert(err, IsNil)
  1835  	// need a change to make the tasks visible
  1836  	s.state.NewChange("install", "...").AddAll(ts)
  1837  
  1838  	_, err = snapstate.Disable(s.state, "some-snap")
  1839  	c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`)
  1840  }
  1841  
  1842  func (s *snapmgrTestSuite) TestDoInstallWithSlots(c *C) {
  1843  	s.state.Lock()
  1844  	defer s.state.Unlock()
  1845  
  1846  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
  1847  
  1848  	ts, err := snapstate.Install(context.Background(), s.state, "snap-content-slot", nil, 0, snapstate.Flags{})
  1849  	c.Assert(err, IsNil)
  1850  
  1851  	var snapsup snapstate.SnapSetup
  1852  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  1853  	c.Assert(err, IsNil)
  1854  
  1855  	c.Check(snapsup.PlugsOnly, Equals, false)
  1856  }
  1857  
  1858  func (s *snapmgrTestSuite) TestDoUpdateHadSlots(c *C) {
  1859  	s.state.Lock()
  1860  	defer s.state.Unlock()
  1861  
  1862  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1863  		Active: true,
  1864  		Sequence: []*snap.SideInfo{
  1865  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  1866  		},
  1867  		Current:  snap.R(4),
  1868  		SnapType: "app",
  1869  	})
  1870  
  1871  	snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
  1872  		if name != "some-snap" {
  1873  			return s.fakeBackend.ReadInfo(name, si)
  1874  		}
  1875  
  1876  		info := &snap.Info{
  1877  			SideInfo: *si,
  1878  			SnapType: snap.TypeApp,
  1879  		}
  1880  		info.Slots = map[string]*snap.SlotInfo{
  1881  			"some-slot": {
  1882  				Snap:      info,
  1883  				Name:      "shared-content",
  1884  				Interface: "content",
  1885  				Attrs: map[string]interface{}{
  1886  					"content": "shared-content",
  1887  				},
  1888  			},
  1889  		}
  1890  		return info, nil
  1891  	})
  1892  
  1893  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{})
  1894  	c.Assert(err, IsNil)
  1895  
  1896  	var snapsup snapstate.SnapSetup
  1897  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  1898  	c.Assert(err, IsNil)
  1899  
  1900  	c.Check(snapsup.PlugsOnly, Equals, false)
  1901  }
  1902  
  1903  func (s *snapmgrTestSuite) TestDoInstallChannelDefault(c *C) {
  1904  	s.state.Lock()
  1905  	defer s.state.Unlock()
  1906  
  1907  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
  1908  	c.Assert(err, IsNil)
  1909  
  1910  	var snapsup snapstate.SnapSetup
  1911  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  1912  	c.Assert(err, IsNil)
  1913  
  1914  	c.Check(snapsup.Channel, Equals, "stable")
  1915  }
  1916  
  1917  func (s *snapmgrTestSuite) TestInstallRevision(c *C) {
  1918  	s.state.Lock()
  1919  	defer s.state.Unlock()
  1920  
  1921  	opts := &snapstate.RevisionOptions{Revision: snap.R(7)}
  1922  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{})
  1923  	c.Assert(err, IsNil)
  1924  
  1925  	var snapsup snapstate.SnapSetup
  1926  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  1927  	c.Assert(err, IsNil)
  1928  
  1929  	c.Check(snapsup.Revision(), Equals, snap.R(7))
  1930  }
  1931  
  1932  func (s *snapmgrTestSuite) TestInstallTooEarly(c *C) {
  1933  	s.state.Lock()
  1934  	defer s.state.Unlock()
  1935  
  1936  	s.state.Set("seeded", nil)
  1937  
  1938  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
  1939  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  1940  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
  1941  }
  1942  
  1943  func (s *snapmgrTestSuite) TestInstallConflict(c *C) {
  1944  	s.state.Lock()
  1945  	defer s.state.Unlock()
  1946  
  1947  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
  1948  	c.Assert(err, IsNil)
  1949  	// need a change to make the tasks visible
  1950  	s.state.NewChange("install", "...").AddAll(ts)
  1951  
  1952  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
  1953  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  1954  	c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`)
  1955  }
  1956  
  1957  func (s *snapmgrTestSuite) TestInstallAliasConflict(c *C) {
  1958  	s.state.Lock()
  1959  	defer s.state.Unlock()
  1960  
  1961  	snapstate.Set(s.state, "otherfoosnap", &snapstate.SnapState{
  1962  		Sequence: []*snap.SideInfo{
  1963  			{RealName: "otherfoosnap", Revision: snap.R(30)},
  1964  		},
  1965  		Current: snap.R(30),
  1966  		Active:  true,
  1967  		Aliases: map[string]*snapstate.AliasTarget{
  1968  			"foo.bar": {Manual: "bar"},
  1969  		},
  1970  		SnapType: "app",
  1971  	})
  1972  
  1973  	_, err := snapstate.Install(context.Background(), s.state, "foo", nil, 0, snapstate.Flags{})
  1974  	c.Assert(err, ErrorMatches, `snap "foo" command namespace conflicts with alias "foo\.bar" for "otherfoosnap" snap`)
  1975  }
  1976  
  1977  func (s *snapmgrTestSuite) TestInstallStrictIgnoresClassic(c *C) {
  1978  	restore := maybeMockClassicSupport(c)
  1979  	defer restore()
  1980  
  1981  	s.state.Lock()
  1982  	defer s.state.Unlock()
  1983  
  1984  	opts := &snapstate.RevisionOptions{Channel: "channel-for-strict"}
  1985  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true})
  1986  	c.Assert(err, IsNil)
  1987  
  1988  	c.Assert(err, IsNil)
  1989  
  1990  	chg := s.state.NewChange("install", "install snap")
  1991  	chg.AddAll(ts)
  1992  
  1993  	s.state.Unlock()
  1994  	defer s.se.Stop()
  1995  	s.settle(c)
  1996  	s.state.Lock()
  1997  
  1998  	c.Assert(chg.Err(), IsNil)
  1999  	c.Assert(chg.IsReady(), Equals, true)
  2000  
  2001  	// verify snap is *not* classic
  2002  	var snapst snapstate.SnapState
  2003  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2004  	c.Assert(err, IsNil)
  2005  	c.Check(snapst.Channel, Equals, "channel-for-strict")
  2006  	c.Check(snapst.Classic, Equals, false)
  2007  }
  2008  
  2009  // A sneakyStore changes the state when called
  2010  type sneakyStore struct {
  2011  	*fakeStore
  2012  	state *state.State
  2013  }
  2014  
  2015  func (s sneakyStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, user *auth.UserState, opts *store.RefreshOptions) ([]*snap.Info, error) {
  2016  	s.state.Lock()
  2017  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2018  		Active:   true,
  2019  		Channel:  "edge",
  2020  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}},
  2021  		Current:  snap.R(1),
  2022  		SnapType: "app",
  2023  	})
  2024  	s.state.Unlock()
  2025  	return s.fakeStore.SnapAction(ctx, currentSnaps, actions, user, opts)
  2026  }
  2027  
  2028  func (s *snapmgrTestSuite) TestInstallStateConflict(c *C) {
  2029  	s.state.Lock()
  2030  	defer s.state.Unlock()
  2031  
  2032  	snapstate.ReplaceStore(s.state, sneakyStore{fakeStore: s.fakeStore, state: s.state})
  2033  
  2034  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
  2035  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  2036  	c.Assert(err, ErrorMatches, `snap "some-snap" has changes in progress`)
  2037  }
  2038  
  2039  func (s *snapmgrTestSuite) TestInstallPathTooEarly(c *C) {
  2040  	s.state.Lock()
  2041  	defer s.state.Unlock()
  2042  
  2043  	r := snapstatetest.MockDeviceModel(nil)
  2044  	defer r()
  2045  
  2046  	mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0")
  2047  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{})
  2048  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  2049  	c.Assert(err, ErrorMatches, `too early for operation, device model not yet acknowledged`)
  2050  
  2051  }
  2052  
  2053  func (s *snapmgrTestSuite) TestInstallPathConflict(c *C) {
  2054  	s.state.Lock()
  2055  	defer s.state.Unlock()
  2056  
  2057  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
  2058  	c.Assert(err, IsNil)
  2059  	// need a change to make the tasks visible
  2060  	s.state.NewChange("install", "...").AddAll(ts)
  2061  
  2062  	mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0")
  2063  	_, _, err = snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{})
  2064  	c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`)
  2065  }
  2066  
  2067  func (s *snapmgrTestSuite) TestInstallPathMissingName(c *C) {
  2068  	s.state.Lock()
  2069  	defer s.state.Unlock()
  2070  
  2071  	mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0")
  2072  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{}, mockSnap, "", "", snapstate.Flags{})
  2073  	c.Assert(err, ErrorMatches, fmt.Sprintf(`internal error: snap name to install %q not provided`, mockSnap))
  2074  }
  2075  
  2076  func (s *snapmgrTestSuite) TestInstallPathSnapIDRevisionUnset(c *C) {
  2077  	s.state.Lock()
  2078  	defer s.state.Unlock()
  2079  
  2080  	mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0")
  2081  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "snapididid"}, mockSnap, "", "", snapstate.Flags{})
  2082  	c.Assert(err, ErrorMatches, fmt.Sprintf(`internal error: snap id set to install %q but revision is unset`, mockSnap))
  2083  }
  2084  
  2085  func (s *snapmgrTestSuite) TestInstallPathValidateFlags(c *C) {
  2086  	s.state.Lock()
  2087  	defer s.state.Unlock()
  2088  
  2089  	mockSnap := makeTestSnap(c, `name: some-snap
  2090  version: 1.0
  2091  confinement: devmode
  2092  `)
  2093  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{})
  2094  	c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`)
  2095  }
  2096  
  2097  func (s *snapmgrTestSuite) TestInstallPathStrictIgnoresClassic(c *C) {
  2098  	restore := maybeMockClassicSupport(c)
  2099  	defer restore()
  2100  
  2101  	s.state.Lock()
  2102  	defer s.state.Unlock()
  2103  
  2104  	mockSnap := makeTestSnap(c, `name: some-snap
  2105  version: 1.0
  2106  confinement: strict
  2107  `)
  2108  
  2109  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{Classic: true})
  2110  	c.Assert(err, IsNil)
  2111  
  2112  	c.Assert(err, IsNil)
  2113  
  2114  	chg := s.state.NewChange("install", "install snap")
  2115  	chg.AddAll(ts)
  2116  
  2117  	s.state.Unlock()
  2118  	defer s.se.Stop()
  2119  	s.settle(c)
  2120  	s.state.Lock()
  2121  
  2122  	c.Assert(chg.Err(), IsNil)
  2123  	c.Assert(chg.IsReady(), Equals, true)
  2124  
  2125  	// verify snap is *not* classic
  2126  	var snapst snapstate.SnapState
  2127  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2128  	c.Assert(err, IsNil)
  2129  	c.Check(snapst.Classic, Equals, false)
  2130  }
  2131  
  2132  func (s *snapmgrTestSuite) TestParallelInstanceInstallNotAllowed(c *C) {
  2133  	s.state.Lock()
  2134  	defer s.state.Unlock()
  2135  
  2136  	snapstate.ReplaceStore(s.state, sneakyStore{fakeStore: s.fakeStore, state: s.state})
  2137  
  2138  	tr := config.NewTransaction(s.state)
  2139  	tr.Set("core", "experimental.parallel-instances", true)
  2140  	tr.Commit()
  2141  
  2142  	_, err := snapstate.Install(context.Background(), s.state, "core_foo", nil, 0, snapstate.Flags{})
  2143  	c.Check(err, ErrorMatches, `cannot install snap of type os as "core_foo"`)
  2144  
  2145  	_, err = snapstate.Install(context.Background(), s.state, "some-base_foo", nil, 0, snapstate.Flags{})
  2146  	c.Check(err, ErrorMatches, `cannot install snap of type base as "some-base_foo"`)
  2147  
  2148  	_, err = snapstate.Install(context.Background(), s.state, "some-gadget_foo", nil, 0, snapstate.Flags{})
  2149  	c.Check(err, ErrorMatches, `cannot install snap of type gadget as "some-gadget_foo"`)
  2150  
  2151  	_, err = snapstate.Install(context.Background(), s.state, "some-kernel_foo", nil, 0, snapstate.Flags{})
  2152  	c.Check(err, ErrorMatches, `cannot install snap of type kernel as "some-kernel_foo"`)
  2153  
  2154  	_, err = snapstate.Install(context.Background(), s.state, "some-snapd_foo", nil, 0, snapstate.Flags{})
  2155  	c.Check(err, ErrorMatches, `cannot install snap of type snapd as "some-snapd_foo"`)
  2156  }
  2157  
  2158  func (s *snapmgrTestSuite) TestInstallPathFailsEarlyOnEpochMismatch(c *C) {
  2159  	s.state.Lock()
  2160  	defer s.state.Unlock()
  2161  
  2162  	// have epoch 1* installed
  2163  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2164  		Active:   true,
  2165  		Channel:  "edge",
  2166  		Sequence: []*snap.SideInfo{{RealName: "some-snap", Revision: snap.R(7)}},
  2167  		Current:  snap.R(7),
  2168  	})
  2169  
  2170  	// try to install epoch 42
  2171  	mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0\nepoch: 42\n")
  2172  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{})
  2173  	c.Assert(err, ErrorMatches, `cannot refresh "some-snap" to local snap with epoch 42, because it can't read the current epoch of 1\*`)
  2174  }
  2175  
  2176  func (s *snapmgrTestSuite) TestUpdateTasksPropagatesErrors(c *C) {
  2177  	s.state.Lock()
  2178  	defer s.state.Unlock()
  2179  
  2180  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2181  		Active:   true,
  2182  		Channel:  "edge",
  2183  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "fakestore-please-error-on-refresh", Revision: snap.R(7)}},
  2184  		Current:  snap.R(7),
  2185  	})
  2186  
  2187  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2188  	c.Assert(err, ErrorMatches, `failing as requested`)
  2189  }
  2190  
  2191  func (s *snapmgrTestSuite) TestUpdateTasks(c *C) {
  2192  	s.state.Lock()
  2193  	defer s.state.Unlock()
  2194  
  2195  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2196  		Active:   true,
  2197  		Channel:  "edge",
  2198  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  2199  		Current:  snap.R(7),
  2200  		SnapType: "app",
  2201  	})
  2202  
  2203  	validateCalled := false
  2204  	happyValidateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  2205  		validateCalled = true
  2206  		return refreshes, nil
  2207  	}
  2208  	// hook it up
  2209  	snapstate.ValidateRefreshes = happyValidateRefreshes
  2210  
  2211  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2212  	c.Assert(err, IsNil)
  2213  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  2214  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  2215  
  2216  	c.Check(validateCalled, Equals, true)
  2217  
  2218  	var snapsup snapstate.SnapSetup
  2219  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  2220  	c.Assert(err, IsNil)
  2221  
  2222  	c.Check(snapsup.Channel, Equals, "some-channel")
  2223  }
  2224  
  2225  func (s *snapmgrTestSuite) TestUpdateWithDeviceContext(c *C) {
  2226  	s.state.Lock()
  2227  	defer s.state.Unlock()
  2228  
  2229  	// unset the global store, it will need to come via the device context
  2230  	snapstate.ReplaceStore(s.state, nil)
  2231  
  2232  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  2233  		DeviceModel: DefaultModel(),
  2234  		CtxStore:    s.fakeStore,
  2235  	}
  2236  
  2237  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2238  		Active:   true,
  2239  		Channel:  "edge",
  2240  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  2241  		Current:  snap.R(7),
  2242  		SnapType: "app",
  2243  	})
  2244  
  2245  	validateCalled := false
  2246  	happyValidateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx1 snapstate.DeviceContext) ([]*snap.Info, error) {
  2247  		c.Check(deviceCtx1, Equals, deviceCtx)
  2248  		validateCalled = true
  2249  		return refreshes, nil
  2250  	}
  2251  	// hook it up
  2252  	snapstate.ValidateRefreshes = happyValidateRefreshes
  2253  
  2254  	ts, err := snapstate.UpdateWithDeviceContext(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}, deviceCtx, "")
  2255  	c.Assert(err, IsNil)
  2256  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  2257  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  2258  
  2259  	c.Check(validateCalled, Equals, true)
  2260  }
  2261  
  2262  func (s *snapmgrTestSuite) TestUpdateWithDeviceContextToRevision(c *C) {
  2263  	s.state.Lock()
  2264  	defer s.state.Unlock()
  2265  
  2266  	// unset the global store, it will need to come via the device context
  2267  	snapstate.ReplaceStore(s.state, nil)
  2268  
  2269  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  2270  		DeviceModel: DefaultModel(),
  2271  		CtxStore:    s.fakeStore,
  2272  	}
  2273  
  2274  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2275  		Active: true,
  2276  		Sequence: []*snap.SideInfo{
  2277  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  2278  		},
  2279  		Current:  snap.R(5),
  2280  		SnapType: "app",
  2281  		UserID:   1,
  2282  	})
  2283  
  2284  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(11)}
  2285  	ts, err := snapstate.UpdateWithDeviceContext(s.state, "some-snap", opts, 0, snapstate.Flags{}, deviceCtx, "")
  2286  	c.Assert(err, IsNil)
  2287  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  2288  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  2289  }
  2290  
  2291  func (s *snapmgrTestSuite) TestUpdateTasksCoreSetsIgnoreOnConfigure(c *C) {
  2292  	s.state.Lock()
  2293  	defer s.state.Unlock()
  2294  
  2295  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  2296  		Active:   true,
  2297  		Channel:  "edge",
  2298  		Sequence: []*snap.SideInfo{{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(7)}},
  2299  		Current:  snap.R(7),
  2300  		SnapType: "os",
  2301  	})
  2302  
  2303  	oldConfigure := snapstate.Configure
  2304  	defer func() { snapstate.Configure = oldConfigure }()
  2305  
  2306  	var configureFlags int
  2307  	snapstate.Configure = func(st *state.State, snapName string, patch map[string]interface{}, flags int) *state.TaskSet {
  2308  		configureFlags = flags
  2309  		return state.NewTaskSet()
  2310  	}
  2311  
  2312  	_, err := snapstate.Update(s.state, "core", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2313  	c.Assert(err, IsNil)
  2314  
  2315  	// ensure the core snap sets the "ignore-hook-error" flag
  2316  	c.Check(configureFlags&snapstate.IgnoreHookError, Equals, 1)
  2317  }
  2318  
  2319  func (s *snapmgrTestSuite) TestUpdateDevModeConfinementFiltering(c *C) {
  2320  	restore := maybeMockClassicSupport(c)
  2321  	defer restore()
  2322  
  2323  	s.state.Lock()
  2324  	defer s.state.Unlock()
  2325  
  2326  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2327  		Active:   true,
  2328  		Channel:  "channel-for-devmode",
  2329  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  2330  		Current:  snap.R(7),
  2331  		SnapType: "app",
  2332  	})
  2333  
  2334  	// updated snap is devmode, refresh without --devmode, do nothing
  2335  	// TODO: better error message here
  2336  	_, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  2337  	c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`)
  2338  
  2339  	// updated snap is devmode, refresh with --devmode
  2340  	_, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{DevMode: true})
  2341  	c.Assert(err, IsNil)
  2342  }
  2343  
  2344  func (s *snapmgrTestSuite) TestUpdateClassicConfinementFiltering(c *C) {
  2345  	restore := maybeMockClassicSupport(c)
  2346  	defer restore()
  2347  
  2348  	s.state.Lock()
  2349  	defer s.state.Unlock()
  2350  
  2351  	snapstate.Set(s.state, "some-snap-now-classic", &snapstate.SnapState{
  2352  		Active:   true,
  2353  		Sequence: []*snap.SideInfo{{RealName: "some-snap-now-classic", SnapID: "some-snap-now-classic-id", Revision: snap.R(7)}},
  2354  		Current:  snap.R(7),
  2355  		SnapType: "app",
  2356  	})
  2357  
  2358  	// updated snap is classic, refresh without --classic, do nothing
  2359  	// TODO: better error message here
  2360  	_, err := snapstate.Update(s.state, "some-snap-now-classic", nil, s.user.ID, snapstate.Flags{})
  2361  	c.Assert(err, ErrorMatches, `.* requires classic confinement`)
  2362  
  2363  	// updated snap is classic, refresh with --classic
  2364  	ts, err := snapstate.Update(s.state, "some-snap-now-classic", nil, s.user.ID, snapstate.Flags{Classic: true})
  2365  	c.Assert(err, IsNil)
  2366  
  2367  	chg := s.state.NewChange("refresh", "refresh snap")
  2368  	chg.AddAll(ts)
  2369  
  2370  	s.state.Unlock()
  2371  	defer s.se.Stop()
  2372  	s.settle(c)
  2373  	s.state.Lock()
  2374  
  2375  	c.Assert(chg.Err(), IsNil)
  2376  	c.Assert(chg.IsReady(), Equals, true)
  2377  
  2378  	// verify snap is in classic
  2379  	var snapst snapstate.SnapState
  2380  	err = snapstate.Get(s.state, "some-snap-now-classic", &snapst)
  2381  	c.Assert(err, IsNil)
  2382  	c.Check(snapst.Classic, Equals, true)
  2383  }
  2384  
  2385  func (s *snapmgrTestSuite) TestUpdateClassicFromClassic(c *C) {
  2386  	restore := maybeMockClassicSupport(c)
  2387  	defer restore()
  2388  
  2389  	s.state.Lock()
  2390  	defer s.state.Unlock()
  2391  
  2392  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2393  		Active:   true,
  2394  		Channel:  "channel-for-classic",
  2395  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  2396  		Current:  snap.R(7),
  2397  		SnapType: "app",
  2398  		Flags:    snapstate.Flags{Classic: true},
  2399  	})
  2400  
  2401  	// snap installed with --classic, update needs classic, refresh with --classic works
  2402  	ts, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{Classic: true})
  2403  	c.Assert(err, IsNil)
  2404  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  2405  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
  2406  	c.Assert(err, IsNil)
  2407  	c.Check(snapsup.Flags.Classic, Equals, true)
  2408  
  2409  	// devmode overrides the snapsetup classic flag
  2410  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{DevMode: true})
  2411  	c.Assert(err, IsNil)
  2412  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  2413  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  2414  	c.Assert(err, IsNil)
  2415  	c.Check(snapsup.Flags.Classic, Equals, false)
  2416  
  2417  	// jailmode overrides it too (you need to provide both)
  2418  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{JailMode: true})
  2419  	c.Assert(err, IsNil)
  2420  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  2421  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  2422  	c.Assert(err, IsNil)
  2423  	c.Check(snapsup.Flags.Classic, Equals, false)
  2424  
  2425  	// jailmode and classic together gets you both
  2426  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{JailMode: true, Classic: true})
  2427  	c.Assert(err, IsNil)
  2428  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  2429  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  2430  	c.Assert(err, IsNil)
  2431  	c.Check(snapsup.Flags.Classic, Equals, true)
  2432  
  2433  	// snap installed with --classic, update needs classic, refresh without --classic works
  2434  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  2435  	c.Assert(err, IsNil)
  2436  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  2437  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  2438  	c.Assert(err, IsNil)
  2439  	c.Check(snapsup.Flags.Classic, Equals, true)
  2440  
  2441  	chg := s.state.NewChange("refresh", "refresh snap")
  2442  	chg.AddAll(ts)
  2443  
  2444  	s.state.Unlock()
  2445  	defer s.se.Stop()
  2446  	s.settle(c)
  2447  	s.state.Lock()
  2448  
  2449  	// verify snap is in classic
  2450  	var snapst snapstate.SnapState
  2451  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2452  	c.Assert(err, IsNil)
  2453  	c.Check(snapst.Classic, Equals, true)
  2454  }
  2455  
  2456  func (s *snapmgrTestSuite) TestUpdateStrictFromClassic(c *C) {
  2457  	restore := maybeMockClassicSupport(c)
  2458  	defer restore()
  2459  
  2460  	s.state.Lock()
  2461  	defer s.state.Unlock()
  2462  
  2463  	snapstate.Set(s.state, "some-snap-was-classic", &snapstate.SnapState{
  2464  		Active:   true,
  2465  		Channel:  "channel",
  2466  		Sequence: []*snap.SideInfo{{RealName: "some-snap-was-classic", SnapID: "some-snap-was-classic-id", Revision: snap.R(7)}},
  2467  		Current:  snap.R(7),
  2468  		SnapType: "app",
  2469  		Flags:    snapstate.Flags{Classic: true},
  2470  	})
  2471  
  2472  	// snap installed with --classic, update does not need classic, refresh works without --classic
  2473  	_, err := snapstate.Update(s.state, "some-snap-was-classic", nil, s.user.ID, snapstate.Flags{})
  2474  	c.Assert(err, IsNil)
  2475  
  2476  	// snap installed with --classic, update does not need classic, refresh works with --classic
  2477  	_, err = snapstate.Update(s.state, "some-snap-was-classic", nil, s.user.ID, snapstate.Flags{Classic: true})
  2478  	c.Assert(err, IsNil)
  2479  }
  2480  
  2481  func (s *snapmgrTestSuite) TestUpdateChannelFallback(c *C) {
  2482  	s.state.Lock()
  2483  	defer s.state.Unlock()
  2484  
  2485  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2486  		Active:   true,
  2487  		Channel:  "edge",
  2488  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  2489  		Current:  snap.R(7),
  2490  		SnapType: "app",
  2491  	})
  2492  
  2493  	ts, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  2494  	c.Assert(err, IsNil)
  2495  
  2496  	var snapsup snapstate.SnapSetup
  2497  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  2498  	c.Assert(err, IsNil)
  2499  
  2500  	c.Check(snapsup.Channel, Equals, "edge")
  2501  }
  2502  
  2503  func (s *snapmgrTestSuite) TestUpdateTooEarly(c *C) {
  2504  	s.state.Lock()
  2505  	defer s.state.Unlock()
  2506  
  2507  	s.state.Set("seeded", nil)
  2508  
  2509  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2510  		Active:   true,
  2511  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  2512  		Current:  snap.R(7),
  2513  		SnapType: "app",
  2514  	})
  2515  
  2516  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2517  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  2518  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
  2519  }
  2520  
  2521  func (s *snapmgrTestSuite) TestUpdateConflict(c *C) {
  2522  	s.state.Lock()
  2523  	defer s.state.Unlock()
  2524  
  2525  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2526  		Active:   true,
  2527  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  2528  		Current:  snap.R(7),
  2529  		SnapType: "app",
  2530  	})
  2531  
  2532  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2533  	c.Assert(err, IsNil)
  2534  	// need a change to make the tasks visible
  2535  	s.state.NewChange("refresh", "...").AddAll(ts)
  2536  
  2537  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2538  	c.Assert(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`)
  2539  }
  2540  
  2541  func (s *snapmgrTestSuite) TestRemoveTasks(c *C) {
  2542  	s.state.Lock()
  2543  	defer s.state.Unlock()
  2544  
  2545  	snapstate.Set(s.state, "foo", &snapstate.SnapState{
  2546  		Active: true,
  2547  		Sequence: []*snap.SideInfo{
  2548  			{RealName: "foo", Revision: snap.R(11)},
  2549  		},
  2550  		Current:  snap.R(11),
  2551  		SnapType: "app",
  2552  	})
  2553  
  2554  	ts, err := snapstate.Remove(s.state, "foo", snap.R(0), nil)
  2555  	c.Assert(err, IsNil)
  2556  
  2557  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  2558  	verifyRemoveTasks(c, ts)
  2559  }
  2560  
  2561  func (s *snapmgrTestSuite) TestRemoveTasksAutoSnapshotDisabled(c *C) {
  2562  	snapstate.AutomaticSnapshot = func(st *state.State, instanceName string) (ts *state.TaskSet, err error) {
  2563  		return nil, snapstate.ErrNothingToDo
  2564  	}
  2565  
  2566  	s.state.Lock()
  2567  	defer s.state.Unlock()
  2568  
  2569  	snapstate.Set(s.state, "foo", &snapstate.SnapState{
  2570  		Active: true,
  2571  		Sequence: []*snap.SideInfo{
  2572  			{RealName: "foo", Revision: snap.R(11)},
  2573  		},
  2574  		Current:  snap.R(11),
  2575  		SnapType: "app",
  2576  	})
  2577  
  2578  	ts, err := snapstate.Remove(s.state, "foo", snap.R(0), nil)
  2579  	c.Assert(err, IsNil)
  2580  
  2581  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
  2582  		"stop-snap-services",
  2583  		"run-hook[remove]",
  2584  		"auto-disconnect",
  2585  		"remove-aliases",
  2586  		"unlink-snap",
  2587  		"remove-profiles",
  2588  		"clear-snap",
  2589  		"discard-snap",
  2590  	})
  2591  }
  2592  
  2593  func (s *snapmgrTestSuite) TestRemoveTasksAutoSnapshotDisabledByPurgeFlag(c *C) {
  2594  	s.state.Lock()
  2595  	defer s.state.Unlock()
  2596  
  2597  	snapstate.Set(s.state, "foo", &snapstate.SnapState{
  2598  		Active: true,
  2599  		Sequence: []*snap.SideInfo{
  2600  			{RealName: "foo", Revision: snap.R(11)},
  2601  		},
  2602  		Current:  snap.R(11),
  2603  		SnapType: "app",
  2604  	})
  2605  
  2606  	ts, err := snapstate.Remove(s.state, "foo", snap.R(0), &snapstate.RemoveFlags{Purge: true})
  2607  	c.Assert(err, IsNil)
  2608  
  2609  	c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
  2610  		"stop-snap-services",
  2611  		"run-hook[remove]",
  2612  		"auto-disconnect",
  2613  		"remove-aliases",
  2614  		"unlink-snap",
  2615  		"remove-profiles",
  2616  		"clear-snap",
  2617  		"discard-snap",
  2618  	})
  2619  }
  2620  
  2621  func (s *snapmgrTestSuite) TestRemoveHookNotExecutedIfNotLastRevison(c *C) {
  2622  	s.state.Lock()
  2623  	defer s.state.Unlock()
  2624  
  2625  	snapstate.Set(s.state, "foo", &snapstate.SnapState{
  2626  		Active: true,
  2627  		Sequence: []*snap.SideInfo{
  2628  			{RealName: "foo", Revision: snap.R(11)},
  2629  			{RealName: "foo", Revision: snap.R(12)},
  2630  		},
  2631  		Current: snap.R(12),
  2632  	})
  2633  
  2634  	ts, err := snapstate.Remove(s.state, "foo", snap.R(11), nil)
  2635  	c.Assert(err, IsNil)
  2636  
  2637  	runHooks := tasksWithKind(ts, "run-hook")
  2638  	// no 'remove' hook task
  2639  	c.Assert(runHooks, HasLen, 0)
  2640  }
  2641  
  2642  func (s *snapmgrTestSuite) TestRemoveConflict(c *C) {
  2643  	s.state.Lock()
  2644  	defer s.state.Unlock()
  2645  
  2646  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2647  		Active:   true,
  2648  		Sequence: []*snap.SideInfo{{RealName: "some-snap", Revision: snap.R(11)}},
  2649  		Current:  snap.R(11),
  2650  	})
  2651  
  2652  	ts, err := snapstate.Remove(s.state, "some-snap", snap.R(0), nil)
  2653  	c.Assert(err, IsNil)
  2654  	// need a change to make the tasks visible
  2655  	s.state.NewChange("remove", "...").AddAll(ts)
  2656  
  2657  	_, err = snapstate.Remove(s.state, "some-snap", snap.R(0), nil)
  2658  	c.Assert(err, ErrorMatches, `snap "some-snap" has "remove" change in progress`)
  2659  }
  2660  
  2661  func (s *snapmgrTestSuite) TestInstallRunThrough(c *C) {
  2662  	s.state.Lock()
  2663  	defer s.state.Unlock()
  2664  
  2665  	// we start without the auxiliary store info
  2666  	c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FileAbsent)
  2667  
  2668  	chg := s.state.NewChange("install", "install a snap")
  2669  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  2670  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2671  	c.Assert(err, IsNil)
  2672  	chg.AddAll(ts)
  2673  
  2674  	s.state.Unlock()
  2675  	defer s.se.Stop()
  2676  	s.settle(c)
  2677  	s.state.Lock()
  2678  
  2679  	// ensure all our tasks ran
  2680  	c.Assert(chg.Err(), IsNil)
  2681  	c.Assert(chg.IsReady(), Equals, true)
  2682  	c.Check(snapstate.Installing(s.state), Equals, false)
  2683  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  2684  		macaroon: s.user.StoreMacaroon,
  2685  		name:     "some-snap",
  2686  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2687  	}})
  2688  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
  2689  	expected := fakeOps{
  2690  		{
  2691  			op:     "storesvc-snap-action",
  2692  			userID: 1,
  2693  		},
  2694  		{
  2695  			op: "storesvc-snap-action:action",
  2696  			action: store.SnapAction{
  2697  				Action:       "install",
  2698  				InstanceName: "some-snap",
  2699  				Channel:      "some-channel",
  2700  			},
  2701  			revno:  snap.R(11),
  2702  			userID: 1,
  2703  		},
  2704  		{
  2705  			op:   "storesvc-download",
  2706  			name: "some-snap",
  2707  		},
  2708  		{
  2709  			op:    "validate-snap:Doing",
  2710  			name:  "some-snap",
  2711  			revno: snap.R(11),
  2712  		},
  2713  		{
  2714  			op:  "current",
  2715  			old: "<no-current>",
  2716  		},
  2717  		{
  2718  			op:   "open-snap-file",
  2719  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2720  			sinfo: snap.SideInfo{
  2721  				RealName: "some-snap",
  2722  				SnapID:   "some-snap-id",
  2723  				Channel:  "some-channel",
  2724  				Revision: snap.R(11),
  2725  			},
  2726  		},
  2727  		{
  2728  			op:    "setup-snap",
  2729  			name:  "some-snap",
  2730  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2731  			revno: snap.R(11),
  2732  		},
  2733  		{
  2734  			op:   "copy-data",
  2735  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2736  			old:  "<no-old>",
  2737  		},
  2738  		{
  2739  			op:    "setup-profiles:Doing",
  2740  			name:  "some-snap",
  2741  			revno: snap.R(11),
  2742  		},
  2743  		{
  2744  			op: "candidate",
  2745  			sinfo: snap.SideInfo{
  2746  				RealName: "some-snap",
  2747  				SnapID:   "some-snap-id",
  2748  				Channel:  "some-channel",
  2749  				Revision: snap.R(11),
  2750  			},
  2751  		},
  2752  		{
  2753  			op:   "link-snap",
  2754  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2755  		},
  2756  		{
  2757  			op:    "auto-connect:Doing",
  2758  			name:  "some-snap",
  2759  			revno: snap.R(11),
  2760  		},
  2761  		{
  2762  			op: "update-aliases",
  2763  		},
  2764  		{
  2765  			op:    "cleanup-trash",
  2766  			name:  "some-snap",
  2767  			revno: snap.R(11),
  2768  		},
  2769  	}
  2770  	// start with an easier-to-read error if this fails:
  2771  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2772  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2773  
  2774  	// check progress
  2775  	ta := ts.Tasks()
  2776  	task := ta[1]
  2777  	_, cur, total := task.Progress()
  2778  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  2779  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  2780  	c.Check(task.Summary(), Equals, `Download snap "some-snap" (11) from channel "some-channel"`)
  2781  
  2782  	// check link/start snap summary
  2783  	linkTask := ta[len(ta)-8]
  2784  	c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (11) available to the system`)
  2785  	startTask := ta[len(ta)-3]
  2786  	c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (11) services`)
  2787  
  2788  	// verify snap-setup in the task state
  2789  	var snapsup snapstate.SnapSetup
  2790  	err = task.Get("snap-setup", &snapsup)
  2791  	c.Assert(err, IsNil)
  2792  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  2793  		Channel:  "some-channel",
  2794  		UserID:   s.user.ID,
  2795  		SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2796  		DownloadInfo: &snap.DownloadInfo{
  2797  			DownloadURL: "https://some-server.com/some/path.snap",
  2798  		},
  2799  		SideInfo:  snapsup.SideInfo,
  2800  		Type:      snap.TypeApp,
  2801  		PlugsOnly: true,
  2802  	})
  2803  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  2804  		RealName: "some-snap",
  2805  		Channel:  "some-channel",
  2806  		Revision: snap.R(11),
  2807  		SnapID:   "some-snap-id",
  2808  	})
  2809  
  2810  	// verify snaps in the system state
  2811  	var snaps map[string]*snapstate.SnapState
  2812  	err = s.state.Get("snaps", &snaps)
  2813  	c.Assert(err, IsNil)
  2814  
  2815  	snapst := snaps["some-snap"]
  2816  	c.Assert(snapst, NotNil)
  2817  	c.Assert(snapst.Active, Equals, true)
  2818  	c.Assert(snapst.Channel, Equals, "some-channel")
  2819  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2820  		RealName: "some-snap",
  2821  		SnapID:   "some-snap-id",
  2822  		Channel:  "some-channel",
  2823  		Revision: snap.R(11),
  2824  	})
  2825  	c.Assert(snapst.Required, Equals, false)
  2826  
  2827  	// we end with the auxiliary store info
  2828  	c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FilePresent)
  2829  }
  2830  
  2831  func (s *snapmgrTestSuite) TestParallelInstanceInstallRunThrough(c *C) {
  2832  	s.state.Lock()
  2833  	defer s.state.Unlock()
  2834  
  2835  	tr := config.NewTransaction(s.state)
  2836  	tr.Set("core", "experimental.parallel-instances", true)
  2837  	tr.Commit()
  2838  
  2839  	chg := s.state.NewChange("install", "install a snap")
  2840  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  2841  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap_instance", opts, s.user.ID, snapstate.Flags{})
  2842  	c.Assert(err, IsNil)
  2843  	chg.AddAll(ts)
  2844  
  2845  	s.state.Unlock()
  2846  	s.settle(c)
  2847  	s.state.Lock()
  2848  
  2849  	// ensure all our tasks ran
  2850  	c.Assert(chg.Err(), IsNil)
  2851  	c.Assert(chg.IsReady(), Equals, true)
  2852  	c.Check(snapstate.Installing(s.state), Equals, false)
  2853  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  2854  		macaroon: s.user.StoreMacaroon,
  2855  		name:     "some-snap",
  2856  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"),
  2857  	}})
  2858  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
  2859  	expected := fakeOps{
  2860  		{
  2861  			op:     "storesvc-snap-action",
  2862  			userID: 1,
  2863  		},
  2864  		{
  2865  			op: "storesvc-snap-action:action",
  2866  			action: store.SnapAction{
  2867  				Action:       "install",
  2868  				InstanceName: "some-snap_instance",
  2869  				Channel:      "some-channel",
  2870  			},
  2871  			revno:  snap.R(11),
  2872  			userID: 1,
  2873  		},
  2874  		{
  2875  			op:   "storesvc-download",
  2876  			name: "some-snap",
  2877  		},
  2878  		{
  2879  			op:    "validate-snap:Doing",
  2880  			name:  "some-snap_instance",
  2881  			revno: snap.R(11),
  2882  		},
  2883  		{
  2884  			op:  "current",
  2885  			old: "<no-current>",
  2886  		},
  2887  		{
  2888  			op:   "open-snap-file",
  2889  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"),
  2890  			sinfo: snap.SideInfo{
  2891  				RealName: "some-snap",
  2892  				SnapID:   "some-snap-id",
  2893  				Channel:  "some-channel",
  2894  				Revision: snap.R(11),
  2895  			},
  2896  		},
  2897  		{
  2898  			op:    "setup-snap",
  2899  			name:  "some-snap_instance",
  2900  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"),
  2901  			revno: snap.R(11),
  2902  		},
  2903  		{
  2904  			op:   "copy-data",
  2905  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/11"),
  2906  			old:  "<no-old>",
  2907  		},
  2908  		{
  2909  			op:    "setup-profiles:Doing",
  2910  			name:  "some-snap_instance",
  2911  			revno: snap.R(11),
  2912  		},
  2913  		{
  2914  			op: "candidate",
  2915  			sinfo: snap.SideInfo{
  2916  				RealName: "some-snap",
  2917  				SnapID:   "some-snap-id",
  2918  				Channel:  "some-channel",
  2919  				Revision: snap.R(11),
  2920  			},
  2921  		},
  2922  		{
  2923  			op:   "link-snap",
  2924  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/11"),
  2925  		},
  2926  		{
  2927  			op:    "auto-connect:Doing",
  2928  			name:  "some-snap_instance",
  2929  			revno: snap.R(11),
  2930  		},
  2931  		{
  2932  			op: "update-aliases",
  2933  		},
  2934  		{
  2935  			op:    "cleanup-trash",
  2936  			name:  "some-snap_instance",
  2937  			revno: snap.R(11),
  2938  		},
  2939  	}
  2940  	// start with an easier-to-read error if this fails:
  2941  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2942  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2943  
  2944  	// check progress
  2945  	ta := ts.Tasks()
  2946  	task := ta[1]
  2947  	_, cur, total := task.Progress()
  2948  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  2949  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  2950  	c.Check(task.Summary(), Equals, `Download snap "some-snap_instance" (11) from channel "some-channel"`)
  2951  
  2952  	// check link/start snap summary
  2953  	linkTask := ta[len(ta)-8]
  2954  	c.Check(linkTask.Summary(), Equals, `Make snap "some-snap_instance" (11) available to the system`)
  2955  	startTask := ta[len(ta)-3]
  2956  	c.Check(startTask.Summary(), Equals, `Start snap "some-snap_instance" (11) services`)
  2957  
  2958  	// verify snap-setup in the task state
  2959  	var snapsup snapstate.SnapSetup
  2960  	err = task.Get("snap-setup", &snapsup)
  2961  	c.Assert(err, IsNil)
  2962  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  2963  		Channel:  "some-channel",
  2964  		UserID:   s.user.ID,
  2965  		SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"),
  2966  		DownloadInfo: &snap.DownloadInfo{
  2967  			DownloadURL: "https://some-server.com/some/path.snap",
  2968  		},
  2969  		SideInfo:    snapsup.SideInfo,
  2970  		Type:        snap.TypeApp,
  2971  		PlugsOnly:   true,
  2972  		InstanceKey: "instance",
  2973  	})
  2974  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  2975  		RealName: "some-snap",
  2976  		Channel:  "some-channel",
  2977  		Revision: snap.R(11),
  2978  		SnapID:   "some-snap-id",
  2979  	})
  2980  
  2981  	// verify snaps in the system state
  2982  	var snaps map[string]*snapstate.SnapState
  2983  	err = s.state.Get("snaps", &snaps)
  2984  	c.Assert(err, IsNil)
  2985  
  2986  	snapst := snaps["some-snap_instance"]
  2987  	c.Assert(snapst, NotNil)
  2988  	c.Assert(snapst.Active, Equals, true)
  2989  	c.Assert(snapst.Channel, Equals, "some-channel")
  2990  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2991  		RealName: "some-snap",
  2992  		SnapID:   "some-snap-id",
  2993  		Channel:  "some-channel",
  2994  		Revision: snap.R(11),
  2995  	})
  2996  	c.Assert(snapst.Required, Equals, false)
  2997  	c.Assert(snapst.InstanceKey, Equals, "instance")
  2998  
  2999  	runHooks := tasksWithKind(ts, "run-hook")
  3000  	c.Assert(taskKinds(runHooks), DeepEquals, []string{"run-hook[install]", "run-hook[configure]", "run-hook[check-health]"})
  3001  	for _, hookTask := range runHooks {
  3002  		c.Assert(hookTask.Kind(), Equals, "run-hook")
  3003  		var hooksup hookstate.HookSetup
  3004  		err = hookTask.Get("hook-setup", &hooksup)
  3005  		c.Assert(err, IsNil)
  3006  		c.Assert(hooksup.Snap, Equals, "some-snap_instance")
  3007  	}
  3008  }
  3009  
  3010  func (s *snapmgrTestSuite) TestInstallUndoRunThroughJustOneSnap(c *C) {
  3011  	s.state.Lock()
  3012  	defer s.state.Unlock()
  3013  
  3014  	chg := s.state.NewChange("install", "install a snap")
  3015  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  3016  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3017  	c.Assert(err, IsNil)
  3018  	chg.AddAll(ts)
  3019  
  3020  	tasks := ts.Tasks()
  3021  	last := tasks[len(tasks)-1]
  3022  	// sanity
  3023  	c.Assert(last.Lanes(), HasLen, 1)
  3024  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  3025  	terr.WaitFor(last)
  3026  	terr.JoinLane(last.Lanes()[0])
  3027  	chg.AddTask(terr)
  3028  
  3029  	s.state.Unlock()
  3030  	defer s.se.Stop()
  3031  	s.settle(c)
  3032  	s.state.Lock()
  3033  
  3034  	// ensure all our tasks ran
  3035  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  3036  		macaroon: s.user.StoreMacaroon,
  3037  		name:     "some-snap",
  3038  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  3039  	}})
  3040  	expected := fakeOps{
  3041  		{
  3042  			op:     "storesvc-snap-action",
  3043  			userID: 1,
  3044  		},
  3045  		{
  3046  			op: "storesvc-snap-action:action",
  3047  			action: store.SnapAction{
  3048  				Action:       "install",
  3049  				InstanceName: "some-snap",
  3050  				Channel:      "some-channel",
  3051  			},
  3052  			revno:  snap.R(11),
  3053  			userID: 1,
  3054  		},
  3055  		{
  3056  			op:   "storesvc-download",
  3057  			name: "some-snap",
  3058  		},
  3059  		{
  3060  			op:    "validate-snap:Doing",
  3061  			name:  "some-snap",
  3062  			revno: snap.R(11),
  3063  		},
  3064  		{
  3065  			op:  "current",
  3066  			old: "<no-current>",
  3067  		},
  3068  		{
  3069  			op:   "open-snap-file",
  3070  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  3071  			sinfo: snap.SideInfo{
  3072  				RealName: "some-snap",
  3073  				SnapID:   "some-snap-id",
  3074  				Channel:  "some-channel",
  3075  				Revision: snap.R(11),
  3076  			},
  3077  		},
  3078  		{
  3079  			op:    "setup-snap",
  3080  			name:  "some-snap",
  3081  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  3082  			revno: snap.R(11),
  3083  		},
  3084  		{
  3085  			op:   "copy-data",
  3086  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  3087  			old:  "<no-old>",
  3088  		},
  3089  		{
  3090  			op:    "setup-profiles:Doing",
  3091  			name:  "some-snap",
  3092  			revno: snap.R(11),
  3093  		},
  3094  		{
  3095  			op: "candidate",
  3096  			sinfo: snap.SideInfo{
  3097  				RealName: "some-snap",
  3098  				SnapID:   "some-snap-id",
  3099  				Channel:  "some-channel",
  3100  				Revision: snap.R(11),
  3101  			},
  3102  		},
  3103  		{
  3104  			op:   "link-snap",
  3105  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  3106  		},
  3107  		{
  3108  			op:    "auto-connect:Doing",
  3109  			name:  "some-snap",
  3110  			revno: snap.R(11),
  3111  		},
  3112  		{
  3113  			op: "update-aliases",
  3114  		},
  3115  		{
  3116  			op:   "remove-snap-aliases",
  3117  			name: "some-snap",
  3118  		},
  3119  		{
  3120  			op:   "discard-namespace",
  3121  			name: "some-snap",
  3122  		},
  3123  		{
  3124  			op:   "unlink-snap",
  3125  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  3126  		},
  3127  		{
  3128  			op:    "setup-profiles:Undoing",
  3129  			name:  "some-snap",
  3130  			revno: snap.R(11),
  3131  		},
  3132  		{
  3133  			op:   "undo-copy-snap-data",
  3134  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  3135  			old:  "<no-old>",
  3136  		},
  3137  		{
  3138  			op:   "remove-snap-data-dir",
  3139  			name: "some-snap",
  3140  			path: filepath.Join(dirs.SnapDataDir, "some-snap"),
  3141  		},
  3142  		{
  3143  			op:    "undo-setup-snap",
  3144  			name:  "some-snap",
  3145  			stype: "app",
  3146  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  3147  		},
  3148  		{
  3149  			op:   "remove-snap-dir",
  3150  			name: "some-snap",
  3151  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  3152  		},
  3153  	}
  3154  	// start with an easier-to-read error if this fails:
  3155  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  3156  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  3157  }
  3158  
  3159  func (s *snapmgrTestSuite) TestInstallWithCohortRunThrough(c *C) {
  3160  	s.state.Lock()
  3161  	defer s.state.Unlock()
  3162  
  3163  	chg := s.state.NewChange("install", "install a snap")
  3164  	opts := &snapstate.RevisionOptions{Channel: "some-channel", CohortKey: "scurries"}
  3165  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3166  	c.Assert(err, IsNil)
  3167  	chg.AddAll(ts)
  3168  
  3169  	s.state.Unlock()
  3170  	defer s.se.Stop()
  3171  	s.settle(c)
  3172  	s.state.Lock()
  3173  
  3174  	// ensure all our tasks ran
  3175  	c.Assert(chg.Err(), IsNil)
  3176  	c.Assert(chg.IsReady(), Equals, true)
  3177  	c.Check(snapstate.Installing(s.state), Equals, false)
  3178  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  3179  		macaroon: s.user.StoreMacaroon,
  3180  		name:     "some-snap",
  3181  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"),
  3182  	}})
  3183  	expected := fakeOps{
  3184  		{
  3185  			op:     "storesvc-snap-action",
  3186  			userID: 1,
  3187  		},
  3188  		{
  3189  			op: "storesvc-snap-action:action",
  3190  			action: store.SnapAction{
  3191  				Action:       "install",
  3192  				InstanceName: "some-snap",
  3193  				CohortKey:    "scurries",
  3194  				Channel:      "some-channel",
  3195  			},
  3196  			revno:  snap.R(666),
  3197  			userID: 1,
  3198  		},
  3199  		{
  3200  			op:   "storesvc-download",
  3201  			name: "some-snap",
  3202  		},
  3203  		{
  3204  			op:    "validate-snap:Doing",
  3205  			name:  "some-snap",
  3206  			revno: snap.R(666),
  3207  		},
  3208  		{
  3209  			op:  "current",
  3210  			old: "<no-current>",
  3211  		},
  3212  		{
  3213  			op:   "open-snap-file",
  3214  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"),
  3215  			sinfo: snap.SideInfo{
  3216  				RealName: "some-snap",
  3217  				SnapID:   "some-snap-id",
  3218  				Revision: snap.R(666),
  3219  				Channel:  "some-channel",
  3220  			},
  3221  		},
  3222  		{
  3223  			op:    "setup-snap",
  3224  			name:  "some-snap",
  3225  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"),
  3226  			revno: snap.R(666),
  3227  		},
  3228  		{
  3229  			op:   "copy-data",
  3230  			path: filepath.Join(dirs.SnapMountDir, "some-snap/666"),
  3231  			old:  "<no-old>",
  3232  		},
  3233  		{
  3234  			op:    "setup-profiles:Doing",
  3235  			name:  "some-snap",
  3236  			revno: snap.R(666),
  3237  		},
  3238  		{
  3239  			op: "candidate",
  3240  			sinfo: snap.SideInfo{
  3241  				RealName: "some-snap",
  3242  				SnapID:   "some-snap-id",
  3243  				Revision: snap.R(666),
  3244  				Channel:  "some-channel",
  3245  			},
  3246  		},
  3247  		{
  3248  			op:   "link-snap",
  3249  			path: filepath.Join(dirs.SnapMountDir, "some-snap/666"),
  3250  		},
  3251  		{
  3252  			op:    "auto-connect:Doing",
  3253  			name:  "some-snap",
  3254  			revno: snap.R(666),
  3255  		},
  3256  		{
  3257  			op: "update-aliases",
  3258  		},
  3259  		{
  3260  			op:    "cleanup-trash",
  3261  			name:  "some-snap",
  3262  			revno: snap.R(666),
  3263  		},
  3264  	}
  3265  	// start with an easier-to-read error if this fails:
  3266  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  3267  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  3268  
  3269  	// check progress
  3270  	ta := ts.Tasks()
  3271  	task := ta[1]
  3272  	_, cur, total := task.Progress()
  3273  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  3274  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  3275  	c.Check(task.Summary(), Equals, `Download snap "some-snap" (666) from channel "some-channel"`)
  3276  
  3277  	// check link/start snap summary
  3278  	linkTask := ta[len(ta)-8]
  3279  	c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (666) available to the system`)
  3280  	startTask := ta[len(ta)-3]
  3281  	c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (666) services`)
  3282  
  3283  	// verify snap-setup in the task state
  3284  	var snapsup snapstate.SnapSetup
  3285  	err = task.Get("snap-setup", &snapsup)
  3286  	c.Assert(err, IsNil)
  3287  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  3288  		Channel:  "some-channel",
  3289  		UserID:   s.user.ID,
  3290  		SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"),
  3291  		DownloadInfo: &snap.DownloadInfo{
  3292  			DownloadURL: "https://some-server.com/some/path.snap",
  3293  		},
  3294  		SideInfo:  snapsup.SideInfo,
  3295  		Type:      snap.TypeApp,
  3296  		PlugsOnly: true,
  3297  		CohortKey: "scurries",
  3298  	})
  3299  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  3300  		RealName: "some-snap",
  3301  		Revision: snap.R(666),
  3302  		SnapID:   "some-snap-id",
  3303  		Channel:  "some-channel",
  3304  	})
  3305  
  3306  	// verify snaps in the system state
  3307  	var snaps map[string]*snapstate.SnapState
  3308  	err = s.state.Get("snaps", &snaps)
  3309  	c.Assert(err, IsNil)
  3310  
  3311  	snapst := snaps["some-snap"]
  3312  	c.Assert(snapst, NotNil)
  3313  	c.Assert(snapst.Active, Equals, true)
  3314  	c.Assert(snapst.Channel, Equals, "some-channel")
  3315  	c.Assert(snapst.CohortKey, Equals, "scurries")
  3316  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  3317  		RealName: "some-snap",
  3318  		SnapID:   "some-snap-id",
  3319  		Revision: snap.R(666),
  3320  		Channel:  "some-channel",
  3321  	})
  3322  	c.Assert(snapst.Required, Equals, false)
  3323  }
  3324  
  3325  func (s *snapmgrTestSuite) TestInstallWithRevisionRunThrough(c *C) {
  3326  	s.state.Lock()
  3327  	defer s.state.Unlock()
  3328  
  3329  	chg := s.state.NewChange("install", "install a snap")
  3330  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
  3331  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3332  	c.Assert(err, IsNil)
  3333  	chg.AddAll(ts)
  3334  
  3335  	s.state.Unlock()
  3336  	defer s.se.Stop()
  3337  	s.settle(c)
  3338  	s.state.Lock()
  3339  
  3340  	// ensure all our tasks ran
  3341  	c.Assert(chg.Err(), IsNil)
  3342  	c.Assert(chg.IsReady(), Equals, true)
  3343  	c.Check(snapstate.Installing(s.state), Equals, false)
  3344  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  3345  		macaroon: s.user.StoreMacaroon,
  3346  		name:     "some-snap",
  3347  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
  3348  	}})
  3349  	expected := fakeOps{
  3350  		{
  3351  			op:     "storesvc-snap-action",
  3352  			userID: 1,
  3353  		},
  3354  		{
  3355  			op: "storesvc-snap-action:action",
  3356  			action: store.SnapAction{
  3357  				Action:       "install",
  3358  				InstanceName: "some-snap",
  3359  				Revision:     snap.R(42),
  3360  			},
  3361  			revno:  snap.R(42),
  3362  			userID: 1,
  3363  		},
  3364  		{
  3365  			op:   "storesvc-download",
  3366  			name: "some-snap",
  3367  		},
  3368  		{
  3369  			op:    "validate-snap:Doing",
  3370  			name:  "some-snap",
  3371  			revno: snap.R(42),
  3372  		},
  3373  		{
  3374  			op:  "current",
  3375  			old: "<no-current>",
  3376  		},
  3377  		{
  3378  			op:   "open-snap-file",
  3379  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
  3380  			sinfo: snap.SideInfo{
  3381  				RealName: "some-snap",
  3382  				SnapID:   "some-snap-id",
  3383  				Revision: snap.R(42),
  3384  			},
  3385  		},
  3386  		{
  3387  			op:    "setup-snap",
  3388  			name:  "some-snap",
  3389  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
  3390  			revno: snap.R(42),
  3391  		},
  3392  		{
  3393  			op:   "copy-data",
  3394  			path: filepath.Join(dirs.SnapMountDir, "some-snap/42"),
  3395  			old:  "<no-old>",
  3396  		},
  3397  		{
  3398  			op:    "setup-profiles:Doing",
  3399  			name:  "some-snap",
  3400  			revno: snap.R(42),
  3401  		},
  3402  		{
  3403  			op: "candidate",
  3404  			sinfo: snap.SideInfo{
  3405  				RealName: "some-snap",
  3406  				SnapID:   "some-snap-id",
  3407  				Revision: snap.R(42),
  3408  			},
  3409  		},
  3410  		{
  3411  			op:   "link-snap",
  3412  			path: filepath.Join(dirs.SnapMountDir, "some-snap/42"),
  3413  		},
  3414  		{
  3415  			op:    "auto-connect:Doing",
  3416  			name:  "some-snap",
  3417  			revno: snap.R(42),
  3418  		},
  3419  		{
  3420  			op: "update-aliases",
  3421  		},
  3422  		{
  3423  			op:    "cleanup-trash",
  3424  			name:  "some-snap",
  3425  			revno: snap.R(42),
  3426  		},
  3427  	}
  3428  	// start with an easier-to-read error if this fails:
  3429  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  3430  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  3431  
  3432  	// check progress
  3433  	ta := ts.Tasks()
  3434  	task := ta[1]
  3435  	_, cur, total := task.Progress()
  3436  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  3437  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  3438  	c.Check(task.Summary(), Equals, `Download snap "some-snap" (42) from channel "some-channel"`)
  3439  
  3440  	// check link/start snap summary
  3441  	linkTask := ta[len(ta)-8]
  3442  	c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (42) available to the system`)
  3443  	startTask := ta[len(ta)-3]
  3444  	c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (42) services`)
  3445  
  3446  	// verify snap-setup in the task state
  3447  	var snapsup snapstate.SnapSetup
  3448  	err = task.Get("snap-setup", &snapsup)
  3449  	c.Assert(err, IsNil)
  3450  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  3451  		Channel:  "some-channel",
  3452  		UserID:   s.user.ID,
  3453  		SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
  3454  		DownloadInfo: &snap.DownloadInfo{
  3455  			DownloadURL: "https://some-server.com/some/path.snap",
  3456  		},
  3457  		SideInfo:  snapsup.SideInfo,
  3458  		Type:      snap.TypeApp,
  3459  		PlugsOnly: true,
  3460  	})
  3461  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  3462  		RealName: "some-snap",
  3463  		Revision: snap.R(42),
  3464  		SnapID:   "some-snap-id",
  3465  	})
  3466  
  3467  	// verify snaps in the system state
  3468  	var snaps map[string]*snapstate.SnapState
  3469  	err = s.state.Get("snaps", &snaps)
  3470  	c.Assert(err, IsNil)
  3471  
  3472  	snapst := snaps["some-snap"]
  3473  	c.Assert(snapst, NotNil)
  3474  	c.Assert(snapst.Active, Equals, true)
  3475  	c.Assert(snapst.Channel, Equals, "some-channel")
  3476  	c.Assert(snapst.CohortKey, Equals, "")
  3477  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  3478  		RealName: "some-snap",
  3479  		SnapID:   "some-snap-id",
  3480  		Revision: snap.R(42),
  3481  	})
  3482  	c.Assert(snapst.Required, Equals, false)
  3483  }
  3484  
  3485  func (s *snapmgrTestSuite) TestInstallStartOrder(c *C) {
  3486  	s.state.Lock()
  3487  	defer s.state.Unlock()
  3488  
  3489  	chg := s.state.NewChange("install", "install a snap")
  3490  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  3491  	ts, err := snapstate.Install(context.Background(), s.state, "services-snap", opts, s.user.ID, snapstate.Flags{})
  3492  	c.Assert(err, IsNil)
  3493  	chg.AddAll(ts)
  3494  
  3495  	s.state.Unlock()
  3496  	defer s.se.Stop()
  3497  	s.settle(c)
  3498  	s.state.Lock()
  3499  
  3500  	// ensure all our tasks ran
  3501  	c.Assert(chg.Err(), IsNil)
  3502  	c.Assert(chg.IsReady(), Equals, true)
  3503  	c.Check(snapstate.Installing(s.state), Equals, false)
  3504  	op := s.fakeBackend.ops.First("start-snap-services")
  3505  	c.Assert(op, NotNil)
  3506  	c.Assert(op, DeepEquals, &fakeOp{
  3507  		op:   "start-snap-services",
  3508  		path: filepath.Join(dirs.SnapMountDir, "services-snap/11"),
  3509  		// ordered to preserve after/before relation
  3510  		services: []string{"svc1", "svc3", "svc2"},
  3511  	})
  3512  }
  3513  
  3514  func (s *snapmgrTestSuite) TestInstalling(c *C) {
  3515  	s.state.Lock()
  3516  	defer s.state.Unlock()
  3517  
  3518  	c.Check(snapstate.Installing(s.state), Equals, false)
  3519  
  3520  	chg := s.state.NewChange("install", "install a snap")
  3521  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
  3522  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{})
  3523  	c.Assert(err, IsNil)
  3524  	chg.AddAll(ts)
  3525  
  3526  	c.Check(snapstate.Installing(s.state), Equals, true)
  3527  }
  3528  
  3529  func (s *snapmgrTestSuite) TestUpdateAmendRunThrough(c *C) {
  3530  	si := snap.SideInfo{
  3531  		RealName: "some-snap",
  3532  		Revision: snap.R(-42),
  3533  	}
  3534  	snaptest.MockSnap(c, `name: some-snap`, &si)
  3535  
  3536  	s.state.Lock()
  3537  	defer s.state.Unlock()
  3538  
  3539  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3540  		Active:   true,
  3541  		Sequence: []*snap.SideInfo{&si},
  3542  		Current:  si.Revision,
  3543  		SnapType: "app",
  3544  		Channel:  "stable",
  3545  	})
  3546  
  3547  	chg := s.state.NewChange("refresh", "refresh a snap")
  3548  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
  3549  	c.Assert(err, IsNil)
  3550  	chg.AddAll(ts)
  3551  
  3552  	s.state.Unlock()
  3553  	defer s.se.Stop()
  3554  	s.settle(c)
  3555  	s.state.Lock()
  3556  
  3557  	// ensure all our tasks ran
  3558  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  3559  		macaroon: s.user.StoreMacaroon,
  3560  		name:     "some-snap",
  3561  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  3562  	}})
  3563  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
  3564  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, []string{
  3565  		"storesvc-snap-action",
  3566  		"storesvc-snap-action:action",
  3567  		"storesvc-download",
  3568  		"validate-snap:Doing",
  3569  		"current",
  3570  		"open-snap-file",
  3571  		"setup-snap",
  3572  		"remove-snap-aliases",
  3573  		"unlink-snap",
  3574  		"copy-data",
  3575  		"setup-profiles:Doing",
  3576  		"candidate",
  3577  		"link-snap",
  3578  		"auto-connect:Doing",
  3579  		"update-aliases",
  3580  		"cleanup-trash",
  3581  	})
  3582  	// just check the interesting op
  3583  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  3584  		op: "storesvc-snap-action:action",
  3585  		action: store.SnapAction{
  3586  			Action:       "install", // we asked for an Update, but an amend is actually an Install
  3587  			InstanceName: "some-snap",
  3588  			Channel:      "some-channel",
  3589  			Epoch:        snap.E("1*"), // in amend, epoch in the action is not nil!
  3590  			Flags:        store.SnapActionEnforceValidation,
  3591  		},
  3592  		revno:  snap.R(11),
  3593  		userID: 1,
  3594  	})
  3595  
  3596  	task := ts.Tasks()[1]
  3597  	// verify snapSetup info
  3598  	var snapsup snapstate.SnapSetup
  3599  	err = task.Get("snap-setup", &snapsup)
  3600  	c.Assert(err, IsNil)
  3601  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  3602  		Channel: "some-channel",
  3603  		UserID:  s.user.ID,
  3604  
  3605  		SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  3606  		DownloadInfo: &snap.DownloadInfo{
  3607  			DownloadURL: "https://some-server.com/some/path.snap",
  3608  		},
  3609  		SideInfo:  snapsup.SideInfo,
  3610  		Type:      snap.TypeApp,
  3611  		PlugsOnly: true,
  3612  		Flags:     snapstate.Flags{Amend: true},
  3613  	})
  3614  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  3615  		RealName: "some-snap",
  3616  		Revision: snap.R(11),
  3617  		Channel:  "some-channel",
  3618  		SnapID:   "some-snap-id",
  3619  	})
  3620  
  3621  	// verify services stop reason
  3622  	verifyStopReason(c, ts, "refresh")
  3623  
  3624  	// check post-refresh hook
  3625  	task = ts.Tasks()[14]
  3626  	c.Assert(task.Kind(), Equals, "run-hook")
  3627  	c.Assert(task.Summary(), Matches, `Run post-refresh hook of "some-snap" snap if present`)
  3628  
  3629  	// verify snaps in the system state
  3630  	var snapst snapstate.SnapState
  3631  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3632  	c.Assert(err, IsNil)
  3633  
  3634  	c.Assert(snapst.Active, Equals, true)
  3635  	c.Assert(snapst.Sequence, HasLen, 2)
  3636  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  3637  		RealName: "some-snap",
  3638  		Channel:  "",
  3639  		Revision: snap.R(-42),
  3640  	})
  3641  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  3642  		RealName: "some-snap",
  3643  		Channel:  "some-channel",
  3644  		SnapID:   "some-snap-id",
  3645  		Revision: snap.R(11),
  3646  	})
  3647  }
  3648  
  3649  func (s *snapmgrTestSuite) TestUpdateRunThrough(c *C) {
  3650  	// we start without the auxiliary store info (or with an older one)
  3651  	c.Check(snapstate.AuxStoreInfoFilename("services-snap-id"), testutil.FileAbsent)
  3652  
  3653  	// use services-snap here to make sure services would be stopped/started appropriately
  3654  	si := snap.SideInfo{
  3655  		RealName: "services-snap",
  3656  		Revision: snap.R(7),
  3657  		SnapID:   "services-snap-id",
  3658  	}
  3659  	snaptest.MockSnap(c, `name: services-snap`, &si)
  3660  	fi, err := os.Stat(snap.MountFile("services-snap", si.Revision))
  3661  	c.Assert(err, IsNil)
  3662  	refreshedDate := fi.ModTime()
  3663  	// look at disk
  3664  	r := snapstate.MockRevisionDate(nil)
  3665  	defer r()
  3666  
  3667  	s.state.Lock()
  3668  	defer s.state.Unlock()
  3669  
  3670  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  3671  		Active:    true,
  3672  		Sequence:  []*snap.SideInfo{&si},
  3673  		Current:   si.Revision,
  3674  		SnapType:  "app",
  3675  		Channel:   "stable",
  3676  		CohortKey: "embattled",
  3677  	})
  3678  
  3679  	chg := s.state.NewChange("refresh", "refresh a snap")
  3680  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{
  3681  		Channel:   "some-channel",
  3682  		CohortKey: "some-cohort",
  3683  	}, s.user.ID, snapstate.Flags{})
  3684  	c.Assert(err, IsNil)
  3685  	chg.AddAll(ts)
  3686  
  3687  	s.state.Unlock()
  3688  	defer s.se.Stop()
  3689  	s.settle(c)
  3690  	s.state.Lock()
  3691  
  3692  	expected := fakeOps{
  3693  		{
  3694  			op: "storesvc-snap-action",
  3695  			curSnaps: []store.CurrentSnap{{
  3696  				InstanceName:    "services-snap",
  3697  				SnapID:          "services-snap-id",
  3698  				Revision:        snap.R(7),
  3699  				TrackingChannel: "stable",
  3700  				RefreshedDate:   refreshedDate,
  3701  				Epoch:           snap.E("0"),
  3702  				CohortKey:       "embattled",
  3703  			}},
  3704  			userID: 1,
  3705  		},
  3706  		{
  3707  			op: "storesvc-snap-action:action",
  3708  			action: store.SnapAction{
  3709  				Action:       "refresh",
  3710  				InstanceName: "services-snap",
  3711  				SnapID:       "services-snap-id",
  3712  				Channel:      "some-channel",
  3713  				CohortKey:    "some-cohort",
  3714  				Flags:        store.SnapActionEnforceValidation,
  3715  			},
  3716  			revno:  snap.R(11),
  3717  			userID: 1,
  3718  		},
  3719  		{
  3720  			op:   "storesvc-download",
  3721  			name: "services-snap",
  3722  		},
  3723  		{
  3724  			op:    "validate-snap:Doing",
  3725  			name:  "services-snap",
  3726  			revno: snap.R(11),
  3727  		},
  3728  		{
  3729  			op:  "current",
  3730  			old: filepath.Join(dirs.SnapMountDir, "services-snap/7"),
  3731  		},
  3732  		{
  3733  			op:   "open-snap-file",
  3734  			path: filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
  3735  			sinfo: snap.SideInfo{
  3736  				RealName: "services-snap",
  3737  				SnapID:   "services-snap-id",
  3738  				Channel:  "some-channel",
  3739  				Revision: snap.R(11),
  3740  			},
  3741  		},
  3742  		{
  3743  			op:    "setup-snap",
  3744  			name:  "services-snap",
  3745  			path:  filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
  3746  			revno: snap.R(11),
  3747  		},
  3748  		{
  3749  			op:   "stop-snap-services:refresh",
  3750  			path: filepath.Join(dirs.SnapMountDir, "services-snap/7"),
  3751  		},
  3752  		{
  3753  			op:   "remove-snap-aliases",
  3754  			name: "services-snap",
  3755  		},
  3756  		{
  3757  			op:   "unlink-snap",
  3758  			path: filepath.Join(dirs.SnapMountDir, "services-snap/7"),
  3759  		},
  3760  		{
  3761  			op:   "copy-data",
  3762  			path: filepath.Join(dirs.SnapMountDir, "services-snap/11"),
  3763  			old:  filepath.Join(dirs.SnapMountDir, "services-snap/7"),
  3764  		},
  3765  		{
  3766  			op:    "setup-profiles:Doing",
  3767  			name:  "services-snap",
  3768  			revno: snap.R(11),
  3769  		},
  3770  		{
  3771  			op: "candidate",
  3772  			sinfo: snap.SideInfo{
  3773  				RealName: "services-snap",
  3774  				SnapID:   "services-snap-id",
  3775  				Channel:  "some-channel",
  3776  				Revision: snap.R(11),
  3777  			},
  3778  		},
  3779  		{
  3780  			op:   "link-snap",
  3781  			path: filepath.Join(dirs.SnapMountDir, "services-snap/11"),
  3782  		},
  3783  		{
  3784  			op:    "auto-connect:Doing",
  3785  			name:  "services-snap",
  3786  			revno: snap.R(11),
  3787  		},
  3788  		{
  3789  			op: "update-aliases",
  3790  		},
  3791  		{
  3792  			op:       "start-snap-services",
  3793  			path:     filepath.Join(dirs.SnapMountDir, "services-snap/11"),
  3794  			services: []string{"svc1", "svc3", "svc2"},
  3795  		},
  3796  		{
  3797  			op:    "cleanup-trash",
  3798  			name:  "services-snap",
  3799  			revno: snap.R(11),
  3800  		},
  3801  	}
  3802  
  3803  	// ensure all our tasks ran
  3804  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  3805  		macaroon: s.user.StoreMacaroon,
  3806  		name:     "services-snap",
  3807  		target:   filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
  3808  	}})
  3809  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
  3810  	// start with an easier-to-read error if this fails:
  3811  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  3812  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  3813  
  3814  	// check progress
  3815  	task := ts.Tasks()[1]
  3816  	_, cur, total := task.Progress()
  3817  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  3818  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  3819  
  3820  	// verify snapSetup info
  3821  	var snapsup snapstate.SnapSetup
  3822  	err = task.Get("snap-setup", &snapsup)
  3823  	c.Assert(err, IsNil)
  3824  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  3825  		Channel:   "some-channel",
  3826  		CohortKey: "some-cohort",
  3827  		UserID:    s.user.ID,
  3828  
  3829  		SnapPath: filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
  3830  		DownloadInfo: &snap.DownloadInfo{
  3831  			DownloadURL: "https://some-server.com/some/path.snap",
  3832  		},
  3833  		SideInfo:  snapsup.SideInfo,
  3834  		Type:      snap.TypeApp,
  3835  		PlugsOnly: true,
  3836  	})
  3837  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  3838  		RealName: "services-snap",
  3839  		Revision: snap.R(11),
  3840  		Channel:  "some-channel",
  3841  		SnapID:   "services-snap-id",
  3842  	})
  3843  
  3844  	// verify services stop reason
  3845  	verifyStopReason(c, ts, "refresh")
  3846  
  3847  	// check post-refresh hook
  3848  	task = ts.Tasks()[14]
  3849  	c.Assert(task.Kind(), Equals, "run-hook")
  3850  	c.Assert(task.Summary(), Matches, `Run post-refresh hook of "services-snap" snap if present`)
  3851  
  3852  	// verify snaps in the system state
  3853  	var snapst snapstate.SnapState
  3854  	err = snapstate.Get(s.state, "services-snap", &snapst)
  3855  	c.Assert(err, IsNil)
  3856  
  3857  	c.Assert(snapst.Active, Equals, true)
  3858  	c.Assert(snapst.Sequence, HasLen, 2)
  3859  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  3860  		RealName: "services-snap",
  3861  		SnapID:   "services-snap-id",
  3862  		Channel:  "",
  3863  		Revision: snap.R(7),
  3864  	})
  3865  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  3866  		RealName: "services-snap",
  3867  		Channel:  "some-channel",
  3868  		SnapID:   "services-snap-id",
  3869  		Revision: snap.R(11),
  3870  	})
  3871  	c.Check(snapst.CohortKey, Equals, "some-cohort")
  3872  
  3873  	// we end up with the auxiliary store info
  3874  	c.Check(snapstate.AuxStoreInfoFilename("services-snap-id"), testutil.FilePresent)
  3875  }
  3876  
  3877  func (s *snapmgrTestSuite) TestParallelInstanceUpdateRunThrough(c *C) {
  3878  	// use services-snap here to make sure services would be stopped/started appropriately
  3879  	si := snap.SideInfo{
  3880  		RealName: "services-snap",
  3881  		Revision: snap.R(7),
  3882  		SnapID:   "services-snap-id",
  3883  	}
  3884  	snaptest.MockSnapInstance(c, "services-snap_instance", `name: services-snap`, &si)
  3885  	fi, err := os.Stat(snap.MountFile("services-snap_instance", si.Revision))
  3886  	c.Assert(err, IsNil)
  3887  	refreshedDate := fi.ModTime()
  3888  	// look at disk
  3889  	r := snapstate.MockRevisionDate(nil)
  3890  	defer r()
  3891  
  3892  	s.state.Lock()
  3893  	defer s.state.Unlock()
  3894  
  3895  	tr := config.NewTransaction(s.state)
  3896  	tr.Set("core", "experimental.parallel-instances", true)
  3897  	tr.Commit()
  3898  
  3899  	snapstate.Set(s.state, "services-snap_instance", &snapstate.SnapState{
  3900  		Active:      true,
  3901  		Sequence:    []*snap.SideInfo{&si},
  3902  		Current:     si.Revision,
  3903  		SnapType:    "app",
  3904  		Channel:     "stable",
  3905  		InstanceKey: "instance",
  3906  	})
  3907  
  3908  	chg := s.state.NewChange("refresh", "refresh a snap")
  3909  	ts, err := snapstate.Update(s.state, "services-snap_instance", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  3910  	c.Assert(err, IsNil)
  3911  	chg.AddAll(ts)
  3912  
  3913  	s.state.Unlock()
  3914  	s.settle(c)
  3915  	s.state.Lock()
  3916  
  3917  	expected := fakeOps{
  3918  		{
  3919  			op: "storesvc-snap-action",
  3920  			curSnaps: []store.CurrentSnap{{
  3921  				InstanceName:    "services-snap_instance",
  3922  				SnapID:          "services-snap-id",
  3923  				Revision:        snap.R(7),
  3924  				TrackingChannel: "stable",
  3925  				RefreshedDate:   refreshedDate,
  3926  				Epoch:           snap.E("0"),
  3927  			}},
  3928  			userID: 1,
  3929  		},
  3930  		{
  3931  			op: "storesvc-snap-action:action",
  3932  			action: store.SnapAction{
  3933  				Action:       "refresh",
  3934  				SnapID:       "services-snap-id",
  3935  				InstanceName: "services-snap_instance",
  3936  				Channel:      "some-channel",
  3937  				Flags:        store.SnapActionEnforceValidation,
  3938  			},
  3939  			revno:  snap.R(11),
  3940  			userID: 1,
  3941  		},
  3942  		{
  3943  			op:   "storesvc-download",
  3944  			name: "services-snap",
  3945  		},
  3946  		{
  3947  			op:    "validate-snap:Doing",
  3948  			name:  "services-snap_instance",
  3949  			revno: snap.R(11),
  3950  		},
  3951  		{
  3952  			op:  "current",
  3953  			old: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  3954  		},
  3955  		{
  3956  			op:   "open-snap-file",
  3957  			path: filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  3958  			sinfo: snap.SideInfo{
  3959  				RealName: "services-snap",
  3960  				SnapID:   "services-snap-id",
  3961  				Channel:  "some-channel",
  3962  				Revision: snap.R(11),
  3963  			},
  3964  		},
  3965  		{
  3966  			op:    "setup-snap",
  3967  			name:  "services-snap_instance",
  3968  			path:  filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  3969  			revno: snap.R(11),
  3970  		},
  3971  		{
  3972  			op:   "stop-snap-services:refresh",
  3973  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  3974  		},
  3975  		{
  3976  			op:   "remove-snap-aliases",
  3977  			name: "services-snap_instance",
  3978  		},
  3979  		{
  3980  			op:   "unlink-snap",
  3981  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  3982  		},
  3983  		{
  3984  			op:   "copy-data",
  3985  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"),
  3986  			old:  filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  3987  		},
  3988  		{
  3989  			op:    "setup-profiles:Doing",
  3990  			name:  "services-snap_instance",
  3991  			revno: snap.R(11),
  3992  		},
  3993  		{
  3994  			op: "candidate",
  3995  			sinfo: snap.SideInfo{
  3996  				RealName: "services-snap",
  3997  				SnapID:   "services-snap-id",
  3998  				Channel:  "some-channel",
  3999  				Revision: snap.R(11),
  4000  			},
  4001  		},
  4002  		{
  4003  			op:   "link-snap",
  4004  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"),
  4005  		},
  4006  		{
  4007  			op:    "auto-connect:Doing",
  4008  			name:  "services-snap_instance",
  4009  			revno: snap.R(11),
  4010  		},
  4011  		{
  4012  			op: "update-aliases",
  4013  		},
  4014  		{
  4015  			op:       "start-snap-services",
  4016  			path:     filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"),
  4017  			services: []string{"svc1", "svc3", "svc2"},
  4018  		},
  4019  		{
  4020  			op:    "cleanup-trash",
  4021  			name:  "services-snap_instance",
  4022  			revno: snap.R(11),
  4023  		},
  4024  	}
  4025  
  4026  	// ensure all our tasks ran
  4027  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  4028  		macaroon: s.user.StoreMacaroon,
  4029  		name:     "services-snap",
  4030  		target:   filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  4031  	}})
  4032  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
  4033  	// start with an easier-to-read error if this fails:
  4034  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  4035  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  4036  
  4037  	// check progress
  4038  	task := ts.Tasks()[1]
  4039  	_, cur, total := task.Progress()
  4040  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  4041  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  4042  
  4043  	// verify snapSetup info
  4044  	var snapsup snapstate.SnapSetup
  4045  	err = task.Get("snap-setup", &snapsup)
  4046  	c.Assert(err, IsNil)
  4047  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  4048  		Channel: "some-channel",
  4049  		UserID:  s.user.ID,
  4050  
  4051  		SnapPath: filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  4052  		DownloadInfo: &snap.DownloadInfo{
  4053  			DownloadURL: "https://some-server.com/some/path.snap",
  4054  		},
  4055  		SideInfo:    snapsup.SideInfo,
  4056  		Type:        snap.TypeApp,
  4057  		PlugsOnly:   true,
  4058  		InstanceKey: "instance",
  4059  	})
  4060  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  4061  		RealName: "services-snap",
  4062  		Revision: snap.R(11),
  4063  		Channel:  "some-channel",
  4064  		SnapID:   "services-snap-id",
  4065  	})
  4066  
  4067  	// verify services stop reason
  4068  	verifyStopReason(c, ts, "refresh")
  4069  
  4070  	// check post-refresh hook
  4071  	task = ts.Tasks()[14]
  4072  	c.Assert(task.Kind(), Equals, "run-hook")
  4073  	c.Assert(task.Summary(), Matches, `Run post-refresh hook of "services-snap_instance" snap if present`)
  4074  
  4075  	// verify snaps in the system state
  4076  	var snapst snapstate.SnapState
  4077  	err = snapstate.Get(s.state, "services-snap_instance", &snapst)
  4078  	c.Assert(err, IsNil)
  4079  
  4080  	c.Assert(snapst.InstanceKey, Equals, "instance")
  4081  	c.Assert(snapst.Active, Equals, true)
  4082  	c.Assert(snapst.Sequence, HasLen, 2)
  4083  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  4084  		RealName: "services-snap",
  4085  		SnapID:   "services-snap-id",
  4086  		Channel:  "",
  4087  		Revision: snap.R(7),
  4088  	})
  4089  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  4090  		RealName: "services-snap",
  4091  		Channel:  "some-channel",
  4092  		SnapID:   "services-snap-id",
  4093  		Revision: snap.R(11),
  4094  	})
  4095  }
  4096  
  4097  func (s *snapmgrTestSuite) TestUpdateWithNewBase(c *C) {
  4098  	si := &snap.SideInfo{
  4099  		RealName: "some-snap",
  4100  		SnapID:   "some-snap-id",
  4101  		Revision: snap.R(7),
  4102  	}
  4103  	snaptest.MockSnap(c, `name: some-snap`, si)
  4104  
  4105  	s.state.Lock()
  4106  	defer s.state.Unlock()
  4107  
  4108  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4109  		Active:   true,
  4110  		Channel:  "edge",
  4111  		Sequence: []*snap.SideInfo{si},
  4112  		Current:  snap.R(7),
  4113  		SnapType: "app",
  4114  	})
  4115  
  4116  	chg := s.state.NewChange("refresh", "refresh a snap")
  4117  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-base"}, s.user.ID, snapstate.Flags{})
  4118  	c.Assert(err, IsNil)
  4119  	chg.AddAll(ts)
  4120  
  4121  	s.state.Unlock()
  4122  	defer s.se.Stop()
  4123  	s.settle(c)
  4124  	s.state.Lock()
  4125  
  4126  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  4127  		{macaroon: s.user.StoreMacaroon, name: "some-base", target: filepath.Join(dirs.SnapBlobDir, "some-base_11.snap")},
  4128  		{macaroon: s.user.StoreMacaroon, name: "some-snap", target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap")},
  4129  	})
  4130  }
  4131  
  4132  func (s *snapmgrTestSuite) TestUpdateWithAlreadyInstalledBase(c *C) {
  4133  	si := &snap.SideInfo{
  4134  		RealName: "some-snap",
  4135  		SnapID:   "some-snap-id",
  4136  		Revision: snap.R(7),
  4137  	}
  4138  	snaptest.MockSnap(c, `name: some-snap`, si)
  4139  
  4140  	s.state.Lock()
  4141  	defer s.state.Unlock()
  4142  
  4143  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4144  		Active:   true,
  4145  		Channel:  "edge",
  4146  		Sequence: []*snap.SideInfo{si},
  4147  		Current:  snap.R(7),
  4148  		SnapType: "app",
  4149  	})
  4150  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  4151  		Active:  true,
  4152  		Channel: "stable",
  4153  		Sequence: []*snap.SideInfo{{
  4154  			RealName: "some-base",
  4155  			SnapID:   "some-base-id",
  4156  			Revision: snap.R(1),
  4157  		}},
  4158  		Current:  snap.R(1),
  4159  		SnapType: "base",
  4160  	})
  4161  
  4162  	chg := s.state.NewChange("refresh", "refresh a snap")
  4163  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-base"}, s.user.ID, snapstate.Flags{})
  4164  	c.Assert(err, IsNil)
  4165  	chg.AddAll(ts)
  4166  
  4167  	s.state.Unlock()
  4168  	defer s.se.Stop()
  4169  	s.settle(c)
  4170  	s.state.Lock()
  4171  
  4172  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  4173  		{macaroon: s.user.StoreMacaroon, name: "some-snap", target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap")},
  4174  	})
  4175  }
  4176  
  4177  func (s *snapmgrTestSuite) TestUpdateWithNewDefaultProvider(c *C) {
  4178  	s.state.Lock()
  4179  	defer s.state.Unlock()
  4180  
  4181  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
  4182  	repo := interfaces.NewRepository()
  4183  	ifacerepo.Replace(s.state, repo)
  4184  
  4185  	si := &snap.SideInfo{
  4186  		RealName: "snap-content-plug",
  4187  		SnapID:   "snap-content-plug-id",
  4188  		Revision: snap.R(7),
  4189  	}
  4190  	snaptest.MockSnap(c, `name: snap-content-plug`, si)
  4191  	snapstate.Set(s.state, "snap-content-plug", &snapstate.SnapState{
  4192  		Active:   true,
  4193  		Channel:  "edge",
  4194  		Sequence: []*snap.SideInfo{si},
  4195  		Current:  snap.R(7),
  4196  		SnapType: "app",
  4197  	})
  4198  
  4199  	chg := s.state.NewChange("refresh", "refresh a snap")
  4200  	ts, err := snapstate.Update(s.state, "snap-content-plug", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
  4201  	c.Assert(err, IsNil)
  4202  	chg.AddAll(ts)
  4203  
  4204  	s.state.Unlock()
  4205  	defer s.se.Stop()
  4206  	s.settle(c)
  4207  	s.state.Lock()
  4208  
  4209  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  4210  		{macaroon: s.user.StoreMacaroon, name: "snap-content-plug", target: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_11.snap")},
  4211  		{macaroon: s.user.StoreMacaroon, name: "snap-content-slot", target: filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap")},
  4212  	})
  4213  }
  4214  
  4215  func (s *snapmgrTestSuite) TestUpdateWithInstalledDefaultProvider(c *C) {
  4216  	s.state.Lock()
  4217  	defer s.state.Unlock()
  4218  
  4219  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
  4220  	repo := interfaces.NewRepository()
  4221  	ifacerepo.Replace(s.state, repo)
  4222  
  4223  	si := &snap.SideInfo{
  4224  		RealName: "snap-content-plug",
  4225  		SnapID:   "snap-content-plug-id",
  4226  		Revision: snap.R(7),
  4227  	}
  4228  	snaptest.MockSnap(c, `name: snap-content-plug`, si)
  4229  	snapstate.Set(s.state, "snap-content-plug", &snapstate.SnapState{
  4230  		Active:   true,
  4231  		Channel:  "edge",
  4232  		Sequence: []*snap.SideInfo{si},
  4233  		Current:  snap.R(7),
  4234  		SnapType: "app",
  4235  	})
  4236  	snapstate.Set(s.state, "snap-content-slot", &snapstate.SnapState{
  4237  		Active:  true,
  4238  		Channel: "stable",
  4239  		Sequence: []*snap.SideInfo{{
  4240  			RealName: "snap-content-slot",
  4241  			SnapID:   "snap-content-slot-id",
  4242  			Revision: snap.R(1),
  4243  		}},
  4244  		Current:  snap.R(1),
  4245  		SnapType: "app",
  4246  	})
  4247  
  4248  	chg := s.state.NewChange("refresh", "refresh a snap")
  4249  	ts, err := snapstate.Update(s.state, "snap-content-plug", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
  4250  	c.Assert(err, IsNil)
  4251  	chg.AddAll(ts)
  4252  
  4253  	s.state.Unlock()
  4254  	defer s.se.Stop()
  4255  	s.settle(c)
  4256  	s.state.Lock()
  4257  
  4258  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  4259  		{macaroon: s.user.StoreMacaroon, name: "snap-content-plug", target: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_11.snap")},
  4260  	})
  4261  }
  4262  
  4263  func (s *snapmgrTestSuite) TestUpdateRememberedUserRunThrough(c *C) {
  4264  	s.state.Lock()
  4265  	defer s.state.Unlock()
  4266  
  4267  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4268  		Active: true,
  4269  		Sequence: []*snap.SideInfo{
  4270  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  4271  		},
  4272  		Current:  snap.R(5),
  4273  		SnapType: "app",
  4274  		UserID:   1,
  4275  	})
  4276  
  4277  	chg := s.state.NewChange("refresh", "refresh a snap")
  4278  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, 0, snapstate.Flags{})
  4279  	c.Assert(err, IsNil)
  4280  	chg.AddAll(ts)
  4281  
  4282  	s.state.Unlock()
  4283  	defer s.se.Stop()
  4284  	s.settle(c)
  4285  	s.state.Lock()
  4286  
  4287  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  4288  	c.Assert(chg.Err(), IsNil)
  4289  
  4290  	for _, op := range s.fakeBackend.ops {
  4291  		switch op.op {
  4292  		case "storesvc-snap-action":
  4293  			c.Check(op.userID, Equals, 1)
  4294  		case "storesvc-download":
  4295  			snapName := op.name
  4296  			c.Check(s.fakeStore.downloads[0], DeepEquals, fakeDownload{
  4297  				macaroon: "macaroon",
  4298  				name:     "some-snap",
  4299  				target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  4300  			}, Commentf(snapName))
  4301  		}
  4302  	}
  4303  }
  4304  
  4305  func (s *snapmgrTestSuite) TestUpdateToRevisionRememberedUserRunThrough(c *C) {
  4306  	s.state.Lock()
  4307  	defer s.state.Unlock()
  4308  
  4309  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4310  		Active: true,
  4311  		Sequence: []*snap.SideInfo{
  4312  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  4313  		},
  4314  		Current:  snap.R(5),
  4315  		SnapType: "app",
  4316  		UserID:   1,
  4317  	})
  4318  
  4319  	chg := s.state.NewChange("refresh", "refresh a snap")
  4320  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(11)}, 0, snapstate.Flags{})
  4321  	c.Assert(err, IsNil)
  4322  	chg.AddAll(ts)
  4323  
  4324  	s.state.Unlock()
  4325  	defer s.se.Stop()
  4326  	s.settle(c)
  4327  	s.state.Lock()
  4328  
  4329  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  4330  	c.Assert(chg.Err(), IsNil)
  4331  
  4332  	for _, op := range s.fakeBackend.ops {
  4333  		switch op.op {
  4334  		case "storesvc-snap-action:action":
  4335  			c.Check(op.userID, Equals, 1)
  4336  		case "storesvc-download":
  4337  			snapName := op.name
  4338  			c.Check(s.fakeStore.downloads[0], DeepEquals, fakeDownload{
  4339  				macaroon: "macaroon",
  4340  				name:     "some-snap",
  4341  				target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  4342  			}, Commentf(snapName))
  4343  		}
  4344  	}
  4345  }
  4346  
  4347  func (s *snapmgrTestSuite) TestUpdateModelKernelSwitchTrackRunThrough(c *C) {
  4348  	// use services-snap here to make sure services would be stopped/started appropriately
  4349  	si := snap.SideInfo{
  4350  		RealName: "kernel",
  4351  		Revision: snap.R(7),
  4352  		SnapID:   "kernel-id",
  4353  	}
  4354  	snaptest.MockSnap(c, `name: kernel`, &si)
  4355  	fi, err := os.Stat(snap.MountFile("kernel", si.Revision))
  4356  	c.Assert(err, IsNil)
  4357  	refreshedDate := fi.ModTime()
  4358  	// look at disk
  4359  	r := snapstate.MockRevisionDate(nil)
  4360  	defer r()
  4361  
  4362  	s.state.Lock()
  4363  	defer s.state.Unlock()
  4364  
  4365  	r1 := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  4366  	defer r1()
  4367  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  4368  		Active:   true,
  4369  		Sequence: []*snap.SideInfo{&si},
  4370  		Current:  si.Revision,
  4371  		Channel:  "18/stable",
  4372  	})
  4373  
  4374  	chg := s.state.NewChange("refresh", "refresh a snap")
  4375  	ts, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "edge"}, s.user.ID, snapstate.Flags{})
  4376  	c.Assert(err, IsNil)
  4377  	chg.AddAll(ts)
  4378  
  4379  	s.state.Unlock()
  4380  	defer s.se.Stop()
  4381  	s.settle(c)
  4382  	s.state.Lock()
  4383  
  4384  	c.Check(chg.Status(), Equals, state.DoneStatus)
  4385  
  4386  	c.Assert(len(s.fakeBackend.ops) > 2, Equals, true)
  4387  	c.Assert(s.fakeBackend.ops[:2], DeepEquals, fakeOps{
  4388  		{
  4389  			op: "storesvc-snap-action",
  4390  			curSnaps: []store.CurrentSnap{{
  4391  				InstanceName:    "kernel",
  4392  				SnapID:          "kernel-id",
  4393  				Revision:        snap.R(7),
  4394  				TrackingChannel: "18/stable",
  4395  				RefreshedDate:   refreshedDate,
  4396  				Epoch:           snap.E("1*"),
  4397  			}},
  4398  			userID: 1,
  4399  		}, {
  4400  			op: "storesvc-snap-action:action",
  4401  			action: store.SnapAction{
  4402  				Action:       "refresh",
  4403  				InstanceName: "kernel",
  4404  				SnapID:       "kernel-id",
  4405  				Channel:      "18/edge",
  4406  				Flags:        store.SnapActionEnforceValidation,
  4407  			},
  4408  			revno:  snap.R(11),
  4409  			userID: 1,
  4410  		},
  4411  	})
  4412  
  4413  	// check progress
  4414  	task := ts.Tasks()[1]
  4415  	_, cur, total := task.Progress()
  4416  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  4417  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  4418  
  4419  	// verify snapSetup info
  4420  	var snapsup snapstate.SnapSetup
  4421  	err = task.Get("snap-setup", &snapsup)
  4422  	c.Assert(err, IsNil)
  4423  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  4424  		Channel: "18/edge",
  4425  		UserID:  s.user.ID,
  4426  
  4427  		SnapPath: filepath.Join(dirs.SnapBlobDir, "kernel_11.snap"),
  4428  		DownloadInfo: &snap.DownloadInfo{
  4429  			DownloadURL: "https://some-server.com/some/path.snap",
  4430  		},
  4431  		SideInfo:  snapsup.SideInfo,
  4432  		Type:      snap.TypeKernel,
  4433  		PlugsOnly: true,
  4434  	})
  4435  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  4436  		RealName: "kernel",
  4437  		Revision: snap.R(11),
  4438  		Channel:  "18/edge",
  4439  		SnapID:   "kernel-id",
  4440  	})
  4441  
  4442  	// verify snaps in the system state
  4443  	var snapst snapstate.SnapState
  4444  	err = snapstate.Get(s.state, "kernel", &snapst)
  4445  	c.Assert(err, IsNil)
  4446  
  4447  	c.Assert(snapst.Active, Equals, true)
  4448  	c.Assert(snapst.Channel, Equals, "18/edge")
  4449  	c.Assert(snapst.Sequence, HasLen, 2)
  4450  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  4451  		RealName: "kernel",
  4452  		SnapID:   "kernel-id",
  4453  		Channel:  "",
  4454  		Revision: snap.R(7),
  4455  	})
  4456  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  4457  		RealName: "kernel",
  4458  		Channel:  "18/edge",
  4459  		SnapID:   "kernel-id",
  4460  		Revision: snap.R(11),
  4461  	})
  4462  }
  4463  
  4464  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsNoUserRunThrough(c *C) {
  4465  	s.state.Lock()
  4466  	defer s.state.Unlock()
  4467  
  4468  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4469  		Active: true,
  4470  		Sequence: []*snap.SideInfo{
  4471  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  4472  		},
  4473  		Current:  snap.R(1),
  4474  		SnapType: "os",
  4475  	})
  4476  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4477  		Active: true,
  4478  		Sequence: []*snap.SideInfo{
  4479  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  4480  		},
  4481  		Current:  snap.R(5),
  4482  		SnapType: "app",
  4483  		UserID:   1,
  4484  	})
  4485  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  4486  		Active: true,
  4487  		Sequence: []*snap.SideInfo{
  4488  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  4489  		},
  4490  		Current:  snap.R(2),
  4491  		SnapType: "app",
  4492  		UserID:   2,
  4493  	})
  4494  
  4495  	chg := s.state.NewChange("refresh", "refresh all snaps")
  4496  	// no user is passed to use for UpdateMany
  4497  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4498  	c.Assert(err, IsNil)
  4499  	for _, ts := range tts {
  4500  		chg.AddAll(ts)
  4501  	}
  4502  	c.Check(updated, HasLen, 3)
  4503  
  4504  	s.state.Unlock()
  4505  	defer s.se.Stop()
  4506  	s.settle(c)
  4507  	s.state.Lock()
  4508  
  4509  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  4510  	c.Assert(chg.Err(), IsNil)
  4511  
  4512  	macaroonMap := map[string]string{
  4513  		"core":          "",
  4514  		"some-snap":     "macaroon",
  4515  		"services-snap": "macaroon2",
  4516  	}
  4517  
  4518  	seen := make(map[string]int)
  4519  	ir := 0
  4520  	di := 0
  4521  	for _, op := range s.fakeBackend.ops {
  4522  		switch op.op {
  4523  		case "storesvc-snap-action":
  4524  			ir++
  4525  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  4526  				{
  4527  					InstanceName:  "core",
  4528  					SnapID:        "core-snap-id",
  4529  					Revision:      snap.R(1),
  4530  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  4531  					Epoch:         snap.E("1*"),
  4532  				},
  4533  				{
  4534  					InstanceName:  "services-snap",
  4535  					SnapID:        "services-snap-id",
  4536  					Revision:      snap.R(2),
  4537  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  4538  					Epoch:         snap.E("0"),
  4539  				},
  4540  				{
  4541  					InstanceName:  "some-snap",
  4542  					SnapID:        "some-snap-id",
  4543  					Revision:      snap.R(5),
  4544  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  4545  					Epoch:         snap.E("1*"),
  4546  				},
  4547  			})
  4548  		case "storesvc-snap-action:action":
  4549  			snapID := op.action.SnapID
  4550  			seen[snapID] = op.userID
  4551  		case "storesvc-download":
  4552  			snapName := op.name
  4553  			fakeDl := s.fakeStore.downloads[di]
  4554  			// check target path separately and clear it
  4555  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  4556  			fakeDl.target = ""
  4557  			c.Check(fakeDl, DeepEquals, fakeDownload{
  4558  				macaroon: macaroonMap[snapName],
  4559  				name:     snapName,
  4560  			}, Commentf(snapName))
  4561  			di++
  4562  		}
  4563  	}
  4564  	c.Check(ir, Equals, 2)
  4565  	// we check all snaps with each user
  4566  	c.Check(seen["some-snap-id"], Equals, 1)
  4567  	c.Check(seen["services-snap-id"], Equals, 2)
  4568  	// coalesced with one of the others
  4569  	c.Check(seen["core-snap-id"] > 0, Equals, true)
  4570  }
  4571  
  4572  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsUserRunThrough(c *C) {
  4573  	s.state.Lock()
  4574  	defer s.state.Unlock()
  4575  
  4576  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4577  		Active: true,
  4578  		Sequence: []*snap.SideInfo{
  4579  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  4580  		},
  4581  		Current:  snap.R(1),
  4582  		SnapType: "os",
  4583  	})
  4584  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4585  		Active: true,
  4586  		Sequence: []*snap.SideInfo{
  4587  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  4588  		},
  4589  		Current:  snap.R(5),
  4590  		SnapType: "app",
  4591  		UserID:   1,
  4592  	})
  4593  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  4594  		Active: true,
  4595  		Sequence: []*snap.SideInfo{
  4596  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  4597  		},
  4598  		Current:  snap.R(2),
  4599  		SnapType: "app",
  4600  		UserID:   2,
  4601  	})
  4602  
  4603  	chg := s.state.NewChange("refresh", "refresh all snaps")
  4604  	// do UpdateMany using user 2 as fallback
  4605  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 2, nil)
  4606  	c.Assert(err, IsNil)
  4607  	for _, ts := range tts {
  4608  		chg.AddAll(ts)
  4609  	}
  4610  	c.Check(updated, HasLen, 3)
  4611  
  4612  	s.state.Unlock()
  4613  	defer s.se.Stop()
  4614  	s.settle(c)
  4615  	s.state.Lock()
  4616  
  4617  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  4618  	c.Assert(chg.Err(), IsNil)
  4619  
  4620  	macaroonMap := map[string]string{
  4621  		"core":          "macaroon2",
  4622  		"some-snap":     "macaroon",
  4623  		"services-snap": "macaroon2",
  4624  	}
  4625  
  4626  	type snapIDuserID struct {
  4627  		snapID string
  4628  		userID int
  4629  	}
  4630  	seen := make(map[snapIDuserID]bool)
  4631  	ir := 0
  4632  	di := 0
  4633  	for _, op := range s.fakeBackend.ops {
  4634  		switch op.op {
  4635  		case "storesvc-snap-action":
  4636  			ir++
  4637  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  4638  				{
  4639  					InstanceName:  "core",
  4640  					SnapID:        "core-snap-id",
  4641  					Revision:      snap.R(1),
  4642  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  4643  					Epoch:         snap.E("1*"),
  4644  				},
  4645  				{
  4646  					InstanceName:  "services-snap",
  4647  					SnapID:        "services-snap-id",
  4648  					Revision:      snap.R(2),
  4649  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  4650  					Epoch:         snap.E("0"),
  4651  				},
  4652  				{
  4653  					InstanceName:  "some-snap",
  4654  					SnapID:        "some-snap-id",
  4655  					Revision:      snap.R(5),
  4656  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  4657  					Epoch:         snap.E("1*"),
  4658  				},
  4659  			})
  4660  		case "storesvc-snap-action:action":
  4661  			snapID := op.action.SnapID
  4662  			seen[snapIDuserID{snapID: snapID, userID: op.userID}] = true
  4663  		case "storesvc-download":
  4664  			snapName := op.name
  4665  			fakeDl := s.fakeStore.downloads[di]
  4666  			// check target path separately and clear it
  4667  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  4668  			fakeDl.target = ""
  4669  			c.Check(fakeDl, DeepEquals, fakeDownload{
  4670  				macaroon: macaroonMap[snapName],
  4671  				name:     snapName,
  4672  			}, Commentf(snapName))
  4673  			di++
  4674  		}
  4675  	}
  4676  	c.Check(ir, Equals, 2)
  4677  	// we check all snaps with each user
  4678  	c.Check(seen, DeepEquals, map[snapIDuserID]bool{
  4679  		{snapID: "core-snap-id", userID: 2}:     true,
  4680  		{snapID: "some-snap-id", userID: 1}:     true,
  4681  		{snapID: "services-snap-id", userID: 2}: true,
  4682  	})
  4683  
  4684  	var coreState, snapState snapstate.SnapState
  4685  	// user in SnapState was preserved
  4686  	err = snapstate.Get(s.state, "some-snap", &snapState)
  4687  	c.Assert(err, IsNil)
  4688  	c.Check(snapState.UserID, Equals, 1)
  4689  	c.Check(snapState.Current, DeepEquals, snap.R(11))
  4690  
  4691  	// user in SnapState was set
  4692  	err = snapstate.Get(s.state, "core", &coreState)
  4693  	c.Assert(err, IsNil)
  4694  	c.Check(coreState.UserID, Equals, 2)
  4695  	c.Check(coreState.Current, DeepEquals, snap.R(11))
  4696  
  4697  }
  4698  
  4699  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsUserWithNoStoreAuthRunThrough(c *C) {
  4700  	s.state.Lock()
  4701  	defer s.state.Unlock()
  4702  
  4703  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4704  		Active: true,
  4705  		Sequence: []*snap.SideInfo{
  4706  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  4707  		},
  4708  		Current:  snap.R(1),
  4709  		SnapType: "os",
  4710  	})
  4711  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4712  		Active: true,
  4713  		Sequence: []*snap.SideInfo{
  4714  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  4715  		},
  4716  		Current:  snap.R(5),
  4717  		SnapType: "app",
  4718  		UserID:   1,
  4719  	})
  4720  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  4721  		Active: true,
  4722  		Sequence: []*snap.SideInfo{
  4723  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  4724  		},
  4725  		Current:  snap.R(2),
  4726  		SnapType: "app",
  4727  		UserID:   3,
  4728  	})
  4729  
  4730  	chg := s.state.NewChange("refresh", "refresh all snaps")
  4731  	// no user is passed to use for UpdateMany
  4732  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4733  	c.Assert(err, IsNil)
  4734  	for _, ts := range tts {
  4735  		chg.AddAll(ts)
  4736  	}
  4737  	c.Check(updated, HasLen, 3)
  4738  
  4739  	s.state.Unlock()
  4740  	defer s.se.Stop()
  4741  	s.settle(c)
  4742  	s.state.Lock()
  4743  
  4744  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  4745  	c.Assert(chg.Err(), IsNil)
  4746  
  4747  	macaroonMap := map[string]string{
  4748  		"core":          "",
  4749  		"some-snap":     "macaroon",
  4750  		"services-snap": "",
  4751  	}
  4752  
  4753  	seen := make(map[string]int)
  4754  	ir := 0
  4755  	di := 0
  4756  	for _, op := range s.fakeBackend.ops {
  4757  		switch op.op {
  4758  		case "storesvc-snap-action":
  4759  			ir++
  4760  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  4761  				{
  4762  					InstanceName:  "core",
  4763  					SnapID:        "core-snap-id",
  4764  					Revision:      snap.R(1),
  4765  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  4766  					Epoch:         snap.E("1*"),
  4767  				},
  4768  				{
  4769  					InstanceName:  "services-snap",
  4770  					SnapID:        "services-snap-id",
  4771  					Revision:      snap.R(2),
  4772  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  4773  					Epoch:         snap.E("0"),
  4774  				},
  4775  				{
  4776  					InstanceName:  "some-snap",
  4777  					SnapID:        "some-snap-id",
  4778  					Revision:      snap.R(5),
  4779  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  4780  					Epoch:         snap.E("1*"),
  4781  				},
  4782  			})
  4783  		case "storesvc-snap-action:action":
  4784  			snapID := op.action.SnapID
  4785  			if _, ok := seen[snapID]; !ok {
  4786  				seen[snapID] = op.userID
  4787  			}
  4788  		case "storesvc-download":
  4789  			snapName := op.name
  4790  			fakeDl := s.fakeStore.downloads[di]
  4791  			// check target path separately and clear it
  4792  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  4793  			fakeDl.target = ""
  4794  			c.Check(fakeDl, DeepEquals, fakeDownload{
  4795  				macaroon: macaroonMap[snapName],
  4796  				name:     snapName,
  4797  			}, Commentf(snapName))
  4798  			di++
  4799  		}
  4800  	}
  4801  	c.Check(ir, Equals, 1)
  4802  	// we check all snaps with each user
  4803  	c.Check(seen["some-snap-id"], Equals, 1)
  4804  	// coalesced with request for 1
  4805  	c.Check(seen["services-snap-id"], Equals, 1)
  4806  	c.Check(seen["core-snap-id"], Equals, 1)
  4807  }
  4808  
  4809  func (s *snapmgrTestSuite) TestUpdateUndoRunThrough(c *C) {
  4810  	si := snap.SideInfo{
  4811  		RealName: "some-snap",
  4812  		SnapID:   "some-snap-id",
  4813  		Revision: snap.R(7),
  4814  	}
  4815  
  4816  	s.state.Lock()
  4817  	defer s.state.Unlock()
  4818  
  4819  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4820  		Active:   true,
  4821  		Sequence: []*snap.SideInfo{&si},
  4822  		Current:  si.Revision,
  4823  		SnapType: "app",
  4824  	})
  4825  
  4826  	chg := s.state.NewChange("install", "install a snap")
  4827  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4828  	c.Assert(err, IsNil)
  4829  	chg.AddAll(ts)
  4830  
  4831  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "/some-snap/11")
  4832  
  4833  	s.state.Unlock()
  4834  	defer s.se.Stop()
  4835  	s.settle(c)
  4836  	s.state.Lock()
  4837  
  4838  	expected := fakeOps{
  4839  		{
  4840  			op: "storesvc-snap-action",
  4841  			curSnaps: []store.CurrentSnap{{
  4842  				InstanceName:  "some-snap",
  4843  				SnapID:        "some-snap-id",
  4844  				Revision:      snap.R(7),
  4845  				RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  4846  				Epoch:         snap.E("1*"),
  4847  			}},
  4848  			userID: 1,
  4849  		},
  4850  		{
  4851  			op: "storesvc-snap-action:action",
  4852  			action: store.SnapAction{
  4853  				Action:       "refresh",
  4854  				InstanceName: "some-snap",
  4855  				SnapID:       "some-snap-id",
  4856  				Channel:      "some-channel",
  4857  				Flags:        store.SnapActionEnforceValidation,
  4858  			},
  4859  			revno:  snap.R(11),
  4860  			userID: 1,
  4861  		},
  4862  		{
  4863  			op:   "storesvc-download",
  4864  			name: "some-snap",
  4865  		},
  4866  		{
  4867  			op:    "validate-snap:Doing",
  4868  			name:  "some-snap",
  4869  			revno: snap.R(11),
  4870  		},
  4871  		{
  4872  			op:  "current",
  4873  			old: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  4874  		},
  4875  		{
  4876  			op:   "open-snap-file",
  4877  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  4878  			sinfo: snap.SideInfo{
  4879  				RealName: "some-snap",
  4880  				SnapID:   "some-snap-id",
  4881  				Channel:  "some-channel",
  4882  				Revision: snap.R(11),
  4883  			},
  4884  		},
  4885  		{
  4886  			op:    "setup-snap",
  4887  			name:  "some-snap",
  4888  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  4889  			revno: snap.R(11),
  4890  		},
  4891  		{
  4892  			op:   "remove-snap-aliases",
  4893  			name: "some-snap",
  4894  		},
  4895  		{
  4896  			op:   "unlink-snap",
  4897  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  4898  		},
  4899  		{
  4900  			op:   "copy-data",
  4901  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  4902  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  4903  		},
  4904  		{
  4905  			op:    "setup-profiles:Doing",
  4906  			name:  "some-snap",
  4907  			revno: snap.R(11),
  4908  		},
  4909  		{
  4910  			op: "candidate",
  4911  			sinfo: snap.SideInfo{
  4912  				RealName: "some-snap",
  4913  				SnapID:   "some-snap-id",
  4914  				Channel:  "some-channel",
  4915  				Revision: snap.R(11),
  4916  			},
  4917  		},
  4918  		{
  4919  			op:   "link-snap.failed",
  4920  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  4921  		},
  4922  		{
  4923  			op:   "unlink-snap",
  4924  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  4925  		},
  4926  		{
  4927  			op:    "setup-profiles:Undoing",
  4928  			name:  "some-snap",
  4929  			revno: snap.R(11),
  4930  		},
  4931  		{
  4932  			op:   "undo-copy-snap-data",
  4933  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  4934  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  4935  		},
  4936  		{
  4937  			op:   "link-snap",
  4938  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  4939  		},
  4940  		{
  4941  			op: "update-aliases",
  4942  		},
  4943  		{
  4944  			op:    "undo-setup-snap",
  4945  			name:  "some-snap",
  4946  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  4947  			stype: "app",
  4948  		},
  4949  		{
  4950  			op:   "remove-snap-dir",
  4951  			name: "some-snap",
  4952  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  4953  		},
  4954  	}
  4955  
  4956  	// ensure all our tasks ran
  4957  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  4958  		macaroon: s.user.StoreMacaroon,
  4959  		name:     "some-snap",
  4960  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  4961  	}})
  4962  	// start with an easier-to-read error if this fails:
  4963  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  4964  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  4965  
  4966  	// verify snaps in the system state
  4967  	var snapst snapstate.SnapState
  4968  	err = snapstate.Get(s.state, "some-snap", &snapst)
  4969  	c.Assert(err, IsNil)
  4970  
  4971  	c.Assert(snapst.Active, Equals, true)
  4972  	c.Assert(snapst.Sequence, HasLen, 1)
  4973  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  4974  		RealName: "some-snap",
  4975  		SnapID:   "some-snap-id",
  4976  		Channel:  "",
  4977  		Revision: snap.R(7),
  4978  	})
  4979  }
  4980  
  4981  func lastWithLane(tasks []*state.Task) *state.Task {
  4982  	for i := len(tasks) - 1; i >= 0; i-- {
  4983  		if lanes := tasks[i].Lanes(); len(lanes) == 1 && lanes[0] != 0 {
  4984  			return tasks[i]
  4985  		}
  4986  	}
  4987  	return nil
  4988  }
  4989  
  4990  func (s *snapmgrTestSuite) TestUpdateUndoRestoresRevisionConfig(c *C) {
  4991  	var errorTaskExecuted bool
  4992  
  4993  	// overwrite error-trigger task handler with custom one for this test
  4994  	erroringHandler := func(task *state.Task, _ *tomb.Tomb) error {
  4995  		st := task.State()
  4996  		st.Lock()
  4997  		defer st.Unlock()
  4998  
  4999  		// modify current config of some-snap
  5000  		tr := config.NewTransaction(st)
  5001  		tr.Set("some-snap", "foo", "canary")
  5002  		tr.Commit()
  5003  
  5004  		errorTaskExecuted = true
  5005  		return errors.New("error out")
  5006  	}
  5007  	s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil)
  5008  
  5009  	si := snap.SideInfo{
  5010  		RealName: "some-snap",
  5011  		SnapID:   "some-snap-id",
  5012  		Revision: snap.R(7),
  5013  	}
  5014  	si2 := snap.SideInfo{
  5015  		RealName: "some-snap",
  5016  		SnapID:   "some-snap-id",
  5017  		Revision: snap.R(6),
  5018  	}
  5019  
  5020  	s.state.Lock()
  5021  	defer s.state.Unlock()
  5022  
  5023  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5024  		Active:   true,
  5025  		Sequence: []*snap.SideInfo{&si2, &si},
  5026  		Channel:  "stable",
  5027  		Current:  si.Revision,
  5028  		SnapType: "app",
  5029  	})
  5030  
  5031  	// set some configuration
  5032  	tr := config.NewTransaction(s.state)
  5033  	tr.Set("some-snap", "foo", "revision 7 value")
  5034  	tr.Commit()
  5035  	config.SaveRevisionConfig(s.state, "some-snap", snap.R(7))
  5036  
  5037  	chg := s.state.NewChange("install", "install a snap")
  5038  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  5039  	c.Assert(err, IsNil)
  5040  	chg.AddAll(ts)
  5041  
  5042  	last := lastWithLane(ts.Tasks())
  5043  	c.Assert(last, NotNil)
  5044  
  5045  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  5046  	terr.WaitFor(last)
  5047  	terr.JoinLane(last.Lanes()[0])
  5048  	chg.AddTask(terr)
  5049  
  5050  	s.state.Unlock()
  5051  	defer s.se.Stop()
  5052  	s.settle(c)
  5053  	s.state.Lock()
  5054  
  5055  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  5056  	c.Check(errorTaskExecuted, Equals, true)
  5057  
  5058  	// after undoing the update some-snap config should be restored to that of rev.7
  5059  	var val string
  5060  	tr = config.NewTransaction(s.state)
  5061  	c.Assert(tr.Get("some-snap", "foo", &val), IsNil)
  5062  	c.Check(val, Equals, "revision 7 value")
  5063  }
  5064  
  5065  func (s *snapmgrTestSuite) TestUpdateTotalUndoRunThrough(c *C) {
  5066  	si := snap.SideInfo{
  5067  		RealName: "some-snap",
  5068  		SnapID:   "some-snap-id",
  5069  		Revision: snap.R(7),
  5070  	}
  5071  
  5072  	s.state.Lock()
  5073  	defer s.state.Unlock()
  5074  
  5075  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5076  		Active:   true,
  5077  		Sequence: []*snap.SideInfo{&si},
  5078  		Channel:  "stable",
  5079  		Current:  si.Revision,
  5080  		SnapType: "app",
  5081  	})
  5082  
  5083  	chg := s.state.NewChange("install", "install a snap")
  5084  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  5085  	c.Assert(err, IsNil)
  5086  	chg.AddAll(ts)
  5087  
  5088  	// We need to make it not be rerefresh, and we could do just
  5089  	// that but instead we do the 'right' thing and attach it to
  5090  	// the last task that's on a lane.
  5091  	last := lastWithLane(ts.Tasks())
  5092  	c.Assert(last, NotNil)
  5093  
  5094  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  5095  	terr.WaitFor(last)
  5096  	terr.JoinLane(last.Lanes()[0])
  5097  	chg.AddTask(terr)
  5098  
  5099  	s.state.Unlock()
  5100  	defer s.se.Stop()
  5101  	s.settle(c)
  5102  	s.state.Lock()
  5103  
  5104  	expected := fakeOps{
  5105  		{
  5106  			op: "storesvc-snap-action",
  5107  			curSnaps: []store.CurrentSnap{{
  5108  				InstanceName:    "some-snap",
  5109  				SnapID:          "some-snap-id",
  5110  				Revision:        snap.R(7),
  5111  				TrackingChannel: "stable",
  5112  				RefreshedDate:   fakeRevDateEpoch.AddDate(0, 0, 7),
  5113  				Epoch:           snap.E("1*"),
  5114  			}},
  5115  			userID: 1,
  5116  		},
  5117  		{
  5118  			op: "storesvc-snap-action:action",
  5119  			action: store.SnapAction{
  5120  				Action:       "refresh",
  5121  				InstanceName: "some-snap",
  5122  				SnapID:       "some-snap-id",
  5123  				Channel:      "some-channel",
  5124  				Flags:        store.SnapActionEnforceValidation,
  5125  			},
  5126  			revno:  snap.R(11),
  5127  			userID: 1,
  5128  		},
  5129  		{
  5130  			op:   "storesvc-download",
  5131  			name: "some-snap",
  5132  		},
  5133  		{
  5134  			op:    "validate-snap:Doing",
  5135  			name:  "some-snap",
  5136  			revno: snap.R(11),
  5137  		},
  5138  		{
  5139  			op:  "current",
  5140  			old: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  5141  		},
  5142  		{
  5143  			op:   "open-snap-file",
  5144  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  5145  			sinfo: snap.SideInfo{
  5146  				RealName: "some-snap",
  5147  				SnapID:   "some-snap-id",
  5148  				Channel:  "some-channel",
  5149  				Revision: snap.R(11),
  5150  			},
  5151  		},
  5152  		{
  5153  			op:    "setup-snap",
  5154  			name:  "some-snap",
  5155  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  5156  			revno: snap.R(11),
  5157  		},
  5158  		{
  5159  			op:   "remove-snap-aliases",
  5160  			name: "some-snap",
  5161  		},
  5162  		{
  5163  			op:   "unlink-snap",
  5164  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  5165  		},
  5166  		{
  5167  			op:   "copy-data",
  5168  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  5169  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  5170  		},
  5171  		{
  5172  			op:    "setup-profiles:Doing",
  5173  			name:  "some-snap",
  5174  			revno: snap.R(11),
  5175  		},
  5176  		{
  5177  			op: "candidate",
  5178  			sinfo: snap.SideInfo{
  5179  				RealName: "some-snap",
  5180  				SnapID:   "some-snap-id",
  5181  				Channel:  "some-channel",
  5182  				Revision: snap.R(11),
  5183  			},
  5184  		},
  5185  		{
  5186  			op:   "link-snap",
  5187  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  5188  		},
  5189  		{
  5190  			op:    "auto-connect:Doing",
  5191  			name:  "some-snap",
  5192  			revno: snap.R(11),
  5193  		},
  5194  		{
  5195  			op: "update-aliases",
  5196  		},
  5197  		// undoing everything from here down...
  5198  		{
  5199  			op:   "remove-snap-aliases",
  5200  			name: "some-snap",
  5201  		},
  5202  		{
  5203  			op:   "unlink-snap",
  5204  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  5205  		},
  5206  		{
  5207  			op:    "setup-profiles:Undoing",
  5208  			name:  "some-snap",
  5209  			revno: snap.R(11),
  5210  		},
  5211  		{
  5212  			op:   "undo-copy-snap-data",
  5213  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  5214  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  5215  		},
  5216  		{
  5217  			op:   "link-snap",
  5218  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  5219  		},
  5220  		{
  5221  			op: "update-aliases",
  5222  		},
  5223  		{
  5224  			op:    "undo-setup-snap",
  5225  			name:  "some-snap",
  5226  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  5227  			stype: "app",
  5228  		},
  5229  		{
  5230  			op:   "remove-snap-dir",
  5231  			name: "some-snap",
  5232  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  5233  		},
  5234  	}
  5235  
  5236  	// ensure all our tasks ran
  5237  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  5238  		macaroon: s.user.StoreMacaroon,
  5239  		name:     "some-snap",
  5240  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  5241  	}})
  5242  	// friendlier failure first
  5243  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  5244  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  5245  
  5246  	// verify snaps in the system state
  5247  	var snapst snapstate.SnapState
  5248  	err = snapstate.Get(s.state, "some-snap", &snapst)
  5249  	c.Assert(err, IsNil)
  5250  
  5251  	c.Assert(snapst.Active, Equals, true)
  5252  	c.Assert(snapst.Channel, Equals, "stable")
  5253  	c.Assert(snapst.Sequence, HasLen, 1)
  5254  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  5255  		RealName: "some-snap",
  5256  		SnapID:   "some-snap-id",
  5257  		Channel:  "",
  5258  		Revision: snap.R(7),
  5259  	})
  5260  }
  5261  
  5262  func (s *snapmgrTestSuite) TestUpdateSameRevision(c *C) {
  5263  	si := snap.SideInfo{
  5264  		RealName: "some-snap",
  5265  		SnapID:   "some-snap-id",
  5266  		Revision: snap.R(7),
  5267  	}
  5268  
  5269  	s.state.Lock()
  5270  	defer s.state.Unlock()
  5271  
  5272  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5273  		Active:   true,
  5274  		Sequence: []*snap.SideInfo{&si},
  5275  		Channel:  "channel-for-7",
  5276  		Current:  si.Revision,
  5277  	})
  5278  
  5279  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  5280  	c.Assert(err, Equals, store.ErrNoUpdateAvailable)
  5281  }
  5282  
  5283  // A noResultsStore returns no results for install/refresh requests
  5284  type noResultsStore struct {
  5285  	*fakeStore
  5286  }
  5287  
  5288  func (n noResultsStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, user *auth.UserState, opts *store.RefreshOptions) ([]*snap.Info, error) {
  5289  	return nil, &store.SnapActionError{NoResults: true}
  5290  }
  5291  
  5292  func (s *snapmgrTestSuite) TestUpdateNoStoreResults(c *C) {
  5293  	s.state.Lock()
  5294  	defer s.state.Unlock()
  5295  
  5296  	snapstate.ReplaceStore(s.state, noResultsStore{fakeStore: s.fakeStore})
  5297  
  5298  	// this is an atypical case in which the store didn't return
  5299  	// an error nor a result, we are defensive and return
  5300  	// a reasonable error
  5301  	si := snap.SideInfo{
  5302  		RealName: "some-snap",
  5303  		SnapID:   "some-snap-id",
  5304  		Revision: snap.R(7),
  5305  	}
  5306  
  5307  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5308  		Active:   true,
  5309  		Sequence: []*snap.SideInfo{&si},
  5310  		Channel:  "channel-for-7",
  5311  		Current:  si.Revision,
  5312  	})
  5313  
  5314  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  5315  	c.Assert(err, Equals, snapstate.ErrMissingExpectedResult)
  5316  }
  5317  
  5318  func (s *snapmgrTestSuite) TestUpdateNoStoreResultsWithChannelChange(c *C) {
  5319  	s.state.Lock()
  5320  	defer s.state.Unlock()
  5321  
  5322  	snapstate.ReplaceStore(s.state, noResultsStore{fakeStore: s.fakeStore})
  5323  
  5324  	// this is an atypical case in which the store didn't return
  5325  	// an error nor a result, we are defensive and return
  5326  	// a reasonable error
  5327  	si := snap.SideInfo{
  5328  		RealName: "some-snap",
  5329  		SnapID:   "some-snap-id",
  5330  		Revision: snap.R(7),
  5331  	}
  5332  
  5333  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5334  		Active:   true,
  5335  		Sequence: []*snap.SideInfo{&si},
  5336  		Channel:  "channel-for-9",
  5337  		Current:  si.Revision,
  5338  	})
  5339  
  5340  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  5341  	c.Assert(err, Equals, snapstate.ErrMissingExpectedResult)
  5342  }
  5343  
  5344  func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchesChannel(c *C) {
  5345  	si := snap.SideInfo{
  5346  		RealName: "some-snap",
  5347  		SnapID:   "some-snap-id",
  5348  		Revision: snap.R(7),
  5349  	}
  5350  
  5351  	s.state.Lock()
  5352  	defer s.state.Unlock()
  5353  
  5354  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5355  		Active:   true,
  5356  		Sequence: []*snap.SideInfo{&si},
  5357  		Channel:  "other-chanenl",
  5358  		Current:  si.Revision,
  5359  	})
  5360  
  5361  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  5362  	c.Assert(err, IsNil)
  5363  	c.Check(ts.Tasks(), HasLen, 1)
  5364  	c.Check(ts.Tasks()[0].Kind(), Equals, "switch-snap-channel")
  5365  }
  5366  
  5367  func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchesChannelConflict(c *C) {
  5368  	si := snap.SideInfo{
  5369  		RealName: "some-snap",
  5370  		SnapID:   "some-snap-id",
  5371  		Revision: snap.R(7),
  5372  	}
  5373  
  5374  	s.state.Lock()
  5375  	defer s.state.Unlock()
  5376  
  5377  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5378  		Active:   true,
  5379  		Sequence: []*snap.SideInfo{&si},
  5380  		Channel:  "other-channel",
  5381  		Current:  si.Revision,
  5382  	})
  5383  
  5384  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  5385  	c.Assert(err, IsNil)
  5386  	// make it visible
  5387  	s.state.NewChange("refresh", "refresh a snap").AddAll(ts)
  5388  
  5389  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  5390  	c.Check(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`)
  5391  }
  5392  
  5393  func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchChannelRunThrough(c *C) {
  5394  	si := snap.SideInfo{
  5395  		RealName: "some-snap",
  5396  		SnapID:   "some-snap-id",
  5397  		Channel:  "other-channel",
  5398  		Revision: snap.R(7),
  5399  	}
  5400  
  5401  	s.state.Lock()
  5402  	defer s.state.Unlock()
  5403  
  5404  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5405  		Active:   true,
  5406  		Sequence: []*snap.SideInfo{&si},
  5407  		Channel:  "other-channel",
  5408  		Current:  si.Revision,
  5409  	})
  5410  
  5411  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  5412  	c.Assert(err, IsNil)
  5413  	chg := s.state.NewChange("refresh", "refresh a snap")
  5414  	chg.AddAll(ts)
  5415  
  5416  	s.state.Unlock()
  5417  	defer s.se.Stop()
  5418  	s.settle(c)
  5419  	s.state.Lock()
  5420  
  5421  	expected := fakeOps{
  5422  		// we just expect the "storesvc-snap-action" ops, we
  5423  		// don't have a fakeOp for switchChannel because it has
  5424  		// not a backend method, it just manipulates the state
  5425  		{
  5426  			op: "storesvc-snap-action",
  5427  			curSnaps: []store.CurrentSnap{{
  5428  				InstanceName:    "some-snap",
  5429  				SnapID:          "some-snap-id",
  5430  				Revision:        snap.R(7),
  5431  				TrackingChannel: "other-channel",
  5432  				RefreshedDate:   fakeRevDateEpoch.AddDate(0, 0, 7),
  5433  				Epoch:           snap.E("1*"),
  5434  			}},
  5435  			userID: 1,
  5436  		},
  5437  
  5438  		{
  5439  			op: "storesvc-snap-action:action",
  5440  			action: store.SnapAction{
  5441  				Action:       "refresh",
  5442  				InstanceName: "some-snap",
  5443  				SnapID:       "some-snap-id",
  5444  				Channel:      "channel-for-7",
  5445  				Flags:        store.SnapActionEnforceValidation,
  5446  			},
  5447  			userID: 1,
  5448  		},
  5449  	}
  5450  
  5451  	// start with an easier-to-read error if this fails:
  5452  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  5453  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  5454  
  5455  	// verify snapSetup info
  5456  	var snapsup snapstate.SnapSetup
  5457  	task := ts.Tasks()[0]
  5458  	err = task.Get("snap-setup", &snapsup)
  5459  	c.Assert(err, IsNil)
  5460  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  5461  		Channel:  "channel-for-7",
  5462  		SideInfo: snapsup.SideInfo,
  5463  	})
  5464  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  5465  		RealName: "some-snap",
  5466  		SnapID:   "some-snap-id",
  5467  		Revision: snap.R(7),
  5468  		Channel:  "channel-for-7",
  5469  	})
  5470  
  5471  	// verify snaps in the system state
  5472  	var snapst snapstate.SnapState
  5473  	err = snapstate.Get(s.state, "some-snap", &snapst)
  5474  	c.Assert(err, IsNil)
  5475  
  5476  	c.Assert(snapst.Active, Equals, true)
  5477  	c.Assert(snapst.Sequence, HasLen, 1)
  5478  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  5479  		RealName: "some-snap",
  5480  		SnapID:   "some-snap-id",
  5481  		Channel:  "channel-for-7",
  5482  		Revision: snap.R(7),
  5483  	})
  5484  }
  5485  
  5486  func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidation(c *C) {
  5487  	si := snap.SideInfo{
  5488  		RealName: "some-snap",
  5489  		SnapID:   "some-snap-id",
  5490  		Revision: snap.R(7),
  5491  	}
  5492  
  5493  	s.state.Lock()
  5494  	defer s.state.Unlock()
  5495  
  5496  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5497  		Active:   true,
  5498  		Sequence: []*snap.SideInfo{&si},
  5499  		Channel:  "channel-for-7",
  5500  		Current:  si.Revision,
  5501  	})
  5502  
  5503  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  5504  	c.Assert(err, IsNil)
  5505  	c.Check(ts.Tasks(), HasLen, 1)
  5506  	c.Check(ts.Tasks()[0].Kind(), Equals, "toggle-snap-flags")
  5507  }
  5508  
  5509  func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidationConflict(c *C) {
  5510  	si := snap.SideInfo{
  5511  		RealName: "some-snap",
  5512  		SnapID:   "some-snap-id",
  5513  		Revision: snap.R(7),
  5514  	}
  5515  
  5516  	s.state.Lock()
  5517  	defer s.state.Unlock()
  5518  
  5519  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5520  		Active:   true,
  5521  		Sequence: []*snap.SideInfo{&si},
  5522  		Channel:  "channel-for-7",
  5523  		Current:  si.Revision,
  5524  	})
  5525  
  5526  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  5527  	c.Assert(err, IsNil)
  5528  	// make it visible
  5529  	s.state.NewChange("refresh", "refresh a snap").AddAll(ts)
  5530  
  5531  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  5532  	c.Check(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`)
  5533  
  5534  }
  5535  
  5536  func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidationRunThrough(c *C) {
  5537  	si := snap.SideInfo{
  5538  		RealName: "some-snap",
  5539  		SnapID:   "some-snap-id",
  5540  		Revision: snap.R(7),
  5541  		Channel:  "channel-for-7",
  5542  	}
  5543  
  5544  	s.state.Lock()
  5545  	defer s.state.Unlock()
  5546  
  5547  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5548  		Active:   true,
  5549  		Sequence: []*snap.SideInfo{&si},
  5550  		Channel:  "channel-for-7",
  5551  		Current:  si.Revision,
  5552  	})
  5553  
  5554  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  5555  	c.Assert(err, IsNil)
  5556  
  5557  	chg := s.state.NewChange("refresh", "refresh a snap")
  5558  	chg.AddAll(ts)
  5559  
  5560  	s.state.Unlock()
  5561  	defer s.se.Stop()
  5562  	s.settle(c)
  5563  	s.state.Lock()
  5564  
  5565  	// verify snapSetup info
  5566  	var snapsup snapstate.SnapSetup
  5567  	task := ts.Tasks()[0]
  5568  	err = task.Get("snap-setup", &snapsup)
  5569  	c.Assert(err, IsNil)
  5570  	c.Check(snapsup, DeepEquals, snapstate.SnapSetup{
  5571  		SideInfo: snapsup.SideInfo,
  5572  		Flags: snapstate.Flags{
  5573  			IgnoreValidation: true,
  5574  		},
  5575  	})
  5576  	c.Check(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  5577  		RealName: "some-snap",
  5578  		SnapID:   "some-snap-id",
  5579  		Revision: snap.R(7),
  5580  		Channel:  "channel-for-7",
  5581  	})
  5582  
  5583  	// verify snaps in the system state
  5584  	var snapst snapstate.SnapState
  5585  	err = snapstate.Get(s.state, "some-snap", &snapst)
  5586  	c.Assert(err, IsNil)
  5587  
  5588  	c.Check(snapst.Active, Equals, true)
  5589  	c.Check(snapst.Sequence, HasLen, 1)
  5590  	c.Check(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  5591  		RealName: "some-snap",
  5592  		SnapID:   "some-snap-id",
  5593  		Channel:  "channel-for-7",
  5594  		Revision: snap.R(7),
  5595  	})
  5596  	c.Check(snapst.IgnoreValidation, Equals, true)
  5597  }
  5598  
  5599  func (s *snapmgrTestSuite) TestUpdateValidateRefreshesSaysNo(c *C) {
  5600  	si := snap.SideInfo{
  5601  		RealName: "some-snap",
  5602  		SnapID:   "some-snap-id",
  5603  		Revision: snap.R(7),
  5604  	}
  5605  
  5606  	s.state.Lock()
  5607  	defer s.state.Unlock()
  5608  
  5609  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5610  		Active:   true,
  5611  		Sequence: []*snap.SideInfo{&si},
  5612  		Current:  si.Revision,
  5613  	})
  5614  
  5615  	validateErr := errors.New("refresh control error")
  5616  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  5617  		c.Check(refreshes, HasLen, 1)
  5618  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  5619  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  5620  		c.Check(ignoreValidation, HasLen, 0)
  5621  		return nil, validateErr
  5622  	}
  5623  	// hook it up
  5624  	snapstate.ValidateRefreshes = validateRefreshes
  5625  
  5626  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
  5627  	c.Assert(err, Equals, validateErr)
  5628  }
  5629  
  5630  func (s *snapmgrTestSuite) TestUpdateValidateRefreshesSaysNoButIgnoreValidationIsSet(c *C) {
  5631  	si := snap.SideInfo{
  5632  		RealName: "some-snap",
  5633  		SnapID:   "some-snap-id",
  5634  		Revision: snap.R(7),
  5635  	}
  5636  
  5637  	s.state.Lock()
  5638  	defer s.state.Unlock()
  5639  
  5640  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5641  		Active:   true,
  5642  		Sequence: []*snap.SideInfo{&si},
  5643  		Current:  si.Revision,
  5644  		SnapType: "app",
  5645  	})
  5646  
  5647  	validateErr := errors.New("refresh control error")
  5648  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  5649  		return nil, validateErr
  5650  	}
  5651  	// hook it up
  5652  	snapstate.ValidateRefreshes = validateRefreshes
  5653  
  5654  	flags := snapstate.Flags{JailMode: true, IgnoreValidation: true}
  5655  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  5656  	c.Assert(err, IsNil)
  5657  
  5658  	var snapsup snapstate.SnapSetup
  5659  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  5660  	c.Assert(err, IsNil)
  5661  	c.Check(snapsup.Flags, DeepEquals, flags.ForSnapSetup())
  5662  }
  5663  
  5664  func (s *snapmgrTestSuite) TestUpdateIgnoreValidationSticky(c *C) {
  5665  	si := snap.SideInfo{
  5666  		RealName: "some-snap",
  5667  		SnapID:   "some-snap-id",
  5668  		Revision: snap.R(7),
  5669  	}
  5670  
  5671  	s.state.Lock()
  5672  	defer s.state.Unlock()
  5673  
  5674  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5675  		Active:   true,
  5676  		Sequence: []*snap.SideInfo{&si},
  5677  		Current:  si.Revision,
  5678  		SnapType: "app",
  5679  	})
  5680  
  5681  	validateErr := errors.New("refresh control error")
  5682  	validateRefreshesFail := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  5683  		c.Check(refreshes, HasLen, 1)
  5684  		if len(ignoreValidation) == 0 {
  5685  			return nil, validateErr
  5686  		}
  5687  		c.Check(ignoreValidation, DeepEquals, map[string]bool{
  5688  			"some-snap": true,
  5689  		})
  5690  		return refreshes, nil
  5691  	}
  5692  	// hook it up
  5693  	snapstate.ValidateRefreshes = validateRefreshesFail
  5694  
  5695  	flags := snapstate.Flags{IgnoreValidation: true}
  5696  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  5697  	c.Assert(err, IsNil)
  5698  
  5699  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  5700  		op: "storesvc-snap-action",
  5701  		curSnaps: []store.CurrentSnap{{
  5702  			InstanceName:     "some-snap",
  5703  			SnapID:           "some-snap-id",
  5704  			Revision:         snap.R(7),
  5705  			IgnoreValidation: false,
  5706  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  5707  			Epoch:            snap.E("1*"),
  5708  		}},
  5709  		userID: 1,
  5710  	})
  5711  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  5712  		op:    "storesvc-snap-action:action",
  5713  		revno: snap.R(11),
  5714  		action: store.SnapAction{
  5715  			Action:       "refresh",
  5716  			InstanceName: "some-snap",
  5717  			SnapID:       "some-snap-id",
  5718  			Channel:      "stable",
  5719  			Flags:        store.SnapActionIgnoreValidation,
  5720  		},
  5721  		userID: 1,
  5722  	})
  5723  
  5724  	chg := s.state.NewChange("refresh", "refresh snap")
  5725  	chg.AddAll(ts)
  5726  
  5727  	s.state.Unlock()
  5728  	defer s.se.Stop()
  5729  	s.settle(c)
  5730  	s.state.Lock()
  5731  
  5732  	// verify snap has IgnoreValidation set
  5733  	var snapst snapstate.SnapState
  5734  	err = snapstate.Get(s.state, "some-snap", &snapst)
  5735  	c.Assert(err, IsNil)
  5736  	c.Check(snapst.IgnoreValidation, Equals, true)
  5737  	c.Check(snapst.Current, Equals, snap.R(11))
  5738  
  5739  	s.fakeBackend.ops = nil
  5740  	s.fakeStore.refreshRevnos = map[string]snap.Revision{
  5741  		"some-snap-id": snap.R(12),
  5742  	}
  5743  	_, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  5744  	c.Assert(err, IsNil)
  5745  	c.Check(tts, HasLen, 2)
  5746  	verifyLastTasksetIsReRefresh(c, tts)
  5747  
  5748  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  5749  		op: "storesvc-snap-action",
  5750  		curSnaps: []store.CurrentSnap{{
  5751  			InstanceName:     "some-snap",
  5752  			SnapID:           "some-snap-id",
  5753  			Revision:         snap.R(11),
  5754  			TrackingChannel:  "stable",
  5755  			IgnoreValidation: true,
  5756  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 11),
  5757  			Epoch:            snap.E("1*"),
  5758  		}},
  5759  		userID: 1,
  5760  	})
  5761  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  5762  		op:    "storesvc-snap-action:action",
  5763  		revno: snap.R(12),
  5764  		action: store.SnapAction{
  5765  			Action:       "refresh",
  5766  			InstanceName: "some-snap",
  5767  			SnapID:       "some-snap-id",
  5768  			Flags:        0,
  5769  		},
  5770  		userID: 1,
  5771  	})
  5772  
  5773  	chg = s.state.NewChange("refresh", "refresh snaps")
  5774  	chg.AddAll(tts[0])
  5775  
  5776  	s.state.Unlock()
  5777  	defer s.se.Stop()
  5778  	s.settle(c)
  5779  	s.state.Lock()
  5780  
  5781  	snapst = snapstate.SnapState{}
  5782  	err = snapstate.Get(s.state, "some-snap", &snapst)
  5783  	c.Assert(err, IsNil)
  5784  	c.Check(snapst.IgnoreValidation, Equals, true)
  5785  	c.Check(snapst.Current, Equals, snap.R(12))
  5786  
  5787  	// reset ignore validation
  5788  	s.fakeBackend.ops = nil
  5789  	s.fakeStore.refreshRevnos = map[string]snap.Revision{
  5790  		"some-snap-id": snap.R(11),
  5791  	}
  5792  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  5793  		return refreshes, nil
  5794  	}
  5795  	// hook it up
  5796  	snapstate.ValidateRefreshes = validateRefreshes
  5797  	flags = snapstate.Flags{}
  5798  	ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  5799  	c.Assert(err, IsNil)
  5800  
  5801  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  5802  		op: "storesvc-snap-action",
  5803  		curSnaps: []store.CurrentSnap{{
  5804  			InstanceName:     "some-snap",
  5805  			SnapID:           "some-snap-id",
  5806  			Revision:         snap.R(12),
  5807  			TrackingChannel:  "stable",
  5808  			IgnoreValidation: true,
  5809  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 12),
  5810  			Epoch:            snap.E("1*"),
  5811  		}},
  5812  		userID: 1,
  5813  	})
  5814  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  5815  		op:    "storesvc-snap-action:action",
  5816  		revno: snap.R(11),
  5817  		action: store.SnapAction{
  5818  			Action:       "refresh",
  5819  			InstanceName: "some-snap",
  5820  			SnapID:       "some-snap-id",
  5821  			Channel:      "stable",
  5822  			Flags:        store.SnapActionEnforceValidation,
  5823  		},
  5824  		userID: 1,
  5825  	})
  5826  
  5827  	chg = s.state.NewChange("refresh", "refresh snap")
  5828  	chg.AddAll(ts)
  5829  
  5830  	s.state.Unlock()
  5831  	defer s.se.Stop()
  5832  	s.settle(c)
  5833  	s.state.Lock()
  5834  
  5835  	snapst = snapstate.SnapState{}
  5836  	err = snapstate.Get(s.state, "some-snap", &snapst)
  5837  	c.Assert(err, IsNil)
  5838  	c.Check(snapst.IgnoreValidation, Equals, false)
  5839  	c.Check(snapst.Current, Equals, snap.R(11))
  5840  }
  5841  
  5842  func (s *snapmgrTestSuite) TestParallelInstanceUpdateIgnoreValidationSticky(c *C) {
  5843  	si := snap.SideInfo{
  5844  		RealName: "some-snap",
  5845  		SnapID:   "some-snap-id",
  5846  		Revision: snap.R(7),
  5847  	}
  5848  
  5849  	s.state.Lock()
  5850  	defer s.state.Unlock()
  5851  
  5852  	tr := config.NewTransaction(s.state)
  5853  	tr.Set("core", "experimental.parallel-instances", true)
  5854  	tr.Commit()
  5855  
  5856  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5857  		Active:   true,
  5858  		Sequence: []*snap.SideInfo{&si},
  5859  		Current:  si.Revision,
  5860  		SnapType: "app",
  5861  	})
  5862  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  5863  		Active:      true,
  5864  		Sequence:    []*snap.SideInfo{&si},
  5865  		Current:     si.Revision,
  5866  		SnapType:    "app",
  5867  		InstanceKey: "instance",
  5868  	})
  5869  
  5870  	validateErr := errors.New("refresh control error")
  5871  	validateRefreshesFail := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  5872  		c.Check(refreshes, HasLen, 2)
  5873  		if len(ignoreValidation) == 0 {
  5874  			return nil, validateErr
  5875  		}
  5876  		c.Check(ignoreValidation, DeepEquals, map[string]bool{
  5877  			"some-snap_instance": true,
  5878  		})
  5879  		return refreshes, nil
  5880  	}
  5881  	// hook it up
  5882  	snapstate.ValidateRefreshes = validateRefreshesFail
  5883  
  5884  	flags := snapstate.Flags{IgnoreValidation: true}
  5885  	ts, err := snapstate.Update(s.state, "some-snap_instance", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  5886  	c.Assert(err, IsNil)
  5887  
  5888  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  5889  		op: "storesvc-snap-action",
  5890  		curSnaps: []store.CurrentSnap{{
  5891  			InstanceName:     "some-snap",
  5892  			SnapID:           "some-snap-id",
  5893  			Revision:         snap.R(7),
  5894  			IgnoreValidation: false,
  5895  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  5896  			Epoch:            snap.E("1*"),
  5897  		}, {
  5898  			InstanceName:     "some-snap_instance",
  5899  			SnapID:           "some-snap-id",
  5900  			Revision:         snap.R(7),
  5901  			IgnoreValidation: false,
  5902  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  5903  			Epoch:            snap.E("1*"),
  5904  		}},
  5905  		userID: 1,
  5906  	})
  5907  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  5908  		op:    "storesvc-snap-action:action",
  5909  		revno: snap.R(11),
  5910  		action: store.SnapAction{
  5911  			Action:       "refresh",
  5912  			InstanceName: "some-snap_instance",
  5913  			SnapID:       "some-snap-id",
  5914  			Channel:      "stable",
  5915  			Flags:        store.SnapActionIgnoreValidation,
  5916  		},
  5917  		userID: 1,
  5918  	})
  5919  
  5920  	chg := s.state.NewChange("refresh", "refresh snaps")
  5921  	chg.AddAll(ts)
  5922  
  5923  	s.state.Unlock()
  5924  	defer s.se.Stop()
  5925  	s.settle(c)
  5926  	s.state.Lock()
  5927  
  5928  	// ensure all our tasks ran
  5929  	c.Assert(chg.Err(), IsNil)
  5930  	c.Assert(chg.IsReady(), Equals, true)
  5931  
  5932  	// verify snap 'instance' has IgnoreValidation set and the snap was
  5933  	// updated
  5934  	var snapst snapstate.SnapState
  5935  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  5936  	c.Assert(err, IsNil)
  5937  	c.Check(snapst.IgnoreValidation, Equals, true)
  5938  	c.Check(snapst.Current, Equals, snap.R(11))
  5939  	// and the other snap does not
  5940  	err = snapstate.Get(s.state, "some-snap", &snapst)
  5941  	c.Assert(err, IsNil)
  5942  	c.Check(snapst.Current, Equals, snap.R(7))
  5943  	c.Check(snapst.IgnoreValidation, Equals, false)
  5944  
  5945  	s.fakeBackend.ops = nil
  5946  	s.fakeStore.refreshRevnos = map[string]snap.Revision{
  5947  		"some-snap-id": snap.R(12),
  5948  	}
  5949  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "some-snap_instance"}, s.user.ID, nil)
  5950  	c.Assert(err, IsNil)
  5951  	c.Check(tts, HasLen, 3)
  5952  	verifyLastTasksetIsReRefresh(c, tts)
  5953  	sort.Strings(updates)
  5954  	c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"})
  5955  
  5956  	chg = s.state.NewChange("refresh", "refresh snaps")
  5957  	for _, ts := range tts[:len(tts)-1] {
  5958  		chg.AddAll(ts)
  5959  	}
  5960  
  5961  	s.state.Unlock()
  5962  	s.settle(c)
  5963  	s.state.Lock()
  5964  
  5965  	// ensure all our tasks ran
  5966  	c.Assert(chg.Err(), IsNil)
  5967  	c.Assert(chg.IsReady(), Equals, true)
  5968  
  5969  	err = snapstate.Get(s.state, "some-snap", &snapst)
  5970  	c.Assert(err, IsNil)
  5971  	c.Check(snapst.IgnoreValidation, Equals, false)
  5972  	c.Check(snapst.Current, Equals, snap.R(12))
  5973  
  5974  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  5975  	c.Assert(err, IsNil)
  5976  	c.Check(snapst.IgnoreValidation, Equals, true)
  5977  	c.Check(snapst.Current, Equals, snap.R(12))
  5978  
  5979  	for i := 0; i < 2; i++ {
  5980  		op := s.fakeBackend.ops[i]
  5981  		switch op.op {
  5982  		case "storesvc-snap-action":
  5983  			c.Check(op, DeepEquals, fakeOp{
  5984  				op: "storesvc-snap-action",
  5985  				curSnaps: []store.CurrentSnap{{
  5986  					InstanceName:     "some-snap",
  5987  					SnapID:           "some-snap-id",
  5988  					Revision:         snap.R(7),
  5989  					IgnoreValidation: false,
  5990  					RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  5991  					Epoch:            snap.E("1*"),
  5992  				}, {
  5993  					InstanceName:     "some-snap_instance",
  5994  					SnapID:           "some-snap-id",
  5995  					Revision:         snap.R(11),
  5996  					TrackingChannel:  "stable",
  5997  					IgnoreValidation: true,
  5998  					RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 11),
  5999  					Epoch:            snap.E("1*"),
  6000  				}},
  6001  				userID: 1,
  6002  			})
  6003  		case "storesvc-snap-action:action":
  6004  			switch op.action.InstanceName {
  6005  			case "some-snap":
  6006  				c.Check(op, DeepEquals, fakeOp{
  6007  					op:    "storesvc-snap-action:action",
  6008  					revno: snap.R(12),
  6009  					action: store.SnapAction{
  6010  						Action:       "refresh",
  6011  						InstanceName: "some-snap",
  6012  						SnapID:       "some-snap-id",
  6013  						Flags:        0,
  6014  					},
  6015  					userID: 1,
  6016  				})
  6017  			case "some-snap_instance":
  6018  				c.Check(op, DeepEquals, fakeOp{
  6019  					op:    "storesvc-snap-action:action",
  6020  					revno: snap.R(12),
  6021  					action: store.SnapAction{
  6022  						Action:       "refresh",
  6023  						InstanceName: "some-snap_instance",
  6024  						SnapID:       "some-snap-id",
  6025  						Flags:        0,
  6026  					},
  6027  					userID: 1,
  6028  				})
  6029  			default:
  6030  				c.Fatalf("unexpected instance name %q", op.action.InstanceName)
  6031  			}
  6032  		default:
  6033  			c.Fatalf("unexpected action %q", op.op)
  6034  		}
  6035  	}
  6036  
  6037  }
  6038  
  6039  func (s *snapmgrTestSuite) TestUpdateFromLocal(c *C) {
  6040  	si := snap.SideInfo{
  6041  		RealName: "some-snap",
  6042  		Revision: snap.R("x1"),
  6043  	}
  6044  
  6045  	s.state.Lock()
  6046  	defer s.state.Unlock()
  6047  
  6048  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  6049  		Active:   true,
  6050  		Sequence: []*snap.SideInfo{&si},
  6051  		Channel:  "channel-for-7",
  6052  		Current:  si.Revision,
  6053  	})
  6054  
  6055  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  6056  	c.Assert(err, Equals, store.ErrLocalSnap)
  6057  }
  6058  
  6059  func (s *snapmgrTestSuite) TestUpdateAmend(c *C) {
  6060  	si := snap.SideInfo{
  6061  		RealName: "some-snap",
  6062  		Revision: snap.R("x1"),
  6063  	}
  6064  
  6065  	s.state.Lock()
  6066  	defer s.state.Unlock()
  6067  
  6068  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  6069  		Active:   true,
  6070  		Sequence: []*snap.SideInfo{&si},
  6071  		Channel:  "channel-for-7",
  6072  		Current:  si.Revision,
  6073  	})
  6074  
  6075  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{Amend: true})
  6076  	c.Assert(err, IsNil)
  6077  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  6078  
  6079  	// ensure we go from local to store revision-7
  6080  	var snapsup snapstate.SnapSetup
  6081  	tasks := ts.Tasks()
  6082  	c.Check(tasks[1].Kind(), Equals, "download-snap")
  6083  	err = tasks[1].Get("snap-setup", &snapsup)
  6084  	c.Assert(err, IsNil)
  6085  	c.Check(snapsup.Revision(), Equals, snap.R(7))
  6086  }
  6087  
  6088  func (s *snapmgrTestSuite) TestUpdateAmendSnapNotFound(c *C) {
  6089  	si := snap.SideInfo{
  6090  		RealName: "snap-unknown",
  6091  		Revision: snap.R("x1"),
  6092  	}
  6093  
  6094  	s.state.Lock()
  6095  	defer s.state.Unlock()
  6096  
  6097  	snapstate.Set(s.state, "snap-unknown", &snapstate.SnapState{
  6098  		Active:   true,
  6099  		Sequence: []*snap.SideInfo{&si},
  6100  		Channel:  "stable",
  6101  		Current:  si.Revision,
  6102  	})
  6103  
  6104  	_, err := snapstate.Update(s.state, "snap-unknown", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{Amend: true})
  6105  	c.Assert(err, Equals, store.ErrSnapNotFound)
  6106  }
  6107  
  6108  func (s *snapmgrTestSuite) TestSingleUpdateBlockedRevision(c *C) {
  6109  	// single updates should *not* set the block list
  6110  	si7 := snap.SideInfo{
  6111  		RealName: "some-snap",
  6112  		SnapID:   "some-snap-id",
  6113  		Revision: snap.R(7),
  6114  	}
  6115  	si11 := snap.SideInfo{
  6116  		RealName: "some-snap",
  6117  		SnapID:   "some-snap-id",
  6118  		Revision: snap.R(11),
  6119  	}
  6120  
  6121  	s.state.Lock()
  6122  	defer s.state.Unlock()
  6123  
  6124  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  6125  		Active:   true,
  6126  		Sequence: []*snap.SideInfo{&si7, &si11},
  6127  		Current:  si7.Revision,
  6128  		SnapType: "app",
  6129  	})
  6130  
  6131  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  6132  	c.Assert(err, IsNil)
  6133  
  6134  	c.Assert(s.fakeBackend.ops, HasLen, 2)
  6135  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  6136  		op: "storesvc-snap-action",
  6137  		curSnaps: []store.CurrentSnap{{
  6138  			InstanceName:  "some-snap",
  6139  			SnapID:        "some-snap-id",
  6140  			Revision:      snap.R(7),
  6141  			RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  6142  			Epoch:         snap.E("1*"),
  6143  		}},
  6144  		userID: 1,
  6145  	})
  6146  }
  6147  
  6148  func (s *snapmgrTestSuite) TestMultiUpdateBlockedRevision(c *C) {
  6149  	// multi-updates should *not* set the block list
  6150  	si7 := snap.SideInfo{
  6151  		RealName: "some-snap",
  6152  		SnapID:   "some-snap-id",
  6153  		Revision: snap.R(7),
  6154  	}
  6155  	si11 := snap.SideInfo{
  6156  		RealName: "some-snap",
  6157  		SnapID:   "some-snap-id",
  6158  		Revision: snap.R(11),
  6159  	}
  6160  
  6161  	s.state.Lock()
  6162  	defer s.state.Unlock()
  6163  
  6164  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  6165  		Active:   true,
  6166  		Sequence: []*snap.SideInfo{&si7, &si11},
  6167  		Current:  si7.Revision,
  6168  		SnapType: "app",
  6169  	})
  6170  
  6171  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  6172  	c.Assert(err, IsNil)
  6173  	c.Check(updates, DeepEquals, []string{"some-snap"})
  6174  
  6175  	c.Assert(s.fakeBackend.ops, HasLen, 2)
  6176  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  6177  		op: "storesvc-snap-action",
  6178  		curSnaps: []store.CurrentSnap{{
  6179  			InstanceName:  "some-snap",
  6180  			SnapID:        "some-snap-id",
  6181  			Revision:      snap.R(7),
  6182  			RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  6183  			Epoch:         snap.E("1*"),
  6184  		}},
  6185  		userID: 1,
  6186  	})
  6187  }
  6188  
  6189  func (s *snapmgrTestSuite) TestAllUpdateBlockedRevision(c *C) {
  6190  	//  update-all *should* set the block list
  6191  	si7 := snap.SideInfo{
  6192  		RealName: "some-snap",
  6193  		SnapID:   "some-snap-id",
  6194  		Revision: snap.R(7),
  6195  	}
  6196  	si11 := snap.SideInfo{
  6197  		RealName: "some-snap",
  6198  		SnapID:   "some-snap-id",
  6199  		Revision: snap.R(11),
  6200  	}
  6201  
  6202  	s.state.Lock()
  6203  	defer s.state.Unlock()
  6204  
  6205  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  6206  		Active:   true,
  6207  		Sequence: []*snap.SideInfo{&si7, &si11},
  6208  		Current:  si7.Revision,
  6209  	})
  6210  
  6211  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil)
  6212  	c.Check(err, IsNil)
  6213  	c.Check(updates, HasLen, 0)
  6214  
  6215  	c.Assert(s.fakeBackend.ops, HasLen, 2)
  6216  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  6217  		op: "storesvc-snap-action",
  6218  		curSnaps: []store.CurrentSnap{{
  6219  			InstanceName:  "some-snap",
  6220  			SnapID:        "some-snap-id",
  6221  			Revision:      snap.R(7),
  6222  			RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  6223  			Block:         []snap.Revision{snap.R(11)},
  6224  			Epoch:         snap.E("1*"),
  6225  		}},
  6226  		userID: 1,
  6227  	})
  6228  }
  6229  
  6230  var orthogonalAutoAliasesScenarios = []struct {
  6231  	aliasesBefore map[string][]string
  6232  	names         []string
  6233  	prune         []string
  6234  	update        bool
  6235  	new           bool
  6236  }{
  6237  	{nil, nil, nil, true, true},
  6238  	{nil, []string{"some-snap"}, nil, true, false},
  6239  	{nil, []string{"other-snap"}, nil, false, true},
  6240  	{map[string][]string{"some-snap": {"aliasA", "aliasC"}}, []string{"some-snap"}, nil, true, false},
  6241  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, []string{"other-snap"}, []string{"other-snap"}, false, false},
  6242  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, nil, []string{"other-snap"}, true, false},
  6243  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, []string{"some-snap"}, nil, true, false},
  6244  	{map[string][]string{"other-snap": {"aliasC"}}, []string{"other-snap"}, []string{"other-snap"}, false, true},
  6245  	{map[string][]string{"other-snap": {"aliasC"}}, nil, []string{"other-snap"}, true, true},
  6246  	{map[string][]string{"other-snap": {"aliasC"}}, []string{"some-snap"}, nil, true, false},
  6247  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, []string{"some-snap"}, []string{"other-snap"}, true, false},
  6248  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, nil, []string{"other-snap", "some-snap"}, true, true},
  6249  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, []string{"other-snap"}, []string{"other-snap", "some-snap"}, false, true},
  6250  	{map[string][]string{"some-snap": {"aliasB"}}, nil, []string{"some-snap"}, true, true},
  6251  	{map[string][]string{"some-snap": {"aliasB"}}, []string{"other-snap"}, []string{"some-snap"}, false, true},
  6252  	{map[string][]string{"some-snap": {"aliasB"}}, []string{"some-snap"}, nil, true, false},
  6253  	{map[string][]string{"other-snap": {"aliasA"}}, nil, []string{"other-snap"}, true, true},
  6254  	{map[string][]string{"other-snap": {"aliasA"}}, []string{"other-snap"}, []string{"other-snap"}, false, true},
  6255  	{map[string][]string{"other-snap": {"aliasA"}}, []string{"some-snap"}, []string{"other-snap"}, true, false},
  6256  }
  6257  
  6258  func (s *snapmgrTestSuite) TestUpdateManyAutoAliasesScenarios(c *C) {
  6259  	s.state.Lock()
  6260  	defer s.state.Unlock()
  6261  
  6262  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
  6263  		Active: true,
  6264  		Sequence: []*snap.SideInfo{
  6265  			{RealName: "other-snap", SnapID: "other-snap-id", Revision: snap.R(2)},
  6266  		},
  6267  		Current:  snap.R(2),
  6268  		SnapType: "app",
  6269  	})
  6270  
  6271  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  6272  		switch info.InstanceName() {
  6273  		case "some-snap":
  6274  			return map[string]string{"aliasA": "cmdA"}, nil
  6275  		case "other-snap":
  6276  			return map[string]string{"aliasB": "cmdB"}, nil
  6277  		}
  6278  		return nil, nil
  6279  	}
  6280  
  6281  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  6282  		Active: true,
  6283  		Sequence: []*snap.SideInfo{
  6284  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  6285  		},
  6286  		Current:  snap.R(4),
  6287  		SnapType: "app",
  6288  	})
  6289  
  6290  	expectedSet := func(aliases []string) map[string]bool {
  6291  		res := make(map[string]bool, len(aliases))
  6292  		for _, alias := range aliases {
  6293  			res[alias] = true
  6294  		}
  6295  		return res
  6296  	}
  6297  
  6298  	for _, scenario := range orthogonalAutoAliasesScenarios {
  6299  		for _, instanceName := range []string{"some-snap", "other-snap"} {
  6300  			var snapst snapstate.SnapState
  6301  			err := snapstate.Get(s.state, instanceName, &snapst)
  6302  			c.Assert(err, IsNil)
  6303  			snapst.Aliases = nil
  6304  			snapst.AutoAliasesDisabled = false
  6305  			if autoAliases := scenario.aliasesBefore[instanceName]; autoAliases != nil {
  6306  				targets := make(map[string]*snapstate.AliasTarget)
  6307  				for _, alias := range autoAliases {
  6308  					targets[alias] = &snapstate.AliasTarget{Auto: "cmd" + alias[len(alias)-1:]}
  6309  				}
  6310  
  6311  				snapst.Aliases = targets
  6312  			}
  6313  			snapstate.Set(s.state, instanceName, &snapst)
  6314  		}
  6315  
  6316  		updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, scenario.names, s.user.ID, nil)
  6317  		c.Check(err, IsNil)
  6318  		verifyLastTasksetIsReRefresh(c, tts)
  6319  
  6320  		_, dropped, err := snapstate.AutoAliasesDelta(s.state, []string{"some-snap", "other-snap"})
  6321  		c.Assert(err, IsNil)
  6322  
  6323  		j := 0
  6324  		expectedUpdatesSet := make(map[string]bool)
  6325  		var expectedPruned map[string]map[string]bool
  6326  		var pruneTs *state.TaskSet
  6327  		if len(scenario.prune) != 0 {
  6328  			pruneTs = tts[0]
  6329  			j++
  6330  			taskAliases := make(map[string]map[string]bool)
  6331  			for _, aliasTask := range pruneTs.Tasks() {
  6332  				c.Check(aliasTask.Kind(), Equals, "prune-auto-aliases")
  6333  				var aliases []string
  6334  				err := aliasTask.Get("aliases", &aliases)
  6335  				c.Assert(err, IsNil)
  6336  				snapsup, err := snapstate.TaskSnapSetup(aliasTask)
  6337  				c.Assert(err, IsNil)
  6338  				taskAliases[snapsup.InstanceName()] = expectedSet(aliases)
  6339  			}
  6340  			expectedPruned = make(map[string]map[string]bool)
  6341  			for _, instanceName := range scenario.prune {
  6342  				expectedPruned[instanceName] = expectedSet(dropped[instanceName])
  6343  				if instanceName == "other-snap" && !scenario.new && !scenario.update {
  6344  					expectedUpdatesSet["other-snap"] = true
  6345  				}
  6346  			}
  6347  			c.Check(taskAliases, DeepEquals, expectedPruned)
  6348  		}
  6349  		if scenario.update {
  6350  			updateTs := tts[j]
  6351  			j++
  6352  			expectedUpdatesSet["some-snap"] = true
  6353  			first := updateTs.Tasks()[0]
  6354  			c.Check(first.Kind(), Equals, "prerequisites")
  6355  			wait := false
  6356  			if expectedPruned["other-snap"]["aliasA"] {
  6357  				wait = true
  6358  			} else if expectedPruned["some-snap"] != nil {
  6359  				wait = true
  6360  			}
  6361  			if wait {
  6362  				c.Check(first.WaitTasks(), DeepEquals, pruneTs.Tasks())
  6363  			} else {
  6364  				c.Check(first.WaitTasks(), HasLen, 0)
  6365  			}
  6366  		}
  6367  		if scenario.new {
  6368  			newTs := tts[j]
  6369  			j++
  6370  			expectedUpdatesSet["other-snap"] = true
  6371  			tasks := newTs.Tasks()
  6372  			c.Check(tasks, HasLen, 1)
  6373  			aliasTask := tasks[0]
  6374  			c.Check(aliasTask.Kind(), Equals, "refresh-aliases")
  6375  
  6376  			wait := false
  6377  			if expectedPruned["some-snap"]["aliasB"] {
  6378  				wait = true
  6379  			} else if expectedPruned["other-snap"] != nil {
  6380  				wait = true
  6381  			}
  6382  			if wait {
  6383  				c.Check(aliasTask.WaitTasks(), DeepEquals, pruneTs.Tasks())
  6384  			} else {
  6385  				c.Check(aliasTask.WaitTasks(), HasLen, 0)
  6386  			}
  6387  		}
  6388  		c.Assert(j, Equals, len(tts)-1, Commentf("%#v", scenario))
  6389  
  6390  		// check reported updated names
  6391  		c.Check(len(updates) > 0, Equals, true)
  6392  		sort.Strings(updates)
  6393  		expectedUpdates := make([]string, 0, len(expectedUpdatesSet))
  6394  		for x := range expectedUpdatesSet {
  6395  			expectedUpdates = append(expectedUpdates, x)
  6396  		}
  6397  		sort.Strings(expectedUpdates)
  6398  		c.Check(updates, DeepEquals, expectedUpdates)
  6399  	}
  6400  }
  6401  
  6402  func (s *snapmgrTestSuite) TestUpdateOneAutoAliasesScenarios(c *C) {
  6403  	s.state.Lock()
  6404  	defer s.state.Unlock()
  6405  
  6406  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
  6407  		Active: true,
  6408  		Sequence: []*snap.SideInfo{
  6409  			{RealName: "other-snap", SnapID: "other-snap-id", Revision: snap.R(2)},
  6410  		},
  6411  		Current:  snap.R(2),
  6412  		SnapType: "app",
  6413  	})
  6414  
  6415  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  6416  		switch info.InstanceName() {
  6417  		case "some-snap":
  6418  			return map[string]string{"aliasA": "cmdA"}, nil
  6419  		case "other-snap":
  6420  			return map[string]string{"aliasB": "cmdB"}, nil
  6421  		}
  6422  		return nil, nil
  6423  	}
  6424  
  6425  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  6426  		Active: true,
  6427  		Sequence: []*snap.SideInfo{
  6428  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  6429  		},
  6430  		Current:  snap.R(4),
  6431  		SnapType: "app",
  6432  	})
  6433  
  6434  	expectedSet := func(aliases []string) map[string]bool {
  6435  		res := make(map[string]bool, len(aliases))
  6436  		for _, alias := range aliases {
  6437  			res[alias] = true
  6438  		}
  6439  		return res
  6440  	}
  6441  
  6442  	for _, scenario := range orthogonalAutoAliasesScenarios {
  6443  		if len(scenario.names) != 1 {
  6444  			continue
  6445  		}
  6446  
  6447  		for _, instanceName := range []string{"some-snap", "other-snap"} {
  6448  			var snapst snapstate.SnapState
  6449  			err := snapstate.Get(s.state, instanceName, &snapst)
  6450  			c.Assert(err, IsNil)
  6451  			snapst.Aliases = nil
  6452  			snapst.AutoAliasesDisabled = false
  6453  			if autoAliases := scenario.aliasesBefore[instanceName]; autoAliases != nil {
  6454  				targets := make(map[string]*snapstate.AliasTarget)
  6455  				for _, alias := range autoAliases {
  6456  					targets[alias] = &snapstate.AliasTarget{Auto: "cmd" + alias[len(alias)-1:]}
  6457  				}
  6458  
  6459  				snapst.Aliases = targets
  6460  			}
  6461  			snapstate.Set(s.state, instanceName, &snapst)
  6462  		}
  6463  
  6464  		ts, err := snapstate.Update(s.state, scenario.names[0], nil, s.user.ID, snapstate.Flags{})
  6465  		c.Assert(err, IsNil)
  6466  		_, dropped, err := snapstate.AutoAliasesDelta(s.state, []string{"some-snap", "other-snap"})
  6467  		c.Assert(err, IsNil)
  6468  
  6469  		j := 0
  6470  
  6471  		tasks := ts.Tasks()
  6472  		// make sure the last task from Update is the rerefresh
  6473  		c.Assert(tasks[len(tasks)-1].Kind(), Equals, "check-rerefresh")
  6474  		tasks = tasks[:len(tasks)-1] // and now forget about it
  6475  
  6476  		var expectedPruned map[string]map[string]bool
  6477  		var pruneTasks []*state.Task
  6478  		if len(scenario.prune) != 0 {
  6479  			nprune := len(scenario.prune)
  6480  			pruneTasks = tasks[:nprune]
  6481  			j += nprune
  6482  			taskAliases := make(map[string]map[string]bool)
  6483  			for _, aliasTask := range pruneTasks {
  6484  				c.Check(aliasTask.Kind(), Equals, "prune-auto-aliases")
  6485  				var aliases []string
  6486  				err := aliasTask.Get("aliases", &aliases)
  6487  				c.Assert(err, IsNil)
  6488  				snapsup, err := snapstate.TaskSnapSetup(aliasTask)
  6489  				c.Assert(err, IsNil)
  6490  				taskAliases[snapsup.InstanceName()] = expectedSet(aliases)
  6491  			}
  6492  			expectedPruned = make(map[string]map[string]bool)
  6493  			for _, instanceName := range scenario.prune {
  6494  				expectedPruned[instanceName] = expectedSet(dropped[instanceName])
  6495  			}
  6496  			c.Check(taskAliases, DeepEquals, expectedPruned)
  6497  		}
  6498  		if scenario.update {
  6499  			first := tasks[j]
  6500  			j += 19
  6501  			c.Check(first.Kind(), Equals, "prerequisites")
  6502  			wait := false
  6503  			if expectedPruned["other-snap"]["aliasA"] {
  6504  				wait = true
  6505  			} else if expectedPruned["some-snap"] != nil {
  6506  				wait = true
  6507  			}
  6508  			if wait {
  6509  				c.Check(first.WaitTasks(), DeepEquals, pruneTasks)
  6510  			} else {
  6511  				c.Check(first.WaitTasks(), HasLen, 0)
  6512  			}
  6513  		}
  6514  		if scenario.new {
  6515  			aliasTask := tasks[j]
  6516  			j++
  6517  			c.Check(aliasTask.Kind(), Equals, "refresh-aliases")
  6518  			wait := false
  6519  			if expectedPruned["some-snap"]["aliasB"] {
  6520  				wait = true
  6521  			} else if expectedPruned["other-snap"] != nil {
  6522  				wait = true
  6523  			}
  6524  			if wait {
  6525  				c.Check(aliasTask.WaitTasks(), DeepEquals, pruneTasks)
  6526  			} else {
  6527  				c.Check(aliasTask.WaitTasks(), HasLen, 0)
  6528  			}
  6529  		}
  6530  		c.Assert(len(tasks), Equals, j, Commentf("%#v", scenario))
  6531  
  6532  		// conflict checks are triggered
  6533  		chg := s.state.NewChange("update", "...")
  6534  		chg.AddAll(ts)
  6535  		err = snapstate.CheckChangeConflict(s.state, scenario.names[0], nil)
  6536  		c.Check(err, ErrorMatches, `.* has "update" change in progress`)
  6537  		chg.SetStatus(state.DoneStatus)
  6538  	}
  6539  }
  6540  
  6541  func (s *snapmgrTestSuite) TestUpdateLocalSnapFails(c *C) {
  6542  	si := snap.SideInfo{
  6543  		RealName: "some-snap",
  6544  		Revision: snap.R(7),
  6545  	}
  6546  
  6547  	s.state.Lock()
  6548  	defer s.state.Unlock()
  6549  
  6550  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  6551  		Active:   true,
  6552  		Sequence: []*snap.SideInfo{&si},
  6553  		Current:  si.Revision,
  6554  	})
  6555  
  6556  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  6557  	c.Assert(err, Equals, store.ErrLocalSnap)
  6558  }
  6559  
  6560  func (s *snapmgrTestSuite) TestUpdateDisabledUnsupported(c *C) {
  6561  	si := snap.SideInfo{
  6562  		RealName: "some-snap",
  6563  		SnapID:   "some-snap-id",
  6564  		Revision: snap.R(7),
  6565  	}
  6566  
  6567  	s.state.Lock()
  6568  	defer s.state.Unlock()
  6569  
  6570  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  6571  		Active:   false,
  6572  		Sequence: []*snap.SideInfo{&si},
  6573  		Current:  si.Revision,
  6574  	})
  6575  
  6576  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  6577  	c.Assert(err, ErrorMatches, `refreshing disabled snap "some-snap" not supported`)
  6578  }
  6579  
  6580  func (s *snapmgrTestSuite) TestUpdateKernelTrackChecksSwitchingTracks(c *C) {
  6581  	si := snap.SideInfo{
  6582  		RealName: "kernel",
  6583  		SnapID:   "kernel-id",
  6584  		Revision: snap.R(7),
  6585  	}
  6586  
  6587  	s.state.Lock()
  6588  	defer s.state.Unlock()
  6589  
  6590  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  6591  	defer r()
  6592  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  6593  		Active:   true,
  6594  		Sequence: []*snap.SideInfo{&si},
  6595  		Current:  si.Revision,
  6596  		Channel:  "18/stable",
  6597  	})
  6598  
  6599  	// switching tracks is not ok
  6600  	_, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{})
  6601  	c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`)
  6602  
  6603  	// no change to the channel is ok
  6604  	_, err = snapstate.Update(s.state, "kernel", nil, s.user.ID, snapstate.Flags{})
  6605  	c.Assert(err, IsNil)
  6606  
  6607  	// switching risk level is ok
  6608  	_, err = snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "18/beta"}, s.user.ID, snapstate.Flags{})
  6609  	c.Assert(err, IsNil)
  6610  
  6611  	// switching just risk within the pinned track is ok
  6612  	_, err = snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "beta"}, s.user.ID, snapstate.Flags{})
  6613  	c.Assert(err, IsNil)
  6614  }
  6615  
  6616  func (s *snapmgrTestSuite) TestUpdateGadgetTrackChecksSwitchingTracks(c *C) {
  6617  	si := snap.SideInfo{
  6618  		RealName: "brand-gadget",
  6619  		SnapID:   "brand-gadget-id",
  6620  		Revision: snap.R(7),
  6621  	}
  6622  
  6623  	s.state.Lock()
  6624  	defer s.state.Unlock()
  6625  
  6626  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
  6627  	defer r()
  6628  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
  6629  		Active:   true,
  6630  		Sequence: []*snap.SideInfo{&si},
  6631  		Current:  si.Revision,
  6632  		Channel:  "18/stable",
  6633  	})
  6634  
  6635  	// switching tracks is not ok
  6636  	_, err := snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{})
  6637  	c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`)
  6638  
  6639  	// no change to the channel is ok
  6640  	_, err = snapstate.Update(s.state, "brand-gadget", nil, s.user.ID, snapstate.Flags{})
  6641  	c.Assert(err, IsNil)
  6642  
  6643  	// switching risk level is ok
  6644  	_, err = snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "18/beta"}, s.user.ID, snapstate.Flags{})
  6645  	c.Assert(err, IsNil)
  6646  
  6647  	// switching just risk within the pinned track is ok
  6648  	_, err = snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "beta"}, s.user.ID, snapstate.Flags{})
  6649  	c.Assert(err, IsNil)
  6650  
  6651  }
  6652  
  6653  func makeTestSnap(c *C, snapYamlContent string) (snapFilePath string) {
  6654  	return snaptest.MakeTestSnapWithFiles(c, snapYamlContent, nil)
  6655  }
  6656  
  6657  func (s *snapmgrTestSuite) TestInstallFirstLocalRunThrough(c *C) {
  6658  	// use the real thing for this one
  6659  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
  6660  
  6661  	s.state.Lock()
  6662  	defer s.state.Unlock()
  6663  
  6664  	mockSnap := makeTestSnap(c, `name: mock
  6665  version: 1.0`)
  6666  	chg := s.state.NewChange("install", "install a local snap")
  6667  	ts, info, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{})
  6668  	c.Assert(err, IsNil)
  6669  	chg.AddAll(ts)
  6670  
  6671  	// ensure the returned info is correct
  6672  	c.Check(info.SideInfo.RealName, Equals, "mock")
  6673  	c.Check(info.Version, Equals, "1.0")
  6674  
  6675  	s.state.Unlock()
  6676  	defer s.se.Stop()
  6677  	s.settle(c)
  6678  	s.state.Lock()
  6679  
  6680  	expected := fakeOps{
  6681  		{
  6682  			// only local install was run, i.e. first actions are pseudo-action current
  6683  			op:  "current",
  6684  			old: "<no-current>",
  6685  		},
  6686  		{
  6687  			// and setup-snap
  6688  			op:    "setup-snap",
  6689  			name:  "mock",
  6690  			path:  mockSnap,
  6691  			revno: snap.R("x1"),
  6692  		},
  6693  		{
  6694  			op:   "copy-data",
  6695  			path: filepath.Join(dirs.SnapMountDir, "mock/x1"),
  6696  			old:  "<no-old>",
  6697  		},
  6698  		{
  6699  			op:    "setup-profiles:Doing",
  6700  			name:  "mock",
  6701  			revno: snap.R("x1"),
  6702  		},
  6703  		{
  6704  			op: "candidate",
  6705  			sinfo: snap.SideInfo{
  6706  				RealName: "mock",
  6707  				Revision: snap.R("x1"),
  6708  			},
  6709  		},
  6710  		{
  6711  			op:   "link-snap",
  6712  			path: filepath.Join(dirs.SnapMountDir, "mock/x1"),
  6713  		},
  6714  		{
  6715  			op:    "auto-connect:Doing",
  6716  			name:  "mock",
  6717  			revno: snap.R("x1"),
  6718  		},
  6719  		{
  6720  			op: "update-aliases",
  6721  		},
  6722  		{
  6723  			op:    "cleanup-trash",
  6724  			name:  "mock",
  6725  			revno: snap.R("x1"),
  6726  		},
  6727  	}
  6728  
  6729  	// start with an easier-to-read error if this fails:
  6730  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  6731  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  6732  
  6733  	// verify snapSetup info
  6734  	var snapsup snapstate.SnapSetup
  6735  	task := ts.Tasks()[1]
  6736  	err = task.Get("snap-setup", &snapsup)
  6737  	c.Assert(err, IsNil)
  6738  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  6739  		SnapPath:  mockSnap,
  6740  		SideInfo:  snapsup.SideInfo,
  6741  		Type:      snap.TypeApp,
  6742  		PlugsOnly: true,
  6743  	})
  6744  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  6745  		RealName: "mock",
  6746  		Revision: snap.R(-1),
  6747  	})
  6748  
  6749  	// verify snaps in the system state
  6750  	var snapst snapstate.SnapState
  6751  	err = snapstate.Get(s.state, "mock", &snapst)
  6752  	c.Assert(err, IsNil)
  6753  
  6754  	c.Assert(snapst.Active, Equals, true)
  6755  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  6756  		RealName: "mock",
  6757  		Channel:  "",
  6758  		Revision: snap.R(-1),
  6759  	})
  6760  	c.Assert(snapst.LocalRevision(), Equals, snap.R(-1))
  6761  }
  6762  
  6763  func (s *snapmgrTestSuite) TestInstallSubsequentLocalRunThrough(c *C) {
  6764  	// use the real thing for this one
  6765  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
  6766  
  6767  	s.state.Lock()
  6768  	defer s.state.Unlock()
  6769  
  6770  	snapstate.Set(s.state, "mock", &snapstate.SnapState{
  6771  		Active: true,
  6772  		Sequence: []*snap.SideInfo{
  6773  			{RealName: "mock", Revision: snap.R(-2)},
  6774  		},
  6775  		Current:  snap.R(-2),
  6776  		SnapType: "app",
  6777  	})
  6778  
  6779  	mockSnap := makeTestSnap(c, `name: mock
  6780  version: 1.0
  6781  epoch: 1*
  6782  `)
  6783  	chg := s.state.NewChange("install", "install a local snap")
  6784  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{})
  6785  	c.Assert(err, IsNil)
  6786  	chg.AddAll(ts)
  6787  
  6788  	s.state.Unlock()
  6789  	defer s.se.Stop()
  6790  	s.settle(c)
  6791  	s.state.Lock()
  6792  
  6793  	expected := fakeOps{
  6794  		{
  6795  			op:  "current",
  6796  			old: filepath.Join(dirs.SnapMountDir, "mock/x2"),
  6797  		},
  6798  		{
  6799  			op:    "setup-snap",
  6800  			name:  "mock",
  6801  			path:  mockSnap,
  6802  			revno: snap.R("x3"),
  6803  		},
  6804  		{
  6805  			op:   "remove-snap-aliases",
  6806  			name: "mock",
  6807  		},
  6808  		{
  6809  			op:   "unlink-snap",
  6810  			path: filepath.Join(dirs.SnapMountDir, "mock/x2"),
  6811  		},
  6812  		{
  6813  			op:   "copy-data",
  6814  			path: filepath.Join(dirs.SnapMountDir, "mock/x3"),
  6815  			old:  filepath.Join(dirs.SnapMountDir, "mock/x2"),
  6816  		},
  6817  		{
  6818  			op:    "setup-profiles:Doing",
  6819  			name:  "mock",
  6820  			revno: snap.R(-3),
  6821  		},
  6822  		{
  6823  			op: "candidate",
  6824  			sinfo: snap.SideInfo{
  6825  				RealName: "mock",
  6826  				Revision: snap.R(-3),
  6827  			},
  6828  		},
  6829  		{
  6830  			op:   "link-snap",
  6831  			path: filepath.Join(dirs.SnapMountDir, "mock/x3"),
  6832  		},
  6833  		{
  6834  			op:    "auto-connect:Doing",
  6835  			name:  "mock",
  6836  			revno: snap.R("x3"),
  6837  		},
  6838  		{
  6839  			op: "update-aliases",
  6840  		},
  6841  		{
  6842  			op:    "cleanup-trash",
  6843  			name:  "mock",
  6844  			revno: snap.R("x3"),
  6845  		},
  6846  	}
  6847  
  6848  	// start with an easier-to-read error if this fails:
  6849  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  6850  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  6851  
  6852  	// verify snapSetup info
  6853  	var snapsup snapstate.SnapSetup
  6854  	task := ts.Tasks()[1]
  6855  	err = task.Get("snap-setup", &snapsup)
  6856  	c.Assert(err, IsNil)
  6857  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  6858  		SnapPath:  mockSnap,
  6859  		SideInfo:  snapsup.SideInfo,
  6860  		Type:      snap.TypeApp,
  6861  		PlugsOnly: true,
  6862  	})
  6863  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  6864  		RealName: "mock",
  6865  		Revision: snap.R(-3),
  6866  	})
  6867  
  6868  	// verify snaps in the system state
  6869  	var snapst snapstate.SnapState
  6870  	err = snapstate.Get(s.state, "mock", &snapst)
  6871  	c.Assert(err, IsNil)
  6872  
  6873  	c.Assert(snapst.Active, Equals, true)
  6874  	c.Assert(snapst.Sequence, HasLen, 2)
  6875  	c.Assert(snapst.CurrentSideInfo(), DeepEquals, &snap.SideInfo{
  6876  		RealName: "mock",
  6877  		Channel:  "",
  6878  		Revision: snap.R(-3),
  6879  	})
  6880  	c.Assert(snapst.LocalRevision(), Equals, snap.R(-3))
  6881  }
  6882  
  6883  func (s *snapmgrTestSuite) TestInstallOldSubsequentLocalRunThrough(c *C) {
  6884  	// use the real thing for this one
  6885  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
  6886  
  6887  	s.state.Lock()
  6888  	defer s.state.Unlock()
  6889  
  6890  	snapstate.Set(s.state, "mock", &snapstate.SnapState{
  6891  		Active: true,
  6892  		Sequence: []*snap.SideInfo{
  6893  			{RealName: "mock", Revision: snap.R(100001)},
  6894  		},
  6895  		Current:  snap.R(100001),
  6896  		SnapType: "app",
  6897  	})
  6898  
  6899  	mockSnap := makeTestSnap(c, `name: mock
  6900  version: 1.0
  6901  epoch: 1*
  6902  `)
  6903  	chg := s.state.NewChange("install", "install a local snap")
  6904  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{})
  6905  	c.Assert(err, IsNil)
  6906  	chg.AddAll(ts)
  6907  
  6908  	s.state.Unlock()
  6909  	defer s.se.Stop()
  6910  	s.settle(c)
  6911  	s.state.Lock()
  6912  
  6913  	expected := fakeOps{
  6914  		{
  6915  			// ensure only local install was run, i.e. first action is pseudo-action current
  6916  			op:  "current",
  6917  			old: filepath.Join(dirs.SnapMountDir, "mock/100001"),
  6918  		},
  6919  		{
  6920  			// and setup-snap
  6921  			op:    "setup-snap",
  6922  			name:  "mock",
  6923  			path:  mockSnap,
  6924  			revno: snap.R("x1"),
  6925  		},
  6926  		{
  6927  			op:   "remove-snap-aliases",
  6928  			name: "mock",
  6929  		},
  6930  		{
  6931  			op:   "unlink-snap",
  6932  			path: filepath.Join(dirs.SnapMountDir, "mock/100001"),
  6933  		},
  6934  		{
  6935  			op:   "copy-data",
  6936  			path: filepath.Join(dirs.SnapMountDir, "mock/x1"),
  6937  			old:  filepath.Join(dirs.SnapMountDir, "mock/100001"),
  6938  		},
  6939  		{
  6940  			op:    "setup-profiles:Doing",
  6941  			name:  "mock",
  6942  			revno: snap.R("x1"),
  6943  		},
  6944  		{
  6945  			op: "candidate",
  6946  			sinfo: snap.SideInfo{
  6947  				RealName: "mock",
  6948  				Revision: snap.R("x1"),
  6949  			},
  6950  		},
  6951  		{
  6952  			op:   "link-snap",
  6953  			path: filepath.Join(dirs.SnapMountDir, "mock/x1"),
  6954  		},
  6955  		{
  6956  			op:    "auto-connect:Doing",
  6957  			name:  "mock",
  6958  			revno: snap.R("x1"),
  6959  		},
  6960  		{
  6961  			op: "update-aliases",
  6962  		},
  6963  		{
  6964  			// and cleanup
  6965  			op:    "cleanup-trash",
  6966  			name:  "mock",
  6967  			revno: snap.R("x1"),
  6968  		},
  6969  	}
  6970  	// start with an easier-to-read error if this fails:
  6971  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  6972  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  6973  
  6974  	var snapst snapstate.SnapState
  6975  	err = snapstate.Get(s.state, "mock", &snapst)
  6976  	c.Assert(err, IsNil)
  6977  
  6978  	c.Assert(snapst.Active, Equals, true)
  6979  	c.Assert(snapst.Sequence, HasLen, 2)
  6980  	c.Assert(snapst.CurrentSideInfo(), DeepEquals, &snap.SideInfo{
  6981  		RealName: "mock",
  6982  		Channel:  "",
  6983  		Revision: snap.R(-1),
  6984  	})
  6985  	c.Assert(snapst.LocalRevision(), Equals, snap.R(-1))
  6986  }
  6987  
  6988  func (s *snapmgrTestSuite) TestInstallPathWithMetadataRunThrough(c *C) {
  6989  	// use the real thing for this one
  6990  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
  6991  
  6992  	s.state.Lock()
  6993  	defer s.state.Unlock()
  6994  
  6995  	someSnap := makeTestSnap(c, `name: orig-name
  6996  version: 1.0`)
  6997  	chg := s.state.NewChange("install", "install a local snap")
  6998  
  6999  	si := &snap.SideInfo{
  7000  		RealName: "some-snap",
  7001  		SnapID:   "some-snap-id",
  7002  		Revision: snap.R(42),
  7003  	}
  7004  	ts, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "", snapstate.Flags{Required: true})
  7005  	c.Assert(err, IsNil)
  7006  	chg.AddAll(ts)
  7007  
  7008  	s.state.Unlock()
  7009  	defer s.se.Stop()
  7010  	s.settle(c)
  7011  	s.state.Lock()
  7012  
  7013  	// ensure only local install was run, i.e. first actions are pseudo-action current
  7014  	c.Assert(s.fakeBackend.ops.Ops(), HasLen, 9)
  7015  	c.Check(s.fakeBackend.ops[0].op, Equals, "current")
  7016  	c.Check(s.fakeBackend.ops[0].old, Equals, "<no-current>")
  7017  	// and setup-snap
  7018  	c.Check(s.fakeBackend.ops[1].op, Equals, "setup-snap")
  7019  	c.Check(s.fakeBackend.ops[1].name, Equals, "some-snap")
  7020  	c.Check(s.fakeBackend.ops[1].path, Matches, `.*/orig-name_1.0_all.snap`)
  7021  	c.Check(s.fakeBackend.ops[1].revno, Equals, snap.R(42))
  7022  
  7023  	c.Check(s.fakeBackend.ops[4].op, Equals, "candidate")
  7024  	c.Check(s.fakeBackend.ops[4].sinfo, DeepEquals, *si)
  7025  	c.Check(s.fakeBackend.ops[5].op, Equals, "link-snap")
  7026  	c.Check(s.fakeBackend.ops[5].path, Equals, filepath.Join(dirs.SnapMountDir, "some-snap/42"))
  7027  
  7028  	// verify snapSetup info
  7029  	var snapsup snapstate.SnapSetup
  7030  	task := ts.Tasks()[0]
  7031  	err = task.Get("snap-setup", &snapsup)
  7032  	c.Assert(err, IsNil)
  7033  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  7034  		SnapPath: someSnap,
  7035  		SideInfo: snapsup.SideInfo,
  7036  		Flags: snapstate.Flags{
  7037  			Required: true,
  7038  		},
  7039  		Type:      snap.TypeApp,
  7040  		PlugsOnly: true,
  7041  	})
  7042  	c.Assert(snapsup.SideInfo, DeepEquals, si)
  7043  
  7044  	// verify snaps in the system state
  7045  	var snapst snapstate.SnapState
  7046  	err = snapstate.Get(s.state, "some-snap", &snapst)
  7047  	c.Assert(err, IsNil)
  7048  
  7049  	c.Assert(snapst.Active, Equals, true)
  7050  	c.Assert(snapst.Channel, Equals, "")
  7051  	c.Assert(snapst.Sequence[0], DeepEquals, si)
  7052  	c.Assert(snapst.LocalRevision().Unset(), Equals, true)
  7053  	c.Assert(snapst.Required, Equals, true)
  7054  }
  7055  
  7056  func (s *snapmgrTestSuite) TestRemoveRunThrough(c *C) {
  7057  	c.Assert(snapstate.KeepAuxStoreInfo("some-snap-id", nil), IsNil)
  7058  	c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FilePresent)
  7059  	si := snap.SideInfo{
  7060  		SnapID:   "some-snap-id",
  7061  		RealName: "some-snap",
  7062  		Revision: snap.R(7),
  7063  	}
  7064  
  7065  	s.state.Lock()
  7066  	defer s.state.Unlock()
  7067  
  7068  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  7069  		Active:   true,
  7070  		Sequence: []*snap.SideInfo{&si},
  7071  		Current:  si.Revision,
  7072  		SnapType: "app",
  7073  	})
  7074  
  7075  	chg := s.state.NewChange("remove", "remove a snap")
  7076  	ts, err := snapstate.Remove(s.state, "some-snap", snap.R(0), nil)
  7077  	c.Assert(err, IsNil)
  7078  	chg.AddAll(ts)
  7079  
  7080  	s.state.Unlock()
  7081  	defer s.se.Stop()
  7082  	s.settle(c)
  7083  	s.state.Lock()
  7084  
  7085  	expected := fakeOps{
  7086  		{
  7087  			op:    "auto-disconnect:Doing",
  7088  			name:  "some-snap",
  7089  			revno: snap.R(7),
  7090  		},
  7091  		{
  7092  			op:   "remove-snap-aliases",
  7093  			name: "some-snap",
  7094  		},
  7095  		{
  7096  			op:   "unlink-snap",
  7097  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  7098  		},
  7099  		{
  7100  			op:    "remove-profiles:Doing",
  7101  			name:  "some-snap",
  7102  			revno: snap.R(7),
  7103  		},
  7104  		{
  7105  			op:   "remove-snap-data",
  7106  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  7107  		},
  7108  		{
  7109  			op:   "remove-snap-common-data",
  7110  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  7111  		},
  7112  		{
  7113  			op:   "remove-snap-data-dir",
  7114  			name: "some-snap",
  7115  			path: filepath.Join(dirs.SnapDataDir, "some-snap"),
  7116  		},
  7117  		{
  7118  			op:    "remove-snap-files",
  7119  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  7120  			stype: "app",
  7121  		},
  7122  		{
  7123  			op:   "discard-namespace",
  7124  			name: "some-snap",
  7125  		},
  7126  		{
  7127  			op:   "remove-snap-dir",
  7128  			name: "some-snap",
  7129  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  7130  		},
  7131  	}
  7132  	// start with an easier-to-read error if this fails:
  7133  	c.Check(len(s.fakeBackend.ops), Equals, len(expected))
  7134  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  7135  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  7136  
  7137  	// verify snapSetup info
  7138  	tasks := ts.Tasks()
  7139  	for _, t := range tasks {
  7140  		if t.Kind() == "run-hook" {
  7141  			continue
  7142  		}
  7143  		if t.Kind() == "save-snapshot" {
  7144  			continue
  7145  		}
  7146  		snapsup, err := snapstate.TaskSnapSetup(t)
  7147  		c.Assert(err, IsNil)
  7148  
  7149  		var expSnapSetup *snapstate.SnapSetup
  7150  		switch t.Kind() {
  7151  		case "discard-conns":
  7152  			expSnapSetup = &snapstate.SnapSetup{
  7153  				SideInfo: &snap.SideInfo{
  7154  					RealName: "some-snap",
  7155  				},
  7156  			}
  7157  		case "clear-snap", "discard-snap":
  7158  			expSnapSetup = &snapstate.SnapSetup{
  7159  				SideInfo: &snap.SideInfo{
  7160  					RealName: "some-snap",
  7161  					SnapID:   "some-snap-id",
  7162  					Revision: snap.R(7),
  7163  				},
  7164  			}
  7165  		default:
  7166  			expSnapSetup = &snapstate.SnapSetup{
  7167  				SideInfo: &snap.SideInfo{
  7168  					RealName: "some-snap",
  7169  					Revision: snap.R(7),
  7170  					SnapID:   "some-snap-id",
  7171  				},
  7172  				Type:      snap.TypeApp,
  7173  				PlugsOnly: true,
  7174  			}
  7175  
  7176  		}
  7177  
  7178  		c.Check(snapsup, DeepEquals, expSnapSetup, Commentf(t.Kind()))
  7179  	}
  7180  
  7181  	// verify snaps in the system state
  7182  	var snapst snapstate.SnapState
  7183  	err = snapstate.Get(s.state, "some-snap", &snapst)
  7184  	c.Assert(err, Equals, state.ErrNoState)
  7185  	c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FileAbsent)
  7186  
  7187  }
  7188  
  7189  func (s *snapmgrTestSuite) TestParallelInstanceRemoveRunThrough(c *C) {
  7190  	si := snap.SideInfo{
  7191  		RealName: "some-snap",
  7192  		Revision: snap.R(7),
  7193  	}
  7194  
  7195  	s.state.Lock()
  7196  	defer s.state.Unlock()
  7197  
  7198  	// pretend we have both a regular snap and a parallel instance
  7199  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  7200  		Active:      true,
  7201  		Sequence:    []*snap.SideInfo{&si},
  7202  		Current:     si.Revision,
  7203  		SnapType:    "app",
  7204  		InstanceKey: "instance",
  7205  	})
  7206  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  7207  		Active:   true,
  7208  		Sequence: []*snap.SideInfo{&si},
  7209  		Current:  si.Revision,
  7210  		SnapType: "app",
  7211  	})
  7212  
  7213  	chg := s.state.NewChange("remove", "remove a snap")
  7214  	ts, err := snapstate.Remove(s.state, "some-snap_instance", snap.R(0), nil)
  7215  	c.Assert(err, IsNil)
  7216  	chg.AddAll(ts)
  7217  
  7218  	s.state.Unlock()
  7219  	s.settle(c)
  7220  	s.state.Lock()
  7221  
  7222  	expected := fakeOps{
  7223  		{
  7224  			op:    "auto-disconnect:Doing",
  7225  			name:  "some-snap_instance",
  7226  			revno: snap.R(7),
  7227  		},
  7228  		{
  7229  			op:   "remove-snap-aliases",
  7230  			name: "some-snap_instance",
  7231  		},
  7232  		{
  7233  			op:   "unlink-snap",
  7234  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  7235  		},
  7236  		{
  7237  			op:    "remove-profiles:Doing",
  7238  			name:  "some-snap_instance",
  7239  			revno: snap.R(7),
  7240  		},
  7241  		{
  7242  			op:   "remove-snap-data",
  7243  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  7244  		},
  7245  		{
  7246  			op:   "remove-snap-common-data",
  7247  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  7248  		},
  7249  		{
  7250  			op:             "remove-snap-data-dir",
  7251  			name:           "some-snap_instance",
  7252  			path:           filepath.Join(dirs.SnapDataDir, "some-snap"),
  7253  			otherInstances: true,
  7254  		},
  7255  		{
  7256  			op:    "remove-snap-files",
  7257  			path:  filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  7258  			stype: "app",
  7259  		},
  7260  		{
  7261  			op:   "discard-namespace",
  7262  			name: "some-snap_instance",
  7263  		},
  7264  		{
  7265  			op:             "remove-snap-dir",
  7266  			name:           "some-snap_instance",
  7267  			path:           filepath.Join(dirs.SnapMountDir, "some-snap"),
  7268  			otherInstances: true,
  7269  		},
  7270  	}
  7271  	// start with an easier-to-read error if this fails:
  7272  	c.Check(len(s.fakeBackend.ops), Equals, len(expected))
  7273  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  7274  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  7275  
  7276  	// verify snapSetup info
  7277  	tasks := ts.Tasks()
  7278  	for _, t := range tasks {
  7279  		if t.Kind() == "run-hook" {
  7280  			continue
  7281  		}
  7282  		if t.Kind() == "save-snapshot" {
  7283  			continue
  7284  		}
  7285  		snapsup, err := snapstate.TaskSnapSetup(t)
  7286  		c.Assert(err, IsNil)
  7287  
  7288  		var expSnapSetup *snapstate.SnapSetup
  7289  		switch t.Kind() {
  7290  		case "discard-conns":
  7291  			expSnapSetup = &snapstate.SnapSetup{
  7292  				SideInfo: &snap.SideInfo{
  7293  					RealName: "some-snap",
  7294  				},
  7295  				InstanceKey: "instance",
  7296  			}
  7297  		case "clear-snap", "discard-snap":
  7298  			expSnapSetup = &snapstate.SnapSetup{
  7299  				SideInfo: &snap.SideInfo{
  7300  					RealName: "some-snap",
  7301  					Revision: snap.R(7),
  7302  				},
  7303  				InstanceKey: "instance",
  7304  			}
  7305  		default:
  7306  			expSnapSetup = &snapstate.SnapSetup{
  7307  				SideInfo: &snap.SideInfo{
  7308  					RealName: "some-snap",
  7309  					Revision: snap.R(7),
  7310  				},
  7311  				Type:        snap.TypeApp,
  7312  				PlugsOnly:   true,
  7313  				InstanceKey: "instance",
  7314  			}
  7315  
  7316  		}
  7317  
  7318  		c.Check(snapsup, DeepEquals, expSnapSetup, Commentf(t.Kind()))
  7319  	}
  7320  
  7321  	// verify snaps in the system state
  7322  	var snapst snapstate.SnapState
  7323  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  7324  	c.Assert(err, Equals, state.ErrNoState)
  7325  
  7326  	// the non-instance snap is still there
  7327  	err = snapstate.Get(s.state, "some-snap", &snapst)
  7328  	c.Assert(err, IsNil)
  7329  }
  7330  
  7331  func (s *snapmgrTestSuite) TestParallelInstanceRemoveRunThroughOtherInstances(c *C) {
  7332  	si := snap.SideInfo{
  7333  		RealName: "some-snap",
  7334  		Revision: snap.R(7),
  7335  	}
  7336  
  7337  	s.state.Lock()
  7338  	defer s.state.Unlock()
  7339  
  7340  	// pretend we have both a regular snap and a parallel instance
  7341  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  7342  		Active:      true,
  7343  		Sequence:    []*snap.SideInfo{&si},
  7344  		Current:     si.Revision,
  7345  		SnapType:    "app",
  7346  		InstanceKey: "instance",
  7347  	})
  7348  	snapstate.Set(s.state, "some-snap_other", &snapstate.SnapState{
  7349  		Active:      true,
  7350  		Sequence:    []*snap.SideInfo{&si},
  7351  		Current:     si.Revision,
  7352  		SnapType:    "app",
  7353  		InstanceKey: "other",
  7354  	})
  7355  
  7356  	chg := s.state.NewChange("remove", "remove a snap")
  7357  	ts, err := snapstate.Remove(s.state, "some-snap_instance", snap.R(0), nil)
  7358  	c.Assert(err, IsNil)
  7359  	chg.AddAll(ts)
  7360  
  7361  	s.state.Unlock()
  7362  	s.settle(c)
  7363  	s.state.Lock()
  7364  
  7365  	expected := fakeOps{
  7366  		{
  7367  			op:    "auto-disconnect:Doing",
  7368  			name:  "some-snap_instance",
  7369  			revno: snap.R(7),
  7370  		},
  7371  		{
  7372  			op:   "remove-snap-aliases",
  7373  			name: "some-snap_instance",
  7374  		},
  7375  		{
  7376  			op:   "unlink-snap",
  7377  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  7378  		},
  7379  		{
  7380  			op:    "remove-profiles:Doing",
  7381  			name:  "some-snap_instance",
  7382  			revno: snap.R(7),
  7383  		},
  7384  		{
  7385  			op:   "remove-snap-data",
  7386  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  7387  		},
  7388  		{
  7389  			op:   "remove-snap-common-data",
  7390  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  7391  		},
  7392  		{
  7393  			op:             "remove-snap-data-dir",
  7394  			name:           "some-snap_instance",
  7395  			path:           filepath.Join(dirs.SnapDataDir, "some-snap"),
  7396  			otherInstances: true,
  7397  		},
  7398  		{
  7399  			op:    "remove-snap-files",
  7400  			path:  filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  7401  			stype: "app",
  7402  		},
  7403  		{
  7404  			op:   "discard-namespace",
  7405  			name: "some-snap_instance",
  7406  		},
  7407  		{
  7408  			op:             "remove-snap-dir",
  7409  			name:           "some-snap_instance",
  7410  			path:           filepath.Join(dirs.SnapMountDir, "some-snap"),
  7411  			otherInstances: true,
  7412  		},
  7413  	}
  7414  	// start with an easier-to-read error if this fails:
  7415  	c.Check(len(s.fakeBackend.ops), Equals, len(expected))
  7416  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  7417  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  7418  
  7419  	// verify snaps in the system state
  7420  	var snapst snapstate.SnapState
  7421  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  7422  	c.Assert(err, Equals, state.ErrNoState)
  7423  
  7424  	// the other instance is still there
  7425  	err = snapstate.Get(s.state, "some-snap_other", &snapst)
  7426  	c.Assert(err, IsNil)
  7427  }
  7428  
  7429  func (s *snapmgrTestSuite) TestRemoveWithManyRevisionsRunThrough(c *C) {
  7430  	si3 := snap.SideInfo{
  7431  		SnapID:   "some-snap-id",
  7432  		RealName: "some-snap",
  7433  		Revision: snap.R(3),
  7434  	}
  7435  
  7436  	si5 := snap.SideInfo{
  7437  		SnapID:   "some-snap-id",
  7438  		RealName: "some-snap",
  7439  		Revision: snap.R(5),
  7440  	}
  7441  
  7442  	si7 := snap.SideInfo{
  7443  		SnapID:   "some-snap-id",
  7444  		RealName: "some-snap",
  7445  		Revision: snap.R(7),
  7446  	}
  7447  
  7448  	s.state.Lock()
  7449  	defer s.state.Unlock()
  7450  
  7451  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  7452  		Active:   true,
  7453  		Sequence: []*snap.SideInfo{&si5, &si3, &si7},
  7454  		Current:  si7.Revision,
  7455  		SnapType: "app",
  7456  	})
  7457  
  7458  	chg := s.state.NewChange("remove", "remove a snap")
  7459  	ts, err := snapstate.Remove(s.state, "some-snap", snap.R(0), nil)
  7460  	c.Assert(err, IsNil)
  7461  	chg.AddAll(ts)
  7462  
  7463  	s.state.Unlock()
  7464  	defer s.se.Stop()
  7465  	s.settle(c)
  7466  	s.state.Lock()
  7467  
  7468  	expected := fakeOps{
  7469  		{
  7470  			op:    "auto-disconnect:Doing",
  7471  			name:  "some-snap",
  7472  			revno: snap.R(7),
  7473  		},
  7474  		{
  7475  			op:   "remove-snap-aliases",
  7476  			name: "some-snap",
  7477  		},
  7478  		{
  7479  			op:   "unlink-snap",
  7480  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  7481  		},
  7482  		{
  7483  			op:    "remove-profiles:Doing",
  7484  			name:  "some-snap",
  7485  			revno: snap.R(7),
  7486  		},
  7487  		{
  7488  			op:   "remove-snap-data",
  7489  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  7490  		},
  7491  		{
  7492  			op:    "remove-snap-files",
  7493  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  7494  			stype: "app",
  7495  		},
  7496  		{
  7497  			op:   "remove-snap-data",
  7498  			path: filepath.Join(dirs.SnapMountDir, "some-snap/3"),
  7499  		},
  7500  		{
  7501  			op:    "remove-snap-files",
  7502  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/3"),
  7503  			stype: "app",
  7504  		},
  7505  		{
  7506  			op:   "remove-snap-data",
  7507  			path: filepath.Join(dirs.SnapMountDir, "some-snap/5"),
  7508  		},
  7509  		{
  7510  			op:   "remove-snap-common-data",
  7511  			path: filepath.Join(dirs.SnapMountDir, "some-snap/5"),
  7512  		},
  7513  		{
  7514  			op:   "remove-snap-data-dir",
  7515  			name: "some-snap",
  7516  			path: filepath.Join(dirs.SnapDataDir, "some-snap"),
  7517  		},
  7518  		{
  7519  			op:    "remove-snap-files",
  7520  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/5"),
  7521  			stype: "app",
  7522  		},
  7523  		{
  7524  			op:   "discard-namespace",
  7525  			name: "some-snap",
  7526  		},
  7527  		{
  7528  			op:   "remove-snap-dir",
  7529  			name: "some-snap",
  7530  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  7531  		},
  7532  	}
  7533  	// start with an easier-to-read error if this fails:
  7534  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  7535  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  7536  
  7537  	// verify snapSetup info
  7538  	tasks := ts.Tasks()
  7539  	revnos := []snap.Revision{{N: 7}, {N: 3}, {N: 5}}
  7540  	whichRevno := 0
  7541  	for _, t := range tasks {
  7542  		if t.Kind() == "run-hook" {
  7543  			continue
  7544  		}
  7545  		if t.Kind() == "save-snapshot" {
  7546  			continue
  7547  		}
  7548  		snapsup, err := snapstate.TaskSnapSetup(t)
  7549  		c.Assert(err, IsNil)
  7550  
  7551  		var expSnapSetup *snapstate.SnapSetup
  7552  		switch t.Kind() {
  7553  		case "discard-conns":
  7554  			expSnapSetup = &snapstate.SnapSetup{
  7555  				SideInfo: &snap.SideInfo{
  7556  					SnapID:   "some-snap-id",
  7557  					RealName: "some-snap",
  7558  				},
  7559  			}
  7560  		case "clear-snap", "discard-snap":
  7561  			expSnapSetup = &snapstate.SnapSetup{
  7562  				SideInfo: &snap.SideInfo{
  7563  					SnapID:   "some-snap-id",
  7564  					RealName: "some-snap",
  7565  					Revision: revnos[whichRevno],
  7566  				},
  7567  			}
  7568  		default:
  7569  			expSnapSetup = &snapstate.SnapSetup{
  7570  				SideInfo: &snap.SideInfo{
  7571  					SnapID:   "some-snap-id",
  7572  					RealName: "some-snap",
  7573  					Revision: snap.R(7),
  7574  				},
  7575  				Type:      snap.TypeApp,
  7576  				PlugsOnly: true,
  7577  			}
  7578  
  7579  		}
  7580  
  7581  		c.Check(snapsup, DeepEquals, expSnapSetup, Commentf(t.Kind()))
  7582  
  7583  		if t.Kind() == "discard-snap" {
  7584  			whichRevno++
  7585  		}
  7586  	}
  7587  
  7588  	// verify snaps in the system state
  7589  	var snapst snapstate.SnapState
  7590  	err = snapstate.Get(s.state, "some-snap", &snapst)
  7591  	c.Assert(err, Equals, state.ErrNoState)
  7592  }
  7593  
  7594  func (s *snapmgrTestSuite) TestRemoveOneRevisionRunThrough(c *C) {
  7595  	si3 := snap.SideInfo{
  7596  		RealName: "some-snap",
  7597  		Revision: snap.R(3),
  7598  	}
  7599  
  7600  	si5 := snap.SideInfo{
  7601  		RealName: "some-snap",
  7602  		Revision: snap.R(5),
  7603  	}
  7604  
  7605  	si7 := snap.SideInfo{
  7606  		RealName: "some-snap",
  7607  		Revision: snap.R(7),
  7608  	}
  7609  
  7610  	s.state.Lock()
  7611  	defer s.state.Unlock()
  7612  
  7613  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  7614  		Active:   true,
  7615  		Sequence: []*snap.SideInfo{&si5, &si3, &si7},
  7616  		Current:  si7.Revision,
  7617  		SnapType: "app",
  7618  	})
  7619  
  7620  	chg := s.state.NewChange("remove", "remove a snap")
  7621  	ts, err := snapstate.Remove(s.state, "some-snap", snap.R(3), nil)
  7622  	c.Assert(err, IsNil)
  7623  	chg.AddAll(ts)
  7624  
  7625  	s.state.Unlock()
  7626  	defer s.se.Stop()
  7627  	s.settle(c)
  7628  	s.state.Lock()
  7629  
  7630  	c.Check(len(s.fakeBackend.ops), Equals, 2)
  7631  	expected := fakeOps{
  7632  		{
  7633  			op:   "remove-snap-data",
  7634  			path: filepath.Join(dirs.SnapMountDir, "some-snap/3"),
  7635  		},
  7636  		{
  7637  			op:    "remove-snap-files",
  7638  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/3"),
  7639  			stype: "app",
  7640  		},
  7641  	}
  7642  	// start with an easier-to-read error if this fails:
  7643  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  7644  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  7645  
  7646  	// verify snapSetup info
  7647  	tasks := ts.Tasks()
  7648  	for _, t := range tasks {
  7649  		if t.Kind() == "save-snapshot" {
  7650  			continue
  7651  		}
  7652  		snapsup, err := snapstate.TaskSnapSetup(t)
  7653  		c.Assert(err, IsNil)
  7654  
  7655  		expSnapSetup := &snapstate.SnapSetup{
  7656  			SideInfo: &snap.SideInfo{
  7657  				RealName: "some-snap",
  7658  				Revision: snap.R(3),
  7659  			},
  7660  		}
  7661  
  7662  		c.Check(snapsup, DeepEquals, expSnapSetup, Commentf(t.Kind()))
  7663  	}
  7664  
  7665  	// verify snaps in the system state
  7666  	var snapst snapstate.SnapState
  7667  	err = snapstate.Get(s.state, "some-snap", &snapst)
  7668  	c.Assert(err, IsNil)
  7669  	c.Check(snapst.Sequence, HasLen, 2)
  7670  }
  7671  
  7672  func (s *snapmgrTestSuite) TestRemoveLastRevisionRunThrough(c *C) {
  7673  	si := snap.SideInfo{
  7674  		RealName: "some-snap",
  7675  		Revision: snap.R(2),
  7676  	}
  7677  
  7678  	s.state.Lock()
  7679  	defer s.state.Unlock()
  7680  
  7681  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  7682  		Active:   false,
  7683  		Sequence: []*snap.SideInfo{&si},
  7684  		Current:  si.Revision,
  7685  		SnapType: "app",
  7686  	})
  7687  
  7688  	chg := s.state.NewChange("remove", "remove a snap")
  7689  	ts, err := snapstate.Remove(s.state, "some-snap", snap.R(2), nil)
  7690  	c.Assert(err, IsNil)
  7691  	chg.AddAll(ts)
  7692  
  7693  	s.state.Unlock()
  7694  	defer s.se.Stop()
  7695  	s.settle(c)
  7696  	s.state.Lock()
  7697  
  7698  	c.Check(len(s.fakeBackend.ops), Equals, 7)
  7699  	expected := fakeOps{
  7700  		{
  7701  			op:    "auto-disconnect:Doing",
  7702  			name:  "some-snap",
  7703  			revno: snap.R(2),
  7704  		},
  7705  		{
  7706  			op:   "remove-snap-data",
  7707  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  7708  		},
  7709  		{
  7710  			op:   "remove-snap-common-data",
  7711  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  7712  		},
  7713  		{
  7714  			op:   "remove-snap-data-dir",
  7715  			name: "some-snap",
  7716  			path: filepath.Join(dirs.SnapDataDir, "some-snap"),
  7717  		},
  7718  		{
  7719  			op:    "remove-snap-files",
  7720  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  7721  			stype: "app",
  7722  		},
  7723  		{
  7724  			op:   "discard-namespace",
  7725  			name: "some-snap",
  7726  		},
  7727  		{
  7728  			op:   "remove-snap-dir",
  7729  			name: "some-snap",
  7730  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  7731  		},
  7732  	}
  7733  	// start with an easier-to-read error if this fails:
  7734  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  7735  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  7736  
  7737  	// verify snapSetup info
  7738  	tasks := ts.Tasks()
  7739  	for _, t := range tasks {
  7740  		if t.Kind() == "run-hook" {
  7741  			continue
  7742  		}
  7743  		if t.Kind() == "save-snapshot" {
  7744  			continue
  7745  		}
  7746  		snapsup, err := snapstate.TaskSnapSetup(t)
  7747  		c.Assert(err, IsNil)
  7748  
  7749  		expSnapSetup := &snapstate.SnapSetup{
  7750  			SideInfo: &snap.SideInfo{
  7751  				RealName: "some-snap",
  7752  			},
  7753  		}
  7754  		if t.Kind() != "discard-conns" {
  7755  			expSnapSetup.SideInfo.Revision = snap.R(2)
  7756  		}
  7757  		if t.Kind() == "auto-disconnect" {
  7758  			expSnapSetup.PlugsOnly = true
  7759  			expSnapSetup.Type = "app"
  7760  		}
  7761  
  7762  		c.Check(snapsup, DeepEquals, expSnapSetup, Commentf(t.Kind()))
  7763  	}
  7764  
  7765  	// verify snaps in the system state
  7766  	var snapst snapstate.SnapState
  7767  	err = snapstate.Get(s.state, "some-snap", &snapst)
  7768  	c.Assert(err, Equals, state.ErrNoState)
  7769  }
  7770  
  7771  func (s *snapmgrTestSuite) TestRemoveCurrentActiveRevisionRefused(c *C) {
  7772  	si := snap.SideInfo{
  7773  		RealName: "some-snap",
  7774  		Revision: snap.R(2),
  7775  	}
  7776  
  7777  	s.state.Lock()
  7778  	defer s.state.Unlock()
  7779  
  7780  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  7781  		Active:   true,
  7782  		Sequence: []*snap.SideInfo{&si},
  7783  		Current:  si.Revision,
  7784  		SnapType: "app",
  7785  	})
  7786  
  7787  	_, err := snapstate.Remove(s.state, "some-snap", snap.R(2), nil)
  7788  
  7789  	c.Check(err, ErrorMatches, `cannot remove active revision 2 of snap "some-snap"`)
  7790  }
  7791  
  7792  func (s *snapmgrTestSuite) TestRemoveCurrentRevisionOfSeveralRefused(c *C) {
  7793  	si := snap.SideInfo{
  7794  		RealName: "some-snap",
  7795  		Revision: snap.R(2),
  7796  	}
  7797  
  7798  	s.state.Lock()
  7799  	defer s.state.Unlock()
  7800  
  7801  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  7802  		Active:   true,
  7803  		Sequence: []*snap.SideInfo{&si, &si},
  7804  		Current:  si.Revision,
  7805  		SnapType: "app",
  7806  	})
  7807  
  7808  	_, err := snapstate.Remove(s.state, "some-snap", snap.R(2), nil)
  7809  	c.Assert(err, NotNil)
  7810  	c.Check(err.Error(), Equals, `cannot remove active revision 2 of snap "some-snap" (revert first?)`)
  7811  }
  7812  
  7813  func (s *snapmgrTestSuite) TestRemoveMissingRevisionRefused(c *C) {
  7814  	si := snap.SideInfo{
  7815  		RealName: "some-snap",
  7816  		Revision: snap.R(2),
  7817  	}
  7818  
  7819  	s.state.Lock()
  7820  	defer s.state.Unlock()
  7821  
  7822  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  7823  		Active:   true,
  7824  		Sequence: []*snap.SideInfo{&si},
  7825  		Current:  si.Revision,
  7826  		SnapType: "app",
  7827  	})
  7828  
  7829  	_, err := snapstate.Remove(s.state, "some-snap", snap.R(1), nil)
  7830  
  7831  	c.Check(err, ErrorMatches, `revision 1 of snap "some-snap" is not installed`)
  7832  }
  7833  
  7834  func (s *snapmgrTestSuite) TestRemoveRefused(c *C) {
  7835  	si := snap.SideInfo{
  7836  		RealName: "gadget",
  7837  		Revision: snap.R(7),
  7838  	}
  7839  
  7840  	s.state.Lock()
  7841  	defer s.state.Unlock()
  7842  
  7843  	snapstate.Set(s.state, "gadget", &snapstate.SnapState{
  7844  		Active:   true,
  7845  		Sequence: []*snap.SideInfo{&si},
  7846  		Current:  si.Revision,
  7847  		SnapType: "app",
  7848  	})
  7849  
  7850  	_, err := snapstate.Remove(s.state, "gadget", snap.R(0), nil)
  7851  
  7852  	c.Check(err, ErrorMatches, `snap "gadget" is not removable`)
  7853  }
  7854  
  7855  func (s *snapmgrTestSuite) TestRemoveRefusedLastRevision(c *C) {
  7856  	si := snap.SideInfo{
  7857  		RealName: "gadget",
  7858  		Revision: snap.R(7),
  7859  	}
  7860  
  7861  	s.state.Lock()
  7862  	defer s.state.Unlock()
  7863  
  7864  	snapstate.Set(s.state, "gadget", &snapstate.SnapState{
  7865  		Active:   false,
  7866  		Sequence: []*snap.SideInfo{&si},
  7867  		Current:  si.Revision,
  7868  		SnapType: "app",
  7869  	})
  7870  
  7871  	_, err := snapstate.Remove(s.state, "gadget", snap.R(7), nil)
  7872  
  7873  	c.Check(err, ErrorMatches, `snap "gadget" is not removable`)
  7874  }
  7875  
  7876  func (s *snapmgrTestSuite) TestRemoveDeletesConfigOnLastRevision(c *C) {
  7877  	si := snap.SideInfo{
  7878  		RealName: "some-snap",
  7879  		Revision: snap.R(7),
  7880  	}
  7881  
  7882  	s.state.Lock()
  7883  	defer s.state.Unlock()
  7884  
  7885  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  7886  		Active:   true,
  7887  		Sequence: []*snap.SideInfo{&si},
  7888  		Current:  si.Revision,
  7889  		SnapType: "app",
  7890  	})
  7891  
  7892  	snapstate.Set(s.state, "another-snap", &snapstate.SnapState{
  7893  		Active:   true,
  7894  		Sequence: []*snap.SideInfo{&si},
  7895  		Current:  si.Revision,
  7896  		SnapType: "app",
  7897  	})
  7898  
  7899  	tr := config.NewTransaction(s.state)
  7900  	tr.Set("some-snap", "foo", "bar")
  7901  	tr.Commit()
  7902  
  7903  	// a config for some other snap to verify its not accidentally destroyed
  7904  	tr = config.NewTransaction(s.state)
  7905  	tr.Set("another-snap", "bar", "baz")
  7906  	tr.Commit()
  7907  
  7908  	var res string
  7909  	tr = config.NewTransaction(s.state)
  7910  	c.Assert(tr.Get("some-snap", "foo", &res), IsNil)
  7911  	c.Assert(tr.Get("another-snap", "bar", &res), IsNil)
  7912  
  7913  	chg := s.state.NewChange("remove", "remove a snap")
  7914  	ts, err := snapstate.Remove(s.state, "some-snap", snap.R(0), nil)
  7915  	c.Assert(err, IsNil)
  7916  	chg.AddAll(ts)
  7917  
  7918  	s.state.Unlock()
  7919  	defer s.se.Stop()
  7920  	s.settle(c)
  7921  	s.state.Lock()
  7922  
  7923  	// verify snaps in the system state
  7924  	var snapst snapstate.SnapState
  7925  	err = snapstate.Get(s.state, "some-snap", &snapst)
  7926  	c.Assert(err, Equals, state.ErrNoState)
  7927  
  7928  	tr = config.NewTransaction(s.state)
  7929  	err = tr.Get("some-snap", "foo", &res)
  7930  	c.Assert(err, NotNil)
  7931  	c.Assert(err, ErrorMatches, `snap "some-snap" has no "foo" configuration option`)
  7932  
  7933  	// and another snap has its config intact
  7934  	c.Assert(tr.Get("another-snap", "bar", &res), IsNil)
  7935  	c.Assert(res, Equals, "baz")
  7936  }
  7937  
  7938  func (s *snapmgrTestSuite) TestRemoveDoesntDeleteConfigIfNotLastRevision(c *C) {
  7939  	si1 := snap.SideInfo{
  7940  		RealName: "some-snap",
  7941  		Revision: snap.R(7),
  7942  	}
  7943  	si2 := snap.SideInfo{
  7944  		RealName: "some-snap",
  7945  		Revision: snap.R(8),
  7946  	}
  7947  
  7948  	s.state.Lock()
  7949  	defer s.state.Unlock()
  7950  
  7951  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  7952  		Active:   true,
  7953  		Sequence: []*snap.SideInfo{&si1, &si2},
  7954  		Current:  si2.Revision,
  7955  		SnapType: "app",
  7956  	})
  7957  
  7958  	tr := config.NewTransaction(s.state)
  7959  	tr.Set("some-snap", "foo", "bar")
  7960  	tr.Commit()
  7961  
  7962  	var res string
  7963  	tr = config.NewTransaction(s.state)
  7964  	c.Assert(tr.Get("some-snap", "foo", &res), IsNil)
  7965  
  7966  	chg := s.state.NewChange("remove", "remove a snap")
  7967  	ts, err := snapstate.Remove(s.state, "some-snap", si1.Revision, nil)
  7968  	c.Assert(err, IsNil)
  7969  	chg.AddAll(ts)
  7970  
  7971  	s.state.Unlock()
  7972  	defer s.se.Stop()
  7973  	s.settle(c)
  7974  	s.state.Lock()
  7975  
  7976  	// verify snaps in the system state
  7977  	var snapst snapstate.SnapState
  7978  	err = snapstate.Get(s.state, "some-snap", &snapst)
  7979  	c.Assert(err, IsNil)
  7980  
  7981  	tr = config.NewTransaction(s.state)
  7982  	c.Assert(tr.Get("some-snap", "foo", &res), IsNil)
  7983  	c.Assert(res, Equals, "bar")
  7984  }
  7985  
  7986  func (s *snapmgrTestSuite) TestUpdateMakesConfigSnapshot(c *C) {
  7987  	s.state.Lock()
  7988  	defer s.state.Unlock()
  7989  
  7990  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  7991  		Active: true,
  7992  		Sequence: []*snap.SideInfo{
  7993  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  7994  		},
  7995  		Current:  snap.R(1),
  7996  		SnapType: "app",
  7997  	})
  7998  
  7999  	tr := config.NewTransaction(s.state)
  8000  	tr.Set("some-snap", "foo", "bar")
  8001  	tr.Commit()
  8002  
  8003  	var cfgs map[string]interface{}
  8004  	// we don't have config snapshots yet
  8005  	c.Assert(s.state.Get("revision-config", &cfgs), Equals, state.ErrNoState)
  8006  
  8007  	chg := s.state.NewChange("update", "update a snap")
  8008  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(2)}
  8009  	ts, err := snapstate.Update(s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  8010  	c.Assert(err, IsNil)
  8011  	chg.AddAll(ts)
  8012  
  8013  	s.state.Unlock()
  8014  	defer s.se.Stop()
  8015  	s.settle(c)
  8016  
  8017  	s.state.Lock()
  8018  	cfgs = nil
  8019  	// config copy of rev. 1 has been made
  8020  	c.Assert(s.state.Get("revision-config", &cfgs), IsNil)
  8021  	c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{
  8022  		"1": map[string]interface{}{
  8023  			"foo": "bar",
  8024  		},
  8025  	})
  8026  }
  8027  
  8028  func (s *snapmgrTestSuite) TestRevertRestoresConfigSnapshot(c *C) {
  8029  	s.state.Lock()
  8030  	defer s.state.Unlock()
  8031  
  8032  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8033  		Active: true,
  8034  		Sequence: []*snap.SideInfo{
  8035  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  8036  			{RealName: "some-snap", Revision: snap.R(2)},
  8037  		},
  8038  		Current:  snap.R(2),
  8039  		SnapType: "app",
  8040  	})
  8041  
  8042  	// set configuration for current snap
  8043  	tr := config.NewTransaction(s.state)
  8044  	tr.Set("some-snap", "foo", "100")
  8045  	tr.Commit()
  8046  
  8047  	// make config snapshot for rev.1
  8048  	config.SaveRevisionConfig(s.state, "some-snap", snap.R(1))
  8049  
  8050  	// modify for rev. 2
  8051  	tr = config.NewTransaction(s.state)
  8052  	tr.Set("some-snap", "foo", "200")
  8053  	tr.Commit()
  8054  
  8055  	chg := s.state.NewChange("revert", "revert snap")
  8056  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  8057  	c.Assert(err, IsNil)
  8058  	chg.AddAll(ts)
  8059  
  8060  	s.state.Unlock()
  8061  	defer s.se.Stop()
  8062  	s.settle(c)
  8063  
  8064  	s.state.Lock()
  8065  	// config snapshot of rev. 2 has been made by 'revert'
  8066  	var cfgs map[string]interface{}
  8067  	c.Assert(s.state.Get("revision-config", &cfgs), IsNil)
  8068  	c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{
  8069  		"1": map[string]interface{}{"foo": "100"},
  8070  		"2": map[string]interface{}{"foo": "200"},
  8071  	})
  8072  
  8073  	// current snap configuration has been restored from rev. 1 config snapshot
  8074  	tr = config.NewTransaction(s.state)
  8075  	var res string
  8076  	c.Assert(tr.Get("some-snap", "foo", &res), IsNil)
  8077  	c.Assert(res, Equals, "100")
  8078  }
  8079  
  8080  func (s *snapmgrTestSuite) TestRefreshDoesntRestoreRevisionConfig(c *C) {
  8081  	restore := release.MockOnClassic(false)
  8082  	defer restore()
  8083  
  8084  	s.state.Lock()
  8085  	defer s.state.Unlock()
  8086  
  8087  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8088  		Active: true,
  8089  		Sequence: []*snap.SideInfo{
  8090  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  8091  		},
  8092  		Current:  snap.R(1),
  8093  		SnapType: "app",
  8094  	})
  8095  
  8096  	// set global configuration (affecting current snap)
  8097  	tr := config.NewTransaction(s.state)
  8098  	tr.Set("some-snap", "foo", "100")
  8099  	tr.Commit()
  8100  
  8101  	// set per-revision config for the upcoming rev. 2, we don't expect it restored though
  8102  	// since only revert restores revision configs.
  8103  	s.state.Set("revision-config", map[string]interface{}{
  8104  		"some-snap": map[string]interface{}{
  8105  			"2": map[string]interface{}{"foo": "200"},
  8106  		},
  8107  	})
  8108  
  8109  	// simulate a refresh to rev. 2
  8110  	chg := s.state.NewChange("update", "update some-snap")
  8111  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(2)}, s.user.ID, snapstate.Flags{})
  8112  	c.Assert(err, IsNil)
  8113  	chg.AddAll(ts)
  8114  
  8115  	s.state.Unlock()
  8116  	defer s.se.Stop()
  8117  	s.settle(c)
  8118  
  8119  	s.state.Lock()
  8120  	// config of rev. 1 has been stored in per-revision map
  8121  	var cfgs map[string]interface{}
  8122  	c.Assert(s.state.Get("revision-config", &cfgs), IsNil)
  8123  	c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{
  8124  		"1": map[string]interface{}{"foo": "100"},
  8125  		"2": map[string]interface{}{"foo": "200"},
  8126  	})
  8127  
  8128  	// config of rev. 2 hasn't been restored by refresh, old value returned
  8129  	tr = config.NewTransaction(s.state)
  8130  	var res string
  8131  	c.Assert(tr.Get("some-snap", "foo", &res), IsNil)
  8132  	c.Assert(res, Equals, "100")
  8133  }
  8134  
  8135  func (s *snapmgrTestSuite) TestUpdateDoesGC(c *C) {
  8136  	s.state.Lock()
  8137  	defer s.state.Unlock()
  8138  	restore := release.MockOnClassic(false)
  8139  	defer restore()
  8140  
  8141  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8142  		Active: true,
  8143  		Sequence: []*snap.SideInfo{
  8144  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  8145  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  8146  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  8147  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  8148  		},
  8149  		Current:  snap.R(4),
  8150  		SnapType: "app",
  8151  	})
  8152  
  8153  	chg := s.state.NewChange("update", "update a snap")
  8154  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  8155  	c.Assert(err, IsNil)
  8156  	chg.AddAll(ts)
  8157  
  8158  	s.state.Unlock()
  8159  	defer s.se.Stop()
  8160  	s.settle(c)
  8161  	s.state.Lock()
  8162  
  8163  	// ensure garbage collection runs as the last tasks
  8164  	expectedTail := fakeOps{
  8165  		{
  8166  			op:   "link-snap",
  8167  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  8168  		},
  8169  		{
  8170  			op:    "auto-connect:Doing",
  8171  			name:  "some-snap",
  8172  			revno: snap.R(11),
  8173  		},
  8174  		{
  8175  			op: "update-aliases",
  8176  		},
  8177  		{
  8178  			op:   "remove-snap-data",
  8179  			path: filepath.Join(dirs.SnapMountDir, "some-snap/1"),
  8180  		},
  8181  		{
  8182  			op:    "remove-snap-files",
  8183  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/1"),
  8184  			stype: "app",
  8185  		},
  8186  		{
  8187  			op:   "remove-snap-data",
  8188  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  8189  		},
  8190  		{
  8191  			op:    "remove-snap-files",
  8192  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  8193  			stype: "app",
  8194  		},
  8195  		{
  8196  			op:    "cleanup-trash",
  8197  			name:  "some-snap",
  8198  			revno: snap.R(11),
  8199  		},
  8200  	}
  8201  
  8202  	opsTail := s.fakeBackend.ops[len(s.fakeBackend.ops)-len(expectedTail):]
  8203  	c.Assert(opsTail.Ops(), DeepEquals, expectedTail.Ops())
  8204  	c.Check(opsTail, DeepEquals, expectedTail)
  8205  }
  8206  
  8207  func (s *snapmgrTestSuite) TestRevertNoRevertAgain(c *C) {
  8208  	siNew := snap.SideInfo{
  8209  		RealName: "some-snap",
  8210  		Revision: snap.R(77),
  8211  	}
  8212  
  8213  	si := snap.SideInfo{
  8214  		RealName: "some-snap",
  8215  		Revision: snap.R(7),
  8216  	}
  8217  
  8218  	s.state.Lock()
  8219  	defer s.state.Unlock()
  8220  
  8221  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8222  		Active:   true,
  8223  		Sequence: []*snap.SideInfo{&si, &siNew},
  8224  		Current:  snap.R(7),
  8225  	})
  8226  
  8227  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  8228  	c.Assert(err, ErrorMatches, "no revision to revert to")
  8229  	c.Assert(ts, IsNil)
  8230  }
  8231  
  8232  func (s *snapmgrTestSuite) TestRevertNothingToRevertTo(c *C) {
  8233  	si := snap.SideInfo{
  8234  		RealName: "some-snap",
  8235  		Revision: snap.R(7),
  8236  	}
  8237  
  8238  	s.state.Lock()
  8239  	defer s.state.Unlock()
  8240  
  8241  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8242  		Active:   true,
  8243  		Sequence: []*snap.SideInfo{&si},
  8244  		Current:  si.Revision,
  8245  	})
  8246  
  8247  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  8248  	c.Assert(err, ErrorMatches, "no revision to revert to")
  8249  	c.Assert(ts, IsNil)
  8250  }
  8251  
  8252  func (s *snapmgrTestSuite) TestRevertToRevisionNoValidVersion(c *C) {
  8253  	si := snap.SideInfo{
  8254  		RealName: "some-snap",
  8255  		Revision: snap.R(7),
  8256  	}
  8257  	si2 := snap.SideInfo{
  8258  		RealName: "some-snap",
  8259  		Revision: snap.R(77),
  8260  	}
  8261  
  8262  	s.state.Lock()
  8263  	defer s.state.Unlock()
  8264  
  8265  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8266  		Active:   true,
  8267  		Sequence: []*snap.SideInfo{&si, &si2},
  8268  		Current:  snap.R(77),
  8269  	})
  8270  
  8271  	ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("99"), snapstate.Flags{})
  8272  	c.Assert(err, ErrorMatches, `cannot find revision 99 for snap "some-snap"`)
  8273  	c.Assert(ts, IsNil)
  8274  }
  8275  
  8276  func (s *snapmgrTestSuite) TestRevertToRevisionAlreadyCurrent(c *C) {
  8277  	si := snap.SideInfo{
  8278  		RealName: "some-snap",
  8279  		Revision: snap.R(7),
  8280  	}
  8281  	si2 := snap.SideInfo{
  8282  		RealName: "some-snap",
  8283  		Revision: snap.R(77),
  8284  	}
  8285  
  8286  	s.state.Lock()
  8287  	defer s.state.Unlock()
  8288  
  8289  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8290  		Active:   true,
  8291  		Sequence: []*snap.SideInfo{&si, &si2},
  8292  		Current:  snap.R(77),
  8293  	})
  8294  
  8295  	ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R("77"), snapstate.Flags{})
  8296  	c.Assert(err, ErrorMatches, `already on requested revision`)
  8297  	c.Assert(ts, IsNil)
  8298  }
  8299  
  8300  func (s *snapmgrTestSuite) TestRevertRunThrough(c *C) {
  8301  	si := snap.SideInfo{
  8302  		RealName: "some-snap",
  8303  		Revision: snap.R(7),
  8304  	}
  8305  	siOld := snap.SideInfo{
  8306  		RealName: "some-snap",
  8307  		Revision: snap.R(2),
  8308  	}
  8309  
  8310  	s.state.Lock()
  8311  	defer s.state.Unlock()
  8312  
  8313  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8314  		Active:   true,
  8315  		SnapType: "app",
  8316  		Sequence: []*snap.SideInfo{&siOld, &si},
  8317  		Current:  si.Revision,
  8318  	})
  8319  
  8320  	chg := s.state.NewChange("revert", "revert a snap backwards")
  8321  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  8322  	c.Assert(err, IsNil)
  8323  	chg.AddAll(ts)
  8324  
  8325  	s.state.Unlock()
  8326  	defer s.se.Stop()
  8327  	s.settle(c)
  8328  	s.state.Lock()
  8329  
  8330  	expected := fakeOps{
  8331  		{
  8332  			op:   "remove-snap-aliases",
  8333  			name: "some-snap",
  8334  		},
  8335  		{
  8336  			op:   "unlink-snap",
  8337  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  8338  		},
  8339  		{
  8340  			op:    "setup-profiles:Doing",
  8341  			name:  "some-snap",
  8342  			revno: snap.R(2),
  8343  		},
  8344  		{
  8345  			op: "candidate",
  8346  			sinfo: snap.SideInfo{
  8347  				RealName: "some-snap",
  8348  				Revision: snap.R(2),
  8349  			},
  8350  		},
  8351  		{
  8352  			op:   "link-snap",
  8353  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  8354  		},
  8355  		{
  8356  			op:    "auto-connect:Doing",
  8357  			name:  "some-snap",
  8358  			revno: snap.R(2),
  8359  		},
  8360  		{
  8361  			op: "update-aliases",
  8362  		},
  8363  	}
  8364  	// start with an easier-to-read error if this fails:
  8365  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  8366  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  8367  
  8368  	// verify that the R(2) version is active now and R(7) is still there
  8369  	var snapst snapstate.SnapState
  8370  	err = snapstate.Get(s.state, "some-snap", &snapst)
  8371  	c.Assert(err, IsNil)
  8372  
  8373  	c.Assert(snapst.Active, Equals, true)
  8374  	c.Assert(snapst.Current, Equals, snap.R(2))
  8375  	c.Assert(snapst.Sequence, HasLen, 2)
  8376  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  8377  		RealName: "some-snap",
  8378  		Channel:  "",
  8379  		Revision: snap.R(2),
  8380  	})
  8381  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  8382  		RealName: "some-snap",
  8383  		Channel:  "",
  8384  		Revision: snap.R(7),
  8385  	})
  8386  	c.Assert(snapst.Block(), DeepEquals, []snap.Revision{snap.R(7)})
  8387  }
  8388  
  8389  func (s *snapmgrTestSuite) TestParallelInstanceRevertRunThrough(c *C) {
  8390  	si := snap.SideInfo{
  8391  		RealName: "some-snap",
  8392  		Revision: snap.R(7),
  8393  	}
  8394  	siOld := snap.SideInfo{
  8395  		RealName: "some-snap",
  8396  		Revision: snap.R(2),
  8397  	}
  8398  
  8399  	s.state.Lock()
  8400  	defer s.state.Unlock()
  8401  
  8402  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  8403  		Active:      true,
  8404  		SnapType:    "app",
  8405  		Sequence:    []*snap.SideInfo{&siOld, &si},
  8406  		Current:     si.Revision,
  8407  		InstanceKey: "instance",
  8408  	})
  8409  
  8410  	// another snap withouth instance key
  8411  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8412  		Active:   true,
  8413  		SnapType: "app",
  8414  		Sequence: []*snap.SideInfo{&siOld, &si},
  8415  		Current:  si.Revision,
  8416  	})
  8417  
  8418  	chg := s.state.NewChange("revert", "revert a snap backwards")
  8419  	ts, err := snapstate.Revert(s.state, "some-snap_instance", snapstate.Flags{})
  8420  	c.Assert(err, IsNil)
  8421  	chg.AddAll(ts)
  8422  
  8423  	s.state.Unlock()
  8424  	defer s.se.Stop()
  8425  	s.settle(c)
  8426  	s.state.Lock()
  8427  
  8428  	expected := fakeOps{
  8429  		{
  8430  			op:   "remove-snap-aliases",
  8431  			name: "some-snap_instance",
  8432  		},
  8433  		{
  8434  			op:   "unlink-snap",
  8435  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  8436  		},
  8437  		{
  8438  			op:    "setup-profiles:Doing",
  8439  			name:  "some-snap_instance",
  8440  			revno: snap.R(2),
  8441  		},
  8442  		{
  8443  			op: "candidate",
  8444  			sinfo: snap.SideInfo{
  8445  				RealName: "some-snap",
  8446  				Revision: snap.R(2),
  8447  			},
  8448  		},
  8449  		{
  8450  			op:   "link-snap",
  8451  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/2"),
  8452  		},
  8453  		{
  8454  			op:    "auto-connect:Doing",
  8455  			name:  "some-snap_instance",
  8456  			revno: snap.R(2),
  8457  		},
  8458  		{
  8459  			op: "update-aliases",
  8460  		},
  8461  	}
  8462  	// start with an easier-to-read error if this fails:
  8463  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  8464  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  8465  
  8466  	// verify that the R(2) version is active now and R(7) is still there
  8467  	var snapst snapstate.SnapState
  8468  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  8469  	c.Assert(err, IsNil)
  8470  
  8471  	c.Assert(snapst.Active, Equals, true)
  8472  	c.Assert(snapst.Current, Equals, snap.R(2))
  8473  	c.Assert(snapst.InstanceKey, Equals, "instance")
  8474  	c.Assert(snapst.Sequence, HasLen, 2)
  8475  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  8476  		RealName: "some-snap",
  8477  		Channel:  "",
  8478  		Revision: snap.R(2),
  8479  	})
  8480  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  8481  		RealName: "some-snap",
  8482  		Channel:  "",
  8483  		Revision: snap.R(7),
  8484  	})
  8485  	c.Assert(snapst.Block(), DeepEquals, []snap.Revision{snap.R(7)})
  8486  
  8487  	// non instance snap is not affected
  8488  	var nonInstanceSnapst snapstate.SnapState
  8489  	err = snapstate.Get(s.state, "some-snap", &nonInstanceSnapst)
  8490  	c.Assert(err, IsNil)
  8491  	c.Assert(nonInstanceSnapst.Current, Equals, snap.R(7))
  8492  
  8493  }
  8494  
  8495  func (s *snapmgrTestSuite) TestRevertWithLocalRevisionRunThrough(c *C) {
  8496  	si := snap.SideInfo{
  8497  		RealName: "some-snap",
  8498  		Revision: snap.R(-7),
  8499  	}
  8500  	siOld := snap.SideInfo{
  8501  		RealName: "some-snap",
  8502  		Revision: snap.R(-2),
  8503  	}
  8504  
  8505  	s.state.Lock()
  8506  	defer s.state.Unlock()
  8507  
  8508  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8509  		Active:   true,
  8510  		SnapType: "app",
  8511  		Sequence: []*snap.SideInfo{&siOld, &si},
  8512  		Current:  si.Revision,
  8513  	})
  8514  
  8515  	chg := s.state.NewChange("revert", "revert a snap backwards")
  8516  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  8517  	c.Assert(err, IsNil)
  8518  	chg.AddAll(ts)
  8519  
  8520  	s.state.Unlock()
  8521  	defer s.se.Stop()
  8522  	s.settle(c)
  8523  	s.state.Lock()
  8524  
  8525  	c.Assert(s.fakeBackend.ops.Ops(), HasLen, 7)
  8526  
  8527  	// verify that LocalRevision is still -7
  8528  	var snapst snapstate.SnapState
  8529  	err = snapstate.Get(s.state, "some-snap", &snapst)
  8530  	c.Assert(err, IsNil)
  8531  
  8532  	c.Assert(snapst.LocalRevision(), Equals, snap.R(-7))
  8533  }
  8534  
  8535  func (s *snapmgrTestSuite) TestRevertToRevisionNewVersion(c *C) {
  8536  	siNew := snap.SideInfo{
  8537  		RealName: "some-snap",
  8538  		Revision: snap.R(7),
  8539  		SnapID:   "october",
  8540  	}
  8541  
  8542  	si := snap.SideInfo{
  8543  		RealName: "some-snap",
  8544  		Revision: snap.R(2),
  8545  		SnapID:   "october",
  8546  	}
  8547  
  8548  	s.state.Lock()
  8549  	defer s.state.Unlock()
  8550  
  8551  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8552  		Active:   true,
  8553  		SnapType: "app",
  8554  		Sequence: []*snap.SideInfo{&si, &siNew},
  8555  		Current:  snap.R(2),
  8556  		Channel:  "edge",
  8557  	})
  8558  
  8559  	chg := s.state.NewChange("revert", "revert a snap forward")
  8560  	ts, err := snapstate.RevertToRevision(s.state, "some-snap", snap.R(7), snapstate.Flags{})
  8561  	c.Assert(err, IsNil)
  8562  	chg.AddAll(ts)
  8563  
  8564  	s.state.Unlock()
  8565  	defer s.se.Stop()
  8566  	s.settle(c)
  8567  	s.state.Lock()
  8568  
  8569  	expected := fakeOps{
  8570  		{
  8571  			op:   "remove-snap-aliases",
  8572  			name: "some-snap",
  8573  		},
  8574  		{
  8575  			op:   "unlink-snap",
  8576  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  8577  		},
  8578  		{
  8579  			op:    "setup-profiles:Doing",
  8580  			name:  "some-snap",
  8581  			revno: snap.R(7),
  8582  		},
  8583  		{
  8584  			op:    "candidate",
  8585  			sinfo: siNew,
  8586  		},
  8587  		{
  8588  			op:   "link-snap",
  8589  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  8590  		},
  8591  		{
  8592  			op:    "auto-connect:Doing",
  8593  			name:  "some-snap",
  8594  			revno: snap.R(7),
  8595  		},
  8596  		{
  8597  			op: "update-aliases",
  8598  		},
  8599  	}
  8600  	// start with an easier-to-read error if this fails:
  8601  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  8602  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  8603  
  8604  	// verify that the R(7) version is active now
  8605  	var snapst snapstate.SnapState
  8606  	err = snapstate.Get(s.state, "some-snap", &snapst)
  8607  	c.Assert(err, IsNil)
  8608  
  8609  	c.Check(snapst.Active, Equals, true)
  8610  	c.Check(snapst.Current, Equals, snap.R(7))
  8611  	c.Check(snapst.Sequence, HasLen, 2)
  8612  	c.Check(snapst.Channel, Equals, "edge")
  8613  	c.Check(snapst.CurrentSideInfo(), DeepEquals, &siNew)
  8614  
  8615  	c.Check(snapst.Block(), HasLen, 0)
  8616  }
  8617  
  8618  func (s *snapmgrTestSuite) TestRevertTotalUndoRunThrough(c *C) {
  8619  	si := snap.SideInfo{
  8620  		RealName: "some-snap",
  8621  		Revision: snap.R(1),
  8622  	}
  8623  	si2 := snap.SideInfo{
  8624  		RealName: "some-snap",
  8625  		Revision: snap.R(2),
  8626  	}
  8627  
  8628  	s.state.Lock()
  8629  	defer s.state.Unlock()
  8630  
  8631  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8632  		Active:   true,
  8633  		SnapType: "app",
  8634  		Sequence: []*snap.SideInfo{&si, &si2},
  8635  		Current:  si2.Revision,
  8636  	})
  8637  
  8638  	chg := s.state.NewChange("revert", "revert a snap")
  8639  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  8640  	c.Assert(err, IsNil)
  8641  	chg.AddAll(ts)
  8642  
  8643  	tasks := ts.Tasks()
  8644  	last := tasks[len(tasks)-1]
  8645  
  8646  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  8647  	terr.WaitFor(last)
  8648  	chg.AddTask(terr)
  8649  
  8650  	s.state.Unlock()
  8651  	defer s.se.Stop()
  8652  	s.settle(c)
  8653  	s.state.Lock()
  8654  
  8655  	expected := fakeOps{
  8656  		{
  8657  			op:   "remove-snap-aliases",
  8658  			name: "some-snap",
  8659  		},
  8660  		{
  8661  			op:   "unlink-snap",
  8662  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  8663  		},
  8664  		{
  8665  			op:    "setup-profiles:Doing",
  8666  			name:  "some-snap",
  8667  			revno: snap.R(1),
  8668  		},
  8669  		{
  8670  			op: "candidate",
  8671  			sinfo: snap.SideInfo{
  8672  				RealName: "some-snap",
  8673  				Revision: snap.R(1),
  8674  			},
  8675  		},
  8676  		{
  8677  			op:   "link-snap",
  8678  			path: filepath.Join(dirs.SnapMountDir, "some-snap/1"),
  8679  		},
  8680  		{
  8681  			op:    "auto-connect:Doing",
  8682  			name:  "some-snap",
  8683  			revno: snap.R(1),
  8684  		},
  8685  		{
  8686  			op: "update-aliases",
  8687  		},
  8688  		// undoing everything from here down...
  8689  		{
  8690  			op:   "remove-snap-aliases",
  8691  			name: "some-snap",
  8692  		},
  8693  		{
  8694  			op:   "unlink-snap",
  8695  			path: filepath.Join(dirs.SnapMountDir, "some-snap/1"),
  8696  		},
  8697  		{
  8698  			op:    "setup-profiles:Undoing",
  8699  			name:  "some-snap",
  8700  			revno: snap.R(1),
  8701  		},
  8702  		{
  8703  			op:   "link-snap",
  8704  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  8705  		},
  8706  		{
  8707  			op: "update-aliases",
  8708  		},
  8709  	}
  8710  	// start with an easier-to-read error if this fails:
  8711  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  8712  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  8713  
  8714  	// verify snaps in the system state
  8715  	var snapst snapstate.SnapState
  8716  	err = snapstate.Get(s.state, "some-snap", &snapst)
  8717  	c.Assert(err, IsNil)
  8718  
  8719  	c.Assert(snapst.Active, Equals, true)
  8720  	c.Assert(snapst.Sequence, HasLen, 2)
  8721  	c.Assert(snapst.Current, Equals, si2.Revision)
  8722  }
  8723  
  8724  func (s *snapmgrTestSuite) TestRevertUndoRunThrough(c *C) {
  8725  	si := snap.SideInfo{
  8726  		RealName: "some-snap",
  8727  		Revision: snap.R(1),
  8728  	}
  8729  	si2 := snap.SideInfo{
  8730  		RealName: "some-snap",
  8731  		Revision: snap.R(2),
  8732  	}
  8733  
  8734  	s.state.Lock()
  8735  	defer s.state.Unlock()
  8736  
  8737  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8738  		Active:   true,
  8739  		SnapType: "app",
  8740  		Sequence: []*snap.SideInfo{&si, &si2},
  8741  		Current:  si2.Revision,
  8742  	})
  8743  
  8744  	chg := s.state.NewChange("revert", "install a revert")
  8745  	ts, err := snapstate.Revert(s.state, "some-snap", snapstate.Flags{})
  8746  	c.Assert(err, IsNil)
  8747  	chg.AddAll(ts)
  8748  
  8749  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/1")
  8750  
  8751  	s.state.Unlock()
  8752  	defer s.se.Stop()
  8753  	s.settle(c)
  8754  	s.state.Lock()
  8755  
  8756  	expected := fakeOps{
  8757  		{
  8758  			op:   "remove-snap-aliases",
  8759  			name: "some-snap",
  8760  		},
  8761  		{
  8762  			op:   "unlink-snap",
  8763  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  8764  		},
  8765  		{
  8766  			op:    "setup-profiles:Doing",
  8767  			name:  "some-snap",
  8768  			revno: snap.R(1),
  8769  		},
  8770  		{
  8771  			op: "candidate",
  8772  			sinfo: snap.SideInfo{
  8773  				RealName: "some-snap",
  8774  				Revision: snap.R(1),
  8775  			},
  8776  		},
  8777  		{
  8778  			op:   "link-snap.failed",
  8779  			path: filepath.Join(dirs.SnapMountDir, "some-snap/1"),
  8780  		},
  8781  		// undo stuff here
  8782  		{
  8783  			op:   "unlink-snap",
  8784  			path: filepath.Join(dirs.SnapMountDir, "some-snap/1"),
  8785  		},
  8786  		{
  8787  			op:    "setup-profiles:Undoing",
  8788  			name:  "some-snap",
  8789  			revno: snap.R(1),
  8790  		},
  8791  		{
  8792  			op:   "link-snap",
  8793  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
  8794  		},
  8795  		{
  8796  			op: "update-aliases",
  8797  		},
  8798  	}
  8799  
  8800  	// ensure all our tasks ran
  8801  	// start with an easier-to-read error if this fails:
  8802  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  8803  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  8804  
  8805  	// verify snaps in the system state
  8806  	var snapst snapstate.SnapState
  8807  	err = snapstate.Get(s.state, "some-snap", &snapst)
  8808  	c.Assert(err, IsNil)
  8809  
  8810  	c.Assert(snapst.Active, Equals, true)
  8811  	c.Assert(snapst.Sequence, HasLen, 2)
  8812  	c.Assert(snapst.Current, Equals, snap.R(2))
  8813  }
  8814  
  8815  func (s *snapmgrTestSuite) TestEnableDoesNotEnableAgain(c *C) {
  8816  	si := snap.SideInfo{
  8817  		RealName: "some-snap",
  8818  		Revision: snap.R(7),
  8819  	}
  8820  
  8821  	s.state.Lock()
  8822  	defer s.state.Unlock()
  8823  
  8824  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8825  		Sequence: []*snap.SideInfo{&si},
  8826  		Current:  snap.R(7),
  8827  		Active:   true,
  8828  	})
  8829  
  8830  	ts, err := snapstate.Enable(s.state, "some-snap")
  8831  	c.Assert(err, ErrorMatches, `snap "some-snap" already enabled`)
  8832  	c.Assert(ts, IsNil)
  8833  }
  8834  
  8835  func (s *snapmgrTestSuite) TestEnableRunThrough(c *C) {
  8836  	si := snap.SideInfo{
  8837  		RealName: "some-snap",
  8838  		Revision: snap.R(7),
  8839  		Channel:  "edge",
  8840  		SnapID:   "foo",
  8841  	}
  8842  
  8843  	s.state.Lock()
  8844  	defer s.state.Unlock()
  8845  
  8846  	flags := snapstate.Flags{
  8847  		DevMode:  true,
  8848  		JailMode: true,
  8849  		Classic:  true,
  8850  		TryMode:  true,
  8851  		Required: true,
  8852  	}
  8853  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8854  		Sequence:            []*snap.SideInfo{&si},
  8855  		Current:             si.Revision,
  8856  		Active:              false,
  8857  		Channel:             "edge",
  8858  		Flags:               flags,
  8859  		AliasesPending:      true,
  8860  		AutoAliasesDisabled: true,
  8861  	})
  8862  
  8863  	chg := s.state.NewChange("enable", "enable a snap")
  8864  	ts, err := snapstate.Enable(s.state, "some-snap")
  8865  	c.Assert(err, IsNil)
  8866  	chg.AddAll(ts)
  8867  
  8868  	s.state.Unlock()
  8869  	defer s.se.Stop()
  8870  	s.settle(c)
  8871  	s.state.Lock()
  8872  
  8873  	expected := fakeOps{
  8874  		{
  8875  			op:    "setup-profiles:Doing",
  8876  			name:  "some-snap",
  8877  			revno: snap.R(7),
  8878  		},
  8879  		{
  8880  			op:    "candidate",
  8881  			sinfo: si,
  8882  		},
  8883  		{
  8884  			op:   "link-snap",
  8885  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  8886  		},
  8887  		{
  8888  			op:    "auto-connect:Doing",
  8889  			name:  "some-snap",
  8890  			revno: snap.R(7),
  8891  		},
  8892  		{
  8893  			op: "update-aliases",
  8894  		},
  8895  	}
  8896  	// start with an easier-to-read error if this fails:
  8897  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  8898  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  8899  
  8900  	var snapst snapstate.SnapState
  8901  	err = snapstate.Get(s.state, "some-snap", &snapst)
  8902  	c.Assert(err, IsNil)
  8903  	c.Check(snapst.Flags, DeepEquals, flags)
  8904  
  8905  	c.Assert(snapst.Active, Equals, true)
  8906  	c.Assert(snapst.AliasesPending, Equals, false)
  8907  	c.Assert(snapst.AutoAliasesDisabled, Equals, true)
  8908  
  8909  	info, err := snapst.CurrentInfo()
  8910  	c.Assert(err, IsNil)
  8911  	c.Assert(info.Channel, Equals, "edge")
  8912  	c.Assert(info.SnapID, Equals, "foo")
  8913  
  8914  	first := ts.Tasks()[0]
  8915  	snapsup, err := snapstate.TaskSnapSetup(first)
  8916  	c.Assert(err, IsNil)
  8917  	c.Check(snapsup, DeepEquals, &snapstate.SnapSetup{
  8918  		SideInfo:  &si,
  8919  		Flags:     flags,
  8920  		Type:      snap.TypeApp,
  8921  		PlugsOnly: true,
  8922  	})
  8923  }
  8924  
  8925  func (s *snapmgrTestSuite) TestDisableRunThrough(c *C) {
  8926  	si := snap.SideInfo{
  8927  		RealName: "some-snap",
  8928  		Revision: snap.R(7),
  8929  	}
  8930  
  8931  	s.state.Lock()
  8932  	defer s.state.Unlock()
  8933  
  8934  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  8935  		Sequence: []*snap.SideInfo{&si},
  8936  		Current:  si.Revision,
  8937  		Active:   true,
  8938  		SnapType: "app",
  8939  	})
  8940  
  8941  	chg := s.state.NewChange("disable", "disable a snap")
  8942  	ts, err := snapstate.Disable(s.state, "some-snap")
  8943  	c.Assert(err, IsNil)
  8944  	chg.AddAll(ts)
  8945  
  8946  	s.state.Unlock()
  8947  	defer s.se.Stop()
  8948  	s.settle(c)
  8949  	s.state.Lock()
  8950  
  8951  	expected := fakeOps{
  8952  		{
  8953  			op:   "remove-snap-aliases",
  8954  			name: "some-snap",
  8955  		},
  8956  		{
  8957  			op:   "unlink-snap",
  8958  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  8959  		},
  8960  		{
  8961  			op:    "remove-profiles:Doing",
  8962  			name:  "some-snap",
  8963  			revno: snap.R(7),
  8964  		},
  8965  	}
  8966  	// start with an easier-to-read error if this fails:
  8967  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  8968  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  8969  
  8970  	var snapst snapstate.SnapState
  8971  	err = snapstate.Get(s.state, "some-snap", &snapst)
  8972  	c.Assert(err, IsNil)
  8973  
  8974  	c.Assert(snapst.Active, Equals, false)
  8975  	c.Assert(snapst.AliasesPending, Equals, true)
  8976  
  8977  	first := ts.Tasks()[0]
  8978  	snapsup, err := snapstate.TaskSnapSetup(first)
  8979  	c.Assert(err, IsNil)
  8980  	c.Check(snapsup, DeepEquals, &snapstate.SnapSetup{
  8981  		SideInfo: &snap.SideInfo{
  8982  			RealName: "some-snap",
  8983  			Revision: snap.R(7),
  8984  		},
  8985  		Type:      snap.TypeApp,
  8986  		PlugsOnly: true,
  8987  	})
  8988  }
  8989  
  8990  func (s *snapmgrTestSuite) TestParallelInstanceEnableRunThrough(c *C) {
  8991  	si := snap.SideInfo{
  8992  		RealName: "some-snap",
  8993  		Revision: snap.R(7),
  8994  		Channel:  "edge",
  8995  		SnapID:   "foo",
  8996  	}
  8997  
  8998  	s.state.Lock()
  8999  	defer s.state.Unlock()
  9000  
  9001  	flags := snapstate.Flags{
  9002  		DevMode:  true,
  9003  		JailMode: true,
  9004  		Classic:  true,
  9005  		TryMode:  true,
  9006  		Required: true,
  9007  	}
  9008  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  9009  		Sequence:            []*snap.SideInfo{&si},
  9010  		Current:             si.Revision,
  9011  		Active:              false,
  9012  		Channel:             "edge",
  9013  		Flags:               flags,
  9014  		AliasesPending:      true,
  9015  		AutoAliasesDisabled: true,
  9016  		InstanceKey:         "instance",
  9017  	})
  9018  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  9019  		Sequence:            []*snap.SideInfo{&si},
  9020  		Current:             si.Revision,
  9021  		Active:              false,
  9022  		Channel:             "edge",
  9023  		Flags:               flags,
  9024  		AliasesPending:      true,
  9025  		AutoAliasesDisabled: true,
  9026  	})
  9027  
  9028  	chg := s.state.NewChange("enable", "enable a snap")
  9029  	ts, err := snapstate.Enable(s.state, "some-snap_instance")
  9030  	c.Assert(err, IsNil)
  9031  	chg.AddAll(ts)
  9032  
  9033  	s.state.Unlock()
  9034  	s.settle(c)
  9035  	s.state.Lock()
  9036  
  9037  	expected := fakeOps{
  9038  		{
  9039  			op:    "setup-profiles:Doing",
  9040  			name:  "some-snap_instance",
  9041  			revno: snap.R(7),
  9042  		},
  9043  		{
  9044  			op:    "candidate",
  9045  			sinfo: si,
  9046  		},
  9047  		{
  9048  			op:   "link-snap",
  9049  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  9050  		},
  9051  		{
  9052  			op:    "auto-connect:Doing",
  9053  			name:  "some-snap_instance",
  9054  			revno: snap.R(7),
  9055  		},
  9056  		{
  9057  			op: "update-aliases",
  9058  		},
  9059  	}
  9060  	// start with an easier-to-read error if this fails:
  9061  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  9062  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  9063  
  9064  	var snapst snapstate.SnapState
  9065  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  9066  	c.Assert(err, IsNil)
  9067  	c.Check(snapst.Flags, DeepEquals, flags)
  9068  
  9069  	c.Assert(snapst.InstanceKey, Equals, "instance")
  9070  	c.Assert(snapst.Active, Equals, true)
  9071  	c.Assert(snapst.AliasesPending, Equals, false)
  9072  	c.Assert(snapst.AutoAliasesDisabled, Equals, true)
  9073  
  9074  	info, err := snapst.CurrentInfo()
  9075  	c.Assert(err, IsNil)
  9076  	c.Assert(info.Channel, Equals, "edge")
  9077  	c.Assert(info.SnapID, Equals, "foo")
  9078  
  9079  	// the non-parallel instance is still disabled
  9080  	snapst = snapstate.SnapState{}
  9081  	err = snapstate.Get(s.state, "some-snap", &snapst)
  9082  	c.Assert(err, IsNil)
  9083  	c.Assert(snapst.InstanceKey, Equals, "")
  9084  	c.Assert(snapst.Active, Equals, false)
  9085  }
  9086  
  9087  func (s *snapmgrTestSuite) TestParallelInstanceDisableRunThrough(c *C) {
  9088  	si := snap.SideInfo{
  9089  		RealName: "some-snap",
  9090  		Revision: snap.R(7),
  9091  	}
  9092  
  9093  	s.state.Lock()
  9094  	defer s.state.Unlock()
  9095  
  9096  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  9097  		Sequence: []*snap.SideInfo{&si},
  9098  		Current:  si.Revision,
  9099  		Active:   true,
  9100  	})
  9101  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  9102  		Sequence:    []*snap.SideInfo{&si},
  9103  		Current:     si.Revision,
  9104  		Active:      true,
  9105  		InstanceKey: "instance",
  9106  	})
  9107  
  9108  	chg := s.state.NewChange("disable", "disable a snap")
  9109  	ts, err := snapstate.Disable(s.state, "some-snap_instance")
  9110  	c.Assert(err, IsNil)
  9111  	chg.AddAll(ts)
  9112  
  9113  	s.state.Unlock()
  9114  	s.settle(c)
  9115  	s.state.Lock()
  9116  
  9117  	expected := fakeOps{
  9118  		{
  9119  			op:   "remove-snap-aliases",
  9120  			name: "some-snap_instance",
  9121  		},
  9122  		{
  9123  			op:   "unlink-snap",
  9124  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/7"),
  9125  		},
  9126  		{
  9127  			op:    "remove-profiles:Doing",
  9128  			name:  "some-snap_instance",
  9129  			revno: snap.R(7),
  9130  		},
  9131  	}
  9132  	// start with an easier-to-read error if this fails:
  9133  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  9134  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  9135  
  9136  	var snapst snapstate.SnapState
  9137  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  9138  	c.Assert(err, IsNil)
  9139  	c.Assert(snapst.InstanceKey, Equals, "instance")
  9140  	c.Assert(snapst.Active, Equals, false)
  9141  	c.Assert(snapst.AliasesPending, Equals, true)
  9142  
  9143  	// the non-parallel instance is still active
  9144  	snapst = snapstate.SnapState{}
  9145  	err = snapstate.Get(s.state, "some-snap", &snapst)
  9146  	c.Assert(err, IsNil)
  9147  	c.Assert(snapst.InstanceKey, Equals, "")
  9148  	c.Assert(snapst.Active, Equals, true)
  9149  }
  9150  
  9151  type switchScenario struct {
  9152  	chanFrom string
  9153  	chanTo   string
  9154  	cohFrom  string
  9155  	cohTo    string
  9156  	summary  string
  9157  }
  9158  
  9159  var switchScenarios = map[string]switchScenario{
  9160  	"no cohort at all": {
  9161  		chanFrom: "stable",
  9162  		chanTo:   "some-channel",
  9163  		cohFrom:  "",
  9164  		cohTo:    "",
  9165  		summary:  `Switch snap "some-snap" from channel "stable" to "some-channel"`,
  9166  	},
  9167  	"no cohort, from empty channel": {
  9168  		chanFrom: "",
  9169  		chanTo:   "some-channel",
  9170  		cohFrom:  "",
  9171  		cohTo:    "",
  9172  		summary:  `Switch snap "some-snap" to channel "some-channel"`,
  9173  	},
  9174  	"no cohort change requested": {
  9175  		chanFrom: "stable",
  9176  		chanTo:   "some-channel",
  9177  		cohFrom:  "some-cohort",
  9178  		cohTo:    "some-cohort",
  9179  		summary:  `Switch snap "some-snap" from channel "stable" to "some-channel"`,
  9180  	},
  9181  	"leave cohort": {
  9182  		chanFrom: "stable",
  9183  		chanTo:   "stable",
  9184  		cohFrom:  "some-cohort",
  9185  		cohTo:    "",
  9186  		summary:  `Switch snap "some-snap" away from cohort "…me-cohort"`,
  9187  	},
  9188  	"leave cohort, change channel": {
  9189  		chanFrom: "stable",
  9190  		chanTo:   "edge",
  9191  		cohFrom:  "some-cohort",
  9192  		cohTo:    "",
  9193  		summary:  `Switch snap "some-snap" from channel "stable" to "edge" and away from cohort "…me-cohort"`,
  9194  	},
  9195  	"leave cohort, change from empty channel": {
  9196  		chanFrom: "",
  9197  		chanTo:   "stable",
  9198  		cohFrom:  "some-cohort",
  9199  		cohTo:    "",
  9200  		summary:  `Switch snap "some-snap" to channel "stable" and away from cohort "…me-cohort"`,
  9201  	},
  9202  	"no channel at all": {
  9203  		chanFrom: "",
  9204  		chanTo:   "",
  9205  		cohFrom:  "some-cohort",
  9206  		cohTo:    "some-other-cohort",
  9207  		summary:  `Switch snap "some-snap" from cohort "…me-cohort" to "…er-cohort"`,
  9208  	},
  9209  	"no channel change requested": {
  9210  		chanFrom: "stable",
  9211  		chanTo:   "stable",
  9212  		cohFrom:  "some-cohort",
  9213  		cohTo:    "some-other-cohort",
  9214  		summary:  `Switch snap "some-snap" from cohort "…me-cohort" to "…er-cohort"`,
  9215  	},
  9216  	"no channel change requested, from empty cohort": {
  9217  		chanFrom: "stable",
  9218  		chanTo:   "stable",
  9219  		cohFrom:  "",
  9220  		cohTo:    "some-cohort",
  9221  		summary:  `Switch snap "some-snap" from no cohort to "…me-cohort"`,
  9222  	},
  9223  	"all change": {
  9224  		chanFrom: "stable",
  9225  		chanTo:   "edge",
  9226  		cohFrom:  "some-cohort",
  9227  		cohTo:    "some-other-cohort",
  9228  		summary:  `Switch snap "some-snap" from channel "stable" to "edge" and from cohort "…me-cohort" to "…er-cohort"`,
  9229  	},
  9230  	"all change, from empty channel": {
  9231  		chanFrom: "",
  9232  		chanTo:   "stable",
  9233  		cohFrom:  "some-cohort",
  9234  		cohTo:    "some-other-cohort",
  9235  		summary:  `Switch snap "some-snap" to channel "stable" and from cohort "…me-cohort" to "…er-cohort"`,
  9236  	},
  9237  	"all change, from empty cohort": {
  9238  		chanFrom: "stable",
  9239  		chanTo:   "edge",
  9240  		cohFrom:  "",
  9241  		cohTo:    "some-cohort",
  9242  		summary:  `Switch snap "some-snap" from channel "stable" to "edge" and from no cohort to "…me-cohort"`,
  9243  	},
  9244  	"all change, from empty channel and cohort": {
  9245  		chanFrom: "",
  9246  		chanTo:   "stable",
  9247  		cohFrom:  "",
  9248  		cohTo:    "some-cohort",
  9249  		summary:  `Switch snap "some-snap" to channel "stable" and from no cohort to "…me-cohort"`,
  9250  	},
  9251  	"no change": {
  9252  		chanFrom: "stable",
  9253  		chanTo:   "stable",
  9254  		cohFrom:  "some-cohort",
  9255  		cohTo:    "some-cohort",
  9256  		summary:  `No change switch (no-op)`,
  9257  	},
  9258  }
  9259  
  9260  func (s *snapmgrTestSuite) TestSwitchScenarios(c *C) {
  9261  	for k, t := range switchScenarios {
  9262  		s.testSwitchScenario(c, k, t)
  9263  	}
  9264  }
  9265  
  9266  func (s *snapmgrTestSuite) testSwitchScenario(c *C, desc string, t switchScenario) {
  9267  	comment := Commentf("%q (%+v)", desc, t)
  9268  	si := snap.SideInfo{
  9269  		RealName: "some-snap",
  9270  		Revision: snap.R(7),
  9271  		Channel:  t.chanFrom,
  9272  		SnapID:   "foo",
  9273  	}
  9274  
  9275  	s.state.Lock()
  9276  	defer s.state.Unlock()
  9277  
  9278  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  9279  		Sequence:  []*snap.SideInfo{&si},
  9280  		Current:   si.Revision,
  9281  		Channel:   t.chanFrom,
  9282  		CohortKey: t.cohFrom,
  9283  	})
  9284  
  9285  	summary := snapstate.SwitchSummary("some-snap", t.chanFrom, t.chanTo, t.cohFrom, t.cohTo)
  9286  	c.Check(summary, Equals, t.summary, comment)
  9287  	chg := s.state.NewChange("switch-snap", summary)
  9288  	ts, err := snapstate.Switch(s.state, "some-snap", &snapstate.RevisionOptions{
  9289  		Channel:     t.chanTo,
  9290  		CohortKey:   t.cohTo,
  9291  		LeaveCohort: t.cohFrom != "" && t.cohTo == "",
  9292  	})
  9293  	c.Assert(err, IsNil, comment)
  9294  	chg.AddAll(ts)
  9295  
  9296  	s.state.Unlock()
  9297  	s.settle(c)
  9298  	s.state.Lock()
  9299  
  9300  	// switch is not really really doing anything backend related
  9301  	c.Assert(s.fakeBackend.ops, HasLen, 0, comment)
  9302  
  9303  	expectedChanTo := t.chanTo
  9304  	if t.chanTo == "" {
  9305  		expectedChanTo = t.chanFrom
  9306  	}
  9307  	expectedCohTo := t.cohTo
  9308  
  9309  	// ensure the desired channel/cohort has changed
  9310  	var snapst snapstate.SnapState
  9311  	err = snapstate.Get(s.state, "some-snap", &snapst)
  9312  	c.Assert(err, IsNil, comment)
  9313  	c.Assert(snapst.Channel, Equals, expectedChanTo, comment)
  9314  	c.Assert(snapst.CohortKey, Equals, expectedCohTo, comment)
  9315  
  9316  	// ensure the current info has not changed
  9317  	info, err := snapst.CurrentInfo()
  9318  	c.Assert(err, IsNil, comment)
  9319  	c.Assert(info.Channel, Equals, t.chanFrom, comment)
  9320  }
  9321  
  9322  func (s *snapmgrTestSuite) TestUpdateScenarios(c *C) {
  9323  	// TODO: also use channel-for-7 or equiv to check updates that are switches
  9324  	for k, t := range switchScenarios {
  9325  		s.testUpdateScenario(c, k, t)
  9326  	}
  9327  }
  9328  
  9329  func (s *snapmgrTestSuite) testUpdateScenario(c *C, desc string, t switchScenario) {
  9330  	// reset
  9331  	s.fakeBackend.ops = nil
  9332  
  9333  	comment := Commentf("%q (%+v)", desc, t)
  9334  	si := snap.SideInfo{
  9335  		RealName: "some-snap",
  9336  		Revision: snap.R(7),
  9337  		Channel:  t.chanFrom,
  9338  		SnapID:   "some-snap-id",
  9339  	}
  9340  
  9341  	s.state.Lock()
  9342  	defer s.state.Unlock()
  9343  
  9344  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  9345  		Sequence:  []*snap.SideInfo{&si},
  9346  		Active:    true,
  9347  		Current:   si.Revision,
  9348  		Channel:   t.chanFrom,
  9349  		CohortKey: t.cohFrom,
  9350  	})
  9351  
  9352  	chg := s.state.NewChange("update-snap", t.summary)
  9353  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{
  9354  		Channel:     t.chanTo,
  9355  		CohortKey:   t.cohTo,
  9356  		LeaveCohort: t.cohFrom != "" && t.cohTo == "",
  9357  	}, 0, snapstate.Flags{})
  9358  	c.Assert(err, IsNil, comment)
  9359  	chg.AddAll(ts)
  9360  
  9361  	s.state.Unlock()
  9362  	s.settle(c)
  9363  	s.state.Lock()
  9364  
  9365  	// switch is not really really doing anything backend related
  9366  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, []string{
  9367  		"storesvc-snap-action",
  9368  		"storesvc-snap-action:action",
  9369  		"storesvc-download",
  9370  		"validate-snap:Doing",
  9371  		"current",
  9372  		"open-snap-file",
  9373  		"setup-snap",
  9374  		"remove-snap-aliases",
  9375  		"unlink-snap",
  9376  		"copy-data",
  9377  		"setup-profiles:Doing",
  9378  		"candidate",
  9379  		"link-snap",
  9380  		"auto-connect:Doing",
  9381  		"update-aliases",
  9382  		"cleanup-trash",
  9383  	}, comment)
  9384  
  9385  	expectedChanTo := t.chanTo
  9386  	if t.chanTo == "" {
  9387  		expectedChanTo = t.chanFrom
  9388  	}
  9389  	expectedCohTo := t.cohTo
  9390  
  9391  	// ensure the desired channel/cohort has changed
  9392  	var snapst snapstate.SnapState
  9393  	err = snapstate.Get(s.state, "some-snap", &snapst)
  9394  	c.Assert(err, IsNil, comment)
  9395  	c.Assert(snapst.Channel, Equals, expectedChanTo, comment)
  9396  	c.Assert(snapst.CohortKey, Equals, expectedCohTo, comment)
  9397  
  9398  	// ensure the current info *has* changed
  9399  	info, err := snapst.CurrentInfo()
  9400  	c.Assert(err, IsNil, comment)
  9401  	c.Assert(info.Channel, Equals, expectedChanTo, comment)
  9402  }
  9403  
  9404  func (s *snapmgrTestSuite) TestParallelInstallSwitchRunThrough(c *C) {
  9405  	si := snap.SideInfo{
  9406  		RealName: "some-snap",
  9407  		Revision: snap.R(7),
  9408  		Channel:  "edge",
  9409  		SnapID:   "foo",
  9410  	}
  9411  
  9412  	s.state.Lock()
  9413  	defer s.state.Unlock()
  9414  
  9415  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  9416  		Sequence: []*snap.SideInfo{&si},
  9417  		Current:  si.Revision,
  9418  		Channel:  "edge",
  9419  	})
  9420  
  9421  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  9422  		Sequence:    []*snap.SideInfo{&si},
  9423  		Current:     si.Revision,
  9424  		Channel:     "edge",
  9425  		InstanceKey: "instance",
  9426  	})
  9427  
  9428  	chg := s.state.NewChange("switch-snap", "switch snap to some-channel")
  9429  	ts, err := snapstate.Switch(s.state, "some-snap_instance", &snapstate.RevisionOptions{Channel: "some-channel"})
  9430  	c.Assert(err, IsNil)
  9431  	chg.AddAll(ts)
  9432  
  9433  	s.state.Unlock()
  9434  	defer s.se.Stop()
  9435  	s.settle(c)
  9436  	s.state.Lock()
  9437  
  9438  	// switch is not really really doing anything backend related
  9439  	c.Assert(s.fakeBackend.ops, HasLen, 0)
  9440  
  9441  	// ensure the desired channel has changed
  9442  	var snapst snapstate.SnapState
  9443  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  9444  	c.Assert(err, IsNil)
  9445  	c.Assert(snapst.Channel, Equals, "some-channel")
  9446  
  9447  	// ensure the current info has not changed
  9448  	info, err := snapst.CurrentInfo()
  9449  	c.Assert(err, IsNil)
  9450  	c.Assert(info.Channel, Equals, "edge")
  9451  
  9452  	// Ensure that the non-intance snap is unchanged
  9453  	var nonInstanceSnapst snapstate.SnapState
  9454  	err = snapstate.Get(s.state, "some-snap", &nonInstanceSnapst)
  9455  	c.Assert(err, IsNil)
  9456  	c.Assert(nonInstanceSnapst.Channel, Equals, "edge")
  9457  }
  9458  
  9459  func (s *snapmgrTestSuite) TestDisableDoesNotEnableAgain(c *C) {
  9460  	si := snap.SideInfo{
  9461  		RealName: "some-snap",
  9462  		Revision: snap.R(7),
  9463  	}
  9464  
  9465  	s.state.Lock()
  9466  	defer s.state.Unlock()
  9467  
  9468  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  9469  		Sequence: []*snap.SideInfo{&si},
  9470  		Current:  snap.R(7),
  9471  		Active:   false,
  9472  	})
  9473  
  9474  	ts, err := snapstate.Disable(s.state, "some-snap")
  9475  	c.Assert(err, ErrorMatches, `snap "some-snap" already disabled`)
  9476  	c.Assert(ts, IsNil)
  9477  }
  9478  
  9479  func (s *snapmgrTestSuite) TestUndoMountSnapFailsInCopyData(c *C) {
  9480  	s.state.Lock()
  9481  	defer s.state.Unlock()
  9482  
  9483  	chg := s.state.NewChange("install", "install a snap")
  9484  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  9485  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  9486  	c.Assert(err, IsNil)
  9487  	chg.AddAll(ts)
  9488  
  9489  	s.fakeBackend.copySnapDataFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11")
  9490  
  9491  	s.state.Unlock()
  9492  	defer s.se.Stop()
  9493  	s.settle(c)
  9494  	s.state.Lock()
  9495  
  9496  	expected := fakeOps{
  9497  		{
  9498  			op:     "storesvc-snap-action",
  9499  			userID: 1,
  9500  		},
  9501  		{
  9502  			op: "storesvc-snap-action:action",
  9503  			action: store.SnapAction{
  9504  				Action:       "install",
  9505  				InstanceName: "some-snap",
  9506  				Channel:      "some-channel",
  9507  			},
  9508  			revno:  snap.R(11),
  9509  			userID: 1,
  9510  		},
  9511  		{
  9512  			op:   "storesvc-download",
  9513  			name: "some-snap",
  9514  		},
  9515  		{
  9516  			op:    "validate-snap:Doing",
  9517  			name:  "some-snap",
  9518  			revno: snap.R(11),
  9519  		},
  9520  		{
  9521  			op:  "current",
  9522  			old: "<no-current>",
  9523  		},
  9524  		{
  9525  			op:   "open-snap-file",
  9526  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  9527  			sinfo: snap.SideInfo{
  9528  				RealName: "some-snap",
  9529  				SnapID:   "some-snap-id",
  9530  				Channel:  "some-channel",
  9531  				Revision: snap.R(11),
  9532  			},
  9533  		},
  9534  		{
  9535  			op:    "setup-snap",
  9536  			name:  "some-snap",
  9537  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  9538  			revno: snap.R(11),
  9539  		},
  9540  		{
  9541  			op:   "copy-data.failed",
  9542  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  9543  			old:  "<no-old>",
  9544  		},
  9545  		{
  9546  			op:   "remove-snap-data-dir",
  9547  			name: "some-snap",
  9548  			path: filepath.Join(dirs.SnapDataDir, "some-snap"),
  9549  		},
  9550  		{
  9551  			op:    "undo-setup-snap",
  9552  			name:  "some-snap",
  9553  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  9554  			stype: "app",
  9555  		},
  9556  		{
  9557  			op:   "remove-snap-dir",
  9558  			name: "some-snap",
  9559  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  9560  		},
  9561  	}
  9562  	// start with an easier-to-read error if this fails:
  9563  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  9564  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  9565  }
  9566  
  9567  func (s *snapmgrTestSuite) TestRefreshFailureCausesErrorReport(c *C) {
  9568  	var errSnap, errMsg, errSig string
  9569  	var errExtra map[string]string
  9570  	var n int
  9571  	restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) {
  9572  		errSnap = aSnap
  9573  		errMsg = aErrMsg
  9574  		errSig = aDupSig
  9575  		errExtra = extra
  9576  		n += 1
  9577  		return "oopsid", nil
  9578  	})
  9579  	defer restore()
  9580  
  9581  	si := snap.SideInfo{
  9582  		RealName: "some-snap",
  9583  		SnapID:   "some-snap-id",
  9584  		Revision: snap.R(7),
  9585  	}
  9586  
  9587  	s.state.Lock()
  9588  	defer s.state.Unlock()
  9589  
  9590  	s.state.Set("ubuntu-core-transition-retry", 7)
  9591  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  9592  		Active:   true,
  9593  		Sequence: []*snap.SideInfo{&si},
  9594  		Current:  si.Revision,
  9595  		SnapType: "app",
  9596  	})
  9597  
  9598  	chg := s.state.NewChange("install", "install a snap")
  9599  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  9600  	c.Assert(err, IsNil)
  9601  	chg.AddAll(ts)
  9602  
  9603  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11")
  9604  
  9605  	s.state.Unlock()
  9606  	defer s.se.Stop()
  9607  	s.settle(c)
  9608  	s.state.Lock()
  9609  
  9610  	// verify we generated a failure report
  9611  	c.Check(n, Equals, 1)
  9612  	c.Check(errSnap, Equals, "some-snap")
  9613  	c.Check(errExtra, DeepEquals, map[string]string{
  9614  		"UbuntuCoreTransitionCount": "7",
  9615  		"Channel":                   "some-channel",
  9616  		"Revision":                  "11",
  9617  	})
  9618  	c.Check(errMsg, Matches, `(?sm)change "install": "install a snap"
  9619  prerequisites: Undo
  9620   snap-setup: "some-snap" \(11\) "some-channel"
  9621  download-snap: Undoing
  9622  validate-snap: Done
  9623  .*
  9624  link-snap: Error
  9625   INFO unlink
  9626   ERROR fail
  9627  auto-connect: Hold
  9628  set-auto-aliases: Hold
  9629  setup-aliases: Hold
  9630  run-hook: Hold
  9631  start-snap-services: Hold
  9632  cleanup: Hold
  9633  run-hook: Hold`)
  9634  	c.Check(errSig, Matches, `(?sm)snap-install:
  9635  prerequisites: Undo
  9636   snap-setup: "some-snap"
  9637  download-snap: Undoing
  9638  validate-snap: Done
  9639  .*
  9640  link-snap: Error
  9641   INFO unlink
  9642   ERROR fail
  9643  auto-connect: Hold
  9644  set-auto-aliases: Hold
  9645  setup-aliases: Hold
  9646  run-hook: Hold
  9647  start-snap-services: Hold
  9648  cleanup: Hold
  9649  run-hook: Hold`)
  9650  
  9651  	// run again with empty "ubuntu-core-transition-retry"
  9652  	s.state.Set("ubuntu-core-transition-retry", 0)
  9653  	chg = s.state.NewChange("install", "install a snap")
  9654  	ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  9655  	c.Assert(err, IsNil)
  9656  	chg.AddAll(ts)
  9657  	s.state.Unlock()
  9658  	defer s.se.Stop()
  9659  	s.settle(c)
  9660  	s.state.Lock()
  9661  	// verify that we excluded this field from the bugreport
  9662  	c.Check(n, Equals, 2)
  9663  	c.Check(errExtra, DeepEquals, map[string]string{
  9664  		"Channel":  "some-channel",
  9665  		"Revision": "11",
  9666  	})
  9667  
  9668  }
  9669  
  9670  func (s *snapmgrTestSuite) TestAbortCausesNoErrReport(c *C) {
  9671  	errReported := 0
  9672  	restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) {
  9673  		errReported++
  9674  		return "oops-id", nil
  9675  	})
  9676  	defer restore()
  9677  
  9678  	s.state.Lock()
  9679  	defer s.state.Unlock()
  9680  
  9681  	chg := s.state.NewChange("install", "install a snap")
  9682  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  9683  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  9684  	c.Assert(err, IsNil)
  9685  
  9686  	s.fakeBackend.linkSnapWaitCh = make(chan int)
  9687  	s.fakeBackend.linkSnapWaitTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11")
  9688  	go func() {
  9689  		<-s.fakeBackend.linkSnapWaitCh
  9690  		chg.Abort()
  9691  		s.fakeBackend.linkSnapWaitCh <- 1
  9692  	}()
  9693  
  9694  	chg.AddAll(ts)
  9695  
  9696  	s.state.Unlock()
  9697  	defer s.se.Stop()
  9698  	s.settle(c)
  9699  	s.state.Lock()
  9700  
  9701  	c.Check(chg.Status(), Equals, state.UndoneStatus)
  9702  	c.Assert(errReported, Equals, 0)
  9703  }
  9704  
  9705  func (s *snapmgrTestSuite) TestErrreportDisable(c *C) {
  9706  	s.state.Lock()
  9707  	defer s.state.Unlock()
  9708  
  9709  	tr := config.NewTransaction(s.state)
  9710  	tr.Set("core", "problem-reports.disabled", true)
  9711  	tr.Commit()
  9712  
  9713  	restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) {
  9714  		c.Fatalf("this should not be reached")
  9715  		return "", nil
  9716  	})
  9717  	defer restore()
  9718  
  9719  	chg := s.state.NewChange("install", "install a snap")
  9720  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  9721  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  9722  	c.Assert(err, IsNil)
  9723  	chg.AddAll(ts)
  9724  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11")
  9725  
  9726  	s.state.Unlock()
  9727  	defer s.se.Stop()
  9728  	s.settle(c)
  9729  	s.state.Lock()
  9730  
  9731  	// no failure report was generated
  9732  }
  9733  
  9734  func (s *snapmgrTestSuite) TestEnsureRefreshesAtSeedPolicy(c *C) {
  9735  	// special policy only on classic
  9736  	r := release.MockOnClassic(true)
  9737  	defer r()
  9738  	// set at not seeded yet
  9739  	st := s.state
  9740  	st.Lock()
  9741  	st.Set("seeded", nil)
  9742  	st.Unlock()
  9743  
  9744  	s.snapmgr.Ensure()
  9745  
  9746  	st.Lock()
  9747  	defer st.Unlock()
  9748  
  9749  	// check that refresh policies have run in this case
  9750  	var t1 time.Time
  9751  	err := st.Get("last-refresh-hints", &t1)
  9752  	c.Check(err, IsNil)
  9753  	tr := config.NewTransaction(st)
  9754  	err = tr.Get("core", "refresh.hold", &t1)
  9755  	c.Check(err, IsNil)
  9756  }
  9757  
  9758  func (s *snapmgrTestSuite) TestEsnureCleansOldSideloads(c *C) {
  9759  	filenames := func() []string {
  9760  		filenames, _ := filepath.Glob(filepath.Join(dirs.SnapBlobDir, "*"))
  9761  		return filenames
  9762  	}
  9763  
  9764  	defer snapstate.MockLocalInstallCleanupWait(200 * time.Millisecond)()
  9765  	c.Assert(os.MkdirAll(dirs.SnapBlobDir, 0700), IsNil)
  9766  	// sanity check; note * in go glob matches .foo
  9767  	c.Assert(filenames(), HasLen, 0)
  9768  
  9769  	s0 := filepath.Join(dirs.SnapBlobDir, "some.snap")
  9770  	s1 := filepath.Join(dirs.SnapBlobDir, dirs.LocalInstallBlobTempPrefix+"-12345")
  9771  	s2 := filepath.Join(dirs.SnapBlobDir, dirs.LocalInstallBlobTempPrefix+"-67890")
  9772  
  9773  	c.Assert(ioutil.WriteFile(s0, nil, 0600), IsNil)
  9774  	c.Assert(ioutil.WriteFile(s1, nil, 0600), IsNil)
  9775  	c.Assert(ioutil.WriteFile(s2, nil, 0600), IsNil)
  9776  
  9777  	t1 := time.Now()
  9778  	t0 := t1.Add(-time.Hour)
  9779  
  9780  	c.Assert(os.Chtimes(s0, t0, t0), IsNil)
  9781  	c.Assert(os.Chtimes(s1, t0, t0), IsNil)
  9782  	c.Assert(os.Chtimes(s2, t1, t1), IsNil)
  9783  
  9784  	// all there
  9785  	c.Assert(filenames(), DeepEquals, []string{s1, s2, s0})
  9786  
  9787  	// set last cleanup in the future
  9788  	defer snapstate.MockLocalInstallLastCleanup(t1.Add(time.Minute))()
  9789  	s.snapmgr.Ensure()
  9790  	// all there ( -> cleanup not done)
  9791  	c.Assert(filenames(), DeepEquals, []string{s1, s2, s0})
  9792  
  9793  	// set last cleanup to epoch
  9794  	snapstate.MockLocalInstallLastCleanup(time.Time{})
  9795  
  9796  	s.snapmgr.Ensure()
  9797  	// oldest sideload gone
  9798  	c.Assert(filenames(), DeepEquals, []string{s2, s0})
  9799  
  9800  	time.Sleep(200 * time.Millisecond)
  9801  
  9802  	s.snapmgr.Ensure()
  9803  	// all sideloads gone
  9804  	c.Assert(filenames(), DeepEquals, []string{s0})
  9805  
  9806  }
  9807  
  9808  func (s *snapmgrTestSuite) verifyRefreshLast(c *C) {
  9809  	var lastRefresh time.Time
  9810  
  9811  	s.state.Get("last-refresh", &lastRefresh)
  9812  	c.Check(time.Now().Year(), Equals, lastRefresh.Year())
  9813  }
  9814  
  9815  func makeTestRefreshConfig(st *state.State) {
  9816  	// avoid special at seed policy
  9817  	now := time.Now()
  9818  	st.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, now.Location()))
  9819  
  9820  	tr := config.NewTransaction(st)
  9821  	tr.Set("core", "refresh.timer", "00:00-23:59")
  9822  	tr.Commit()
  9823  }
  9824  
  9825  func (s *snapmgrTestSuite) TestEnsureRefreshRefusesLegacyWeekdaySchedules(c *C) {
  9826  	s.state.Lock()
  9827  	defer s.state.Unlock()
  9828  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  9829  
  9830  	logbuf, restore := logger.MockLogger()
  9831  	defer restore()
  9832  
  9833  	s.state.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, time.UTC))
  9834  	tr := config.NewTransaction(s.state)
  9835  	tr.Set("core", "refresh.timer", "")
  9836  	tr.Set("core", "refresh.schedule", "00:00-23:59/mon@12:00-14:00")
  9837  	tr.Commit()
  9838  
  9839  	// Ensure() also runs ensureRefreshes()
  9840  	s.state.Unlock()
  9841  	s.se.Ensure()
  9842  	s.state.Lock()
  9843  
  9844  	c.Check(logbuf.String(), testutil.Contains, `cannot use refresh.schedule configuration: cannot parse "mon@12:00": not a valid time`)
  9845  	schedule, legacy, err := s.snapmgr.RefreshSchedule()
  9846  	c.Assert(err, IsNil)
  9847  	c.Check(schedule, Equals, "00:00~24:00/4")
  9848  	c.Check(legacy, Equals, false)
  9849  
  9850  	tr = config.NewTransaction(s.state)
  9851  	refreshTimer := "canary"
  9852  	refreshSchedule := "canary"
  9853  	c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil)
  9854  	c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil)
  9855  	c.Check(refreshTimer, Equals, "")
  9856  	c.Check(refreshSchedule, Equals, "00:00-23:59/mon@12:00-14:00")
  9857  }
  9858  
  9859  func (s *snapmgrTestSuite) TestEnsureRefreshLegacyScheduleIsLowerPriority(c *C) {
  9860  	s.state.Lock()
  9861  	defer s.state.Unlock()
  9862  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  9863  
  9864  	s.state.Set("last-refresh", time.Date(2009, 8, 13, 8, 0, 5, 0, time.UTC))
  9865  	tr := config.NewTransaction(s.state)
  9866  	tr.Set("core", "refresh.timer", "00:00-23:59,,mon,12:00-14:00")
  9867  	// legacy schedule is invalid
  9868  	tr.Set("core", "refresh.schedule", "00:00-23:59/mon@12:00-14:00")
  9869  	tr.Commit()
  9870  
  9871  	// Ensure() also runs ensureRefreshes()
  9872  	s.state.Unlock()
  9873  	s.se.Ensure()
  9874  	s.state.Lock()
  9875  
  9876  	// expecting new refresh.timer to have been used, fallback to legacy was
  9877  	// not attempted otherwise it would get reset to the default due to
  9878  	// refresh.schedule being garbage
  9879  	schedule, legacy, err := s.snapmgr.RefreshSchedule()
  9880  	c.Assert(err, IsNil)
  9881  	c.Check(schedule, Equals, "00:00-23:59,,mon,12:00-14:00")
  9882  	c.Check(legacy, Equals, false)
  9883  }
  9884  
  9885  func (s *snapmgrTestSuite) TestEnsureRefreshFallbackToLegacySchedule(c *C) {
  9886  	s.state.Lock()
  9887  	defer s.state.Unlock()
  9888  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  9889  
  9890  	tr := config.NewTransaction(s.state)
  9891  	tr.Set("core", "refresh.timer", "")
  9892  	tr.Set("core", "refresh.schedule", "00:00-23:59")
  9893  	tr.Commit()
  9894  
  9895  	// Ensure() also runs ensureRefreshes()
  9896  	s.state.Unlock()
  9897  	s.se.Ensure()
  9898  	s.state.Lock()
  9899  
  9900  	// refresh.timer is unset, triggering automatic fallback to legacy
  9901  	// schedule if that was set
  9902  	schedule, legacy, err := s.snapmgr.RefreshSchedule()
  9903  	c.Assert(err, IsNil)
  9904  	c.Check(schedule, Equals, "00:00-23:59")
  9905  	c.Check(legacy, Equals, true)
  9906  }
  9907  
  9908  func (s *snapmgrTestSuite) TestEnsureRefreshFallbackToDefaultOnError(c *C) {
  9909  	s.state.Lock()
  9910  	defer s.state.Unlock()
  9911  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  9912  
  9913  	tr := config.NewTransaction(s.state)
  9914  	tr.Set("core", "refresh.timer", "garbage-in")
  9915  	tr.Set("core", "refresh.schedule", "00:00-23:59")
  9916  	tr.Commit()
  9917  
  9918  	// Ensure() also runs ensureRefreshes()
  9919  	s.state.Unlock()
  9920  	s.se.Ensure()
  9921  	s.state.Lock()
  9922  
  9923  	// automatic fallback to default schedule if refresh.timer is set but
  9924  	// cannot be parsed
  9925  	schedule, legacy, err := s.snapmgr.RefreshSchedule()
  9926  	c.Assert(err, IsNil)
  9927  	c.Check(schedule, Equals, "00:00~24:00/4")
  9928  	c.Check(legacy, Equals, false)
  9929  
  9930  	tr = config.NewTransaction(s.state)
  9931  	refreshTimer := "canary"
  9932  	refreshSchedule := "canary"
  9933  	c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil)
  9934  	c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil)
  9935  	c.Check(refreshTimer, Equals, "garbage-in")
  9936  	c.Check(refreshSchedule, Equals, "00:00-23:59")
  9937  }
  9938  
  9939  func (s *snapmgrTestSuite) TestEnsureRefreshFallbackOnEmptyToDefaultSchedule(c *C) {
  9940  	s.state.Lock()
  9941  	defer s.state.Unlock()
  9942  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  9943  
  9944  	tr := config.NewTransaction(s.state)
  9945  	tr.Set("core", "refresh.timer", "")
  9946  	tr.Set("core", "refresh.schedule", "")
  9947  	tr.Commit()
  9948  
  9949  	// Ensure() also runs ensureRefreshes()
  9950  	s.state.Unlock()
  9951  	s.se.Ensure()
  9952  	s.state.Lock()
  9953  
  9954  	// automatic fallback to default schedule if neither refresh.timer nor
  9955  	// refresh.schedule was set
  9956  	schedule, legacy, err := s.snapmgr.RefreshSchedule()
  9957  	c.Assert(err, IsNil)
  9958  	c.Check(schedule, Equals, "00:00~24:00/4")
  9959  	c.Check(legacy, Equals, false)
  9960  
  9961  	tr = config.NewTransaction(s.state)
  9962  	refreshTimer := "canary"
  9963  	refreshSchedule := "canary"
  9964  	c.Assert(tr.Get("core", "refresh.timer", &refreshTimer), IsNil)
  9965  	c.Assert(tr.Get("core", "refresh.schedule", &refreshSchedule), IsNil)
  9966  	c.Check(refreshTimer, Equals, "")
  9967  	c.Check(refreshSchedule, Equals, "")
  9968  }
  9969  
  9970  func (s *snapmgrTestSuite) TestEnsureRefreshesNoUpdate(c *C) {
  9971  	s.state.Lock()
  9972  	defer s.state.Unlock()
  9973  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  9974  
  9975  	makeTestRefreshConfig(s.state)
  9976  
  9977  	// Ensure() also runs ensureRefreshes()
  9978  	s.state.Unlock()
  9979  	s.snapmgr.Ensure()
  9980  	s.state.Lock()
  9981  
  9982  	// nothing needs to be done, but last-refresh got updated
  9983  	c.Check(s.state.Changes(), HasLen, 0)
  9984  	s.verifyRefreshLast(c)
  9985  
  9986  	// ensure the next-refresh time is reset and re-calculated
  9987  	c.Check(s.snapmgr.NextRefresh().IsZero(), Equals, true)
  9988  }
  9989  
  9990  func (s *snapmgrTestSuite) TestEnsureRefreshesAlreadyRanInThisInterval(c *C) {
  9991  	s.state.Lock()
  9992  	defer s.state.Unlock()
  9993  
  9994  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) {
  9995  		return true, nil
  9996  	}
  9997  	nextRefresh := s.snapmgr.NextRefresh()
  9998  	c.Check(nextRefresh.IsZero(), Equals, true)
  9999  
 10000  	now := time.Now()
 10001  	fakeLastRefresh := now.Add(-1 * time.Hour)
 10002  	s.state.Set("last-refresh", fakeLastRefresh)
 10003  
 10004  	tr := config.NewTransaction(s.state)
 10005  	tr.Set("core", "refresh.timer", fmt.Sprintf("00:00-%02d:%02d", now.Hour(), now.Minute()))
 10006  	tr.Commit()
 10007  
 10008  	// Ensure() also runs ensureRefreshes()
 10009  	s.state.Unlock()
 10010  	s.snapmgr.Ensure()
 10011  	s.state.Lock()
 10012  
 10013  	// nothing needs to be done and no refresh was run
 10014  	c.Check(s.state.Changes(), HasLen, 0)
 10015  
 10016  	var refreshLast time.Time
 10017  	s.state.Get("last-refresh", &refreshLast)
 10018  	c.Check(refreshLast.Equal(fakeLastRefresh), Equals, true)
 10019  
 10020  	// but a nextRefresh time got calculated
 10021  	nextRefresh = s.snapmgr.NextRefresh()
 10022  	c.Check(nextRefresh.IsZero(), Equals, false)
 10023  
 10024  	// run ensure again to test that nextRefresh again to ensure that
 10025  	// nextRefresh is not calculated again if nothing changes
 10026  	s.state.Unlock()
 10027  	s.snapmgr.Ensure()
 10028  	s.state.Lock()
 10029  	c.Check(s.snapmgr.NextRefresh(), Equals, nextRefresh)
 10030  }
 10031  
 10032  func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdate(c *C) {
 10033  	s.state.Lock()
 10034  	defer s.state.Unlock()
 10035  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
 10036  
 10037  	makeTestRefreshConfig(s.state)
 10038  
 10039  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 10040  		Active: true,
 10041  		Sequence: []*snap.SideInfo{
 10042  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
 10043  		},
 10044  		Current:  snap.R(1),
 10045  		SnapType: "app",
 10046  	})
 10047  
 10048  	// Ensure() also runs ensureRefreshes() and our test setup has an
 10049  	// update for the "some-snap" in our fake store
 10050  	s.state.Unlock()
 10051  	s.snapmgr.Ensure()
 10052  	s.state.Lock()
 10053  
 10054  	// verify we have an auto-refresh change scheduled now
 10055  	c.Assert(s.state.Changes(), HasLen, 1)
 10056  	chg := s.state.Changes()[0]
 10057  	c.Check(chg.Kind(), Equals, "auto-refresh")
 10058  	c.Check(chg.IsReady(), Equals, false)
 10059  	s.verifyRefreshLast(c)
 10060  
 10061  	checkIsAutoRefresh(c, chg.Tasks(), true)
 10062  }
 10063  
 10064  func (s *snapmgrTestSuite) TestEnsureRefreshesImmediateWithUpdate(c *C) {
 10065  	r := release.MockOnClassic(false)
 10066  	defer r()
 10067  
 10068  	s.state.Lock()
 10069  	defer s.state.Unlock()
 10070  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
 10071  
 10072  	// lastRefresh is unset/zero => immediate refresh try
 10073  
 10074  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 10075  		Active: true,
 10076  		Sequence: []*snap.SideInfo{
 10077  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
 10078  		},
 10079  		Current:  snap.R(1),
 10080  		SnapType: "app",
 10081  	})
 10082  
 10083  	// Ensure() also runs ensureRefreshes() and our test setup has an
 10084  	// update for the "some-snap" in our fake store
 10085  	s.state.Unlock()
 10086  	s.snapmgr.Ensure()
 10087  	s.state.Lock()
 10088  
 10089  	// verify we have an auto-refresh change scheduled now
 10090  	c.Assert(s.state.Changes(), HasLen, 1)
 10091  	chg := s.state.Changes()[0]
 10092  	c.Check(chg.Kind(), Equals, "auto-refresh")
 10093  	c.Check(chg.IsReady(), Equals, false)
 10094  	s.verifyRefreshLast(c)
 10095  }
 10096  
 10097  func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdateError(c *C) {
 10098  	s.state.Lock()
 10099  	defer s.state.Unlock()
 10100  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
 10101  
 10102  	makeTestRefreshConfig(s.state)
 10103  
 10104  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 10105  		Active: true,
 10106  		Sequence: []*snap.SideInfo{
 10107  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
 10108  		},
 10109  		Current:  snap.R(1),
 10110  		SnapType: "app",
 10111  	})
 10112  
 10113  	// Ensure() also runs ensureRefreshes() and our test setup has an
 10114  	// update for the "some-snap" in our fake store
 10115  	s.state.Unlock()
 10116  	s.snapmgr.Ensure()
 10117  	s.state.Lock()
 10118  
 10119  	c.Check(s.state.Changes(), HasLen, 1)
 10120  	chg := s.state.Changes()[0]
 10121  	terr := s.state.NewTask("error-trigger", "simulate an error")
 10122  	tasks := chg.Tasks()
 10123  	for _, t := range tasks[:len(tasks)-2] {
 10124  		terr.WaitFor(t)
 10125  	}
 10126  	chg.AddTask(terr)
 10127  
 10128  	// run the changes
 10129  	s.state.Unlock()
 10130  	s.settle(c)
 10131  	s.state.Lock()
 10132  
 10133  	s.verifyRefreshLast(c)
 10134  }
 10135  
 10136  func (s *snapmgrTestSuite) TestEnsureRefreshesInFlight(c *C) {
 10137  	s.state.Lock()
 10138  	defer s.state.Unlock()
 10139  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
 10140  
 10141  	makeTestRefreshConfig(s.state)
 10142  
 10143  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 10144  		Active: true,
 10145  		Sequence: []*snap.SideInfo{
 10146  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
 10147  		},
 10148  		Current:  snap.R(1),
 10149  		SnapType: "app",
 10150  	})
 10151  
 10152  	// simulate an in-flight change
 10153  	chg := s.state.NewChange("auto-refresh", "...")
 10154  	chg.SetStatus(state.DoStatus)
 10155  	c.Check(s.state.Changes(), HasLen, 1)
 10156  
 10157  	s.state.Unlock()
 10158  	s.snapmgr.Ensure()
 10159  	s.state.Lock()
 10160  
 10161  	// verify no additional change got generated
 10162  	c.Check(s.state.Changes(), HasLen, 1)
 10163  }
 10164  
 10165  func mockAutoRefreshAssertions(f func(st *state.State, userID int) error) func() {
 10166  	origAutoRefreshAssertions := snapstate.AutoRefreshAssertions
 10167  	snapstate.AutoRefreshAssertions = f
 10168  	return func() {
 10169  		snapstate.AutoRefreshAssertions = origAutoRefreshAssertions
 10170  	}
 10171  }
 10172  
 10173  func (s *snapmgrTestSuite) TestEnsureRefreshesWithUpdateStoreError(c *C) {
 10174  	s.state.Lock()
 10175  	defer s.state.Unlock()
 10176  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
 10177  
 10178  	// avoid special at seed policy
 10179  	s.state.Set("last-refresh", time.Time{})
 10180  	autoRefreshAssertionsCalled := 0
 10181  	restore := mockAutoRefreshAssertions(func(st *state.State, userID int) error {
 10182  		// simulate failure in snapstate.AutoRefresh()
 10183  		autoRefreshAssertionsCalled++
 10184  		return fmt.Errorf("simulate store error")
 10185  	})
 10186  	defer restore()
 10187  
 10188  	// check that no change got created and that autoRefreshAssertins
 10189  	// got called once
 10190  	s.state.Unlock()
 10191  	s.snapmgr.Ensure()
 10192  	s.state.Lock()
 10193  	c.Check(s.state.Changes(), HasLen, 0)
 10194  	c.Check(autoRefreshAssertionsCalled, Equals, 1)
 10195  
 10196  	// run Ensure() again and check that AutoRefresh() did not run
 10197  	// again because to test that lastRefreshAttempt backoff is working
 10198  	s.state.Unlock()
 10199  	s.snapmgr.Ensure()
 10200  	s.state.Lock()
 10201  	c.Check(s.state.Changes(), HasLen, 0)
 10202  	c.Check(autoRefreshAssertionsCalled, Equals, 1)
 10203  }
 10204  
 10205  func (s *snapmgrTestSuite) testEnsureRefreshesDisabledViaSnapdControl(c *C, confSet func(*config.Transaction)) {
 10206  	st := s.state
 10207  	st.Lock()
 10208  	defer st.Unlock()
 10209  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
 10210  
 10211  	makeTestRefreshConfig(st)
 10212  
 10213  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
 10214  		Active: true,
 10215  		Sequence: []*snap.SideInfo{
 10216  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
 10217  		},
 10218  		Current:  snap.R(1),
 10219  		SnapType: "app",
 10220  	})
 10221  
 10222  	// snapstate.AutoRefresh is called from AutoRefresh()
 10223  	autoRefreshAssertionsCalled := 0
 10224  	restore := mockAutoRefreshAssertions(func(st *state.State, userID int) error {
 10225  		autoRefreshAssertionsCalled++
 10226  		return nil
 10227  	})
 10228  	defer restore()
 10229  
 10230  	// pretend the device is refresh-control: managed
 10231  	oldCanManageRefreshes := snapstate.CanManageRefreshes
 10232  	snapstate.CanManageRefreshes = func(*state.State) bool {
 10233  		return true
 10234  	}
 10235  	defer func() { snapstate.CanManageRefreshes = oldCanManageRefreshes }()
 10236  
 10237  	tr := config.NewTransaction(st)
 10238  	confSet(tr)
 10239  	tr.Commit()
 10240  
 10241  	// Ensure() also runs ensureRefreshes()
 10242  	st.Unlock()
 10243  	s.snapmgr.Ensure()
 10244  	st.Lock()
 10245  
 10246  	// no refresh was called (i.e. no update to last-refresh)
 10247  	var lastRefresh time.Time
 10248  	st.Get("last-refresh", &lastRefresh)
 10249  	c.Check(lastRefresh.Year(), Equals, 2009)
 10250  
 10251  	// AutoRefresh was not called
 10252  	c.Check(autoRefreshAssertionsCalled, Equals, 0)
 10253  
 10254  	// The last refresh hints got updated
 10255  	var lastRefreshHints time.Time
 10256  	st.Get("last-refresh-hints", &lastRefreshHints)
 10257  	c.Check(lastRefreshHints.Year(), Equals, time.Now().Year())
 10258  }
 10259  
 10260  func (s *snapmgrTestSuite) TestEnsureRefreshDisableLegacy(c *C) {
 10261  	f := func(tr *config.Transaction) {
 10262  		tr.Set("core", "refresh.timer", "")
 10263  		tr.Set("core", "refresh.schedule", "managed")
 10264  	}
 10265  	s.testEnsureRefreshesDisabledViaSnapdControl(c, f)
 10266  }
 10267  
 10268  func (s *snapmgrTestSuite) TestEnsureRefreshDisableNew(c *C) {
 10269  	f := func(tr *config.Transaction) {
 10270  		tr.Set("core", "refresh.timer", "managed")
 10271  		tr.Set("core", "refresh.schedule", "")
 10272  	}
 10273  	s.testEnsureRefreshesDisabledViaSnapdControl(c, f)
 10274  }
 10275  
 10276  func (s *snapmgrTestSuite) TestEnsureRefreshDisableNewTrumpsOld(c *C) {
 10277  	f := func(tr *config.Transaction) {
 10278  		tr.Set("core", "refresh.timer", "managed")
 10279  		tr.Set("core", "refresh.schedule", "00:00-12:00")
 10280  	}
 10281  	s.testEnsureRefreshesDisabledViaSnapdControl(c, f)
 10282  }
 10283  
 10284  func (s *snapmgrTestSuite) TestDefaultRefreshScheduleParsing(c *C) {
 10285  	l, err := timeutil.ParseSchedule(snapstate.DefaultRefreshSchedule)
 10286  	c.Assert(err, IsNil)
 10287  	c.Assert(l, HasLen, 1)
 10288  }
 10289  
 10290  func (s *snapmgrTestSuite) TestWaitRestartBasics(c *C) {
 10291  	r := release.MockOnClassic(true)
 10292  	defer r()
 10293  
 10294  	st := s.state
 10295  	st.Lock()
 10296  	defer st.Unlock()
 10297  
 10298  	task := st.NewTask("auto-connect", "...")
 10299  
 10300  	// not restarting
 10301  	state.MockRestarting(st, state.RestartUnset)
 10302  	si := &snap.SideInfo{RealName: "some-app"}
 10303  	snaptest.MockSnap(c, "name: some-app\nversion: 1", si)
 10304  	snapsup := &snapstate.SnapSetup{SideInfo: si}
 10305  	err := snapstate.WaitRestart(task, snapsup)
 10306  	c.Check(err, IsNil)
 10307  
 10308  	// restarting ... we always wait
 10309  	state.MockRestarting(st, state.RestartDaemon)
 10310  	err = snapstate.WaitRestart(task, snapsup)
 10311  	c.Check(err, FitsTypeOf, &state.Retry{})
 10312  }
 10313  
 10314  type snapmgrQuerySuite struct {
 10315  	st      *state.State
 10316  	restore func()
 10317  }
 10318  
 10319  var _ = Suite(&snapmgrQuerySuite{})
 10320  
 10321  func (s *snapmgrQuerySuite) SetUpTest(c *C) {
 10322  	st := state.New(nil)
 10323  	st.Lock()
 10324  	defer st.Unlock()
 10325  
 10326  	restoreSanitize := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
 10327  	s.restore = func() {
 10328  		restoreSanitize()
 10329  	}
 10330  
 10331  	s.st = st
 10332  
 10333  	dirs.SetRootDir(c.MkDir())
 10334  
 10335  	// Write a snap.yaml with fake name
 10336  	sideInfo11 := &snap.SideInfo{RealName: "name1", Revision: snap.R(11), EditedSummary: "s11", SnapID: "123123123"}
 10337  	sideInfo12 := &snap.SideInfo{RealName: "name1", Revision: snap.R(12), EditedSummary: "s12", SnapID: "123123123"}
 10338  	instanceSideInfo13 := &snap.SideInfo{RealName: "name1", Revision: snap.R(13), EditedSummary: "s13 instance", SnapID: "123123123"}
 10339  	snaptest.MockSnap(c, `
 10340  name: name0
 10341  version: 1.1
 10342  description: |
 10343      Lots of text`, sideInfo11)
 10344  	snaptest.MockSnap(c, `
 10345  name: name0
 10346  version: 1.2
 10347  description: |
 10348      Lots of text`, sideInfo12)
 10349  	snaptest.MockSnapInstance(c, "name1_instance", `
 10350  name: name0
 10351  version: 1.3
 10352  description: |
 10353      Lots of text`, instanceSideInfo13)
 10354  	snapstate.Set(st, "name1", &snapstate.SnapState{
 10355  		Active:   true,
 10356  		Sequence: []*snap.SideInfo{sideInfo11, sideInfo12},
 10357  		Current:  sideInfo12.Revision,
 10358  		SnapType: "app",
 10359  	})
 10360  	snapstate.Set(st, "name1_instance", &snapstate.SnapState{
 10361  		Active:      true,
 10362  		Sequence:    []*snap.SideInfo{instanceSideInfo13},
 10363  		Current:     instanceSideInfo13.Revision,
 10364  		SnapType:    "app",
 10365  		InstanceKey: "instance",
 10366  	})
 10367  
 10368  	// have also a snap being installed
 10369  	/*
 10370  		snapstate.Set(st, "installing", &snapstate.SnapState{
 10371  			Candidate: &snap.SideInfo{RealName: "installing", Revision: snap.R(1)},
 10372  		})
 10373  	*/
 10374  }
 10375  
 10376  func (s *snapmgrQuerySuite) TearDownTest(c *C) {
 10377  	dirs.SetRootDir("")
 10378  	s.restore()
 10379  }
 10380  
 10381  func (s *snapmgrQuerySuite) TestInfo(c *C) {
 10382  	st := s.st
 10383  	st.Lock()
 10384  	defer st.Unlock()
 10385  
 10386  	info, err := snapstate.Info(st, "name1", snap.R(11))
 10387  	c.Assert(err, IsNil)
 10388  
 10389  	c.Check(info.InstanceName(), Equals, "name1")
 10390  	c.Check(info.Revision, Equals, snap.R(11))
 10391  	c.Check(info.Summary(), Equals, "s11")
 10392  	c.Check(info.Version, Equals, "1.1")
 10393  	c.Check(info.Description(), Equals, "Lots of text")
 10394  }
 10395  
 10396  func (s *snapmgrQuerySuite) TestSnapStateCurrentInfo(c *C) {
 10397  	st := s.st
 10398  	st.Lock()
 10399  	defer st.Unlock()
 10400  
 10401  	var snapst snapstate.SnapState
 10402  	err := snapstate.Get(st, "name1", &snapst)
 10403  	c.Assert(err, IsNil)
 10404  
 10405  	info, err := snapst.CurrentInfo()
 10406  	c.Assert(err, IsNil)
 10407  
 10408  	c.Check(info.InstanceName(), Equals, "name1")
 10409  	c.Check(info.Revision, Equals, snap.R(12))
 10410  	c.Check(info.Summary(), Equals, "s12")
 10411  	c.Check(info.Version, Equals, "1.2")
 10412  	c.Check(info.Description(), Equals, "Lots of text")
 10413  	c.Check(info.Media, IsNil)
 10414  }
 10415  
 10416  func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoLoadsAuxiliaryStoreInfo(c *C) {
 10417  	storeInfo := &snapstate.AuxStoreInfo{Media: snap.MediaInfos{
 10418  		{
 10419  			Type: "icon",
 10420  			URL:  "http://example.com/favicon.ico",
 10421  		},
 10422  	}}
 10423  
 10424  	c.Assert(snapstate.KeepAuxStoreInfo("123123123", storeInfo), IsNil)
 10425  
 10426  	st := s.st
 10427  	st.Lock()
 10428  	defer st.Unlock()
 10429  
 10430  	var snapst snapstate.SnapState
 10431  	err := snapstate.Get(st, "name1", &snapst)
 10432  	c.Assert(err, IsNil)
 10433  
 10434  	info, err := snapst.CurrentInfo()
 10435  	c.Assert(err, IsNil)
 10436  
 10437  	c.Check(info.InstanceName(), Equals, "name1")
 10438  	c.Check(info.Revision, Equals, snap.R(12))
 10439  	c.Check(info.Summary(), Equals, "s12")
 10440  	c.Check(info.Version, Equals, "1.2")
 10441  	c.Check(info.Description(), Equals, "Lots of text")
 10442  	c.Check(info.Media, DeepEquals, storeInfo.Media)
 10443  }
 10444  
 10445  func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoParallelInstall(c *C) {
 10446  	st := s.st
 10447  	st.Lock()
 10448  	defer st.Unlock()
 10449  
 10450  	var snapst snapstate.SnapState
 10451  	err := snapstate.Get(st, "name1_instance", &snapst)
 10452  	c.Assert(err, IsNil)
 10453  
 10454  	info, err := snapst.CurrentInfo()
 10455  	c.Assert(err, IsNil)
 10456  
 10457  	c.Check(info.InstanceName(), Equals, "name1_instance")
 10458  	c.Check(info.Revision, Equals, snap.R(13))
 10459  	c.Check(info.Summary(), Equals, "s13 instance")
 10460  	c.Check(info.Version, Equals, "1.3")
 10461  	c.Check(info.Description(), Equals, "Lots of text")
 10462  }
 10463  
 10464  func (s *snapmgrQuerySuite) TestSnapStateCurrentInfoErrNoCurrent(c *C) {
 10465  	snapst := new(snapstate.SnapState)
 10466  	_, err := snapst.CurrentInfo()
 10467  	c.Assert(err, Equals, snapstate.ErrNoCurrent)
 10468  
 10469  }
 10470  
 10471  func (s *snapmgrQuerySuite) TestCurrentInfo(c *C) {
 10472  	st := s.st
 10473  	st.Lock()
 10474  	defer st.Unlock()
 10475  
 10476  	info, err := snapstate.CurrentInfo(st, "name1")
 10477  	c.Assert(err, IsNil)
 10478  
 10479  	c.Check(info.InstanceName(), Equals, "name1")
 10480  	c.Check(info.Revision, Equals, snap.R(12))
 10481  }
 10482  
 10483  func (s *snapmgrQuerySuite) TestCurrentInfoAbsent(c *C) {
 10484  	st := s.st
 10485  	st.Lock()
 10486  	defer st.Unlock()
 10487  
 10488  	_, err := snapstate.CurrentInfo(st, "absent")
 10489  	c.Assert(err, ErrorMatches, `snap "absent" is not installed`)
 10490  }
 10491  
 10492  func (s *snapmgrQuerySuite) TestActiveInfos(c *C) {
 10493  	st := s.st
 10494  	st.Lock()
 10495  	defer st.Unlock()
 10496  
 10497  	infos, err := snapstate.ActiveInfos(st)
 10498  	c.Assert(err, IsNil)
 10499  
 10500  	c.Check(infos, HasLen, 2)
 10501  
 10502  	instanceName := "name1_instance"
 10503  	if infos[0].InstanceName() != instanceName && infos[1].InstanceName() != instanceName {
 10504  		c.Fail()
 10505  	}
 10506  	// need stable ordering
 10507  	if infos[0].InstanceName() == instanceName {
 10508  		infos[1], infos[0] = infos[0], infos[1]
 10509  	}
 10510  
 10511  	c.Check(infos[0].InstanceName(), Equals, "name1")
 10512  	c.Check(infos[0].Revision, Equals, snap.R(12))
 10513  	c.Check(infos[0].Summary(), Equals, "s12")
 10514  	c.Check(infos[0].Version, Equals, "1.2")
 10515  	c.Check(infos[0].Description(), Equals, "Lots of text")
 10516  
 10517  	c.Check(infos[1].InstanceName(), Equals, "name1_instance")
 10518  	c.Check(infos[1].Revision, Equals, snap.R(13))
 10519  	c.Check(infos[1].Summary(), Equals, "s13 instance")
 10520  	c.Check(infos[1].Version, Equals, "1.3")
 10521  	c.Check(infos[1].Description(), Equals, "Lots of text")
 10522  }
 10523  
 10524  func (s *snapmgrQuerySuite) TestGadgetInfo(c *C) {
 10525  	st := s.st
 10526  	st.Lock()
 10527  	defer st.Unlock()
 10528  
 10529  	deviceCtxNoGadget := deviceWithoutGadgetContext()
 10530  	deviceCtx := deviceWithGadgetContext("gadget")
 10531  
 10532  	_, err := snapstate.GadgetInfo(st, deviceCtxNoGadget)
 10533  	c.Assert(err, Equals, state.ErrNoState)
 10534  
 10535  	_, err = snapstate.GadgetInfo(st, deviceCtx)
 10536  	c.Assert(err, Equals, state.ErrNoState)
 10537  
 10538  	sideInfo := &snap.SideInfo{
 10539  		RealName: "gadget",
 10540  		Revision: snap.R(2),
 10541  	}
 10542  	snaptest.MockSnap(c, `
 10543  name: gadget
 10544  type: gadget
 10545  version: v1
 10546  `, sideInfo)
 10547  	snapstate.Set(st, "gadget", &snapstate.SnapState{
 10548  		SnapType: "gadget",
 10549  		Active:   true,
 10550  		Sequence: []*snap.SideInfo{sideInfo},
 10551  		Current:  sideInfo.Revision,
 10552  	})
 10553  
 10554  	info, err := snapstate.GadgetInfo(st, deviceCtx)
 10555  	c.Assert(err, IsNil)
 10556  
 10557  	c.Check(info.InstanceName(), Equals, "gadget")
 10558  	c.Check(info.Revision, Equals, snap.R(2))
 10559  	c.Check(info.Version, Equals, "v1")
 10560  	c.Check(info.GetType(), Equals, snap.TypeGadget)
 10561  }
 10562  
 10563  func (s *snapmgrQuerySuite) TestCoreInfoInternal(c *C) {
 10564  	st := s.st
 10565  	st.Lock()
 10566  	defer st.Unlock()
 10567  
 10568  	for testNr, t := range []struct {
 10569  		expectedSnap string
 10570  		snapNames    []string
 10571  		errMatcher   string
 10572  	}{
 10573  		// nothing
 10574  		{"", []string{}, state.ErrNoState.Error()},
 10575  		// single
 10576  		{"core", []string{"core"}, ""},
 10577  		{"ubuntu-core", []string{"ubuntu-core"}, ""},
 10578  		{"hard-core", []string{"hard-core"}, ""},
 10579  		// unrolled loop to ensure we don't pass because
 10580  		// the order is randomly right
 10581  		{"core", []string{"core", "ubuntu-core"}, ""},
 10582  		{"core", []string{"core", "ubuntu-core"}, ""},
 10583  		{"core", []string{"core", "ubuntu-core"}, ""},
 10584  		{"core", []string{"core", "ubuntu-core"}, ""},
 10585  		{"core", []string{"core", "ubuntu-core"}, ""},
 10586  		{"core", []string{"core", "ubuntu-core"}, ""},
 10587  		{"core", []string{"core", "ubuntu-core"}, ""},
 10588  		{"core", []string{"core", "ubuntu-core"}, ""},
 10589  		// unknown combination
 10590  		{"", []string{"duo-core", "single-core"}, `unexpected cores.*`},
 10591  		// multi-core is not supported
 10592  		{"", []string{"core", "ubuntu-core", "multi-core"}, `unexpected number of cores, got 3`},
 10593  	} {
 10594  		// clear snapstate
 10595  		st.Set("snaps", map[string]*json.RawMessage{})
 10596  
 10597  		for _, snapName := range t.snapNames {
 10598  			sideInfo := &snap.SideInfo{
 10599  				RealName: snapName,
 10600  				Revision: snap.R(1),
 10601  			}
 10602  			snaptest.MockSnap(c, fmt.Sprintf("name: %q\ntype: os\nversion: %q\n", snapName, snapName), sideInfo)
 10603  			snapstate.Set(st, snapName, &snapstate.SnapState{
 10604  				SnapType: string(snap.TypeOS),
 10605  				Active:   true,
 10606  				Sequence: []*snap.SideInfo{sideInfo},
 10607  				Current:  sideInfo.Revision,
 10608  			})
 10609  		}
 10610  
 10611  		info, err := snapstate.CoreInfoInternal(st)
 10612  		if t.errMatcher != "" {
 10613  			c.Assert(err, ErrorMatches, t.errMatcher)
 10614  		} else {
 10615  			c.Assert(info, NotNil)
 10616  			c.Check(info.InstanceName(), Equals, t.expectedSnap, Commentf("(%d) test %q %v", testNr, t.expectedSnap, t.snapNames))
 10617  			c.Check(info.GetType(), Equals, snap.TypeOS)
 10618  		}
 10619  	}
 10620  }
 10621  
 10622  func (s *snapmgrQuerySuite) TestHasSnapOfType(c *C) {
 10623  	st := s.st
 10624  	st.Lock()
 10625  	defer st.Unlock()
 10626  
 10627  	// an app snap is already setup
 10628  	ok, err := snapstate.HasSnapOfType(st, snap.TypeApp)
 10629  	c.Assert(err, IsNil)
 10630  	c.Check(ok, Equals, true)
 10631  
 10632  	for _, x := range []struct {
 10633  		snapName string
 10634  		snapType snap.Type
 10635  	}{
 10636  		{
 10637  			snapName: "gadget",
 10638  			snapType: snap.TypeGadget,
 10639  		},
 10640  		{
 10641  			snapName: "core",
 10642  			snapType: snap.TypeOS,
 10643  		},
 10644  		{
 10645  			snapName: "kernel",
 10646  			snapType: snap.TypeKernel,
 10647  		},
 10648  		{
 10649  			snapName: "base",
 10650  			snapType: snap.TypeBase,
 10651  		},
 10652  	} {
 10653  		ok, err := snapstate.HasSnapOfType(st, x.snapType)
 10654  		c.Assert(err, IsNil)
 10655  		c.Check(ok, Equals, false, Commentf("%q", x.snapType))
 10656  
 10657  		sideInfo := &snap.SideInfo{
 10658  			RealName: x.snapName,
 10659  			Revision: snap.R(2),
 10660  		}
 10661  		snapstate.Set(st, x.snapName, &snapstate.SnapState{
 10662  			SnapType: string(x.snapType),
 10663  			Active:   true,
 10664  			Sequence: []*snap.SideInfo{sideInfo},
 10665  			Current:  sideInfo.Revision,
 10666  		})
 10667  
 10668  		ok, err = snapstate.HasSnapOfType(st, x.snapType)
 10669  		c.Assert(err, IsNil)
 10670  		c.Check(ok, Equals, true)
 10671  	}
 10672  }
 10673  
 10674  func (s *snapmgrQuerySuite) TestPreviousSideInfo(c *C) {
 10675  	st := s.st
 10676  	st.Lock()
 10677  	defer st.Unlock()
 10678  
 10679  	var snapst snapstate.SnapState
 10680  	err := snapstate.Get(st, "name1", &snapst)
 10681  	c.Assert(err, IsNil)
 10682  	c.Assert(snapst.CurrentSideInfo(), NotNil)
 10683  	c.Assert(snapst.CurrentSideInfo().Revision, Equals, snap.R(12))
 10684  	c.Assert(snapstate.PreviousSideInfo(&snapst), NotNil)
 10685  	c.Assert(snapstate.PreviousSideInfo(&snapst).Revision, Equals, snap.R(11))
 10686  }
 10687  
 10688  func (s *snapmgrQuerySuite) TestPreviousSideInfoNoCurrent(c *C) {
 10689  	st := s.st
 10690  	st.Lock()
 10691  	defer st.Unlock()
 10692  
 10693  	snapst := &snapstate.SnapState{}
 10694  	c.Assert(snapstate.PreviousSideInfo(snapst), IsNil)
 10695  }
 10696  
 10697  func (s *snapmgrQuerySuite) TestAll(c *C) {
 10698  	st := s.st
 10699  	st.Lock()
 10700  	defer st.Unlock()
 10701  
 10702  	snapStates, err := snapstate.All(st)
 10703  	c.Assert(err, IsNil)
 10704  	c.Assert(snapStates, HasLen, 2)
 10705  
 10706  	n, err := snapstate.NumSnaps(st)
 10707  	c.Assert(err, IsNil)
 10708  	c.Check(n, Equals, 2)
 10709  
 10710  	snapst := snapStates["name1"]
 10711  	c.Assert(snapst, NotNil)
 10712  
 10713  	c.Check(snapst.Active, Equals, true)
 10714  	c.Check(snapst.CurrentSideInfo(), NotNil)
 10715  
 10716  	info12, err := snap.ReadInfo("name1", snapst.CurrentSideInfo())
 10717  	c.Assert(err, IsNil)
 10718  
 10719  	c.Check(info12.InstanceName(), Equals, "name1")
 10720  	c.Check(info12.Revision, Equals, snap.R(12))
 10721  	c.Check(info12.Summary(), Equals, "s12")
 10722  	c.Check(info12.Version, Equals, "1.2")
 10723  	c.Check(info12.Description(), Equals, "Lots of text")
 10724  
 10725  	info11, err := snap.ReadInfo("name1", snapst.Sequence[0])
 10726  	c.Assert(err, IsNil)
 10727  
 10728  	c.Check(info11.InstanceName(), Equals, "name1")
 10729  	c.Check(info11.Revision, Equals, snap.R(11))
 10730  	c.Check(info11.Version, Equals, "1.1")
 10731  
 10732  	instance := snapStates["name1_instance"]
 10733  	c.Assert(instance, NotNil)
 10734  
 10735  	c.Check(instance.Active, Equals, true)
 10736  	c.Check(instance.CurrentSideInfo(), NotNil)
 10737  
 10738  	info13, err := snap.ReadInfo("name1_instance", instance.CurrentSideInfo())
 10739  	c.Assert(err, IsNil)
 10740  
 10741  	c.Check(info13.InstanceName(), Equals, "name1_instance")
 10742  	c.Check(info13.SnapName(), Equals, "name1")
 10743  	c.Check(info13.Revision, Equals, snap.R(13))
 10744  	c.Check(info13.Summary(), Equals, "s13 instance")
 10745  	c.Check(info13.Version, Equals, "1.3")
 10746  	c.Check(info13.Description(), Equals, "Lots of text")
 10747  
 10748  	info13other, err := snap.ReadInfo("name1_instance", instance.Sequence[0])
 10749  	c.Assert(err, IsNil)
 10750  	c.Check(info13, DeepEquals, info13other)
 10751  }
 10752  
 10753  func (s *snapmgrQuerySuite) TestAllEmptyAndEmptyNormalisation(c *C) {
 10754  	st := state.New(nil)
 10755  	st.Lock()
 10756  	defer st.Unlock()
 10757  
 10758  	snapStates, err := snapstate.All(st)
 10759  	c.Assert(err, IsNil)
 10760  	c.Check(snapStates, HasLen, 0)
 10761  
 10762  	n, err := snapstate.NumSnaps(st)
 10763  	c.Assert(err, IsNil)
 10764  	c.Check(n, Equals, 0)
 10765  
 10766  	snapstate.Set(st, "foo", nil)
 10767  
 10768  	snapStates, err = snapstate.All(st)
 10769  	c.Assert(err, IsNil)
 10770  	c.Check(snapStates, HasLen, 0)
 10771  
 10772  	n, err = snapstate.NumSnaps(st)
 10773  	c.Assert(err, IsNil)
 10774  	c.Check(n, Equals, 0)
 10775  
 10776  	snapstate.Set(st, "foo", &snapstate.SnapState{})
 10777  
 10778  	snapStates, err = snapstate.All(st)
 10779  	c.Assert(err, IsNil)
 10780  	c.Check(snapStates, HasLen, 0)
 10781  
 10782  	n, err = snapstate.NumSnaps(st)
 10783  	c.Assert(err, IsNil)
 10784  	c.Check(n, Equals, 0)
 10785  }
 10786  
 10787  func (s *snapmgrTestSuite) TestTrySetsTryMode(c *C) {
 10788  	s.testTrySetsTryMode(snapstate.Flags{}, c)
 10789  }
 10790  
 10791  func (s *snapmgrTestSuite) TestTrySetsTryModeDevMode(c *C) {
 10792  	s.testTrySetsTryMode(snapstate.Flags{DevMode: true}, c)
 10793  }
 10794  func (s *snapmgrTestSuite) TestTrySetsTryModeJailMode(c *C) {
 10795  	s.testTrySetsTryMode(snapstate.Flags{JailMode: true}, c)
 10796  }
 10797  func (s *snapmgrTestSuite) TestTrySetsTryModeClassic(c *C) {
 10798  	restore := maybeMockClassicSupport(c)
 10799  	defer restore()
 10800  
 10801  	s.testTrySetsTryMode(snapstate.Flags{Classic: true}, c, "confinement: classic\n")
 10802  }
 10803  
 10804  func (s *snapmgrTestSuite) testTrySetsTryMode(flags snapstate.Flags, c *C, extraYaml ...string) {
 10805  	s.state.Lock()
 10806  	defer s.state.Unlock()
 10807  
 10808  	// make mock try dir
 10809  	d := c.MkDir()
 10810  	c.Assert(os.Chmod(d, 0755), IsNil)
 10811  	tryYaml := filepath.Join(d, "meta", "snap.yaml")
 10812  	err := os.MkdirAll(filepath.Dir(tryYaml), 0755)
 10813  	c.Assert(err, IsNil)
 10814  	buf := bytes.Buffer{}
 10815  	buf.WriteString("name: foo\nversion: 1.0\n")
 10816  	if len(extraYaml) > 0 {
 10817  		for _, extra := range extraYaml {
 10818  			buf.WriteString(extra)
 10819  		}
 10820  	}
 10821  	err = ioutil.WriteFile(tryYaml, buf.Bytes(), 0644)
 10822  	c.Assert(err, IsNil)
 10823  
 10824  	chg := s.state.NewChange("try", "try snap")
 10825  	ts, err := snapstate.TryPath(s.state, "foo", d, flags)
 10826  	c.Assert(err, IsNil)
 10827  	chg.AddAll(ts)
 10828  
 10829  	s.state.Unlock()
 10830  	defer s.se.Stop()
 10831  	s.settle(c)
 10832  	s.state.Lock()
 10833  
 10834  	c.Assert(chg.Err(), IsNil)
 10835  	c.Assert(chg.IsReady(), Equals, true)
 10836  
 10837  	// verify snap is in TryMode
 10838  	var snapst snapstate.SnapState
 10839  	err = snapstate.Get(s.state, "foo", &snapst)
 10840  	c.Assert(err, IsNil)
 10841  
 10842  	flags.TryMode = true
 10843  	c.Check(snapst.Flags, DeepEquals, flags)
 10844  
 10845  	c.Check(s.state.TaskCount(), Equals, len(ts.Tasks()))
 10846  	c.Check(taskKinds(ts.Tasks()), DeepEquals, []string{
 10847  		"prerequisites",
 10848  		"prepare-snap",
 10849  		"mount-snap",
 10850  		"copy-snap-data",
 10851  		"setup-profiles",
 10852  		"link-snap",
 10853  		"auto-connect",
 10854  		"set-auto-aliases",
 10855  		"setup-aliases",
 10856  		"run-hook[install]",
 10857  		"start-snap-services",
 10858  		"run-hook[configure]",
 10859  		"run-hook[check-health]",
 10860  	})
 10861  
 10862  }
 10863  
 10864  func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlag(c *C) {
 10865  	restore := maybeMockClassicSupport(c)
 10866  	defer restore()
 10867  	s.testTrySetsTryMode(snapstate.Flags{}, c)
 10868  }
 10869  
 10870  func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlagLeavesDevMode(c *C) {
 10871  	s.testTrySetsTryMode(snapstate.Flags{DevMode: true}, c)
 10872  }
 10873  func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlagLeavesJailMode(c *C) {
 10874  	s.testTrySetsTryMode(snapstate.Flags{JailMode: true}, c)
 10875  }
 10876  func (s *snapmgrTestSuite) TestTryUndoRemovesTryFlagLeavesClassic(c *C) {
 10877  	restore := maybeMockClassicSupport(c)
 10878  	defer restore()
 10879  	s.testTrySetsTryMode(snapstate.Flags{Classic: true}, c, "confinement: classic\n")
 10880  }
 10881  
 10882  func (s *snapmgrTestSuite) testTryUndoRemovesTryFlag(flags snapstate.Flags, c *C) {
 10883  	s.state.Lock()
 10884  	defer s.state.Unlock()
 10885  
 10886  	// simulate existing state for foo
 10887  	var snapst snapstate.SnapState
 10888  	snapst.Sequence = []*snap.SideInfo{
 10889  		{
 10890  			RealName: "foo",
 10891  			Revision: snap.R(23),
 10892  		},
 10893  	}
 10894  	snapst.Flags = flags
 10895  	snapst.Current = snap.R(23)
 10896  	snapstate.Set(s.state, "foo", &snapst)
 10897  	c.Check(snapst.TryMode, Equals, false)
 10898  
 10899  	chg := s.state.NewChange("try", "try snap")
 10900  	ts, err := snapstate.TryPath(s.state, "foo", c.MkDir(), flags)
 10901  	c.Assert(err, IsNil)
 10902  	chg.AddAll(ts)
 10903  
 10904  	last := ts.Tasks()[len(ts.Tasks())-1]
 10905  	terr := s.state.NewTask("error-trigger", "provoking total undo")
 10906  	terr.WaitFor(last)
 10907  	chg.AddTask(terr)
 10908  
 10909  	s.state.Unlock()
 10910  	defer s.se.Stop()
 10911  	s.settle(c)
 10912  	s.state.Lock()
 10913  
 10914  	// verify snap is not in try mode, the state got undone
 10915  	err = snapstate.Get(s.state, "foo", &snapst)
 10916  	c.Assert(err, IsNil)
 10917  	c.Check(snapst.Flags, DeepEquals, flags)
 10918  }
 10919  
 10920  type snapStateSuite struct{}
 10921  
 10922  var _ = Suite(&snapStateSuite{})
 10923  
 10924  func (s *snapStateSuite) TestSnapStateDevMode(c *C) {
 10925  	snapst := &snapstate.SnapState{}
 10926  	c.Check(snapst.DevMode, Equals, false)
 10927  	snapst.Flags.DevMode = true
 10928  	c.Check(snapst.DevMode, Equals, true)
 10929  }
 10930  
 10931  func (s *snapStateSuite) TestSnapStateType(c *C) {
 10932  	snapst := &snapstate.SnapState{}
 10933  	_, err := snapst.Type()
 10934  	c.Check(err, ErrorMatches, "snap type unset")
 10935  
 10936  	snapst.SetType(snap.TypeKernel)
 10937  	typ, err := snapst.Type()
 10938  	c.Assert(err, IsNil)
 10939  	c.Check(typ, Equals, snap.TypeKernel)
 10940  }
 10941  
 10942  func (s *snapStateSuite) TestCurrentSideInfoEmpty(c *C) {
 10943  	var snapst snapstate.SnapState
 10944  	c.Check(snapst.CurrentSideInfo(), IsNil)
 10945  	c.Check(snapst.Current.Unset(), Equals, true)
 10946  }
 10947  
 10948  func (s *snapStateSuite) TestCurrentSideInfoSimple(c *C) {
 10949  	si1 := &snap.SideInfo{Revision: snap.R(1)}
 10950  	snapst := snapstate.SnapState{
 10951  		Sequence: []*snap.SideInfo{si1},
 10952  		Current:  snap.R(1),
 10953  	}
 10954  	c.Check(snapst.CurrentSideInfo(), DeepEquals, si1)
 10955  }
 10956  
 10957  func (s *snapStateSuite) TestCurrentSideInfoInOrder(c *C) {
 10958  	si1 := &snap.SideInfo{Revision: snap.R(1)}
 10959  	si2 := &snap.SideInfo{Revision: snap.R(2)}
 10960  	snapst := snapstate.SnapState{
 10961  		Sequence: []*snap.SideInfo{si1, si2},
 10962  		Current:  snap.R(2),
 10963  	}
 10964  	c.Check(snapst.CurrentSideInfo(), DeepEquals, si2)
 10965  }
 10966  
 10967  func (s *snapStateSuite) TestCurrentSideInfoOutOfOrder(c *C) {
 10968  	si1 := &snap.SideInfo{Revision: snap.R(1)}
 10969  	si2 := &snap.SideInfo{Revision: snap.R(2)}
 10970  	snapst := snapstate.SnapState{
 10971  		Sequence: []*snap.SideInfo{si1, si2},
 10972  		Current:  snap.R(1),
 10973  	}
 10974  	c.Check(snapst.CurrentSideInfo(), DeepEquals, si1)
 10975  }
 10976  
 10977  func (s *snapStateSuite) TestCurrentSideInfoInconsistent(c *C) {
 10978  	snapst := snapstate.SnapState{
 10979  		Sequence: []*snap.SideInfo{
 10980  			{Revision: snap.R(1)},
 10981  		},
 10982  	}
 10983  	c.Check(func() { snapst.CurrentSideInfo() }, PanicMatches, `snapst.Current and snapst.Sequence out of sync:.*`)
 10984  }
 10985  
 10986  func (s *snapStateSuite) TestCurrentSideInfoInconsistentWithCurrent(c *C) {
 10987  	snapst := snapstate.SnapState{Current: snap.R(17)}
 10988  	c.Check(func() { snapst.CurrentSideInfo() }, PanicMatches, `cannot find snapst.Current in the snapst.Sequence`)
 10989  }
 10990  
 10991  func (snapStateSuite) TestDefaultContentPlugProviders(c *C) {
 10992  	info := &snap.Info{
 10993  		Plugs: map[string]*snap.PlugInfo{},
 10994  	}
 10995  
 10996  	info.Plugs["foo"] = &snap.PlugInfo{
 10997  		Snap:      info,
 10998  		Name:      "sound-themes",
 10999  		Interface: "content",
 11000  		Attrs:     map[string]interface{}{"default-provider": "common-themes", "content": "foo"},
 11001  	}
 11002  	info.Plugs["bar"] = &snap.PlugInfo{
 11003  		Snap:      info,
 11004  		Name:      "visual-themes",
 11005  		Interface: "content",
 11006  		Attrs:     map[string]interface{}{"default-provider": "common-themes", "content": "bar"},
 11007  	}
 11008  	info.Plugs["baz"] = &snap.PlugInfo{
 11009  		Snap:      info,
 11010  		Name:      "not-themes",
 11011  		Interface: "content",
 11012  		Attrs:     map[string]interface{}{"default-provider": "some-snap", "content": "baz"},
 11013  	}
 11014  	info.Plugs["qux"] = &snap.PlugInfo{Snap: info, Interface: "not-content"}
 11015  
 11016  	st := state.New(nil)
 11017  	st.Lock()
 11018  	defer st.Unlock()
 11019  
 11020  	repo := interfaces.NewRepository()
 11021  	ifacerepo.Replace(st, repo)
 11022  
 11023  	providers := snapstate.DefaultContentPlugProviders(st, info)
 11024  	sort.Strings(providers)
 11025  	c.Check(providers, DeepEquals, []string{"common-themes", "some-snap"})
 11026  }
 11027  
 11028  type snapSetupSuite struct{}
 11029  
 11030  var _ = Suite(&snapSetupSuite{})
 11031  
 11032  type canRemoveSuite struct {
 11033  	st        *state.State
 11034  	deviceCtx snapstate.DeviceContext
 11035  
 11036  	bootloader *bootloadertest.MockBootloader
 11037  }
 11038  
 11039  var _ = Suite(&canRemoveSuite{})
 11040  
 11041  func (s *canRemoveSuite) SetUpTest(c *C) {
 11042  	dirs.SetRootDir(c.MkDir())
 11043  	s.st = state.New(nil)
 11044  	s.deviceCtx = &snapstatetest.TrivialDeviceContext{DeviceModel: DefaultModel()}
 11045  
 11046  	s.bootloader = bootloadertest.Mock("mock", c.MkDir())
 11047  	bootloader.Force(s.bootloader)
 11048  }
 11049  
 11050  func (s *canRemoveSuite) TearDownTest(c *C) {
 11051  	dirs.SetRootDir("/")
 11052  	bootloader.Force(nil)
 11053  }
 11054  
 11055  func (s *canRemoveSuite) TestAppAreAlwaysOKToRemove(c *C) {
 11056  	info := &snap.Info{
 11057  		SnapType: snap.TypeApp,
 11058  	}
 11059  	info.RealName = "foo"
 11060  
 11061  	c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, false, s.deviceCtx), Equals, true)
 11062  	c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, true, s.deviceCtx), Equals, true)
 11063  }
 11064  
 11065  func (s *canRemoveSuite) TestLastGadgetsAreNotOK(c *C) {
 11066  	info := &snap.Info{
 11067  		SnapType: snap.TypeGadget,
 11068  	}
 11069  	info.RealName = "foo"
 11070  
 11071  	c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{}, true, s.deviceCtx), Equals, false)
 11072  }
 11073  
 11074  func (s *canRemoveSuite) TestLastOSAndKernelAreNotOK(c *C) {
 11075  	os := &snap.Info{
 11076  		SnapType: snap.TypeOS,
 11077  	}
 11078  	os.RealName = "os"
 11079  	kernel := &snap.Info{
 11080  		SnapType: snap.TypeKernel,
 11081  	}
 11082  	// this kernel part of the model
 11083  	kernel.RealName = "kernel"
 11084  
 11085  	c.Check(snapstate.CanRemove(s.st, os, &snapstate.SnapState{}, true, s.deviceCtx), Equals, false)
 11086  
 11087  	c.Check(snapstate.CanRemove(s.st, kernel, &snapstate.SnapState{}, true, s.deviceCtx), Equals, false)
 11088  }
 11089  
 11090  func (s *canRemoveSuite) TestKernelBootInUseIsKept(c *C) {
 11091  	kernel := &snap.Info{
 11092  		SnapType: snap.TypeKernel,
 11093  		SideInfo: snap.SideInfo{
 11094  			Revision: snap.R(3),
 11095  		},
 11096  	}
 11097  	kernel.RealName = "kernel"
 11098  
 11099  	s.bootloader.SetBootKernel(fmt.Sprintf("%s_%s.snap", kernel.RealName, kernel.SideInfo.Revision))
 11100  
 11101  	removeAll := false
 11102  	c.Check(snapstate.CanRemove(s.st, kernel, &snapstate.SnapState{}, removeAll, s.deviceCtx), Equals, false)
 11103  }
 11104  
 11105  func (s *canRemoveSuite) TestOstInUseIsKept(c *C) {
 11106  	base := &snap.Info{
 11107  		SnapType: snap.TypeBase,
 11108  		SideInfo: snap.SideInfo{
 11109  			Revision: snap.R(3),
 11110  		},
 11111  	}
 11112  	base.RealName = "core18"
 11113  
 11114  	s.bootloader.SetBootBase(fmt.Sprintf("%s_%s.snap", base.RealName, base.SideInfo.Revision))
 11115  
 11116  	removeAll := false
 11117  	c.Check(snapstate.CanRemove(s.st, base, &snapstate.SnapState{}, removeAll, s.deviceCtx), Equals, false)
 11118  }
 11119  
 11120  func (s *canRemoveSuite) TestRemoveNonModelKernelIsOk(c *C) {
 11121  	kernel := &snap.Info{
 11122  		SnapType: snap.TypeKernel,
 11123  	}
 11124  	kernel.RealName = "other-non-model-kernel"
 11125  
 11126  	c.Check(snapstate.CanRemove(s.st, kernel, &snapstate.SnapState{}, true, s.deviceCtx), Equals, true)
 11127  }
 11128  
 11129  func (s *canRemoveSuite) TestRemoveNonModelKernelStillInUseNotOk(c *C) {
 11130  	kernel := &snap.Info{
 11131  		SnapType: snap.TypeKernel,
 11132  		SideInfo: snap.SideInfo{
 11133  			Revision: snap.R(2),
 11134  		},
 11135  	}
 11136  	kernel.RealName = "other-non-model-kernel"
 11137  
 11138  	s.bootloader.SetBootKernel(fmt.Sprintf("%s_%s.snap", kernel.RealName, kernel.SideInfo.Revision))
 11139  
 11140  	c.Check(snapstate.CanRemove(s.st, kernel, &snapstate.SnapState{}, true, s.deviceCtx), Equals, false)
 11141  }
 11142  
 11143  func (s *canRemoveSuite) TestLastOSWithModelBaseIsOk(c *C) {
 11144  	s.st.Lock()
 11145  	defer s.st.Unlock()
 11146  
 11147  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: ModelWithBase("core18")}
 11148  	os := &snap.Info{
 11149  		SnapType: snap.TypeOS,
 11150  	}
 11151  	os.RealName = "os"
 11152  
 11153  	c.Check(snapstate.CanRemove(s.st, os, &snapstate.SnapState{}, true, deviceCtx), Equals, true)
 11154  }
 11155  
 11156  func (s *canRemoveSuite) TestLastOSWithModelBaseButOsInUse(c *C) {
 11157  	s.st.Lock()
 11158  	defer s.st.Unlock()
 11159  
 11160  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: ModelWithBase("core18")}
 11161  
 11162  	// pretend we have a snap installed that has no base (which means
 11163  	// it needs core)
 11164  	si := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}
 11165  	snaptest.MockSnap(c, "name: some-snap\nversion: 1.0", si)
 11166  	snapstate.Set(s.st, "some-snap", &snapstate.SnapState{
 11167  		Active:   true,
 11168  		Sequence: []*snap.SideInfo{si},
 11169  		Current:  snap.R(1),
 11170  	})
 11171  
 11172  	// now pretend we want to remove the core snap
 11173  	os := &snap.Info{
 11174  		SnapType: snap.TypeOS,
 11175  	}
 11176  	os.RealName = "core"
 11177  	c.Check(snapstate.CanRemove(s.st, os, &snapstate.SnapState{}, true, deviceCtx), Equals, false)
 11178  }
 11179  
 11180  func (s *canRemoveSuite) TestOneRevisionIsOK(c *C) {
 11181  	info := &snap.Info{
 11182  		SnapType: snap.TypeGadget,
 11183  	}
 11184  	info.RealName = "foo"
 11185  
 11186  	c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, false, s.deviceCtx), Equals, true)
 11187  }
 11188  
 11189  func (s *canRemoveSuite) TestRequiredIsNotOK(c *C) {
 11190  	info := &snap.Info{
 11191  		SnapType: snap.TypeApp,
 11192  	}
 11193  	info.RealName = "foo"
 11194  
 11195  	c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: false, Flags: snapstate.Flags{Required: true}}, true, s.deviceCtx), Equals, false)
 11196  	c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true, Flags: snapstate.Flags{Required: true}}, true, s.deviceCtx), Equals, false)
 11197  	c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true, Flags: snapstate.Flags{Required: true}}, false, s.deviceCtx), Equals, true)
 11198  }
 11199  
 11200  func (s *canRemoveSuite) TestBaseUnused(c *C) {
 11201  	s.st.Lock()
 11202  	defer s.st.Unlock()
 11203  
 11204  	info := &snap.Info{
 11205  		SnapType: snap.TypeBase,
 11206  	}
 11207  	info.RealName = "some-base"
 11208  
 11209  	c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, false, s.deviceCtx), Equals, true)
 11210  	c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, true, s.deviceCtx), Equals, true)
 11211  }
 11212  
 11213  func (s *canRemoveSuite) TestBaseInUse(c *C) {
 11214  	s.st.Lock()
 11215  	defer s.st.Unlock()
 11216  
 11217  	// pretend we have a snap installed that uses "some-base"
 11218  	si := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}
 11219  	snaptest.MockSnap(c, "name: some-snap\nversion: 1.0\nbase: some-base", si)
 11220  	snapstate.Set(s.st, "some-snap", &snapstate.SnapState{
 11221  		Active:   true,
 11222  		Sequence: []*snap.SideInfo{si},
 11223  		Current:  snap.R(1),
 11224  	})
 11225  
 11226  	// pretend now we want to remove "some-base"
 11227  	info := &snap.Info{
 11228  		SnapType: snap.TypeBase,
 11229  	}
 11230  	info.RealName = "some-base"
 11231  	c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, true, s.deviceCtx), Equals, false)
 11232  }
 11233  
 11234  func (s *canRemoveSuite) TestBaseInUseOtherRevision(c *C) {
 11235  	s.st.Lock()
 11236  	defer s.st.Unlock()
 11237  
 11238  	// pretend we have a snap installed that uses "some-base"
 11239  	si := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}
 11240  	si2 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}
 11241  	// older revision uses base
 11242  	snaptest.MockSnap(c, "name: some-snap\nversion: 1.0\nbase: some-base", si)
 11243  	// new one does not
 11244  	snaptest.MockSnap(c, "name: some-snap\nversion: 1.0\n", si2)
 11245  	snapstate.Set(s.st, "some-snap", &snapstate.SnapState{
 11246  		Active:   true,
 11247  		Sequence: []*snap.SideInfo{si, si2},
 11248  		Current:  snap.R(2),
 11249  	})
 11250  
 11251  	// pretend now we want to remove "some-base"
 11252  	info := &snap.Info{
 11253  		SnapType: snap.TypeBase,
 11254  	}
 11255  	info.RealName = "some-base"
 11256  	// revision 1 requires some-base
 11257  	c.Check(snapstate.CanRemove(s.st, info, &snapstate.SnapState{Active: true}, true, s.deviceCtx), Equals, false)
 11258  
 11259  	// now pretend we want to remove the core snap
 11260  	os := &snap.Info{
 11261  		SnapType: snap.TypeOS,
 11262  	}
 11263  	os.RealName = "core"
 11264  	// but revision 2 requires core
 11265  	c.Check(snapstate.CanRemove(s.st, os, &snapstate.SnapState{}, true, s.deviceCtx), Equals, false)
 11266  }
 11267  
 11268  func revs(seq []*snap.SideInfo) []int {
 11269  	revs := make([]int, len(seq))
 11270  	for i, si := range seq {
 11271  		revs[i] = si.Revision.N
 11272  	}
 11273  
 11274  	return revs
 11275  }
 11276  
 11277  type opSeqOpts struct {
 11278  	revert  bool
 11279  	fail    bool
 11280  	before  []int
 11281  	current int
 11282  	via     int
 11283  	after   []int
 11284  }
 11285  
 11286  // build a SnapState with a revision sequence given by `before` and a
 11287  // current revision of `current`. Then refresh --revision via. Then
 11288  // check the revision sequence is as in `after`.
 11289  func (s *snapmgrTestSuite) testOpSequence(c *C, opts *opSeqOpts) (*snapstate.SnapState, *state.TaskSet) {
 11290  	s.state.Lock()
 11291  	defer s.state.Unlock()
 11292  
 11293  	seq := make([]*snap.SideInfo, len(opts.before))
 11294  	for i, n := range opts.before {
 11295  		seq[i] = &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(n)}
 11296  	}
 11297  
 11298  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 11299  		Active:   true,
 11300  		Channel:  "edge",
 11301  		Sequence: seq,
 11302  		Current:  snap.R(opts.current),
 11303  		SnapType: "app",
 11304  	})
 11305  
 11306  	var chg *state.Change
 11307  	var ts *state.TaskSet
 11308  	var err error
 11309  	if opts.revert {
 11310  		chg = s.state.NewChange("revert", "revert a snap")
 11311  		ts, err = snapstate.RevertToRevision(s.state, "some-snap", snap.R(opts.via), snapstate.Flags{})
 11312  	} else {
 11313  		chg = s.state.NewChange("refresh", "refresh a snap")
 11314  		ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(opts.via)}, s.user.ID, snapstate.Flags{})
 11315  	}
 11316  	c.Assert(err, IsNil)
 11317  	if opts.fail {
 11318  		tasks := ts.Tasks()
 11319  		var last *state.Task
 11320  		// don't make a task wait on rerefresh, that's bad
 11321  		for i := len(tasks) - 1; i > 0; i-- {
 11322  			last = tasks[i]
 11323  			if last.Kind() != "check-rerefresh" {
 11324  				break
 11325  			}
 11326  		}
 11327  		terr := s.state.NewTask("error-trigger", "provoking total undo")
 11328  		terr.WaitFor(last)
 11329  		if len(last.Lanes()) > 0 {
 11330  			lanes := last.Lanes()
 11331  			// sanity
 11332  			c.Assert(lanes, HasLen, 1)
 11333  			terr.JoinLane(lanes[0])
 11334  		}
 11335  		chg.AddTask(terr)
 11336  	}
 11337  	chg.AddAll(ts)
 11338  
 11339  	s.state.Unlock()
 11340  	defer s.se.Stop()
 11341  	s.settle(c)
 11342  	s.state.Lock()
 11343  
 11344  	var snapst snapstate.SnapState
 11345  	err = snapstate.Get(s.state, "some-snap", &snapst)
 11346  	c.Assert(err, IsNil)
 11347  	c.Check(revs(snapst.Sequence), DeepEquals, opts.after)
 11348  
 11349  	return &snapst, ts
 11350  }
 11351  
 11352  func (s *snapmgrTestSuite) testUpdateSequence(c *C, opts *opSeqOpts) *state.TaskSet {
 11353  	restore := release.MockOnClassic(false)
 11354  	defer restore()
 11355  
 11356  	opts.revert = false
 11357  	snapst, ts := s.testOpSequence(c, opts)
 11358  	// update always ends with current==seq[-1]==via:
 11359  	c.Check(snapst.Current.N, Equals, opts.after[len(opts.after)-1])
 11360  	c.Check(snapst.Current.N, Equals, opts.via)
 11361  
 11362  	c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 1)
 11363  	c.Check(s.fakeBackend.ops.First("copy-data"), DeepEquals, &fakeOp{
 11364  		op:   "copy-data",
 11365  		path: fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via),
 11366  		old:  fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.current),
 11367  	})
 11368  
 11369  	return ts
 11370  }
 11371  
 11372  func (s *snapmgrTestSuite) testUpdateFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet {
 11373  	restore := release.MockOnClassic(false)
 11374  	defer restore()
 11375  
 11376  	opts.revert = false
 11377  	opts.after = opts.before
 11378  	s.fakeBackend.linkSnapFailTrigger = fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via)
 11379  	snapst, ts := s.testOpSequence(c, opts)
 11380  	// a failed update will always end with current unchanged
 11381  	c.Check(snapst.Current.N, Equals, opts.current)
 11382  
 11383  	ops := s.fakeBackend.ops
 11384  	c.Check(ops.Count("copy-data"), Equals, 1)
 11385  	do := ops.First("copy-data")
 11386  
 11387  	c.Check(ops.Count("undo-copy-snap-data"), Equals, 1)
 11388  	undo := ops.First("undo-copy-snap-data")
 11389  
 11390  	do.op = undo.op
 11391  	c.Check(do, DeepEquals, undo) // i.e. they only differed in the op
 11392  
 11393  	return ts
 11394  }
 11395  
 11396  // testTotal*Failure fails *after* link-snap
 11397  func (s *snapmgrTestSuite) testTotalUpdateFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet {
 11398  	restore := release.MockOnClassic(false)
 11399  	defer restore()
 11400  
 11401  	opts.revert = false
 11402  	opts.fail = true
 11403  	snapst, ts := s.testOpSequence(c, opts)
 11404  	// a failed update will always end with current unchanged
 11405  	c.Check(snapst.Current.N, Equals, opts.current)
 11406  
 11407  	ops := s.fakeBackend.ops
 11408  	c.Check(ops.Count("copy-data"), Equals, 1)
 11409  	do := ops.First("copy-data")
 11410  
 11411  	c.Check(ops.Count("undo-copy-snap-data"), Equals, 1)
 11412  	undo := ops.First("undo-copy-snap-data")
 11413  
 11414  	do.op = undo.op
 11415  	c.Check(do, DeepEquals, undo) // i.e. they only differed in the op
 11416  
 11417  	return ts
 11418  }
 11419  
 11420  func (s *snapmgrTestSuite) testRevertSequence(c *C, opts *opSeqOpts) *state.TaskSet {
 11421  	opts.revert = true
 11422  	opts.after = opts.before
 11423  	snapst, ts := s.testOpSequence(c, opts)
 11424  	// successful revert leaves current == via
 11425  	c.Check(snapst.Current.N, Equals, opts.via)
 11426  
 11427  	c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0)
 11428  
 11429  	return ts
 11430  }
 11431  
 11432  func (s *snapmgrTestSuite) testRevertFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet {
 11433  	opts.revert = true
 11434  	opts.after = opts.before
 11435  	s.fakeBackend.linkSnapFailTrigger = fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via)
 11436  	snapst, ts := s.testOpSequence(c, opts)
 11437  	// a failed revert will always end with current unchanged
 11438  	c.Check(snapst.Current.N, Equals, opts.current)
 11439  
 11440  	c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0)
 11441  	c.Check(s.fakeBackend.ops.Count("undo-copy-snap-data"), Equals, 0)
 11442  
 11443  	return ts
 11444  }
 11445  
 11446  func (s *snapmgrTestSuite) testTotalRevertFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet {
 11447  	opts.revert = true
 11448  	opts.fail = true
 11449  	opts.after = opts.before
 11450  	snapst, ts := s.testOpSequence(c, opts)
 11451  	// a failed revert will always end with current unchanged
 11452  	c.Check(snapst.Current.N, Equals, opts.current)
 11453  
 11454  	c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 0)
 11455  	c.Check(s.fakeBackend.ops.Count("undo-copy-snap-data"), Equals, 0)
 11456  
 11457  	return ts
 11458  }
 11459  
 11460  // *** sequence tests ***
 11461  
 11462  // 1. a boring update
 11463  // 1a. ... that works
 11464  func (s *snapmgrTestSuite) TestSeqNormal(c *C) {
 11465  	s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4, after: []int{2, 3, 4}})
 11466  }
 11467  
 11468  // 1b. that fails during link
 11469  func (s *snapmgrTestSuite) TestSeqNormalFailure(c *C) {
 11470  	s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4})
 11471  }
 11472  
 11473  // 1c. that fails after link
 11474  func (s *snapmgrTestSuite) TestSeqTotalNormalFailure(c *C) {
 11475  	// total updates are failures after sequence trimming => we lose a rev
 11476  	s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 4, after: []int{2, 3}})
 11477  }
 11478  
 11479  // 2. a boring revert
 11480  // 2a. that works
 11481  func (s *snapmgrTestSuite) TestSeqRevert(c *C) {
 11482  	s.testRevertSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2})
 11483  }
 11484  
 11485  // 2b. that fails during link
 11486  func (s *snapmgrTestSuite) TestSeqRevertFailure(c *C) {
 11487  	s.testRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2})
 11488  }
 11489  
 11490  // 2c. that fails after link
 11491  func (s *snapmgrTestSuite) TestSeqTotalRevertFailure(c *C) {
 11492  	s.testTotalRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2})
 11493  }
 11494  
 11495  // 3. a post-revert update
 11496  // 3a. that works
 11497  func (s *snapmgrTestSuite) TestSeqPostRevert(c *C) {
 11498  	s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4, after: []int{1, 2, 4}})
 11499  }
 11500  
 11501  // 3b. that fails during link
 11502  func (s *snapmgrTestSuite) TestSeqPostRevertFailure(c *C) {
 11503  	s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4})
 11504  }
 11505  
 11506  // 3c. that fails after link
 11507  func (s *snapmgrTestSuite) TestSeqTotalPostRevertFailure(c *C) {
 11508  	// lose a rev here as well
 11509  	s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 4, after: []int{1, 2}})
 11510  }
 11511  
 11512  // 3d. manually requesting the one reverted away from
 11513  func (s *snapmgrTestSuite) TestSeqRefreshPostRevertSameRevno(c *C) {
 11514  	s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 3, after: []int{1, 2, 3}})
 11515  }
 11516  
 11517  // 4. a post-revert revert
 11518  // 4a. that works
 11519  func (s *snapmgrTestSuite) TestSeqRevertPostRevert(c *C) {
 11520  	s.testRevertSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1})
 11521  }
 11522  
 11523  // 4b. that fails during link
 11524  func (s *snapmgrTestSuite) TestSeqRevertPostRevertFailure(c *C) {
 11525  	s.testRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1})
 11526  }
 11527  
 11528  // 4c. that fails after link
 11529  func (s *snapmgrTestSuite) TestSeqTotalRevertPostRevertFailure(c *C) {
 11530  	s.testTotalRevertFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 2, via: 1})
 11531  }
 11532  
 11533  // 5. an update that missed a rev
 11534  // 5a. that works
 11535  func (s *snapmgrTestSuite) TestSeqMissedOne(c *C) {
 11536  	s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4, after: []int{1, 2, 4}})
 11537  }
 11538  
 11539  // 5b. that fails during link
 11540  func (s *snapmgrTestSuite) TestSeqMissedOneFailure(c *C) {
 11541  	s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4})
 11542  }
 11543  
 11544  // 5c. that fails after link
 11545  func (s *snapmgrTestSuite) TestSeqTotalMissedOneFailure(c *C) {
 11546  	// we don't lose a rev here because len(Seq) < 3 going in
 11547  	s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2}, current: 2, via: 4, after: []int{1, 2}})
 11548  }
 11549  
 11550  // 6. an update that updates to a revision we already have ("ABA update")
 11551  // 6a. that works
 11552  func (s *snapmgrTestSuite) TestSeqABA(c *C) {
 11553  	s.testUpdateSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2, after: []int{1, 3, 2}})
 11554  	c.Check(s.fakeBackend.ops[len(s.fakeBackend.ops)-1], DeepEquals, fakeOp{
 11555  		op:    "cleanup-trash",
 11556  		name:  "some-snap",
 11557  		revno: snap.R(2),
 11558  	})
 11559  }
 11560  
 11561  // 6b. that fails during link
 11562  func (s *snapmgrTestSuite) TestSeqABAFailure(c *C) {
 11563  	s.testUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2})
 11564  	c.Check(s.fakeBackend.ops.First("cleanup-trash"), IsNil)
 11565  }
 11566  
 11567  // 6c that fails after link
 11568  func (s *snapmgrTestSuite) TestSeqTotalABAFailure(c *C) {
 11569  	// we don't lose a rev here because ABA
 11570  	s.testTotalUpdateFailureSequence(c, &opSeqOpts{before: []int{1, 2, 3}, current: 3, via: 2, after: []int{1, 2, 3}})
 11571  	// XXX: TODO: NOTE!! WARNING!! etc
 11572  	//
 11573  	// if this happens in real life, things will be weird. revno 2 will
 11574  	// have data that has been copied from 3, instead of old 2's data,
 11575  	// because the failure occurred *after* nuking the trash. This can
 11576  	// happen when things are chained. Because of this, if it were to
 11577  	// *actually* happen the correct end sequence would be [1, 3] and not
 11578  	// [1, 2, 3]. IRL this scenario can happen if an update that works is
 11579  	// chained to an update that fails. Detecting this case is rather hard,
 11580  	// and the end result is not nice, and we want to move cleanup to a
 11581  	// separate handler & status that will cope with this better (so trash
 11582  	// gets nuked after all tasks succeeded).
 11583  }
 11584  
 11585  func (s *snapmgrTestSuite) TestSeqRetainConf(c *C) {
 11586  	revseq := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
 11587  
 11588  	for i := 2; i <= 10; i++ {
 11589  		// wot, me, hacky?
 11590  		s.TearDownTest(c)
 11591  		s.SetUpTest(c)
 11592  		s.state.Lock()
 11593  		tr := config.NewTransaction(s.state)
 11594  		tr.Set("core", "refresh.retain", i)
 11595  		tr.Commit()
 11596  		s.state.Unlock()
 11597  
 11598  		s.testUpdateSequence(c, &opSeqOpts{before: revseq[:9], current: 9, via: 10, after: revseq[10-i:]})
 11599  	}
 11600  }
 11601  
 11602  func (s *snapmgrTestSuite) TestUpdateTasksWithOldCurrent(c *C) {
 11603  	s.state.Lock()
 11604  	defer s.state.Unlock()
 11605  	restore := release.MockOnClassic(false)
 11606  	defer restore()
 11607  
 11608  	si1 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}
 11609  	si2 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}
 11610  	si3 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}
 11611  	si4 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}
 11612  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 11613  		Active:   true,
 11614  		Channel:  "edge",
 11615  		Sequence: []*snap.SideInfo{si1, si2, si3, si4},
 11616  		Current:  snap.R(2),
 11617  		SnapType: "app",
 11618  	})
 11619  
 11620  	// run the update
 11621  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
 11622  	c.Assert(err, IsNil)
 11623  
 11624  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 2, ts, s.state)
 11625  
 11626  	// and ensure that it will remove the revisions after "current"
 11627  	// (si3, si4)
 11628  	var snapsup snapstate.SnapSetup
 11629  	tasks := ts.Tasks()
 11630  
 11631  	i := len(tasks) - 8
 11632  	c.Check(tasks[i].Kind(), Equals, "clear-snap")
 11633  	err = tasks[i].Get("snap-setup", &snapsup)
 11634  	c.Assert(err, IsNil)
 11635  	c.Check(snapsup.Revision(), Equals, si3.Revision)
 11636  
 11637  	i = len(tasks) - 6
 11638  	c.Check(tasks[i].Kind(), Equals, "clear-snap")
 11639  	err = tasks[i].Get("snap-setup", &snapsup)
 11640  	c.Assert(err, IsNil)
 11641  	c.Check(snapsup.Revision(), Equals, si4.Revision)
 11642  }
 11643  
 11644  func (s *snapmgrTestSuite) TestUpdateCanDoBackwards(c *C) {
 11645  	si7 := snap.SideInfo{
 11646  		RealName: "some-snap",
 11647  		SnapID:   "some-snap-id",
 11648  		Revision: snap.R(7),
 11649  	}
 11650  	si11 := snap.SideInfo{
 11651  		RealName: "some-snap",
 11652  		SnapID:   "some-snap-id",
 11653  		Revision: snap.R(11),
 11654  	}
 11655  
 11656  	s.state.Lock()
 11657  	defer s.state.Unlock()
 11658  
 11659  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 11660  		Active:   true,
 11661  		Sequence: []*snap.SideInfo{&si7, &si11},
 11662  		Current:  si11.Revision,
 11663  		SnapType: "app",
 11664  	})
 11665  
 11666  	chg := s.state.NewChange("refresh", "refresh a snap")
 11667  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(7)}, s.user.ID, snapstate.Flags{})
 11668  	c.Assert(err, IsNil)
 11669  	chg.AddAll(ts)
 11670  
 11671  	s.state.Unlock()
 11672  	defer s.se.Stop()
 11673  	s.settle(c)
 11674  	s.state.Lock()
 11675  	expected := fakeOps{
 11676  		{
 11677  			op:   "remove-snap-aliases",
 11678  			name: "some-snap",
 11679  		},
 11680  		{
 11681  			op:   "unlink-snap",
 11682  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
 11683  		},
 11684  		{
 11685  			op:   "copy-data",
 11686  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
 11687  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
 11688  		},
 11689  		{
 11690  			op:    "setup-profiles:Doing",
 11691  			name:  "some-snap",
 11692  			revno: snap.R(7),
 11693  		},
 11694  		{
 11695  			op: "candidate",
 11696  			sinfo: snap.SideInfo{
 11697  				RealName: "some-snap",
 11698  				SnapID:   "some-snap-id",
 11699  				Channel:  "",
 11700  				Revision: snap.R(7),
 11701  			},
 11702  		},
 11703  		{
 11704  			op:   "link-snap",
 11705  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
 11706  		},
 11707  		{
 11708  			op:    "auto-connect:Doing",
 11709  			name:  "some-snap",
 11710  			revno: snap.R(7),
 11711  		},
 11712  		{
 11713  			op: "update-aliases",
 11714  		},
 11715  		{
 11716  			op:    "cleanup-trash",
 11717  			name:  "some-snap",
 11718  			revno: snap.R(7),
 11719  		},
 11720  	}
 11721  	// start with an easier-to-read error if this fails:
 11722  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
 11723  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
 11724  }
 11725  
 11726  func (s *snapmgrTestSuite) TestSnapStateNoLocalRevision(c *C) {
 11727  	si7 := snap.SideInfo{
 11728  		RealName: "some-snap",
 11729  		Revision: snap.R(-7),
 11730  	}
 11731  	si11 := snap.SideInfo{
 11732  		RealName: "some-snap",
 11733  		Revision: snap.R(-11),
 11734  	}
 11735  	snapst := &snapstate.SnapState{
 11736  		Sequence: []*snap.SideInfo{&si7, &si11},
 11737  		Current:  si7.Revision,
 11738  	}
 11739  	c.Assert(snapst.LocalRevision(), Equals, snap.R(-11))
 11740  }
 11741  
 11742  func (s *snapmgrTestSuite) TestSnapStateLocalRevision(c *C) {
 11743  	si7 := snap.SideInfo{
 11744  		RealName: "some-snap",
 11745  		Revision: snap.R(7),
 11746  	}
 11747  	snapst := &snapstate.SnapState{
 11748  		Sequence: []*snap.SideInfo{&si7},
 11749  		Current:  si7.Revision,
 11750  	}
 11751  	c.Assert(snapst.LocalRevision().Unset(), Equals, true)
 11752  }
 11753  
 11754  func (s *snapmgrTestSuite) TestInstallMany(c *C) {
 11755  	s.state.Lock()
 11756  	defer s.state.Unlock()
 11757  
 11758  	installed, tts, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0)
 11759  	c.Assert(err, IsNil)
 11760  	c.Assert(tts, HasLen, 2)
 11761  	c.Check(installed, DeepEquals, []string{"one", "two"})
 11762  
 11763  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true)
 11764  
 11765  	for i, ts := range tts {
 11766  		verifyInstallTasks(c, 0, 0, ts, s.state)
 11767  		// check that tasksets are in separate lanes
 11768  		for _, t := range ts.Tasks() {
 11769  			c.Assert(t.Lanes(), DeepEquals, []int{i + 1})
 11770  		}
 11771  	}
 11772  }
 11773  
 11774  func (s *snapmgrTestSuite) TestInstallManyTooEarly(c *C) {
 11775  	s.state.Lock()
 11776  	defer s.state.Unlock()
 11777  
 11778  	s.state.Set("seeded", nil)
 11779  
 11780  	_, _, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0)
 11781  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
 11782  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
 11783  }
 11784  
 11785  func (s *snapmgrTestSuite) TestInstallManyChecksPreconditions(c *C) {
 11786  	s.state.Lock()
 11787  	defer s.state.Unlock()
 11788  
 11789  	_, _, err := snapstate.InstallMany(s.state, []string{"some-snap-now-classic"}, 0)
 11790  	c.Assert(err, NotNil)
 11791  	c.Check(err, DeepEquals, &snapstate.SnapNeedsClassicError{Snap: "some-snap-now-classic"})
 11792  
 11793  	_, _, err = snapstate.InstallMany(s.state, []string{"some-snap_foo"}, 0)
 11794  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.parallel-instances' to true")
 11795  }
 11796  
 11797  func verifyStopReason(c *C, ts *state.TaskSet, reason string) {
 11798  	tl := tasksWithKind(ts, "stop-snap-services")
 11799  	c.Check(tl, HasLen, 1)
 11800  
 11801  	var stopReason string
 11802  	err := tl[0].Get("stop-reason", &stopReason)
 11803  	c.Assert(err, IsNil)
 11804  	c.Check(stopReason, Equals, reason)
 11805  
 11806  }
 11807  
 11808  func (s *snapmgrTestSuite) TestRemoveMany(c *C) {
 11809  	s.state.Lock()
 11810  	defer s.state.Unlock()
 11811  
 11812  	snapstate.Set(s.state, "one", &snapstate.SnapState{
 11813  		Active: true,
 11814  		Sequence: []*snap.SideInfo{
 11815  			{RealName: "one", SnapID: "one-id", Revision: snap.R(1)},
 11816  		},
 11817  		Current: snap.R(1),
 11818  	})
 11819  	snapstate.Set(s.state, "two", &snapstate.SnapState{
 11820  		Active: true,
 11821  		Sequence: []*snap.SideInfo{
 11822  			{RealName: "two", SnapID: "two-id", Revision: snap.R(1)},
 11823  		},
 11824  		Current: snap.R(1),
 11825  	})
 11826  
 11827  	removed, tts, err := snapstate.RemoveMany(s.state, []string{"one", "two"})
 11828  	c.Assert(err, IsNil)
 11829  	c.Assert(tts, HasLen, 2)
 11830  	c.Check(removed, DeepEquals, []string{"one", "two"})
 11831  
 11832  	c.Assert(s.state.TaskCount(), Equals, 8*2)
 11833  	for i, ts := range tts {
 11834  		c.Assert(taskKinds(ts.Tasks()), DeepEquals, []string{
 11835  			"stop-snap-services",
 11836  			"run-hook[remove]",
 11837  			"auto-disconnect",
 11838  			"remove-aliases",
 11839  			"unlink-snap",
 11840  			"remove-profiles",
 11841  			"clear-snap",
 11842  			"discard-snap",
 11843  		})
 11844  		verifyStopReason(c, ts, "remove")
 11845  		// check that tasksets are in separate lanes
 11846  		for _, t := range ts.Tasks() {
 11847  			c.Assert(t.Lanes(), DeepEquals, []int{i + 1})
 11848  		}
 11849  
 11850  	}
 11851  }
 11852  
 11853  func tasksWithKind(ts *state.TaskSet, kind string) []*state.Task {
 11854  	var tasks []*state.Task
 11855  	for _, task := range ts.Tasks() {
 11856  		if task.Kind() == kind {
 11857  			tasks = append(tasks, task)
 11858  		}
 11859  	}
 11860  	return tasks
 11861  }
 11862  
 11863  var gadgetYaml = `
 11864  defaults:
 11865      some-snap-ididididididididididid:
 11866          key: value
 11867  
 11868  volumes:
 11869      volume-id:
 11870          bootloader: grub
 11871  `
 11872  
 11873  func (s *snapmgrTestSuite) prepareGadget(c *C, extraGadgetYaml ...string) {
 11874  	gadgetSideInfo := &snap.SideInfo{RealName: "the-gadget", SnapID: "the-gadget-id", Revision: snap.R(1)}
 11875  	gadgetInfo := snaptest.MockSnap(c, `
 11876  name: the-gadget
 11877  type: gadget
 11878  version: 1.0
 11879  `, gadgetSideInfo)
 11880  
 11881  	gadgetYamlWhole := strings.Join(append([]string{gadgetYaml}, extraGadgetYaml...), "")
 11882  	err := ioutil.WriteFile(filepath.Join(gadgetInfo.MountDir(), "meta/gadget.yaml"), []byte(gadgetYamlWhole), 0600)
 11883  	c.Assert(err, IsNil)
 11884  
 11885  	snapstate.Set(s.state, "the-gadget", &snapstate.SnapState{
 11886  		Active:   true,
 11887  		Sequence: []*snap.SideInfo{&gadgetInfo.SideInfo},
 11888  		Current:  snap.R(1),
 11889  		SnapType: "gadget",
 11890  	})
 11891  }
 11892  
 11893  func deviceWithGadgetContext(gadgetName string) snapstate.DeviceContext {
 11894  	return &snapstatetest.TrivialDeviceContext{
 11895  		DeviceModel: MakeModel(map[string]interface{}{
 11896  			"gadget": gadgetName,
 11897  		}),
 11898  	}
 11899  }
 11900  
 11901  func deviceWithoutGadgetContext() snapstate.DeviceContext {
 11902  	return &snapstatetest.TrivialDeviceContext{
 11903  		DeviceModel: ClassicModel(),
 11904  	}
 11905  }
 11906  
 11907  func (s *snapmgrTestSuite) TestConfigDefaults(c *C) {
 11908  	r := release.MockOnClassic(false)
 11909  	defer r()
 11910  
 11911  	// using MockSnap, we want to read the bits on disk
 11912  	snapstate.MockSnapReadInfo(snap.ReadInfo)
 11913  
 11914  	s.state.Lock()
 11915  	defer s.state.Unlock()
 11916  
 11917  	s.prepareGadget(c)
 11918  
 11919  	deviceCtx := deviceWithGadgetContext("the-gadget")
 11920  
 11921  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 11922  		Active: true,
 11923  		Sequence: []*snap.SideInfo{
 11924  			{RealName: "some-snap", Revision: snap.R(11), SnapID: "some-snap-ididididididididididid"},
 11925  		},
 11926  		Current:  snap.R(11),
 11927  		SnapType: "app",
 11928  	})
 11929  	makeInstalledMockCoreSnap(c)
 11930  
 11931  	defls, err := snapstate.ConfigDefaults(s.state, deviceCtx, "some-snap")
 11932  	c.Assert(err, IsNil)
 11933  	c.Assert(defls, DeepEquals, map[string]interface{}{"key": "value"})
 11934  
 11935  	snapstate.Set(s.state, "local-snap", &snapstate.SnapState{
 11936  		Active: true,
 11937  		Sequence: []*snap.SideInfo{
 11938  			{RealName: "local-snap", Revision: snap.R(5)},
 11939  		},
 11940  		Current:  snap.R(5),
 11941  		SnapType: "app",
 11942  	})
 11943  	_, err = snapstate.ConfigDefaults(s.state, deviceCtx, "local-snap")
 11944  	c.Assert(err, Equals, state.ErrNoState)
 11945  }
 11946  
 11947  func (s *snapmgrTestSuite) TestConfigDefaultsNoGadget(c *C) {
 11948  	r := release.MockOnClassic(false)
 11949  	defer r()
 11950  
 11951  	// using MockSnap, we want to read the bits on disk
 11952  	snapstate.MockSnapReadInfo(snap.ReadInfo)
 11953  
 11954  	s.state.Lock()
 11955  	defer s.state.Unlock()
 11956  
 11957  	deviceCtxNoGadget := deviceWithoutGadgetContext()
 11958  
 11959  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 11960  		Active: true,
 11961  		Sequence: []*snap.SideInfo{
 11962  			{RealName: "some-snap", Revision: snap.R(11), SnapID: "some-snap-ididididididididididid"},
 11963  		},
 11964  		Current:  snap.R(11),
 11965  		SnapType: "app",
 11966  	})
 11967  	makeInstalledMockCoreSnap(c)
 11968  
 11969  	_, err := snapstate.ConfigDefaults(s.state, deviceCtxNoGadget, "some-snap")
 11970  	c.Assert(err, Equals, state.ErrNoState)
 11971  }
 11972  
 11973  func (s *snapmgrTestSuite) TestConfigDefaultsSystemWithCore(c *C) {
 11974  	r := release.MockOnClassic(false)
 11975  	defer r()
 11976  
 11977  	// using MockSnapReadInfo, we want to read the bits on disk
 11978  	snapstate.MockSnapReadInfo(snap.ReadInfo)
 11979  
 11980  	s.state.Lock()
 11981  	defer s.state.Unlock()
 11982  
 11983  	s.prepareGadget(c, `
 11984  defaults:
 11985      system:
 11986          foo: bar
 11987  `)
 11988  
 11989  	deviceCtx := deviceWithGadgetContext("the-gadget")
 11990  
 11991  	snapstate.Set(s.state, "core", &snapstate.SnapState{
 11992  		Active: true,
 11993  		Sequence: []*snap.SideInfo{
 11994  			{RealName: "some-snap", Revision: snap.R(11), SnapID: "the-core-ididididididididididid"},
 11995  		},
 11996  		Current:  snap.R(11),
 11997  		SnapType: "os",
 11998  	})
 11999  
 12000  	makeInstalledMockCoreSnap(c)
 12001  
 12002  	defls, err := snapstate.ConfigDefaults(s.state, deviceCtx, "core")
 12003  	c.Assert(err, IsNil)
 12004  	c.Assert(defls, DeepEquals, map[string]interface{}{"foo": "bar"})
 12005  }
 12006  
 12007  var snapdSnapYaml = `name: snapd
 12008  version: 1.0
 12009  type: snapd
 12010  `
 12011  
 12012  func (s *snapmgrTestSuite) TestConfigDefaultsSystemWithSnapdNoCore(c *C) {
 12013  	r := release.MockOnClassic(false)
 12014  	defer r()
 12015  
 12016  	// using MockSnapReadInfo, we want to read the bits on disk
 12017  	snapstate.MockSnapReadInfo(snap.ReadInfo)
 12018  
 12019  	s.state.Lock()
 12020  	defer s.state.Unlock()
 12021  
 12022  	s.prepareGadget(c, `
 12023  defaults:
 12024      system:
 12025          foo: bar
 12026  `)
 12027  
 12028  	deviceCtx := &snapstatetest.TrivialDeviceContext{
 12029  		DeviceModel: MakeModel(map[string]interface{}{
 12030  			"gadget": "the-gadget",
 12031  			"base":   "the-base",
 12032  		}),
 12033  	}
 12034  
 12035  	snapstate.Set(s.state, "core", nil)
 12036  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
 12037  		Active: true,
 12038  		Sequence: []*snap.SideInfo{
 12039  			{RealName: "snapd", SnapID: "the-snapd-snapidididididididididi", Revision: snap.R(1)},
 12040  		},
 12041  		Current:  snap.R(1),
 12042  		SnapType: "snapd",
 12043  	})
 12044  
 12045  	snaptest.MockSnap(c, snapdSnapYaml, &snap.SideInfo{
 12046  		RealName: "snapd",
 12047  		Revision: snap.R(1),
 12048  	})
 12049  
 12050  	defls, err := snapstate.ConfigDefaults(s.state, deviceCtx, "core")
 12051  	c.Assert(err, IsNil)
 12052  	c.Assert(defls, DeepEquals, map[string]interface{}{"foo": "bar"})
 12053  }
 12054  
 12055  func (s *snapmgrTestSuite) TestConfigDefaultsSystemConflictsCoreSnapId(c *C) {
 12056  	r := release.MockOnClassic(false)
 12057  	defer r()
 12058  
 12059  	// using MockSnapReadInfo, we want to read the bits on disk
 12060  	snapstate.MockSnapReadInfo(snap.ReadInfo)
 12061  
 12062  	s.state.Lock()
 12063  	defer s.state.Unlock()
 12064  
 12065  	s.prepareGadget(c, `
 12066  defaults:
 12067      system:
 12068          foo: bar
 12069      the-core-snapidididididididididi:
 12070          foo: other-bar
 12071          other-key: other-key-default
 12072  `)
 12073  
 12074  	deviceCtx := deviceWithGadgetContext("the-gadget")
 12075  
 12076  	snapstate.Set(s.state, "core", &snapstate.SnapState{
 12077  		Active: true,
 12078  		Sequence: []*snap.SideInfo{
 12079  			{RealName: "core", SnapID: "the-core-snapidididididididididi", Revision: snap.R(1)},
 12080  		},
 12081  		Current:  snap.R(1),
 12082  		SnapType: "os",
 12083  	})
 12084  
 12085  	makeInstalledMockCoreSnap(c)
 12086  
 12087  	// 'system' key defaults take precedence over snap-id ones
 12088  	defls, err := snapstate.ConfigDefaults(s.state, deviceCtx, "core")
 12089  	c.Assert(err, IsNil)
 12090  	c.Assert(defls, DeepEquals, map[string]interface{}{"foo": "bar"})
 12091  }
 12092  
 12093  func (s *snapmgrTestSuite) TestGadgetDefaultsAreNormalizedForConfigHook(c *C) {
 12094  	var mockGadgetSnapYaml = `
 12095  name: canonical-pc
 12096  type: gadget
 12097  `
 12098  	var mockGadgetYaml = []byte(`
 12099  defaults:
 12100    otheridididididididididididididi:
 12101      foo:
 12102        bar: baz
 12103        num: 1.305
 12104  
 12105  volumes:
 12106      volume-id:
 12107          bootloader: grub
 12108  `)
 12109  
 12110  	info := snaptest.MockSnap(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(2)})
 12111  	err := ioutil.WriteFile(filepath.Join(info.MountDir(), "meta", "gadget.yaml"), mockGadgetYaml, 0644)
 12112  	c.Assert(err, IsNil)
 12113  
 12114  	gi, err := gadget.ReadInfo(info.MountDir(), false)
 12115  	c.Assert(err, IsNil)
 12116  	c.Assert(gi, NotNil)
 12117  
 12118  	snapName := "some-snap"
 12119  	hooksup := &hookstate.HookSetup{
 12120  		Snap:        snapName,
 12121  		Hook:        "configure",
 12122  		Optional:    true,
 12123  		IgnoreError: false,
 12124  		TrackError:  false,
 12125  	}
 12126  
 12127  	var contextData map[string]interface{}
 12128  	contextData = map[string]interface{}{"patch": gi.Defaults}
 12129  
 12130  	s.state.Lock()
 12131  	defer s.state.Unlock()
 12132  	c.Assert(hookstate.HookTask(s.state, "", hooksup, contextData), NotNil)
 12133  }
 12134  
 12135  func makeInstalledMockCoreSnap(c *C) {
 12136  	coreSnapYaml := `name: core
 12137  version: 1.0
 12138  type: os
 12139  `
 12140  	snaptest.MockSnap(c, coreSnapYaml, &snap.SideInfo{
 12141  		RealName: "core",
 12142  		Revision: snap.R(1),
 12143  	})
 12144  }
 12145  
 12146  func (s *snapmgrTestSuite) TestGadgetDefaults(c *C) {
 12147  	r := release.MockOnClassic(false)
 12148  	defer r()
 12149  
 12150  	makeInstalledMockCoreSnap(c)
 12151  
 12152  	// using MockSnap, we want to read the bits on disk
 12153  	snapstate.MockSnapReadInfo(snap.ReadInfo)
 12154  
 12155  	s.state.Lock()
 12156  	defer s.state.Unlock()
 12157  
 12158  	s.prepareGadget(c)
 12159  
 12160  	snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0")
 12161  
 12162  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{})
 12163  	c.Assert(err, IsNil)
 12164  
 12165  	var m map[string]interface{}
 12166  	runHooks := tasksWithKind(ts, "run-hook")
 12167  
 12168  	c.Assert(taskKinds(runHooks), DeepEquals, []string{
 12169  		"run-hook[install]",
 12170  		"run-hook[configure]",
 12171  		"run-hook[check-health]",
 12172  	})
 12173  	err = runHooks[1].Get("hook-context", &m)
 12174  	c.Assert(err, IsNil)
 12175  	c.Assert(m, DeepEquals, map[string]interface{}{"use-defaults": true})
 12176  }
 12177  
 12178  func (s *snapmgrTestSuite) TestGadgetDefaultsNotForOS(c *C) {
 12179  	r := release.MockOnClassic(false)
 12180  	defer r()
 12181  
 12182  	// using MockSnap, we want to read the bits on disk
 12183  	snapstate.MockSnapReadInfo(snap.ReadInfo)
 12184  
 12185  	s.state.Lock()
 12186  	defer s.state.Unlock()
 12187  
 12188  	snapstate.Set(s.state, "core", nil)
 12189  
 12190  	s.prepareGadget(c)
 12191  
 12192  	const coreSnapYaml = `
 12193  name: core
 12194  type: os
 12195  version: 1.0
 12196  `
 12197  	snapPath := makeTestSnap(c, coreSnapYaml)
 12198  
 12199  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "core", SnapID: "core-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{})
 12200  	c.Assert(err, IsNil)
 12201  
 12202  	var m map[string]interface{}
 12203  	runHooks := tasksWithKind(ts, "run-hook")
 12204  
 12205  	c.Assert(taskKinds(runHooks), DeepEquals, []string{
 12206  		"run-hook[install]",
 12207  		"run-hook[configure]",
 12208  		"run-hook[check-health]",
 12209  	})
 12210  	// use-defaults flag is part of hook-context which isn't set
 12211  	err = runHooks[1].Get("hook-context", &m)
 12212  	c.Assert(err, Equals, state.ErrNoState)
 12213  }
 12214  
 12215  func (s *snapmgrTestSuite) TestInstallPathSkipConfigure(c *C) {
 12216  	r := release.MockOnClassic(false)
 12217  	defer r()
 12218  
 12219  	makeInstalledMockCoreSnap(c)
 12220  
 12221  	// using MockSnap, we want to read the bits on disk
 12222  	snapstate.MockSnapReadInfo(snap.ReadInfo)
 12223  
 12224  	s.state.Lock()
 12225  	defer s.state.Unlock()
 12226  
 12227  	s.prepareGadget(c)
 12228  
 12229  	snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0")
 12230  
 12231  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{SkipConfigure: true})
 12232  	c.Assert(err, IsNil)
 12233  
 12234  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
 12235  	c.Assert(err, IsNil)
 12236  	// SkipConfigure is consumed and consulted when creating the taskset
 12237  	// but is not copied into SnapSetup
 12238  	c.Check(snapsup.Flags.SkipConfigure, Equals, false)
 12239  }
 12240  
 12241  func (s *snapmgrTestSuite) TestNoReRefreshInUpdate(c *C) {
 12242  	s.state.Lock()
 12243  	defer s.state.Unlock()
 12244  
 12245  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 12246  		Active: true,
 12247  		Sequence: []*snap.SideInfo{
 12248  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
 12249  		},
 12250  		Current:  snap.R(1),
 12251  		SnapType: "app",
 12252  	})
 12253  
 12254  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{NoReRefresh: true})
 12255  	c.Assert(err, IsNil)
 12256  
 12257  	// ensure we have no re-refresh task
 12258  	for _, t := range ts.Tasks() {
 12259  		c.Assert(t.Kind(), Not(Equals), "check-rerefresh")
 12260  	}
 12261  
 12262  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
 12263  	c.Assert(err, IsNil)
 12264  	// NoReRefresh is consumed and consulted when creating the taskset
 12265  	// but is not copied into SnapSetup
 12266  	c.Check(snapsup.Flags.NoReRefresh, Equals, false)
 12267  }
 12268  
 12269  func (s *snapmgrTestSuite) TestGadgetDefaultsInstalled(c *C) {
 12270  	makeInstalledMockCoreSnap(c)
 12271  
 12272  	// using MockSnap, we want to read the bits on disk
 12273  	snapstate.MockSnapReadInfo(snap.ReadInfo)
 12274  
 12275  	s.state.Lock()
 12276  	defer s.state.Unlock()
 12277  
 12278  	s.prepareGadget(c)
 12279  
 12280  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 12281  		Active:   true,
 12282  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}},
 12283  		Current:  snap.R(1),
 12284  		SnapType: "app",
 12285  	})
 12286  
 12287  	snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0")
 12288  
 12289  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, snapPath, "", "edge", snapstate.Flags{})
 12290  	c.Assert(err, IsNil)
 12291  
 12292  	var m map[string]interface{}
 12293  	runHooks := tasksWithKind(ts, "run-hook")
 12294  
 12295  	c.Assert(runHooks[0].Kind(), Equals, "run-hook")
 12296  	err = runHooks[0].Get("hook-context", &m)
 12297  	c.Assert(err, Equals, state.ErrNoState)
 12298  }
 12299  
 12300  func (s *snapmgrTestSuite) TestTransitionCoreTasksNoUbuntuCore(c *C) {
 12301  	s.state.Lock()
 12302  	defer s.state.Unlock()
 12303  
 12304  	snapstate.Set(s.state, "core", &snapstate.SnapState{
 12305  		Active:   true,
 12306  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}},
 12307  		Current:  snap.R(1),
 12308  		SnapType: "os",
 12309  	})
 12310  
 12311  	_, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core")
 12312  	c.Assert(err, ErrorMatches, `cannot transition snap "ubuntu-core": not installed`)
 12313  }
 12314  
 12315  func verifyTransitionConnectionsTasks(c *C, ts *state.TaskSet) {
 12316  	c.Check(taskKinds(ts.Tasks()), DeepEquals, []string{
 12317  		"transition-ubuntu-core",
 12318  	})
 12319  
 12320  	transIf := ts.Tasks()[0]
 12321  	var oldName, newName string
 12322  	err := transIf.Get("old-name", &oldName)
 12323  	c.Assert(err, IsNil)
 12324  	c.Check(oldName, Equals, "ubuntu-core")
 12325  
 12326  	err = transIf.Get("new-name", &newName)
 12327  	c.Assert(err, IsNil)
 12328  	c.Check(newName, Equals, "core")
 12329  }
 12330  
 12331  func (s *snapmgrTestSuite) TestTransitionCoreTasks(c *C) {
 12332  	s.state.Lock()
 12333  	defer s.state.Unlock()
 12334  
 12335  	snapstate.Set(s.state, "core", nil)
 12336  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
 12337  		Active:   true,
 12338  		Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}},
 12339  		Current:  snap.R(1),
 12340  		SnapType: "os",
 12341  	})
 12342  
 12343  	tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core")
 12344  	c.Assert(err, IsNil)
 12345  
 12346  	c.Assert(tsl, HasLen, 3)
 12347  	// 1. install core
 12348  	verifyInstallTasks(c, runCoreConfigure|maybeCore, 0, tsl[0], s.state)
 12349  	// 2 transition-connections
 12350  	verifyTransitionConnectionsTasks(c, tsl[1])
 12351  	// 3 remove-ubuntu-core
 12352  	verifyCoreRemoveTasks(c, tsl[2])
 12353  }
 12354  
 12355  func (s *snapmgrTestSuite) TestTransitionCoreTasksWithUbuntuCoreAndCore(c *C) {
 12356  	s.state.Lock()
 12357  	defer s.state.Unlock()
 12358  
 12359  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
 12360  		Active:   true,
 12361  		Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}},
 12362  		Current:  snap.R(1),
 12363  		SnapType: "os",
 12364  	})
 12365  	snapstate.Set(s.state, "core", &snapstate.SnapState{
 12366  		Active:   true,
 12367  		Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}},
 12368  		Current:  snap.R(1),
 12369  		SnapType: "os",
 12370  	})
 12371  
 12372  	tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core")
 12373  	c.Assert(err, IsNil)
 12374  
 12375  	c.Assert(tsl, HasLen, 2)
 12376  	// 1. transition connections
 12377  	verifyTransitionConnectionsTasks(c, tsl[0])
 12378  	// 2. remove ubuntu-core
 12379  	verifyCoreRemoveTasks(c, tsl[1])
 12380  }
 12381  
 12382  func (s *snapmgrTestSuite) TestTransitionCoreRunThrough(c *C) {
 12383  	s.state.Lock()
 12384  	defer s.state.Unlock()
 12385  
 12386  	snapstate.Set(s.state, "core", nil)
 12387  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
 12388  		Active:   true,
 12389  		Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}},
 12390  		Current:  snap.R(1),
 12391  		SnapType: "os",
 12392  		Channel:  "beta",
 12393  	})
 12394  
 12395  	chg := s.state.NewChange("transition-ubuntu-core", "...")
 12396  	tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core")
 12397  	c.Assert(err, IsNil)
 12398  	for _, ts := range tsl {
 12399  		chg.AddAll(ts)
 12400  	}
 12401  
 12402  	s.state.Unlock()
 12403  	defer s.se.Stop()
 12404  	s.settle(c)
 12405  	s.state.Lock()
 12406  
 12407  	// ensure all our tasks ran
 12408  	c.Assert(chg.Err(), IsNil)
 12409  	c.Assert(chg.IsReady(), Equals, true)
 12410  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
 12411  		name: "core",
 12412  		// the transition has no user associcated with it
 12413  		macaroon: "",
 12414  		target:   filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
 12415  	}})
 12416  	expected := fakeOps{
 12417  		{
 12418  			op: "storesvc-snap-action",
 12419  			curSnaps: []store.CurrentSnap{
 12420  				{
 12421  					InstanceName:    "ubuntu-core",
 12422  					SnapID:          "ubuntu-core-snap-id",
 12423  					Revision:        snap.R(1),
 12424  					TrackingChannel: "beta",
 12425  					RefreshedDate:   fakeRevDateEpoch.AddDate(0, 0, 1),
 12426  					Epoch:           snap.E("1*"),
 12427  				},
 12428  			},
 12429  		},
 12430  		{
 12431  			op: "storesvc-snap-action:action",
 12432  			action: store.SnapAction{
 12433  				Action:       "install",
 12434  				InstanceName: "core",
 12435  				Channel:      "beta",
 12436  			},
 12437  			revno: snap.R(11),
 12438  		},
 12439  		{
 12440  			op:   "storesvc-download",
 12441  			name: "core",
 12442  		},
 12443  		{
 12444  			op:    "validate-snap:Doing",
 12445  			name:  "core",
 12446  			revno: snap.R(11),
 12447  		},
 12448  		{
 12449  			op:  "current",
 12450  			old: "<no-current>",
 12451  		},
 12452  		{
 12453  			op:   "open-snap-file",
 12454  			path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
 12455  			sinfo: snap.SideInfo{
 12456  				RealName: "core",
 12457  				SnapID:   "core-id",
 12458  				Channel:  "beta",
 12459  				Revision: snap.R(11),
 12460  			},
 12461  		},
 12462  		{
 12463  			op:    "setup-snap",
 12464  			name:  "core",
 12465  			path:  filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
 12466  			revno: snap.R(11),
 12467  		},
 12468  		{
 12469  			op:   "copy-data",
 12470  			path: filepath.Join(dirs.SnapMountDir, "core/11"),
 12471  			old:  "<no-old>",
 12472  		},
 12473  		{
 12474  			op:    "setup-profiles:Doing",
 12475  			name:  "core",
 12476  			revno: snap.R(11),
 12477  		},
 12478  		{
 12479  			op: "candidate",
 12480  			sinfo: snap.SideInfo{
 12481  				RealName: "core",
 12482  				SnapID:   "core-id",
 12483  				Channel:  "beta",
 12484  				Revision: snap.R(11),
 12485  			},
 12486  		},
 12487  		{
 12488  			op:   "link-snap",
 12489  			path: filepath.Join(dirs.SnapMountDir, "core/11"),
 12490  		},
 12491  		{
 12492  			op:    "auto-connect:Doing",
 12493  			name:  "core",
 12494  			revno: snap.R(11),
 12495  		},
 12496  		{
 12497  			op: "update-aliases",
 12498  		},
 12499  		{
 12500  			op:   "transition-ubuntu-core:Doing",
 12501  			name: "ubuntu-core",
 12502  		},
 12503  		{
 12504  			op:    "auto-disconnect:Doing",
 12505  			name:  "ubuntu-core",
 12506  			revno: snap.R(1),
 12507  		},
 12508  		{
 12509  			op:   "remove-snap-aliases",
 12510  			name: "ubuntu-core",
 12511  		},
 12512  		{
 12513  			op:   "unlink-snap",
 12514  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
 12515  		},
 12516  		{
 12517  			op:    "remove-profiles:Doing",
 12518  			name:  "ubuntu-core",
 12519  			revno: snap.R(1),
 12520  		},
 12521  		{
 12522  			op:   "remove-snap-data",
 12523  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
 12524  		},
 12525  		{
 12526  			op:   "remove-snap-common-data",
 12527  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
 12528  		},
 12529  		{
 12530  			op:   "remove-snap-data-dir",
 12531  			name: "ubuntu-core",
 12532  			path: filepath.Join(dirs.SnapDataDir, "ubuntu-core"),
 12533  		},
 12534  		{
 12535  			op:    "remove-snap-files",
 12536  			path:  filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
 12537  			stype: "os",
 12538  		},
 12539  		{
 12540  			op:   "discard-namespace",
 12541  			name: "ubuntu-core",
 12542  		},
 12543  		{
 12544  			op:   "remove-snap-dir",
 12545  			name: "ubuntu-core",
 12546  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core"),
 12547  		},
 12548  		{
 12549  			op:    "cleanup-trash",
 12550  			name:  "core",
 12551  			revno: snap.R(11),
 12552  		},
 12553  	}
 12554  	// start with an easier-to-read error if this fails:
 12555  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
 12556  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
 12557  }
 12558  
 12559  func (s *snapmgrTestSuite) TestTransitionCoreRunThroughWithCore(c *C) {
 12560  	s.state.Lock()
 12561  	defer s.state.Unlock()
 12562  
 12563  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
 12564  		Active:   true,
 12565  		Sequence: []*snap.SideInfo{{RealName: "ubuntu-core", SnapID: "ubuntu-core-snap-id", Revision: snap.R(1)}},
 12566  		Current:  snap.R(1),
 12567  		SnapType: "os",
 12568  		Channel:  "stable",
 12569  	})
 12570  	snapstate.Set(s.state, "core", &snapstate.SnapState{
 12571  		Active:   true,
 12572  		Sequence: []*snap.SideInfo{{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(1)}},
 12573  		Current:  snap.R(1),
 12574  		SnapType: "os",
 12575  		Channel:  "stable",
 12576  	})
 12577  
 12578  	chg := s.state.NewChange("transition-ubuntu-core", "...")
 12579  	tsl, err := snapstate.TransitionCore(s.state, "ubuntu-core", "core")
 12580  	c.Assert(err, IsNil)
 12581  	for _, ts := range tsl {
 12582  		chg.AddAll(ts)
 12583  	}
 12584  
 12585  	s.state.Unlock()
 12586  	defer s.se.Stop()
 12587  	s.settle(c)
 12588  	s.state.Lock()
 12589  
 12590  	// ensure all our tasks ran
 12591  	c.Assert(chg.Err(), IsNil)
 12592  	c.Assert(chg.IsReady(), Equals, true)
 12593  	c.Check(s.fakeStore.downloads, HasLen, 0)
 12594  	expected := fakeOps{
 12595  		{
 12596  			op:   "transition-ubuntu-core:Doing",
 12597  			name: "ubuntu-core",
 12598  		},
 12599  		{
 12600  			op:    "auto-disconnect:Doing",
 12601  			name:  "ubuntu-core",
 12602  			revno: snap.R(1),
 12603  		},
 12604  		{
 12605  			op:   "remove-snap-aliases",
 12606  			name: "ubuntu-core",
 12607  		},
 12608  		{
 12609  			op:   "unlink-snap",
 12610  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
 12611  		},
 12612  		{
 12613  			op:    "remove-profiles:Doing",
 12614  			name:  "ubuntu-core",
 12615  			revno: snap.R(1),
 12616  		},
 12617  		{
 12618  			op:   "remove-snap-data",
 12619  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
 12620  		},
 12621  		{
 12622  			op:   "remove-snap-common-data",
 12623  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
 12624  		},
 12625  		{
 12626  			op:   "remove-snap-data-dir",
 12627  			name: "ubuntu-core",
 12628  			path: filepath.Join(dirs.SnapDataDir, "ubuntu-core"),
 12629  		},
 12630  		{
 12631  			op:    "remove-snap-files",
 12632  			path:  filepath.Join(dirs.SnapMountDir, "ubuntu-core/1"),
 12633  			stype: "os",
 12634  		},
 12635  		{
 12636  			op:   "discard-namespace",
 12637  			name: "ubuntu-core",
 12638  		},
 12639  		{
 12640  			op:   "remove-snap-dir",
 12641  			name: "ubuntu-core",
 12642  			path: filepath.Join(dirs.SnapMountDir, "ubuntu-core"),
 12643  		},
 12644  	}
 12645  	// start with an easier-to-read error if this fails:
 12646  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
 12647  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
 12648  }
 12649  
 12650  func (s *snapmgrTestSuite) TestTransitionCoreStartsAutomatically(c *C) {
 12651  	s.state.Lock()
 12652  	defer s.state.Unlock()
 12653  
 12654  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
 12655  		Active:   true,
 12656  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}},
 12657  		Current:  snap.R(1),
 12658  		SnapType: "os",
 12659  	})
 12660  
 12661  	s.state.Unlock()
 12662  	defer s.se.Stop()
 12663  	s.settle(c)
 12664  	s.state.Lock()
 12665  
 12666  	c.Check(s.state.Changes(), HasLen, 1)
 12667  	c.Check(s.state.Changes()[0].Kind(), Equals, "transition-ubuntu-core")
 12668  }
 12669  
 12670  func (s *snapmgrTestSuite) TestTransitionCoreTooEarly(c *C) {
 12671  	s.state.Lock()
 12672  	defer s.state.Unlock()
 12673  
 12674  	r := snapstatetest.MockDeviceModel(nil)
 12675  	defer r()
 12676  
 12677  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
 12678  		Active:   true,
 12679  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}},
 12680  		Current:  snap.R(1),
 12681  		SnapType: "os",
 12682  	})
 12683  
 12684  	s.state.Unlock()
 12685  	defer s.se.Stop()
 12686  	s.settle(c)
 12687  	s.state.Lock()
 12688  
 12689  	c.Check(s.state.Changes(), HasLen, 0)
 12690  	// not counted as a try
 12691  	var t time.Time
 12692  	err := s.state.Get("ubuntu-core-transition-last-retry-time", &t)
 12693  	c.Assert(err, Equals, state.ErrNoState)
 12694  }
 12695  
 12696  func (s *snapmgrTestSuite) TestTransitionCoreTimeLimitWorks(c *C) {
 12697  	s.state.Lock()
 12698  	defer s.state.Unlock()
 12699  
 12700  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
 12701  		Active:   true,
 12702  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}},
 12703  		Current:  snap.R(1),
 12704  		SnapType: "os",
 12705  	})
 12706  
 12707  	// tried 3h ago, no retry
 12708  	s.state.Set("ubuntu-core-transition-last-retry-time", time.Now().Add(-3*time.Hour))
 12709  
 12710  	s.state.Unlock()
 12711  	defer s.se.Stop()
 12712  	s.settle(c)
 12713  	s.state.Lock()
 12714  
 12715  	c.Check(s.state.Changes(), HasLen, 0)
 12716  
 12717  	// tried 7h ago, retry
 12718  	s.state.Set("ubuntu-core-transition-last-retry-time", time.Now().Add(-7*time.Hour))
 12719  
 12720  	s.state.Unlock()
 12721  	defer s.se.Stop()
 12722  	s.settle(c)
 12723  	s.state.Lock()
 12724  	c.Check(s.state.Changes(), HasLen, 1)
 12725  
 12726  	var t time.Time
 12727  	s.state.Get("ubuntu-core-transition-last-retry-time", &t)
 12728  	c.Assert(time.Now().Sub(t) < 2*time.Minute, Equals, true)
 12729  }
 12730  
 12731  func (s *snapmgrTestSuite) TestTransitionCoreNoOtherChanges(c *C) {
 12732  	s.state.Lock()
 12733  	defer s.state.Unlock()
 12734  
 12735  	snapstate.Set(s.state, "ubuntu-core", &snapstate.SnapState{
 12736  		Active:   true,
 12737  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1)}},
 12738  		Current:  snap.R(1),
 12739  		SnapType: "os",
 12740  	})
 12741  	chg := s.state.NewChange("unrelated-change", "unfinished change blocks core transition")
 12742  	chg.SetStatus(state.DoStatus)
 12743  
 12744  	s.state.Unlock()
 12745  	defer s.se.Stop()
 12746  	s.settle(c)
 12747  	s.state.Lock()
 12748  
 12749  	c.Check(s.state.Changes(), HasLen, 1)
 12750  	c.Check(s.state.Changes()[0].Kind(), Equals, "unrelated-change")
 12751  }
 12752  
 12753  func (s *snapmgrTestSuite) TestTransitionCoreBlocksOtherChanges(c *C) {
 12754  	s.state.Lock()
 12755  	defer s.state.Unlock()
 12756  
 12757  	// if we have a ubuntu-core -> core transition
 12758  	chg := s.state.NewChange("transition-ubuntu-core", "...")
 12759  	chg.SetStatus(state.DoStatus)
 12760  
 12761  	// other tasks block until the transition is done
 12762  	opts := &snapstate.RevisionOptions{Channel: "stable"}
 12763  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 12764  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
 12765  	c.Check(err, ErrorMatches, "ubuntu-core to core transition in progress, no other changes allowed until this is done")
 12766  
 12767  	// and when the transition is done, other tasks run
 12768  	chg.SetStatus(state.DoneStatus)
 12769  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 12770  	c.Check(err, IsNil)
 12771  	c.Check(ts, NotNil)
 12772  }
 12773  
 12774  func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesNotRunWithoutSnaps(c *C) {
 12775  	s.state.Lock()
 12776  	defer s.state.Unlock()
 12777  
 12778  	tr := config.NewTransaction(s.state)
 12779  	tr.Set("core", "experimental.snapd-snap", true)
 12780  	tr.Commit()
 12781  
 12782  	// no snaps installed on this system (e.g. fresh classic)
 12783  	snapstate.Set(s.state, "core", nil)
 12784  
 12785  	s.state.Unlock()
 12786  	defer s.se.Stop()
 12787  	s.settle(c)
 12788  	s.state.Lock()
 12789  
 12790  	c.Check(s.state.Changes(), HasLen, 0)
 12791  }
 12792  
 12793  func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesRunWithAnySnap(c *C) {
 12794  	s.state.Lock()
 12795  	defer s.state.Unlock()
 12796  
 12797  	tr := config.NewTransaction(s.state)
 12798  	tr.Set("core", "experimental.snapd-snap", true)
 12799  	tr.Commit()
 12800  
 12801  	// some snap installed on this system but no core
 12802  	snapstate.Set(s.state, "core", nil)
 12803  	snapstate.Set(s.state, "foo", &snapstate.SnapState{
 12804  		Active:   true,
 12805  		Sequence: []*snap.SideInfo{{RealName: "foo", SnapID: "foo-id", Revision: snap.R(1), Channel: "beta"}},
 12806  		Current:  snap.R(1),
 12807  	})
 12808  
 12809  	s.state.Unlock()
 12810  	defer s.se.Stop()
 12811  	s.settle(c)
 12812  	s.state.Lock()
 12813  
 12814  	c.Check(s.state.Changes(), HasLen, 1)
 12815  }
 12816  
 12817  func (s *snapmgrTestSuite) TestTransitionSnapdSnapDoesNotRunWhenNotEnabled(c *C) {
 12818  	s.state.Lock()
 12819  	defer s.state.Unlock()
 12820  
 12821  	snapstate.Set(s.state, "core", &snapstate.SnapState{
 12822  		Active:   true,
 12823  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "beta"}},
 12824  		Current:  snap.R(1),
 12825  		SnapType: "os",
 12826  	})
 12827  
 12828  	s.state.Unlock()
 12829  	defer s.se.Stop()
 12830  	s.settle(c)
 12831  	s.state.Lock()
 12832  
 12833  	c.Check(s.state.Changes(), HasLen, 0)
 12834  }
 12835  
 12836  func (s *snapmgrTestSuite) TestTransitionSnapdSnapStartsAutomaticallyWhenEnabled(c *C) {
 12837  	s.state.Lock()
 12838  	defer s.state.Unlock()
 12839  
 12840  	snapstate.Set(s.state, "core", &snapstate.SnapState{
 12841  		Active:   true,
 12842  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "beta"}},
 12843  		Current:  snap.R(1),
 12844  		SnapType: "os",
 12845  	})
 12846  	tr := config.NewTransaction(s.state)
 12847  	tr.Set("core", "experimental.snapd-snap", true)
 12848  	tr.Commit()
 12849  
 12850  	s.state.Unlock()
 12851  	defer s.se.Stop()
 12852  	s.settle(c)
 12853  	s.state.Lock()
 12854  
 12855  	c.Check(s.state.Changes(), HasLen, 1)
 12856  	chg := s.state.Changes()[0]
 12857  	c.Check(chg.Kind(), Equals, "transition-to-snapd-snap")
 12858  	c.Assert(chg.Err(), IsNil)
 12859  	c.Assert(chg.IsReady(), Equals, true)
 12860  
 12861  	// snapd snap is instaleld from the default channel
 12862  	var snapst snapstate.SnapState
 12863  	snapstate.Get(s.state, "snapd", &snapst)
 12864  	c.Assert(snapst.Channel, Equals, "stable")
 12865  }
 12866  
 12867  func (s *snapmgrTestSuite) TestTransitionSnapdSnapWithCoreRunthrough(c *C) {
 12868  	s.state.Lock()
 12869  	defer s.state.Unlock()
 12870  
 12871  	snapstate.Set(s.state, "core", &snapstate.SnapState{
 12872  		Active:   true,
 12873  		Sequence: []*snap.SideInfo{{RealName: "corecore", SnapID: "core-snap-id", Revision: snap.R(1), Channel: "edge"}},
 12874  		Current:  snap.R(1),
 12875  		SnapType: "os",
 12876  		// TrackingChannel
 12877  		Channel: "beta",
 12878  	})
 12879  	tr := config.NewTransaction(s.state)
 12880  	tr.Set("core", "experimental.snapd-snap", true)
 12881  	tr.Commit()
 12882  
 12883  	s.state.Unlock()
 12884  	defer s.se.Stop()
 12885  	s.settle(c)
 12886  	s.state.Lock()
 12887  
 12888  	c.Assert(s.state.Changes(), HasLen, 1)
 12889  	chg := s.state.Changes()[0]
 12890  	c.Assert(chg.Kind(), Equals, "transition-to-snapd-snap")
 12891  	c.Assert(chg.Err(), IsNil)
 12892  	c.Assert(chg.IsReady(), Equals, true)
 12893  	c.Check(s.fakeStore.downloads, HasLen, 1)
 12894  	ts := state.NewTaskSet(chg.Tasks()...)
 12895  	verifyInstallTasks(c, noConfigure, 0, ts, s.state)
 12896  
 12897  	// ensure preferences from the core snap got transferred over
 12898  	var snapst snapstate.SnapState
 12899  	snapstate.Get(s.state, "snapd", &snapst)
 12900  	c.Assert(snapst.Channel, Equals, "beta")
 12901  }
 12902  
 12903  func (s *snapmgrTestSuite) TestTransitionSnapdSnapTimeLimitWorks(c *C) {
 12904  	s.state.Lock()
 12905  	defer s.state.Unlock()
 12906  
 12907  	tr := config.NewTransaction(s.state)
 12908  	tr.Set("core", "experimental.snapd-snap", true)
 12909  	tr.Commit()
 12910  
 12911  	// tried 3h ago, no retry
 12912  	s.state.Set("snapd-transition-last-retry-time", time.Now().Add(-3*time.Hour))
 12913  
 12914  	s.state.Unlock()
 12915  	defer s.se.Stop()
 12916  	s.settle(c)
 12917  	s.state.Lock()
 12918  
 12919  	c.Check(s.state.Changes(), HasLen, 0)
 12920  
 12921  	// tried 7h ago, retry
 12922  	s.state.Set("snapd-transition-last-retry-time", time.Now().Add(-7*time.Hour))
 12923  
 12924  	s.state.Unlock()
 12925  	defer s.se.Stop()
 12926  	s.settle(c)
 12927  	s.state.Lock()
 12928  	c.Check(s.state.Changes(), HasLen, 1)
 12929  
 12930  	var t time.Time
 12931  	s.state.Get("snapd-transition-last-retry-time", &t)
 12932  	c.Assert(time.Now().Sub(t) < 2*time.Minute, Equals, true)
 12933  }
 12934  
 12935  type unhappyStore struct {
 12936  	*fakeStore
 12937  }
 12938  
 12939  func (s unhappyStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, user *auth.UserState, opts *store.RefreshOptions) ([]*snap.Info, error) {
 12940  
 12941  	return nil, fmt.Errorf("a grumpy store")
 12942  }
 12943  
 12944  func (s *snapmgrTestSuite) TestTransitionSnapdSnapError(c *C) {
 12945  	s.state.Lock()
 12946  	defer s.state.Unlock()
 12947  
 12948  	snapstate.ReplaceStore(s.state, unhappyStore{fakeStore: s.fakeStore})
 12949  
 12950  	tr := config.NewTransaction(s.state)
 12951  	tr.Set("core", "experimental.snapd-snap", true)
 12952  	tr.Commit()
 12953  
 12954  	s.state.Unlock()
 12955  	defer s.se.Stop()
 12956  	err := s.o.Settle(5 * time.Second)
 12957  	c.Assert(err, ErrorMatches, `state ensure errors: \[a grumpy store\]`)
 12958  
 12959  	s.state.Lock()
 12960  	c.Check(s.state.Changes(), HasLen, 0)
 12961  
 12962  	// all the attempts were recorded
 12963  	var t time.Time
 12964  	s.state.Get("snapd-transition-last-retry-time", &t)
 12965  	c.Assert(time.Now().Sub(t) < 2*time.Minute, Equals, true)
 12966  
 12967  	var cnt int
 12968  	s.state.Get("snapd-transition-retry", &cnt)
 12969  	c.Assert(cnt, Equals, 1)
 12970  
 12971  	// the transition is not tried again (because of retry time)
 12972  	s.state.Unlock()
 12973  	err = s.o.Settle(5 * time.Second)
 12974  	c.Assert(err, IsNil)
 12975  	s.state.Lock()
 12976  
 12977  	s.state.Get("snapd-transition-retry", &cnt)
 12978  	c.Assert(cnt, Equals, 1)
 12979  }
 12980  
 12981  func (s *snapmgrTestSuite) TestTransitionSnapdSnapBlocksOtherChanges(c *C) {
 12982  	s.state.Lock()
 12983  	defer s.state.Unlock()
 12984  
 12985  	// if we have a snapd transition
 12986  	chg := s.state.NewChange("transition-to-snapd-snap", "...")
 12987  	chg.SetStatus(state.DoStatus)
 12988  
 12989  	// other tasks block until the transition is done
 12990  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
 12991  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
 12992  	c.Check(err, ErrorMatches, "transition to snapd snap in progress, no other changes allowed until this is done")
 12993  
 12994  	// and when the transition is done, other tasks run
 12995  	chg.SetStatus(state.DoneStatus)
 12996  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
 12997  	c.Check(err, IsNil)
 12998  	c.Check(ts, NotNil)
 12999  }
 13000  
 13001  func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsForUbuntuCore(c *C) {
 13002  	s.checkForceDevModeCleanupRuns(c, "ubuntu-core", true)
 13003  }
 13004  
 13005  func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsForCore(c *C) {
 13006  	s.checkForceDevModeCleanupRuns(c, "core", true)
 13007  }
 13008  
 13009  func (s *snapmgrTestSuite) TestForceDevModeCleanupSkipsRando(c *C) {
 13010  	s.checkForceDevModeCleanupRuns(c, "rando", false)
 13011  }
 13012  
 13013  func (s *snapmgrTestSuite) checkForceDevModeCleanupRuns(c *C, name string, shouldBeReset bool) {
 13014  	r := release.MockForcedDevmode(true)
 13015  	defer r()
 13016  	c.Assert(release.ReleaseInfo.ForceDevMode(), Equals, true)
 13017  
 13018  	s.state.Lock()
 13019  	defer s.state.Unlock()
 13020  
 13021  	snapstate.Set(s.state, name, &snapstate.SnapState{
 13022  		Active: true,
 13023  		Sequence: []*snap.SideInfo{{
 13024  			RealName: name,
 13025  			SnapID:   "id-id-id",
 13026  			Revision: snap.R(1)}},
 13027  		Current:  snap.R(1),
 13028  		SnapType: "os",
 13029  		Flags:    snapstate.Flags{DevMode: true},
 13030  	})
 13031  
 13032  	var snapst1 snapstate.SnapState
 13033  	// sanity check
 13034  	snapstate.Get(s.state, name, &snapst1)
 13035  	c.Assert(snapst1.DevMode, Equals, true)
 13036  
 13037  	s.state.Unlock()
 13038  	defer s.se.Stop()
 13039  	s.settle(c)
 13040  	s.state.Lock()
 13041  
 13042  	var snapst2 snapstate.SnapState
 13043  	snapstate.Get(s.state, name, &snapst2)
 13044  
 13045  	c.Check(snapst2.DevMode, Equals, !shouldBeReset)
 13046  
 13047  	var n int
 13048  	s.state.Get("fix-forced-devmode", &n)
 13049  	c.Check(n, Equals, 1)
 13050  }
 13051  
 13052  func (s *snapmgrTestSuite) TestForceDevModeCleanupRunsNoSnaps(c *C) {
 13053  	r := release.MockForcedDevmode(true)
 13054  	defer r()
 13055  	c.Assert(release.ReleaseInfo.ForceDevMode(), Equals, true)
 13056  
 13057  	defer s.se.Stop()
 13058  	s.settle(c)
 13059  	s.state.Lock()
 13060  	defer s.state.Unlock()
 13061  
 13062  	var n int
 13063  	s.state.Get("fix-forced-devmode", &n)
 13064  	c.Check(n, Equals, 1)
 13065  }
 13066  
 13067  func (s *snapmgrTestSuite) TestForceDevModeCleanupSkipsNonForcedOS(c *C) {
 13068  	r := release.MockForcedDevmode(false)
 13069  	defer r()
 13070  	c.Assert(release.ReleaseInfo.ForceDevMode(), Equals, false)
 13071  
 13072  	s.state.Lock()
 13073  	defer s.state.Unlock()
 13074  
 13075  	snapstate.Set(s.state, "core", &snapstate.SnapState{
 13076  		Active: true,
 13077  		Sequence: []*snap.SideInfo{{
 13078  			RealName: "core",
 13079  			SnapID:   "id-id-id",
 13080  			Revision: snap.R(1)}},
 13081  		Current:  snap.R(1),
 13082  		SnapType: "os",
 13083  		Flags:    snapstate.Flags{DevMode: true},
 13084  	})
 13085  
 13086  	var snapst1 snapstate.SnapState
 13087  	// sanity check
 13088  	snapstate.Get(s.state, "core", &snapst1)
 13089  	c.Assert(snapst1.DevMode, Equals, true)
 13090  
 13091  	s.state.Unlock()
 13092  	defer s.se.Stop()
 13093  	s.settle(c)
 13094  	s.state.Lock()
 13095  
 13096  	var snapst2 snapstate.SnapState
 13097  	snapstate.Get(s.state, "core", &snapst2)
 13098  
 13099  	// no change
 13100  	c.Check(snapst2.DevMode, Equals, true)
 13101  
 13102  	// not really run at all in fact
 13103  	var n int
 13104  	s.state.Get("fix-forced-devmode", &n)
 13105  	c.Check(n, Equals, 0)
 13106  }
 13107  
 13108  func (s *snapmgrTestSuite) TestEnsureAliasesV2(c *C) {
 13109  	s.state.Lock()
 13110  	defer s.state.Unlock()
 13111  
 13112  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
 13113  		switch info.InstanceName() {
 13114  		case "alias-snap":
 13115  			return map[string]string{
 13116  				"alias1": "cmd1",
 13117  				"alias2": "cmd2",
 13118  			}, nil
 13119  		}
 13120  		return nil, nil
 13121  	}
 13122  
 13123  	snapstate.Set(s.state, "core", nil)
 13124  	snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{
 13125  		Sequence: []*snap.SideInfo{
 13126  			{RealName: "alias-snap", Revision: snap.R(11)},
 13127  		},
 13128  		Current: snap.R(11),
 13129  		Active:  true,
 13130  	})
 13131  
 13132  	s.state.Set("aliases", map[string]map[string]string{
 13133  		"alias-snap": {
 13134  			"alias1": "auto",
 13135  		},
 13136  	})
 13137  
 13138  	s.state.Unlock()
 13139  	err := s.snapmgr.Ensure()
 13140  	s.state.Lock()
 13141  	c.Assert(err, IsNil)
 13142  
 13143  	var gone interface{}
 13144  	err = s.state.Get("aliases", &gone)
 13145  	c.Assert(err, Equals, state.ErrNoState)
 13146  
 13147  	var snapst snapstate.SnapState
 13148  	err = snapstate.Get(s.state, "alias-snap", &snapst)
 13149  	c.Assert(err, IsNil)
 13150  
 13151  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
 13152  	c.Check(snapst.AliasesPending, Equals, false)
 13153  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
 13154  		"alias1": {Auto: "cmd1"},
 13155  		"alias2": {Auto: "cmd2"},
 13156  	})
 13157  
 13158  	expected := fakeOps{
 13159  		{
 13160  			op:   "remove-snap-aliases",
 13161  			name: "alias-snap",
 13162  		},
 13163  		{
 13164  			op: "update-aliases",
 13165  			aliases: []*backend.Alias{
 13166  				{Name: "alias1", Target: "alias-snap.cmd1"},
 13167  				{Name: "alias2", Target: "alias-snap.cmd2"},
 13168  			},
 13169  		},
 13170  	}
 13171  	// start with an easier-to-read error if this fails:
 13172  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
 13173  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
 13174  }
 13175  
 13176  func (s *snapmgrTestSuite) TestEnsureAliasesV2SnapDisabled(c *C) {
 13177  	s.state.Lock()
 13178  	defer s.state.Unlock()
 13179  
 13180  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
 13181  		switch info.InstanceName() {
 13182  		case "alias-snap":
 13183  			return map[string]string{
 13184  				"alias1": "cmd1",
 13185  				"alias2": "cmd2",
 13186  			}, nil
 13187  		}
 13188  		return nil, nil
 13189  	}
 13190  
 13191  	snapstate.Set(s.state, "core", nil)
 13192  	snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{
 13193  		Sequence: []*snap.SideInfo{
 13194  			{RealName: "alias-snap", Revision: snap.R(11)},
 13195  		},
 13196  		Current: snap.R(11),
 13197  		Active:  false,
 13198  	})
 13199  
 13200  	s.state.Set("aliases", map[string]map[string]string{
 13201  		"alias-snap": {
 13202  			"alias1": "auto",
 13203  		},
 13204  	})
 13205  
 13206  	s.state.Unlock()
 13207  	err := s.snapmgr.Ensure()
 13208  	s.state.Lock()
 13209  	c.Assert(err, IsNil)
 13210  
 13211  	var gone interface{}
 13212  	err = s.state.Get("aliases", &gone)
 13213  	c.Assert(err, Equals, state.ErrNoState)
 13214  
 13215  	var snapst snapstate.SnapState
 13216  	err = snapstate.Get(s.state, "alias-snap", &snapst)
 13217  	c.Assert(err, IsNil)
 13218  
 13219  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
 13220  	c.Check(snapst.AliasesPending, Equals, true)
 13221  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
 13222  		"alias1": {Auto: "cmd1"},
 13223  		"alias2": {Auto: "cmd2"},
 13224  	})
 13225  
 13226  	expected := fakeOps{
 13227  		{
 13228  			op:   "remove-snap-aliases",
 13229  			name: "alias-snap",
 13230  		},
 13231  	}
 13232  	// start with an easier-to-read error if this fails:
 13233  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
 13234  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
 13235  }
 13236  
 13237  func (s *snapmgrTestSuite) TestEnsureAliasesV2MarkAliasTasksInError(c *C) {
 13238  	s.state.Lock()
 13239  	defer s.state.Unlock()
 13240  
 13241  	s.state.Set("aliases", map[string]map[string]string{
 13242  		"alias-snap": {
 13243  			"alias1": "auto",
 13244  		},
 13245  	})
 13246  
 13247  	// pending old alias task
 13248  	t := s.state.NewTask("alias", "...")
 13249  	t.Set("aliases", map[string]string{})
 13250  	chg := s.state.NewChange("alias chg", "...")
 13251  	chg.AddTask(t)
 13252  
 13253  	s.state.Unlock()
 13254  	err := s.snapmgr.Ensure()
 13255  	s.state.Lock()
 13256  	c.Assert(err, IsNil)
 13257  
 13258  	c.Check(chg.Status(), Equals, state.ErrorStatus)
 13259  	c.Check(chg.IsReady(), Equals, true)
 13260  	c.Check(t.Status(), Equals, state.ErrorStatus)
 13261  }
 13262  
 13263  func (s *snapmgrTestSuite) TestConflictMany(c *C) {
 13264  	s.state.Lock()
 13265  	defer s.state.Unlock()
 13266  
 13267  	for _, instanceName := range []string{"a-snap", "b-snap"} {
 13268  		snapstate.Set(s.state, instanceName, &snapstate.SnapState{
 13269  			Sequence: []*snap.SideInfo{
 13270  				{RealName: instanceName, Revision: snap.R(11)},
 13271  			},
 13272  			Current: snap.R(11),
 13273  			Active:  false,
 13274  		})
 13275  
 13276  		ts, err := snapstate.Enable(s.state, instanceName)
 13277  		c.Assert(err, IsNil)
 13278  		// need a change to make the tasks visible
 13279  		s.state.NewChange("enable", "...").AddAll(ts)
 13280  	}
 13281  
 13282  	// things that should be ok:
 13283  	for _, m := range [][]string{
 13284  		{}, //nothing
 13285  		{"c-snap"},
 13286  		{"c-snap", "d-snap", "e-snap", "f-snap"},
 13287  	} {
 13288  		c.Check(snapstate.CheckChangeConflictMany(s.state, m, ""), IsNil)
 13289  	}
 13290  
 13291  	// things that should not be ok:
 13292  	for _, m := range [][]string{
 13293  		{"a-snap"},
 13294  		{"a-snap", "b-snap"},
 13295  		{"a-snap", "c-snap"},
 13296  		{"b-snap", "c-snap"},
 13297  	} {
 13298  		err := snapstate.CheckChangeConflictMany(s.state, m, "")
 13299  		c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
 13300  		c.Check(err, ErrorMatches, `snap "[^"]*" has "enable" change in progress`)
 13301  	}
 13302  }
 13303  
 13304  func (s *snapmgrTestSuite) TestConflictManyRemodeling(c *C) {
 13305  	s.state.Lock()
 13306  	defer s.state.Unlock()
 13307  
 13308  	chg := s.state.NewChange("remodel", "...")
 13309  	chg.SetStatus(state.DoingStatus)
 13310  
 13311  	err := snapstate.CheckChangeConflictMany(s.state, []string{"a-snap"}, "")
 13312  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
 13313  	c.Check(err, ErrorMatches, `remodeling in progress, no other changes allowed until this is done`)
 13314  }
 13315  
 13316  func (s *snapmgrTestSuite) TestInstallWithoutCoreRunThrough1(c *C) {
 13317  	s.state.Lock()
 13318  	defer s.state.Unlock()
 13319  
 13320  	// pretend we don't have core
 13321  	snapstate.Set(s.state, "core", nil)
 13322  
 13323  	chg := s.state.NewChange("install", "install a snap on a system without core")
 13324  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
 13325  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 13326  	c.Assert(err, IsNil)
 13327  	chg.AddAll(ts)
 13328  
 13329  	s.state.Unlock()
 13330  	defer s.se.Stop()
 13331  	s.settle(c)
 13332  	s.state.Lock()
 13333  
 13334  	// ensure all our tasks ran
 13335  	c.Assert(chg.Err(), IsNil)
 13336  	c.Assert(chg.IsReady(), Equals, true)
 13337  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
 13338  		{
 13339  			macaroon: s.user.StoreMacaroon,
 13340  			name:     "core",
 13341  			target:   filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
 13342  		},
 13343  		{
 13344  			macaroon: s.user.StoreMacaroon,
 13345  			name:     "some-snap",
 13346  			target:   filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
 13347  		}})
 13348  	expected := fakeOps{
 13349  		// we check the snap
 13350  		{
 13351  			op:     "storesvc-snap-action",
 13352  			userID: 1,
 13353  		},
 13354  		{
 13355  			op: "storesvc-snap-action:action",
 13356  			action: store.SnapAction{
 13357  				Action:       "install",
 13358  				InstanceName: "some-snap",
 13359  				Revision:     snap.R(42),
 13360  			},
 13361  			revno:  snap.R(42),
 13362  			userID: 1,
 13363  		},
 13364  		// then we check core because its not installed already
 13365  		// and continue with that
 13366  		{
 13367  			op:     "storesvc-snap-action",
 13368  			userID: 1,
 13369  		},
 13370  		{
 13371  			op: "storesvc-snap-action:action",
 13372  			action: store.SnapAction{
 13373  				Action:       "install",
 13374  				InstanceName: "core",
 13375  				Channel:      "stable",
 13376  			},
 13377  			revno:  snap.R(11),
 13378  			userID: 1,
 13379  		},
 13380  		{
 13381  			op:   "storesvc-download",
 13382  			name: "core",
 13383  		},
 13384  		{
 13385  			op:    "validate-snap:Doing",
 13386  			name:  "core",
 13387  			revno: snap.R(11),
 13388  		},
 13389  		{
 13390  			op:  "current",
 13391  			old: "<no-current>",
 13392  		},
 13393  		{
 13394  			op:   "open-snap-file",
 13395  			path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
 13396  			sinfo: snap.SideInfo{
 13397  				RealName: "core",
 13398  				Channel:  "stable",
 13399  				SnapID:   "core-id",
 13400  				Revision: snap.R(11),
 13401  			},
 13402  		},
 13403  		{
 13404  			op:    "setup-snap",
 13405  			name:  "core",
 13406  			path:  filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
 13407  			revno: snap.R(11),
 13408  		},
 13409  		{
 13410  			op:   "copy-data",
 13411  			path: filepath.Join(dirs.SnapMountDir, "core/11"),
 13412  			old:  "<no-old>",
 13413  		},
 13414  		{
 13415  			op:    "setup-profiles:Doing",
 13416  			name:  "core",
 13417  			revno: snap.R(11),
 13418  		},
 13419  		{
 13420  			op: "candidate",
 13421  			sinfo: snap.SideInfo{
 13422  				RealName: "core",
 13423  				Channel:  "stable",
 13424  				SnapID:   "core-id",
 13425  				Revision: snap.R(11),
 13426  			},
 13427  		},
 13428  		{
 13429  			op:   "link-snap",
 13430  			path: filepath.Join(dirs.SnapMountDir, "core/11"),
 13431  		},
 13432  		{
 13433  			op:    "auto-connect:Doing",
 13434  			name:  "core",
 13435  			revno: snap.R(11),
 13436  		},
 13437  		{
 13438  			op: "update-aliases",
 13439  		},
 13440  		// after core is in place continue with the snap
 13441  		{
 13442  			op:   "storesvc-download",
 13443  			name: "some-snap",
 13444  		},
 13445  		{
 13446  			op:    "validate-snap:Doing",
 13447  			name:  "some-snap",
 13448  			revno: snap.R(42),
 13449  		},
 13450  		{
 13451  			op:  "current",
 13452  			old: "<no-current>",
 13453  		},
 13454  		{
 13455  			op:   "open-snap-file",
 13456  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
 13457  			sinfo: snap.SideInfo{
 13458  				RealName: "some-snap",
 13459  				SnapID:   "some-snap-id",
 13460  				Revision: snap.R(42),
 13461  			},
 13462  		},
 13463  		{
 13464  			op:    "setup-snap",
 13465  			name:  "some-snap",
 13466  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
 13467  			revno: snap.R(42),
 13468  		},
 13469  		{
 13470  			op:   "copy-data",
 13471  			path: filepath.Join(dirs.SnapMountDir, "some-snap/42"),
 13472  			old:  "<no-old>",
 13473  		},
 13474  		{
 13475  			op:    "setup-profiles:Doing",
 13476  			name:  "some-snap",
 13477  			revno: snap.R(42),
 13478  		},
 13479  		{
 13480  			op: "candidate",
 13481  			sinfo: snap.SideInfo{
 13482  				RealName: "some-snap",
 13483  				SnapID:   "some-snap-id",
 13484  				Revision: snap.R(42),
 13485  			},
 13486  		},
 13487  		{
 13488  			op:   "link-snap",
 13489  			path: filepath.Join(dirs.SnapMountDir, "some-snap/42"),
 13490  		},
 13491  		{
 13492  			op:    "auto-connect:Doing",
 13493  			name:  "some-snap",
 13494  			revno: snap.R(42),
 13495  		},
 13496  		{
 13497  			op: "update-aliases",
 13498  		},
 13499  		// cleanups order is random
 13500  		{
 13501  			op:    "cleanup-trash",
 13502  			name:  "core",
 13503  			revno: snap.R(42),
 13504  		},
 13505  		{
 13506  			op:    "cleanup-trash",
 13507  			name:  "some-snap",
 13508  			revno: snap.R(42),
 13509  		},
 13510  	}
 13511  	// start with an easier-to-read error if this fails:
 13512  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
 13513  	// compare the details without the cleanup tasks, the order is random
 13514  	// as they run in parallel
 13515  	opsLenWithoutCleanups := len(s.fakeBackend.ops) - 2
 13516  	c.Assert(s.fakeBackend.ops[:opsLenWithoutCleanups], DeepEquals, expected[:opsLenWithoutCleanups])
 13517  
 13518  	// verify core in the system state
 13519  	var snaps map[string]*snapstate.SnapState
 13520  	err = s.state.Get("snaps", &snaps)
 13521  	c.Assert(err, IsNil)
 13522  
 13523  	snapst := snaps["core"]
 13524  	c.Assert(snapst, NotNil)
 13525  	c.Assert(snapst.Active, Equals, true)
 13526  	c.Assert(snapst.Channel, Equals, "stable")
 13527  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
 13528  		RealName: "core",
 13529  		Channel:  "stable",
 13530  		SnapID:   "core-id",
 13531  		Revision: snap.R(11),
 13532  	})
 13533  }
 13534  
 13535  func (s *snapmgrTestSuite) TestInstallWithoutCoreTwoSnapsRunThrough(c *C) {
 13536  	s.state.Lock()
 13537  	defer s.state.Unlock()
 13538  
 13539  	restore := snapstate.MockPrerequisitesRetryTimeout(10 * time.Millisecond)
 13540  	defer restore()
 13541  
 13542  	// pretend we don't have core
 13543  	snapstate.Set(s.state, "core", nil)
 13544  
 13545  	chg1 := s.state.NewChange("install", "install snap 1")
 13546  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
 13547  	ts1, err := snapstate.Install(context.Background(), s.state, "snap1", opts, s.user.ID, snapstate.Flags{})
 13548  	c.Assert(err, IsNil)
 13549  	chg1.AddAll(ts1)
 13550  
 13551  	chg2 := s.state.NewChange("install", "install snap 2")
 13552  	opts = &snapstate.RevisionOptions{Channel: "some-other-channel", Revision: snap.R(21)}
 13553  	ts2, err := snapstate.Install(context.Background(), s.state, "snap2", opts, s.user.ID, snapstate.Flags{})
 13554  	c.Assert(err, IsNil)
 13555  	chg2.AddAll(ts2)
 13556  
 13557  	s.state.Unlock()
 13558  	defer s.se.Stop()
 13559  	s.settle(c)
 13560  	s.state.Lock()
 13561  
 13562  	// ensure all our tasks ran and core was only installed once
 13563  	c.Assert(chg1.Err(), IsNil)
 13564  	c.Assert(chg2.Err(), IsNil)
 13565  
 13566  	c.Assert(chg1.IsReady(), Equals, true)
 13567  	c.Assert(chg2.IsReady(), Equals, true)
 13568  
 13569  	// order in which the changes run is random
 13570  	if len(chg1.Tasks()) < len(chg2.Tasks()) {
 13571  		chg1, chg2 = chg2, chg1
 13572  	}
 13573  	c.Assert(taskKinds(chg1.Tasks()), HasLen, 28)
 13574  	c.Assert(taskKinds(chg2.Tasks()), HasLen, 14)
 13575  
 13576  	// FIXME: add helpers and do a DeepEquals here for the operations
 13577  }
 13578  
 13579  func (s *snapmgrTestSuite) TestInstallWithoutCoreTwoSnapsWithFailureRunThrough(c *C) {
 13580  	s.state.Lock()
 13581  	defer s.state.Unlock()
 13582  
 13583  	// slightly longer retry timeout to avoid deadlock when we
 13584  	// trigger a retry quickly that the link snap for core does
 13585  	// not have a chance to run
 13586  	restore := snapstate.MockPrerequisitesRetryTimeout(40 * time.Millisecond)
 13587  	defer restore()
 13588  
 13589  	defer s.se.Stop()
 13590  	// Two changes are created, the first will fails, the second will
 13591  	// be fine. The order of what change runs first is random, the
 13592  	// first change will also install core in its own lane. This test
 13593  	// ensures that core gets installed and there are no conflicts
 13594  	// even if core already got installed from the first change.
 13595  	//
 13596  	// It runs multiple times so that both possible cases get a chance
 13597  	// to run
 13598  	for i := 0; i < 5; i++ {
 13599  		// start clean
 13600  		snapstate.Set(s.state, "core", nil)
 13601  		snapstate.Set(s.state, "snap2", nil)
 13602  
 13603  		// chg1 has an error
 13604  		chg1 := s.state.NewChange("install", "install snap 1")
 13605  		opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
 13606  		ts1, err := snapstate.Install(context.Background(), s.state, "snap1", opts, s.user.ID, snapstate.Flags{})
 13607  		c.Assert(err, IsNil)
 13608  		chg1.AddAll(ts1)
 13609  
 13610  		tasks := ts1.Tasks()
 13611  		last := tasks[len(tasks)-1]
 13612  		terr := s.state.NewTask("error-trigger", "provoking total undo")
 13613  		terr.WaitFor(last)
 13614  		chg1.AddTask(terr)
 13615  
 13616  		// chg2 is good
 13617  		chg2 := s.state.NewChange("install", "install snap 2")
 13618  		opts = &snapstate.RevisionOptions{Channel: "some-other-channel", Revision: snap.R(21)}
 13619  		ts2, err := snapstate.Install(context.Background(), s.state, "snap2", opts, s.user.ID, snapstate.Flags{})
 13620  		c.Assert(err, IsNil)
 13621  		chg2.AddAll(ts2)
 13622  
 13623  		// we use our own settle as we need a bigger timeout
 13624  		s.state.Unlock()
 13625  		err = s.o.Settle(15 * time.Second)
 13626  		s.state.Lock()
 13627  		c.Assert(err, IsNil)
 13628  
 13629  		// ensure expected change states
 13630  		c.Check(chg1.Status(), Equals, state.ErrorStatus)
 13631  		c.Check(chg2.Status(), Equals, state.DoneStatus)
 13632  
 13633  		// ensure we have both core and snap2
 13634  		var snapst snapstate.SnapState
 13635  		err = snapstate.Get(s.state, "core", &snapst)
 13636  		c.Assert(err, IsNil)
 13637  		c.Assert(snapst.Active, Equals, true)
 13638  		c.Assert(snapst.Sequence, HasLen, 1)
 13639  		c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
 13640  			RealName: "core",
 13641  			SnapID:   "core-id",
 13642  			Channel:  "stable",
 13643  			Revision: snap.R(11),
 13644  		})
 13645  
 13646  		var snapst2 snapstate.SnapState
 13647  		err = snapstate.Get(s.state, "snap2", &snapst2)
 13648  		c.Assert(err, IsNil)
 13649  		c.Assert(snapst2.Active, Equals, true)
 13650  		c.Assert(snapst2.Sequence, HasLen, 1)
 13651  		c.Assert(snapst2.Sequence[0], DeepEquals, &snap.SideInfo{
 13652  			RealName: "snap2",
 13653  			SnapID:   "snap2-id",
 13654  			Channel:  "",
 13655  			Revision: snap.R(21),
 13656  		})
 13657  
 13658  	}
 13659  }
 13660  
 13661  type behindYourBackStore struct {
 13662  	*fakeStore
 13663  	state *state.State
 13664  
 13665  	coreInstallRequested bool
 13666  	coreInstalled        bool
 13667  	chg                  *state.Change
 13668  }
 13669  
 13670  func (s behindYourBackStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, user *auth.UserState, opts *store.RefreshOptions) ([]*snap.Info, error) {
 13671  	if len(actions) == 1 && actions[0].Action == "install" && actions[0].InstanceName == "core" {
 13672  		s.state.Lock()
 13673  		if !s.coreInstallRequested {
 13674  			s.coreInstallRequested = true
 13675  			snapsup := &snapstate.SnapSetup{
 13676  				SideInfo: &snap.SideInfo{
 13677  					RealName: "core",
 13678  				},
 13679  			}
 13680  			t := s.state.NewTask("prepare", "prepare core")
 13681  			t.Set("snap-setup", snapsup)
 13682  			s.chg = s.state.NewChange("install", "install core")
 13683  			s.chg.AddAll(state.NewTaskSet(t))
 13684  		}
 13685  		if s.chg != nil && !s.coreInstalled {
 13686  			// marks change ready but also
 13687  			// tasks need to also be marked cleaned
 13688  			for _, t := range s.chg.Tasks() {
 13689  				t.SetStatus(state.DoneStatus)
 13690  				t.SetClean()
 13691  			}
 13692  			snapstate.Set(s.state, "core", &snapstate.SnapState{
 13693  				Active: true,
 13694  				Sequence: []*snap.SideInfo{
 13695  					{RealName: "core", Revision: snap.R(1)},
 13696  				},
 13697  				Current:  snap.R(1),
 13698  				SnapType: "os",
 13699  			})
 13700  			s.coreInstalled = true
 13701  		}
 13702  		s.state.Unlock()
 13703  	}
 13704  
 13705  	return s.fakeStore.SnapAction(ctx, currentSnaps, actions, user, opts)
 13706  }
 13707  
 13708  // this test the scenario that some-snap gets installed and during the
 13709  // install (when unlocking for the store info call for core) an
 13710  // explicit "snap install core" happens. In this case the snapstate
 13711  // will return a change conflict. we handle this via a retry, ensure
 13712  // this is actually what happens.
 13713  func (s *snapmgrTestSuite) TestInstallWithoutCoreConflictingInstall(c *C) {
 13714  	s.state.Lock()
 13715  	defer s.state.Unlock()
 13716  
 13717  	restore := snapstate.MockPrerequisitesRetryTimeout(10 * time.Millisecond)
 13718  	defer restore()
 13719  
 13720  	snapstate.ReplaceStore(s.state, behindYourBackStore{fakeStore: s.fakeStore, state: s.state})
 13721  
 13722  	// pretend we don't have core
 13723  	snapstate.Set(s.state, "core", nil)
 13724  
 13725  	// now install a snap that will pull in core
 13726  	chg := s.state.NewChange("install", "install a snap on a system without core")
 13727  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
 13728  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 13729  	c.Assert(err, IsNil)
 13730  	chg.AddAll(ts)
 13731  
 13732  	prereq := ts.Tasks()[0]
 13733  	c.Assert(prereq.Kind(), Equals, "prerequisites")
 13734  	c.Check(prereq.AtTime().IsZero(), Equals, true)
 13735  
 13736  	s.state.Unlock()
 13737  	defer s.se.Stop()
 13738  
 13739  	// start running the change, this will trigger the
 13740  	// prerequisites task, which will trigger the install of core
 13741  	// and also call our mock store which will generate a parallel
 13742  	// change
 13743  	s.se.Ensure()
 13744  	s.se.Wait()
 13745  
 13746  	// change is not ready yet, because the prerequists triggered
 13747  	// a state.Retry{} because of the conflicting change
 13748  	c.Assert(chg.IsReady(), Equals, false)
 13749  	s.state.Lock()
 13750  	// marked for retry
 13751  	c.Check(prereq.AtTime().IsZero(), Equals, false)
 13752  	c.Check(prereq.Status().Ready(), Equals, false)
 13753  	s.state.Unlock()
 13754  
 13755  	// retry interval is 10ms so 20ms should be plenty of time
 13756  	time.Sleep(20 * time.Millisecond)
 13757  	s.settle(c)
 13758  	// chg got retried, core is now installed, things are good
 13759  	c.Assert(chg.IsReady(), Equals, true)
 13760  
 13761  	s.state.Lock()
 13762  
 13763  	// ensure all our tasks ran
 13764  	c.Assert(chg.Err(), IsNil)
 13765  	c.Assert(chg.IsReady(), Equals, true)
 13766  
 13767  	// verify core in the system state
 13768  	var snaps map[string]*snapstate.SnapState
 13769  	err = s.state.Get("snaps", &snaps)
 13770  	c.Assert(err, IsNil)
 13771  
 13772  	snapst := snaps["core"]
 13773  	c.Assert(snapst, NotNil)
 13774  	c.Assert(snapst.Active, Equals, true)
 13775  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
 13776  		RealName: "core",
 13777  		Revision: snap.R(1),
 13778  	})
 13779  
 13780  	snapst = snaps["some-snap"]
 13781  	c.Assert(snapst, NotNil)
 13782  	c.Assert(snapst.Active, Equals, true)
 13783  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
 13784  		RealName: "some-snap",
 13785  		SnapID:   "some-snap-id",
 13786  		Channel:  "some-channel",
 13787  		Revision: snap.R(11),
 13788  	})
 13789  }
 13790  
 13791  type contentStore struct {
 13792  	*fakeStore
 13793  	state *state.State
 13794  }
 13795  
 13796  func (s contentStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, user *auth.UserState, opts *store.RefreshOptions) ([]*snap.Info, error) {
 13797  	snaps, err := s.fakeStore.SnapAction(ctx, currentSnaps, actions, user, opts)
 13798  	if err != nil {
 13799  		return nil, err
 13800  	}
 13801  	if len(snaps) != 1 {
 13802  		panic("expected to be queried for install of only one snap at a time")
 13803  	}
 13804  	info := snaps[0]
 13805  	switch info.InstanceName() {
 13806  	case "snap-content-plug":
 13807  		info.Plugs = map[string]*snap.PlugInfo{
 13808  			"some-plug": {
 13809  				Snap:      info,
 13810  				Name:      "shared-content",
 13811  				Interface: "content",
 13812  				Attrs: map[string]interface{}{
 13813  					"default-provider": "snap-content-slot",
 13814  					"content":          "shared-content",
 13815  				},
 13816  			},
 13817  		}
 13818  	case "snap-content-plug-compat":
 13819  		info.Plugs = map[string]*snap.PlugInfo{
 13820  			"some-plug": {
 13821  				Snap:      info,
 13822  				Name:      "shared-content",
 13823  				Interface: "content",
 13824  				Attrs: map[string]interface{}{
 13825  					"default-provider": "snap-content-slot:some-slot",
 13826  					"content":          "shared-content",
 13827  				},
 13828  			},
 13829  		}
 13830  	case "snap-content-slot":
 13831  		info.Slots = map[string]*snap.SlotInfo{
 13832  			"some-slot": {
 13833  				Snap:      info,
 13834  				Name:      "shared-content",
 13835  				Interface: "content",
 13836  				Attrs: map[string]interface{}{
 13837  					"content": "shared-content",
 13838  				},
 13839  			},
 13840  		}
 13841  	case "snap-content-circular1":
 13842  		info.Plugs = map[string]*snap.PlugInfo{
 13843  			"circular-plug1": {
 13844  				Snap:      info,
 13845  				Name:      "circular-plug1",
 13846  				Interface: "content",
 13847  				Attrs: map[string]interface{}{
 13848  					"default-provider": "snap-content-circular2",
 13849  					"content":          "circular2",
 13850  				},
 13851  			},
 13852  		}
 13853  		info.Slots = map[string]*snap.SlotInfo{
 13854  			"circular-slot1": {
 13855  				Snap:      info,
 13856  				Name:      "circular-slot1",
 13857  				Interface: "content",
 13858  				Attrs: map[string]interface{}{
 13859  					"content": "circular1",
 13860  				},
 13861  			},
 13862  		}
 13863  	case "snap-content-circular2":
 13864  		info.Plugs = map[string]*snap.PlugInfo{
 13865  			"circular-plug2": {
 13866  				Snap:      info,
 13867  				Name:      "circular-plug2",
 13868  				Interface: "content",
 13869  				Attrs: map[string]interface{}{
 13870  					"default-provider": "snap-content-circular1",
 13871  					"content":          "circular2",
 13872  				},
 13873  			},
 13874  		}
 13875  		info.Slots = map[string]*snap.SlotInfo{
 13876  			"circular-slot2": {
 13877  				Snap:      info,
 13878  				Name:      "circular-slot2",
 13879  				Interface: "content",
 13880  				Attrs: map[string]interface{}{
 13881  					"content": "circular1",
 13882  				},
 13883  			},
 13884  		}
 13885  	}
 13886  
 13887  	return []*snap.Info{info}, err
 13888  }
 13889  
 13890  func (s *snapmgrTestSuite) TestInstallDefaultProviderRunThrough(c *C) {
 13891  	s.state.Lock()
 13892  	defer s.state.Unlock()
 13893  
 13894  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
 13895  
 13896  	repo := interfaces.NewRepository()
 13897  	ifacerepo.Replace(s.state, repo)
 13898  
 13899  	chg := s.state.NewChange("install", "install a snap")
 13900  	opts := &snapstate.RevisionOptions{Channel: "stable", Revision: snap.R(42)}
 13901  	ts, err := snapstate.Install(context.Background(), s.state, "snap-content-plug", opts, s.user.ID, snapstate.Flags{})
 13902  	c.Assert(err, IsNil)
 13903  	chg.AddAll(ts)
 13904  
 13905  	s.state.Unlock()
 13906  	defer s.se.Stop()
 13907  	s.settle(c)
 13908  	s.state.Lock()
 13909  
 13910  	// ensure all our tasks ran
 13911  	c.Assert(chg.Err(), IsNil)
 13912  	c.Assert(chg.IsReady(), Equals, true)
 13913  	expected := fakeOps{{
 13914  		op:     "storesvc-snap-action",
 13915  		userID: 1,
 13916  	}, {
 13917  		op: "storesvc-snap-action:action",
 13918  		action: store.SnapAction{
 13919  			Action:       "install",
 13920  			InstanceName: "snap-content-plug",
 13921  			Revision:     snap.R(42),
 13922  		},
 13923  		revno:  snap.R(42),
 13924  		userID: 1,
 13925  	}, {
 13926  		op:     "storesvc-snap-action",
 13927  		userID: 1,
 13928  	}, {
 13929  		op: "storesvc-snap-action:action",
 13930  		action: store.SnapAction{
 13931  			Action:       "install",
 13932  			InstanceName: "snap-content-slot",
 13933  			Channel:      "stable",
 13934  		},
 13935  		revno:  snap.R(11),
 13936  		userID: 1,
 13937  	}, {
 13938  		op:   "storesvc-download",
 13939  		name: "snap-content-slot",
 13940  	}, {
 13941  		op:    "validate-snap:Doing",
 13942  		name:  "snap-content-slot",
 13943  		revno: snap.R(11),
 13944  	}, {
 13945  		op:  "current",
 13946  		old: "<no-current>",
 13947  	}, {
 13948  		op:   "open-snap-file",
 13949  		path: filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap"),
 13950  		sinfo: snap.SideInfo{
 13951  			RealName: "snap-content-slot",
 13952  			Channel:  "stable",
 13953  			SnapID:   "snap-content-slot-id",
 13954  			Revision: snap.R(11),
 13955  		},
 13956  	}, {
 13957  		op:    "setup-snap",
 13958  		name:  "snap-content-slot",
 13959  		path:  filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap"),
 13960  		revno: snap.R(11),
 13961  	}, {
 13962  		op:   "copy-data",
 13963  		path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"),
 13964  		old:  "<no-old>",
 13965  	}, {
 13966  		op:    "setup-profiles:Doing",
 13967  		name:  "snap-content-slot",
 13968  		revno: snap.R(11),
 13969  	}, {
 13970  		op: "candidate",
 13971  		sinfo: snap.SideInfo{
 13972  			RealName: "snap-content-slot",
 13973  			Channel:  "stable",
 13974  			SnapID:   "snap-content-slot-id",
 13975  			Revision: snap.R(11),
 13976  		},
 13977  	}, {
 13978  		op:   "link-snap",
 13979  		path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"),
 13980  	}, {
 13981  		op:    "auto-connect:Doing",
 13982  		name:  "snap-content-slot",
 13983  		revno: snap.R(11),
 13984  	}, {
 13985  		op: "update-aliases",
 13986  	}, {
 13987  		op:   "storesvc-download",
 13988  		name: "snap-content-plug",
 13989  	}, {
 13990  		op:    "validate-snap:Doing",
 13991  		name:  "snap-content-plug",
 13992  		revno: snap.R(42),
 13993  	}, {
 13994  		op:  "current",
 13995  		old: "<no-current>",
 13996  	}, {
 13997  		op:   "open-snap-file",
 13998  		path: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_42.snap"),
 13999  		sinfo: snap.SideInfo{
 14000  			RealName: "snap-content-plug",
 14001  			SnapID:   "snap-content-plug-id",
 14002  			Revision: snap.R(42),
 14003  		},
 14004  	}, {
 14005  		op:    "setup-snap",
 14006  		name:  "snap-content-plug",
 14007  		path:  filepath.Join(dirs.SnapBlobDir, "snap-content-plug_42.snap"),
 14008  		revno: snap.R(42),
 14009  	}, {
 14010  		op:   "copy-data",
 14011  		path: filepath.Join(dirs.SnapMountDir, "snap-content-plug/42"),
 14012  		old:  "<no-old>",
 14013  	}, {
 14014  		op:    "setup-profiles:Doing",
 14015  		name:  "snap-content-plug",
 14016  		revno: snap.R(42),
 14017  	}, {
 14018  		op: "candidate",
 14019  		sinfo: snap.SideInfo{
 14020  			RealName: "snap-content-plug",
 14021  			SnapID:   "snap-content-plug-id",
 14022  			Revision: snap.R(42),
 14023  		},
 14024  	}, {
 14025  		op:   "link-snap",
 14026  		path: filepath.Join(dirs.SnapMountDir, "snap-content-plug/42"),
 14027  	}, {
 14028  		op:    "auto-connect:Doing",
 14029  		name:  "snap-content-plug",
 14030  		revno: snap.R(42),
 14031  	}, {
 14032  		op: "update-aliases",
 14033  	}, {
 14034  		op:    "cleanup-trash",
 14035  		name:  "snap-content-plug",
 14036  		revno: snap.R(42),
 14037  	}, {
 14038  		op:    "cleanup-trash",
 14039  		name:  "snap-content-slot",
 14040  		revno: snap.R(11),
 14041  	},
 14042  	}
 14043  	// snap and default provider are installed in parallel so we can't
 14044  	// do a simple c.Check(ops, DeepEquals, fakeOps{...})
 14045  	c.Check(len(s.fakeBackend.ops), Equals, len(expected))
 14046  	for _, op := range expected {
 14047  		c.Assert(s.fakeBackend.ops, testutil.DeepContains, op)
 14048  	}
 14049  	for _, op := range s.fakeBackend.ops {
 14050  		c.Assert(expected, testutil.DeepContains, op)
 14051  	}
 14052  }
 14053  
 14054  func (s *snapmgrTestSuite) TestInstallDefaultProviderCircular(c *C) {
 14055  	s.state.Lock()
 14056  	defer s.state.Unlock()
 14057  
 14058  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
 14059  
 14060  	repo := interfaces.NewRepository()
 14061  	ifacerepo.Replace(s.state, repo)
 14062  
 14063  	chg := s.state.NewChange("install", "install a snap")
 14064  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
 14065  	ts, err := snapstate.Install(context.Background(), s.state, "snap-content-circular1", opts, s.user.ID, snapstate.Flags{})
 14066  	c.Assert(err, IsNil)
 14067  	chg.AddAll(ts)
 14068  
 14069  	s.state.Unlock()
 14070  	defer s.se.Stop()
 14071  	s.settle(c)
 14072  	s.state.Lock()
 14073  
 14074  	// ensure all our tasks ran
 14075  	c.Assert(chg.Err(), IsNil)
 14076  	c.Assert(chg.IsReady(), Equals, true)
 14077  	// and both circular snaps got linked
 14078  	c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{
 14079  		op:   "link-snap",
 14080  		path: filepath.Join(dirs.SnapMountDir, "snap-content-circular1/42"),
 14081  	})
 14082  	c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{
 14083  		op:   "link-snap",
 14084  		path: filepath.Join(dirs.SnapMountDir, "snap-content-circular2/11"),
 14085  	})
 14086  }
 14087  
 14088  func (s *snapmgrTestSuite) TestInstallDefaultProviderCompat(c *C) {
 14089  	s.state.Lock()
 14090  	defer s.state.Unlock()
 14091  
 14092  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
 14093  
 14094  	repo := interfaces.NewRepository()
 14095  	ifacerepo.Replace(s.state, repo)
 14096  
 14097  	chg := s.state.NewChange("install", "install a snap")
 14098  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
 14099  	ts, err := snapstate.Install(context.Background(), s.state, "snap-content-plug-compat", opts, s.user.ID, snapstate.Flags{})
 14100  	c.Assert(err, IsNil)
 14101  	chg.AddAll(ts)
 14102  
 14103  	s.state.Unlock()
 14104  	defer s.se.Stop()
 14105  	s.settle(c)
 14106  	s.state.Lock()
 14107  
 14108  	// ensure all our tasks ran
 14109  	c.Assert(chg.Err(), IsNil)
 14110  	c.Assert(chg.IsReady(), Equals, true)
 14111  	// and both circular snaps got linked
 14112  	c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{
 14113  		op:   "link-snap",
 14114  		path: filepath.Join(dirs.SnapMountDir, "snap-content-plug-compat/42"),
 14115  	})
 14116  	c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{
 14117  		op:   "link-snap",
 14118  		path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"),
 14119  	})
 14120  }
 14121  
 14122  func (s *snapmgrTestSuite) TestSnapManagerLegacyRefreshSchedule(c *C) {
 14123  	s.state.Lock()
 14124  	defer s.state.Unlock()
 14125  
 14126  	for _, t := range []struct {
 14127  		in     string
 14128  		out    string
 14129  		legacy bool
 14130  	}{
 14131  		{"", snapstate.DefaultRefreshSchedule, false},
 14132  		{"invalid schedule", snapstate.DefaultRefreshSchedule, false},
 14133  		{"8:00-12:00", "8:00-12:00", true},
 14134  		// using the legacy configuration option with a new-style
 14135  		// refresh.timer string is rejected (i.e. the legacy parser is
 14136  		// used for the parsing)
 14137  		{"0:00~24:00/24", snapstate.DefaultRefreshSchedule, false},
 14138  	} {
 14139  		if t.in != "" {
 14140  			tr := config.NewTransaction(s.state)
 14141  			tr.Set("core", "refresh.timer", "")
 14142  			tr.Set("core", "refresh.schedule", t.in)
 14143  			tr.Commit()
 14144  		}
 14145  		scheduleStr, legacy, err := s.snapmgr.RefreshSchedule()
 14146  		c.Check(err, IsNil)
 14147  		c.Check(scheduleStr, Equals, t.out)
 14148  		c.Check(legacy, Equals, t.legacy)
 14149  	}
 14150  }
 14151  
 14152  func (s *snapmgrTestSuite) TestSnapManagerRefreshSchedule(c *C) {
 14153  	s.state.Lock()
 14154  	defer s.state.Unlock()
 14155  
 14156  	for _, t := range []struct {
 14157  		in  string
 14158  		out string
 14159  	}{
 14160  		{"", snapstate.DefaultRefreshSchedule},
 14161  		{"invalid schedule", snapstate.DefaultRefreshSchedule},
 14162  		{"8:00-12:00", "8:00-12:00"},
 14163  		// this is only valid under the new schedule parser
 14164  		{"9:00~15:00/2,,mon,20:00", "9:00~15:00/2,,mon,20:00"},
 14165  	} {
 14166  		if t.in != "" {
 14167  			tr := config.NewTransaction(s.state)
 14168  			tr.Set("core", "refresh.timer", t.in)
 14169  			tr.Commit()
 14170  		}
 14171  		scheduleStr, legacy, err := s.snapmgr.RefreshSchedule()
 14172  		c.Check(err, IsNil)
 14173  		c.Check(scheduleStr, Equals, t.out)
 14174  		c.Check(legacy, Equals, false)
 14175  	}
 14176  }
 14177  
 14178  func (s *snapmgrTestSuite) TestSideInfoPaid(c *C) {
 14179  	s.state.Lock()
 14180  	defer s.state.Unlock()
 14181  	opts := &snapstate.RevisionOptions{Channel: "channel-for-paid"}
 14182  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 14183  	c.Assert(err, IsNil)
 14184  
 14185  	chg := s.state.NewChange("install", "install paid snap")
 14186  	chg.AddAll(ts)
 14187  
 14188  	s.state.Unlock()
 14189  	defer s.se.Stop()
 14190  	s.settle(c)
 14191  	s.state.Lock()
 14192  
 14193  	// verify snap has paid sideinfo
 14194  	var snapst snapstate.SnapState
 14195  	err = snapstate.Get(s.state, "some-snap", &snapst)
 14196  	c.Assert(err, IsNil)
 14197  	c.Check(snapst.CurrentSideInfo().Paid, Equals, true)
 14198  	c.Check(snapst.CurrentSideInfo().Private, Equals, false)
 14199  }
 14200  
 14201  func (s *snapmgrTestSuite) TestSideInfoPrivate(c *C) {
 14202  	s.state.Lock()
 14203  	defer s.state.Unlock()
 14204  	opts := &snapstate.RevisionOptions{Channel: "channel-for-private"}
 14205  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 14206  	c.Assert(err, IsNil)
 14207  
 14208  	chg := s.state.NewChange("install", "install private snap")
 14209  	chg.AddAll(ts)
 14210  
 14211  	s.state.Unlock()
 14212  	defer s.se.Stop()
 14213  	s.settle(c)
 14214  	s.state.Lock()
 14215  
 14216  	// verify snap has private sideinfo
 14217  	var snapst snapstate.SnapState
 14218  	err = snapstate.Get(s.state, "some-snap", &snapst)
 14219  	c.Assert(err, IsNil)
 14220  	c.Check(snapst.CurrentSideInfo().Private, Equals, true)
 14221  	c.Check(snapst.CurrentSideInfo().Paid, Equals, false)
 14222  }
 14223  
 14224  func (s *snapmgrTestSuite) TestInstallPathWithLayoutsChecksFeatureFlag(c *C) {
 14225  	s.state.Lock()
 14226  	defer s.state.Unlock()
 14227  
 14228  	// When layouts are disabled we cannot install a local snap depending on the feature.
 14229  	tr := config.NewTransaction(s.state)
 14230  	tr.Set("core", "experimental.layouts", false)
 14231  	tr.Commit()
 14232  
 14233  	mockSnap := makeTestSnap(c, `name: some-snap
 14234  version: 1.0
 14235  layout:
 14236   /usr:
 14237    bind: $SNAP/usr
 14238  `)
 14239  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{})
 14240  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true")
 14241  
 14242  	// When layouts are enabled we can install a local snap depending on the feature.
 14243  	tr = config.NewTransaction(s.state)
 14244  	tr.Set("core", "experimental.layouts", true)
 14245  	tr.Commit()
 14246  
 14247  	_, _, err = snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{})
 14248  	c.Assert(err, IsNil)
 14249  }
 14250  
 14251  func (s *snapmgrTestSuite) TestInstallPathWithMetadataChannelSwitchKernel(c *C) {
 14252  	// use the real thing for this one
 14253  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
 14254  
 14255  	s.state.Lock()
 14256  	defer s.state.Unlock()
 14257  
 14258  	// snapd cannot be installed unless the model uses a base snap
 14259  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
 14260  	defer r()
 14261  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
 14262  		Sequence: []*snap.SideInfo{
 14263  			{RealName: "kernel", Revision: snap.R(11)},
 14264  		},
 14265  		Channel: "18/stable",
 14266  		Current: snap.R(11),
 14267  		Active:  true,
 14268  	})
 14269  
 14270  	someSnap := makeTestSnap(c, `name: kernel
 14271  version: 1.0`)
 14272  	si := &snap.SideInfo{
 14273  		RealName: "kernel",
 14274  		SnapID:   "kernel-id",
 14275  		Revision: snap.R(42),
 14276  		Channel:  "some-channel",
 14277  	}
 14278  	_, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true})
 14279  	c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "some-channel"`)
 14280  }
 14281  
 14282  func (s *snapmgrTestSuite) TestInstallPathWithMetadataChannelSwitchGadget(c *C) {
 14283  	// use the real thing for this one
 14284  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
 14285  
 14286  	s.state.Lock()
 14287  	defer s.state.Unlock()
 14288  
 14289  	// snapd cannot be installed unless the model uses a base snap
 14290  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
 14291  	defer r()
 14292  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
 14293  		Sequence: []*snap.SideInfo{
 14294  			{RealName: "brand-gadget", Revision: snap.R(11)},
 14295  		},
 14296  		Channel: "18/stable",
 14297  		Current: snap.R(11),
 14298  		Active:  true,
 14299  	})
 14300  
 14301  	someSnap := makeTestSnap(c, `name: brand-gadget
 14302  version: 1.0`)
 14303  	si := &snap.SideInfo{
 14304  		RealName: "brand-gadget",
 14305  		SnapID:   "brand-gadget-id",
 14306  		Revision: snap.R(42),
 14307  		Channel:  "some-channel",
 14308  	}
 14309  	_, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true})
 14310  	c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "some-channel"`)
 14311  }
 14312  
 14313  func (s *snapmgrTestSuite) TestInstallLayoutsChecksFeatureFlag(c *C) {
 14314  	s.state.Lock()
 14315  	defer s.state.Unlock()
 14316  
 14317  	// Layouts are now enabled by default.
 14318  	opts := &snapstate.RevisionOptions{Channel: "channel-for-layout"}
 14319  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 14320  	c.Assert(err, IsNil)
 14321  
 14322  	// Layouts can be explicitly disabled.
 14323  	tr := config.NewTransaction(s.state)
 14324  	tr.Set("core", "experimental.layouts", false)
 14325  	tr.Commit()
 14326  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 14327  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true")
 14328  
 14329  	// Layouts can be explicitly enabled.
 14330  	tr = config.NewTransaction(s.state)
 14331  	tr.Set("core", "experimental.layouts", true)
 14332  	tr.Commit()
 14333  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 14334  	c.Assert(err, IsNil)
 14335  
 14336  	// The default empty value now means "enabled".
 14337  	tr = config.NewTransaction(s.state)
 14338  	tr.Set("core", "experimental.layouts", "")
 14339  	tr.Commit()
 14340  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 14341  	c.Assert(err, IsNil)
 14342  
 14343  	// Layouts are enabled when the controlling flag is reset to nil.
 14344  	tr = config.NewTransaction(s.state)
 14345  	tr.Set("core", "experimental.layouts", nil)
 14346  	tr.Commit()
 14347  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 14348  	c.Assert(err, IsNil)
 14349  
 14350  }
 14351  
 14352  func (s *snapmgrTestSuite) TestUpdateLayoutsChecksFeatureFlag(c *C) {
 14353  	s.state.Lock()
 14354  	defer s.state.Unlock()
 14355  
 14356  	// When layouts are disabled we cannot refresh to a snap depending on the feature.
 14357  	tr := config.NewTransaction(s.state)
 14358  	tr.Set("core", "experimental.layouts", false)
 14359  	tr.Commit()
 14360  
 14361  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 14362  		Active: true,
 14363  		Sequence: []*snap.SideInfo{
 14364  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
 14365  		},
 14366  		Current:  snap.R(1),
 14367  		SnapType: "app",
 14368  	})
 14369  
 14370  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-layout"}, s.user.ID, snapstate.Flags{})
 14371  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true")
 14372  
 14373  	// When layouts are enabled we can refresh to a snap depending on the feature.
 14374  	tr = config.NewTransaction(s.state)
 14375  	tr.Set("core", "experimental.layouts", true)
 14376  	tr.Commit()
 14377  
 14378  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-layout"}, s.user.ID, snapstate.Flags{})
 14379  	c.Assert(err, IsNil)
 14380  }
 14381  
 14382  func (s *snapmgrTestSuite) TestUpdateManyExplicitLayoutsChecksFeatureFlag(c *C) {
 14383  	s.state.Lock()
 14384  	defer s.state.Unlock()
 14385  
 14386  	// When layouts are disabled we cannot refresh multiple snaps if one of them depends on the feature.
 14387  	tr := config.NewTransaction(s.state)
 14388  	tr.Set("core", "experimental.layouts", false)
 14389  	tr.Commit()
 14390  
 14391  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 14392  		Active:  true,
 14393  		Channel: "channel-for-layout",
 14394  		Sequence: []*snap.SideInfo{
 14395  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
 14396  		},
 14397  		Current:  snap.R(1),
 14398  		SnapType: "app",
 14399  	})
 14400  
 14401  	_, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
 14402  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true")
 14403  
 14404  	// When layouts are enabled we can refresh multiple snaps if one of them depends on the feature.
 14405  	tr = config.NewTransaction(s.state)
 14406  	tr.Set("core", "experimental.layouts", true)
 14407  	tr.Commit()
 14408  
 14409  	_, _, err = snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
 14410  	c.Assert(err, IsNil)
 14411  }
 14412  
 14413  func (s *snapmgrTestSuite) TestUpdateManyLayoutsChecksFeatureFlag(c *C) {
 14414  	s.state.Lock()
 14415  	defer s.state.Unlock()
 14416  
 14417  	// When layouts are disabled we cannot refresh multiple snaps if one of them depends on the feature.
 14418  	tr := config.NewTransaction(s.state)
 14419  	tr.Set("core", "experimental.layouts", false)
 14420  	tr.Commit()
 14421  
 14422  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 14423  		Active:  true,
 14424  		Channel: "channel-for-layout",
 14425  		Sequence: []*snap.SideInfo{
 14426  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
 14427  		},
 14428  		Current:  snap.R(1),
 14429  		SnapType: "app",
 14430  	})
 14431  
 14432  	refreshes, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil)
 14433  	c.Assert(err, IsNil)
 14434  	c.Assert(refreshes, HasLen, 0)
 14435  
 14436  	// When layouts are enabled we can refresh multiple snaps if one of them depends on the feature.
 14437  	tr = config.NewTransaction(s.state)
 14438  	tr.Set("core", "experimental.layouts", true)
 14439  	tr.Commit()
 14440  
 14441  	refreshes, _, err = snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil)
 14442  	c.Assert(err, IsNil)
 14443  	c.Assert(refreshes, DeepEquals, []string{"some-snap"})
 14444  }
 14445  
 14446  func (s *snapmgrTestSuite) TestUpdateFailsEarlyOnEpochMismatch(c *C) {
 14447  	s.state.Lock()
 14448  	defer s.state.Unlock()
 14449  
 14450  	snapstate.Set(s.state, "some-epoch-snap", &snapstate.SnapState{
 14451  		Active: true,
 14452  		Sequence: []*snap.SideInfo{
 14453  			{RealName: "some-epoch-snap", SnapID: "some-epoch-snap-id", Revision: snap.R(1)},
 14454  		},
 14455  		Current:  snap.R(1),
 14456  		SnapType: "app",
 14457  	})
 14458  
 14459  	_, err := snapstate.Update(s.state, "some-epoch-snap", nil, 0, snapstate.Flags{})
 14460  	c.Assert(err, ErrorMatches, `cannot refresh "some-epoch-snap" to new revision 11 with epoch 42, because it can't read the current epoch of 13`)
 14461  }
 14462  
 14463  func (s *snapmgrTestSuite) TestParallelInstallValidateFeatureFlag(c *C) {
 14464  	s.state.Lock()
 14465  	defer s.state.Unlock()
 14466  
 14467  	info := &snap.Info{
 14468  		InstanceKey: "foo",
 14469  	}
 14470  
 14471  	err := snapstate.ValidateFeatureFlags(s.state, info)
 14472  	c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`)
 14473  
 14474  	// various forms of disabling
 14475  	tr := config.NewTransaction(s.state)
 14476  	tr.Set("core", "experimental.parallel-instances", false)
 14477  	tr.Commit()
 14478  
 14479  	err = snapstate.ValidateFeatureFlags(s.state, info)
 14480  	c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`)
 14481  
 14482  	tr = config.NewTransaction(s.state)
 14483  	tr.Set("core", "experimental.parallel-instances", "")
 14484  	tr.Commit()
 14485  
 14486  	err = snapstate.ValidateFeatureFlags(s.state, info)
 14487  	c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`)
 14488  
 14489  	tr = config.NewTransaction(s.state)
 14490  	tr.Set("core", "experimental.parallel-instances", nil)
 14491  	tr.Commit()
 14492  
 14493  	err = snapstate.ValidateFeatureFlags(s.state, info)
 14494  	c.Assert(err, ErrorMatches, `experimental feature disabled - test it by setting 'experimental.parallel-instances' to true`)
 14495  
 14496  	tr = config.NewTransaction(s.state)
 14497  	tr.Set("core", "experimental.parallel-instances", "veryfalse")
 14498  	tr.Commit()
 14499  
 14500  	err = snapstate.ValidateFeatureFlags(s.state, info)
 14501  	c.Assert(err, ErrorMatches, `parallel-instances can only be set to 'true' or 'false', got "veryfalse"`)
 14502  
 14503  	// enable parallel instances
 14504  	tr = config.NewTransaction(s.state)
 14505  	tr.Set("core", "experimental.parallel-instances", true)
 14506  	tr.Commit()
 14507  
 14508  	err = snapstate.ValidateFeatureFlags(s.state, info)
 14509  	c.Assert(err, IsNil)
 14510  }
 14511  
 14512  func (s *snapmgrTestSuite) TestParallelInstallInstallPathExperimentalSwitch(c *C) {
 14513  	s.state.Lock()
 14514  	defer s.state.Unlock()
 14515  
 14516  	mockSnap := makeTestSnap(c, `name: some-snap
 14517  version: 1.0
 14518  `)
 14519  	si := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}
 14520  	_, _, err := snapstate.InstallPath(s.state, si, mockSnap, "some-snap_foo", "", snapstate.Flags{})
 14521  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.parallel-instances' to true")
 14522  
 14523  	// enable parallel instances
 14524  	tr := config.NewTransaction(s.state)
 14525  	tr.Set("core", "experimental.parallel-instances", true)
 14526  	tr.Commit()
 14527  
 14528  	_, _, err = snapstate.InstallPath(s.state, si, mockSnap, "some-snap_foo", "", snapstate.Flags{})
 14529  	c.Assert(err, IsNil)
 14530  }
 14531  
 14532  func (s *snapmgrTestSuite) TestInjectTasks(c *C) {
 14533  	s.state.Lock()
 14534  	defer s.state.Unlock()
 14535  
 14536  	lane := s.state.NewLane()
 14537  
 14538  	// setup main task and two tasks waiting for it; all part of same change
 14539  	chg := s.state.NewChange("change", "")
 14540  	t0 := s.state.NewTask("task1", "")
 14541  	chg.AddTask(t0)
 14542  	t0.JoinLane(lane)
 14543  	t01 := s.state.NewTask("task1-1", "")
 14544  	t01.WaitFor(t0)
 14545  	chg.AddTask(t01)
 14546  	t02 := s.state.NewTask("task1-2", "")
 14547  	t02.WaitFor(t0)
 14548  	chg.AddTask(t02)
 14549  
 14550  	// setup extra tasks
 14551  	t1 := s.state.NewTask("task2", "")
 14552  	t2 := s.state.NewTask("task3", "")
 14553  	ts := state.NewTaskSet(t1, t2)
 14554  
 14555  	snapstate.InjectTasks(t0, ts)
 14556  
 14557  	// verify that extra tasks are now part of same change
 14558  	c.Assert(t1.Change().ID(), Equals, t0.Change().ID())
 14559  	c.Assert(t2.Change().ID(), Equals, t0.Change().ID())
 14560  	c.Assert(t1.Change().ID(), Equals, chg.ID())
 14561  
 14562  	c.Assert(t1.Lanes(), DeepEquals, []int{lane})
 14563  
 14564  	// verify that halt tasks of the main task now wait for extra tasks
 14565  	c.Assert(t1.HaltTasks(), HasLen, 2)
 14566  	c.Assert(t2.HaltTasks(), HasLen, 2)
 14567  	c.Assert(t1.HaltTasks(), DeepEquals, t2.HaltTasks())
 14568  
 14569  	ids := []string{t1.HaltTasks()[0].Kind(), t2.HaltTasks()[1].Kind()}
 14570  	sort.Strings(ids)
 14571  	c.Assert(ids, DeepEquals, []string{"task1-1", "task1-2"})
 14572  
 14573  	// verify that extra tasks wait for the main task
 14574  	c.Assert(t1.WaitTasks(), HasLen, 1)
 14575  	c.Assert(t1.WaitTasks()[0].Kind(), Equals, "task1")
 14576  	c.Assert(t2.WaitTasks(), HasLen, 1)
 14577  	c.Assert(t2.WaitTasks()[0].Kind(), Equals, "task1")
 14578  }
 14579  
 14580  func (s *snapmgrTestSuite) TestInjectTasksWithNullChange(c *C) {
 14581  	s.state.Lock()
 14582  	defer s.state.Unlock()
 14583  
 14584  	// setup main task
 14585  	t0 := s.state.NewTask("task1", "")
 14586  	t01 := s.state.NewTask("task1-1", "")
 14587  	t01.WaitFor(t0)
 14588  
 14589  	// setup extra task
 14590  	t1 := s.state.NewTask("task2", "")
 14591  	ts := state.NewTaskSet(t1)
 14592  
 14593  	snapstate.InjectTasks(t0, ts)
 14594  
 14595  	c.Assert(t1.Lanes(), DeepEquals, []int{0})
 14596  
 14597  	// verify that halt tasks of the main task now wait for extra tasks
 14598  	c.Assert(t1.HaltTasks(), HasLen, 1)
 14599  	c.Assert(t1.HaltTasks()[0].Kind(), Equals, "task1-1")
 14600  }
 14601  
 14602  func hasConfigureTask(ts *state.TaskSet) bool {
 14603  	for _, tk := range taskKinds(ts.Tasks()) {
 14604  		if tk == "run-hook[configure]" {
 14605  			return true
 14606  		}
 14607  	}
 14608  	return false
 14609  }
 14610  
 14611  func (s *snapmgrTestSuite) TestNoConfigureForBasesTask(c *C) {
 14612  	s.state.Lock()
 14613  	defer s.state.Unlock()
 14614  
 14615  	// normal snaps get a configure task
 14616  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
 14617  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
 14618  	c.Assert(err, IsNil)
 14619  	c.Check(hasConfigureTask(ts), Equals, true)
 14620  
 14621  	// but bases do not for install
 14622  	ts, err = snapstate.Install(context.Background(), s.state, "some-base", opts, s.user.ID, snapstate.Flags{})
 14623  	c.Assert(err, IsNil)
 14624  	c.Check(hasConfigureTask(ts), Equals, false)
 14625  
 14626  	// or for refresh
 14627  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
 14628  		Active:   true,
 14629  		Channel:  "edge",
 14630  		Sequence: []*snap.SideInfo{{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)}},
 14631  		Current:  snap.R(1),
 14632  		SnapType: "base",
 14633  	})
 14634  	ts, err = snapstate.Update(s.state, "some-base", nil, s.user.ID, snapstate.Flags{})
 14635  	c.Assert(err, IsNil)
 14636  	c.Check(hasConfigureTask(ts), Equals, false)
 14637  }
 14638  
 14639  func (s *snapmgrTestSuite) TestNoSnapdSnapOnCoreWithoutBase(c *C) {
 14640  	s.state.Lock()
 14641  	defer s.state.Unlock()
 14642  	r := release.MockOnClassic(false)
 14643  	defer r()
 14644  
 14645  	// but snapd do not for install
 14646  	_, err := snapstate.Install(context.Background(), s.state, "snapd", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
 14647  	c.Assert(err, ErrorMatches, "cannot install snapd snap on a model without a base snap yet")
 14648  }
 14649  
 14650  func (s *snapmgrTestSuite) TestNoSnapdSnapOnSystemsWithoutBaseOnUbuntuCore(c *C) {
 14651  	s.state.Lock()
 14652  	defer s.state.Unlock()
 14653  	r := release.MockOnClassic(false)
 14654  	defer r()
 14655  
 14656  	// it is not possible to opt-into the snapd snap on core yet
 14657  	tr := config.NewTransaction(s.state)
 14658  	tr.Set("core", "experimental.snapd-snap", true)
 14659  	tr.Commit()
 14660  
 14661  	// but snapd do not for install
 14662  	_, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{})
 14663  	c.Assert(err, ErrorMatches, "cannot install snapd snap on a model without a base snap yet")
 14664  }
 14665  
 14666  func (s *snapmgrTestSuite) TestNoSnapdSnapOnSystemsWithoutBaseButOption(c *C) {
 14667  	s.state.Lock()
 14668  	defer s.state.Unlock()
 14669  
 14670  	tr := config.NewTransaction(s.state)
 14671  	tr.Set("core", "experimental.snapd-snap", true)
 14672  	tr.Commit()
 14673  
 14674  	_, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{})
 14675  	c.Assert(err, IsNil)
 14676  }
 14677  
 14678  func (s *snapmgrTestSuite) TestNoConfigureForSnapdSnap(c *C) {
 14679  	s.state.Lock()
 14680  	defer s.state.Unlock()
 14681  
 14682  	restore := snap.MockSnapdSnapID("snapd-id")
 14683  	defer restore()
 14684  
 14685  	// snapd cannot be installed unless the model uses a base snap
 14686  	r := snapstatetest.MockDeviceModel(ModelWithBase("core18"))
 14687  	defer r()
 14688  
 14689  	// but snapd do not for install
 14690  	ts, err := snapstate.Install(context.Background(), s.state, "snapd", nil, s.user.ID, snapstate.Flags{})
 14691  	c.Assert(err, IsNil)
 14692  	c.Check(hasConfigureTask(ts), Equals, false)
 14693  
 14694  	// or for refresh
 14695  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
 14696  		Active:   true,
 14697  		Channel:  "edge",
 14698  		Sequence: []*snap.SideInfo{{RealName: "snapd", SnapID: "snapd-id", Revision: snap.R(1)}},
 14699  		Current:  snap.R(1),
 14700  		SnapType: "app",
 14701  	})
 14702  	ts, err = snapstate.Update(s.state, "snapd", nil, s.user.ID, snapstate.Flags{})
 14703  	c.Assert(err, IsNil)
 14704  	c.Check(hasConfigureTask(ts), Equals, false)
 14705  
 14706  }
 14707  
 14708  func (s snapmgrTestSuite) TestCanLoadOldSnapSetupWithoutType(c *C) {
 14709  	// ensure we don't crash when loading a SnapSetup json without
 14710  	// a type set
 14711  	oldSnapSetup := []byte(`{
 14712   "snap-path":"/some/path",
 14713   "side-info": {
 14714      "channel": "edge",
 14715      "name": "some-snap",
 14716      "revision": "1",
 14717      "snap-id": "some-snap-id"
 14718   }
 14719  }`)
 14720  	var snapsup snapstate.SnapSetup
 14721  	err := json.Unmarshal(oldSnapSetup, &snapsup)
 14722  	c.Assert(err, IsNil)
 14723  	c.Check(snapsup.SnapPath, Equals, "/some/path")
 14724  	c.Check(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
 14725  		Channel:  "edge",
 14726  		RealName: "some-snap",
 14727  		Revision: snap.R(1),
 14728  		SnapID:   "some-snap-id",
 14729  	})
 14730  	c.Check(snapsup.Type, Equals, snap.Type(""))
 14731  }
 14732  
 14733  func (s snapmgrTestSuite) TestHasOtherInstances(c *C) {
 14734  	s.state.Lock()
 14735  	defer s.state.Unlock()
 14736  
 14737  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
 14738  		Active: true,
 14739  		Sequence: []*snap.SideInfo{
 14740  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
 14741  		},
 14742  		Current:  snap.R(1),
 14743  		SnapType: "app",
 14744  	})
 14745  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
 14746  		Active: true,
 14747  		Sequence: []*snap.SideInfo{
 14748  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
 14749  		},
 14750  		Current:     snap.R(3),
 14751  		SnapType:    "app",
 14752  		InstanceKey: "instance",
 14753  	})
 14754  	snapstate.Set(s.state, "some-other-snap", &snapstate.SnapState{
 14755  		Active: true,
 14756  		Sequence: []*snap.SideInfo{
 14757  			{RealName: "some-other-snap", SnapID: "some-other-snap-id", Revision: snap.R(1)},
 14758  		},
 14759  		Current:  snap.R(1),
 14760  		SnapType: "app",
 14761  	})
 14762  
 14763  	other, err := snapstate.HasOtherInstances(s.state, "some-snap")
 14764  	c.Assert(err, IsNil)
 14765  	c.Assert(other, Equals, true)
 14766  	other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance")
 14767  	c.Assert(err, IsNil)
 14768  	c.Assert(other, Equals, true)
 14769  	other, err = snapstate.HasOtherInstances(s.state, "some-other-snap")
 14770  	c.Assert(err, IsNil)
 14771  	c.Assert(other, Equals, false)
 14772  	// other snaps like only looks at the name of the refence snap
 14773  	other, err = snapstate.HasOtherInstances(s.state, "some-other-snap_instance")
 14774  	c.Assert(err, IsNil)
 14775  	c.Assert(other, Equals, true)
 14776  
 14777  	// remove the snap without instance key
 14778  	snapstate.Set(s.state, "some-snap", nil)
 14779  	// some-snap_instance is like some-snap
 14780  	other, err = snapstate.HasOtherInstances(s.state, "some-snap")
 14781  	c.Assert(err, IsNil)
 14782  	c.Assert(other, Equals, true)
 14783  	other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance")
 14784  	c.Assert(err, IsNil)
 14785  	c.Assert(other, Equals, false)
 14786  
 14787  	// add another snap with instance key
 14788  	snapstate.Set(s.state, "some-snap_other", &snapstate.SnapState{
 14789  		Active: true,
 14790  		Sequence: []*snap.SideInfo{
 14791  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
 14792  		},
 14793  		Current:     snap.R(3),
 14794  		SnapType:    "app",
 14795  		InstanceKey: "other",
 14796  	})
 14797  	other, err = snapstate.HasOtherInstances(s.state, "some-snap")
 14798  	c.Assert(err, IsNil)
 14799  	c.Assert(other, Equals, true)
 14800  	other, err = snapstate.HasOtherInstances(s.state, "some-snap_instance")
 14801  	c.Assert(err, IsNil)
 14802  	c.Assert(other, Equals, true)
 14803  }
 14804  
 14805  func (s *snapmgrTestSuite) TestRequestSalt(c *C) {
 14806  	si := snap.SideInfo{
 14807  		RealName: "other-snap",
 14808  		Revision: snap.R(7),
 14809  		SnapID:   "other-snap-id",
 14810  	}
 14811  	s.state.Lock()
 14812  	defer s.state.Unlock()
 14813  
 14814  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
 14815  		Active:   true,
 14816  		Sequence: []*snap.SideInfo{&si},
 14817  		Current:  si.Revision,
 14818  		SnapType: "app",
 14819  	})
 14820  	snapstate.Set(s.state, "other-snap_instance", &snapstate.SnapState{
 14821  		Active:      true,
 14822  		Sequence:    []*snap.SideInfo{&si},
 14823  		Current:     si.Revision,
 14824  		SnapType:    "app",
 14825  		InstanceKey: "instance",
 14826  	})
 14827  
 14828  	// clear request-salt to have it generated
 14829  	s.state.Set("refresh-privacy-key", nil)
 14830  
 14831  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
 14832  	c.Assert(err, ErrorMatches, "internal error: request salt is unset")
 14833  
 14834  	s.state.Set("refresh-privacy-key", "privacy-key")
 14835  
 14836  	chg := s.state.NewChange("install", "install a snap")
 14837  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
 14838  	c.Assert(err, IsNil)
 14839  	chg.AddAll(ts)
 14840  
 14841  	s.state.Unlock()
 14842  	defer s.se.Stop()
 14843  	s.settle(c)
 14844  	s.state.Lock()
 14845  
 14846  	c.Assert(len(s.fakeBackend.ops) >= 1, Equals, true)
 14847  	storeAction := s.fakeBackend.ops[0]
 14848  	c.Assert(storeAction.op, Equals, "storesvc-snap-action")
 14849  	c.Assert(storeAction.curSnaps, HasLen, 2)
 14850  	c.Assert(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true)
 14851  }
 14852  
 14853  type canDisableSuite struct{}
 14854  
 14855  var _ = Suite(&canDisableSuite{})
 14856  
 14857  func (s *canDisableSuite) TestCanDisable(c *C) {
 14858  	for _, tt := range []struct {
 14859  		typ        snap.Type
 14860  		canDisable bool
 14861  	}{
 14862  		{snap.TypeApp, true},
 14863  		{snap.TypeGadget, false},
 14864  		{snap.TypeKernel, false},
 14865  		{snap.TypeOS, false},
 14866  	} {
 14867  		info := &snap.Info{SnapType: tt.typ}
 14868  		c.Check(snapstate.CanDisable(info), Equals, tt.canDisable)
 14869  	}
 14870  }
 14871  
 14872  func (s *snapmgrTestSuite) TestGadgetConnections(c *C) {
 14873  	r := release.MockOnClassic(false)
 14874  	defer r()
 14875  
 14876  	// using MockSnap, we want to read the bits on disk
 14877  	snapstate.MockSnapReadInfo(snap.ReadInfo)
 14878  
 14879  	deviceCtxNoGadget := deviceWithoutGadgetContext()
 14880  	deviceCtx := deviceWithGadgetContext("the-gadget")
 14881  
 14882  	s.state.Lock()
 14883  	defer s.state.Unlock()
 14884  
 14885  	_, err := snapstate.GadgetConnections(s.state, deviceCtxNoGadget)
 14886  	c.Assert(err, Equals, state.ErrNoState)
 14887  
 14888  	_, err = snapstate.GadgetConnections(s.state, deviceCtx)
 14889  	c.Assert(err, Equals, state.ErrNoState)
 14890  
 14891  	s.prepareGadget(c, `
 14892  connections:
 14893    - plug: snap1idididididididididididididi:plug
 14894      slot: snap2idididididididididididididi:slot
 14895  `)
 14896  
 14897  	conns, err := snapstate.GadgetConnections(s.state, deviceCtx)
 14898  	c.Assert(err, IsNil)
 14899  	c.Check(conns, DeepEquals, []gadget.Connection{
 14900  		{Plug: gadget.ConnectionPlug{SnapID: "snap1idididididididididididididi", Plug: "plug"}, Slot: gadget.ConnectionSlot{SnapID: "snap2idididididididididididididi", Slot: "slot"}}})
 14901  }
 14902  
 14903  func (s *snapmgrTestSuite) TestSnapManagerCanStandby(c *C) {
 14904  	s.state.Lock()
 14905  	defer s.state.Unlock()
 14906  
 14907  	// no snaps -> can standby
 14908  	s.state.Set("snaps", nil)
 14909  	c.Assert(s.snapmgr.CanStandby(), Equals, true)
 14910  
 14911  	// snaps installed -> can *not* standby
 14912  	snapstate.Set(s.state, "core", &snapstate.SnapState{
 14913  		Active: true,
 14914  		Sequence: []*snap.SideInfo{
 14915  			{RealName: "core", Revision: snap.R(1)},
 14916  		},
 14917  		Current:  snap.R(1),
 14918  		SnapType: "os",
 14919  	})
 14920  	c.Assert(s.snapmgr.CanStandby(), Equals, false)
 14921  }
 14922  
 14923  func (s *snapmgrTestSuite) TestResolveChannelPinnedTrack(c *C) {
 14924  	for _, tc := range []struct {
 14925  		snap        string
 14926  		new         string
 14927  		exp         string
 14928  		kernelTrack string
 14929  		gadgetTrack string
 14930  		err         string
 14931  	}{
 14932  		// neither kernel nor gadget
 14933  		{snap: "some-snap"},
 14934  		{snap: "some-snap", new: "stable", exp: "stable"},
 14935  		{snap: "some-snap", new: "foo/stable", exp: "foo/stable"},
 14936  		{snap: "some-snap", new: "stable/with-branch", exp: "stable/with-branch"},
 14937  		{snap: "some-snap", new: "supertrack/stable", exp: "supertrack/stable"},
 14938  		{snap: "some-snap", new: "supertrack/stable/with-branch", exp: "supertrack/stable/with-branch"},
 14939  		// kernel or gadget snap set, but unrelated snap
 14940  		{snap: "some-snap", new: "stable", exp: "stable", kernelTrack: "18"},
 14941  		{snap: "some-snap", new: "foo/stable", exp: "foo/stable", kernelTrack: "18"},
 14942  		{snap: "some-snap", new: "foo/stable", exp: "foo/stable", gadgetTrack: "18"},
 14943  		// no pinned track
 14944  		{snap: "kernel", new: "latest/stable", exp: "latest/stable"},
 14945  		{snap: "kernel", new: "stable", exp: "stable"},
 14946  		{snap: "brand-gadget", new: "stable", exp: "stable"},
 14947  		// not a risk only request
 14948  		{snap: "kernel", new: "", kernelTrack: "18"},
 14949  		{snap: "brand-gadget", new: "", gadgetTrack: "18"},
 14950  		{snap: "kernel", new: "latest/stable", kernelTrack: "18", err: "cannot switch from kernel track.*"},
 14951  		{snap: "kernel", new: "latest/stable/hotfix-123", kernelTrack: "18", err: "cannot switch from kernel track.*"},
 14952  		{snap: "kernel", new: "foo/stable", kernelTrack: "18", err: "cannot switch from kernel track.*"},
 14953  		{snap: "brand-gadget", new: "foo/stable", exp: "18/stable", gadgetTrack: "18", err: "cannot switch from gadget track.*"},
 14954  		{snap: "kernel", new: "18/stable", exp: "18/stable", kernelTrack: "18"},
 14955  		{snap: "kernel", new: "18/stable", exp: "18/stable"},
 14956  		{snap: "brand-gadget", new: "18/stable", exp: "18/stable", gadgetTrack: "18"},
 14957  		{snap: "brand-gadget", new: "18/stable", exp: "18/stable"},
 14958  		// risk/branch within a track
 14959  		{snap: "kernel", new: "stable/hotfix-123", exp: "18/stable/hotfix-123", kernelTrack: "18"},
 14960  		{snap: "kernel", new: "18/stable/hotfix-123", exp: "18/stable/hotfix-123", kernelTrack: "18"},
 14961  		// risk only defaults to pinned gadget track
 14962  		{snap: "brand-gadget", new: "stable", exp: "17/stable", gadgetTrack: "17"},
 14963  		{snap: "brand-gadget", new: "edge", exp: "17/edge", gadgetTrack: "17"},
 14964  		// risk only defaults to pinned kernel track
 14965  		{snap: "kernel", new: "stable", exp: "17/stable", kernelTrack: "17"},
 14966  		{snap: "kernel", new: "edge", exp: "17/edge", kernelTrack: "17"},
 14967  	} {
 14968  		c.Logf("tc: %+v", tc)
 14969  		if tc.kernelTrack != "" && tc.gadgetTrack != "" {
 14970  			c.Fatalf("setting both kernel and gadget tracks is not supported by the test")
 14971  		}
 14972  		var model *asserts.Model
 14973  		switch {
 14974  		case tc.kernelTrack != "":
 14975  			model = ModelWithKernelTrack(tc.kernelTrack)
 14976  		case tc.gadgetTrack != "":
 14977  			model = ModelWithGadgetTrack(tc.gadgetTrack)
 14978  		default:
 14979  			model = DefaultModel()
 14980  		}
 14981  		deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
 14982  		s.state.Lock()
 14983  		ch, err := snapstate.ResolveChannel(s.state, tc.snap, tc.new, deviceCtx)
 14984  		s.state.Unlock()
 14985  		if tc.err != "" {
 14986  			c.Check(err, ErrorMatches, tc.err)
 14987  		} else {
 14988  			c.Check(err, IsNil)
 14989  			c.Check(ch, Equals, tc.exp)
 14990  		}
 14991  	}
 14992  }
 14993  
 14994  func (s *snapmgrTestSuite) TestInstallValidatesInstanceNames(c *C) {
 14995  	s.state.Lock()
 14996  	defer s.state.Unlock()
 14997  
 14998  	_, err := snapstate.Install(context.Background(), s.state, "foo--invalid", nil, 0, snapstate.Flags{})
 14999  	c.Assert(err, ErrorMatches, `invalid instance name: invalid snap name: "foo--invalid"`)
 15000  
 15001  	_, err = snapstate.Install(context.Background(), s.state, "foo_123_456", nil, 0, snapstate.Flags{})
 15002  	c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`)
 15003  
 15004  	_, _, err = snapstate.InstallMany(s.state, []string{"foo--invalid"}, 0)
 15005  	c.Assert(err, ErrorMatches, `invalid instance name: invalid snap name: "foo--invalid"`)
 15006  
 15007  	_, _, err = snapstate.InstallMany(s.state, []string{"foo_123_456"}, 0)
 15008  	c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`)
 15009  
 15010  	mockSnap := makeTestSnap(c, `name: some-snap
 15011  version: 1.0
 15012  epoch: 1*
 15013  `)
 15014  	si := snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}
 15015  	_, _, err = snapstate.InstallPath(s.state, &si, mockSnap, "some-snap_123_456", "", snapstate.Flags{})
 15016  	c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`)
 15017  }
 15018  
 15019  func (s *snapmgrTestSuite) TestGadgetUpdateTaskAddedOnInstall(c *C) {
 15020  	restore := release.MockOnClassic(false)
 15021  	defer restore()
 15022  
 15023  	s.state.Lock()
 15024  	defer s.state.Unlock()
 15025  
 15026  	// task added on install
 15027  	ts, err := snapstate.Install(context.Background(), s.state, "brand-gadget", nil, 0, snapstate.Flags{})
 15028  	c.Assert(err, IsNil)
 15029  
 15030  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
 15031  	verifyInstallTasks(c, updatesGadget, 0, ts, s.state)
 15032  }
 15033  
 15034  func (s *snapmgrTestSuite) TestGadgetUpdateTaskAddedOnRefresh(c *C) {
 15035  	restore := release.MockOnClassic(false)
 15036  	defer restore()
 15037  
 15038  	s.state.Lock()
 15039  	defer s.state.Unlock()
 15040  
 15041  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
 15042  		Active: true,
 15043  		Sequence: []*snap.SideInfo{
 15044  			{RealName: "brand-gadget", SnapID: "brand-gadget-id", Revision: snap.R(1)},
 15045  		},
 15046  		Current:  snap.R(1),
 15047  		SnapType: "gadget",
 15048  	})
 15049  
 15050  	// and on update
 15051  	ts, err := snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{}, 0, snapstate.Flags{})
 15052  	c.Assert(err, IsNil)
 15053  
 15054  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
 15055  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh|updatesGadget, 0, ts, s.state)
 15056  
 15057  }
 15058  
 15059  func (s *snapmgrTestSuite) TestForSnapSetupResetsFlags(c *C) {
 15060  	flags := snapstate.Flags{
 15061  		DevMode:          true,
 15062  		JailMode:         true,
 15063  		Classic:          true,
 15064  		TryMode:          true,
 15065  		Revert:           true,
 15066  		RemoveSnapPath:   true,
 15067  		IgnoreValidation: true,
 15068  		Required:         true,
 15069  		SkipConfigure:    true,
 15070  		Unaliased:        true,
 15071  		Amend:            true,
 15072  		IsAutoRefresh:    true,
 15073  		NoReRefresh:      true,
 15074  		RequireTypeBase:  true,
 15075  	}
 15076  	flags = flags.ForSnapSetup()
 15077  
 15078  	// certain flags get reset, others are not touched
 15079  	c.Check(flags, DeepEquals, snapstate.Flags{
 15080  		DevMode:          true,
 15081  		JailMode:         true,
 15082  		Classic:          true,
 15083  		TryMode:          true,
 15084  		Revert:           true,
 15085  		RemoveSnapPath:   true,
 15086  		IgnoreValidation: true,
 15087  		Required:         true,
 15088  		SkipConfigure:    false,
 15089  		Unaliased:        true,
 15090  		Amend:            true,
 15091  		IsAutoRefresh:    true,
 15092  		NoReRefresh:      false,
 15093  		RequireTypeBase:  false,
 15094  	})
 15095  }