github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/overlord/snapstate/snapstate_test.go (about)

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