github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/snapstate/snapstate_test.go (about)

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