github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/snapstate/snapstate_test.go (about)

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