github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/overlord/snapstate/snapstate_test.go (about)

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