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

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2020 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  	"errors"
    25  	"fmt"
    26  	"os"
    27  	"path/filepath"
    28  	"sort"
    29  
    30  	. "gopkg.in/check.v1"
    31  	"gopkg.in/tomb.v2"
    32  
    33  	"github.com/snapcore/snapd/dirs"
    34  	"github.com/snapcore/snapd/interfaces"
    35  	"github.com/snapcore/snapd/overlord/auth"
    36  	"github.com/snapcore/snapd/overlord/configstate/config"
    37  	"github.com/snapcore/snapd/overlord/ifacestate/ifacerepo"
    38  	"github.com/snapcore/snapd/overlord/snapstate"
    39  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    40  	"github.com/snapcore/snapd/overlord/state"
    41  	"github.com/snapcore/snapd/release"
    42  	"github.com/snapcore/snapd/snap"
    43  	"github.com/snapcore/snapd/snap/snaptest"
    44  	"github.com/snapcore/snapd/store"
    45  	"github.com/snapcore/snapd/testutil"
    46  
    47  	// So it registers Configure.
    48  	_ "github.com/snapcore/snapd/overlord/configstate"
    49  )
    50  
    51  func verifyUpdateTasks(c *C, opts, discards int, ts *state.TaskSet, st *state.State) {
    52  	kinds := taskKinds(ts.Tasks())
    53  
    54  	expected := []string{
    55  		"prerequisites",
    56  		"download-snap",
    57  		"validate-snap",
    58  		"mount-snap",
    59  	}
    60  	expected = append(expected, "run-hook[pre-refresh]")
    61  	if opts&unlinkBefore != 0 {
    62  		expected = append(expected,
    63  			"stop-snap-services",
    64  		)
    65  	}
    66  	if opts&unlinkBefore != 0 {
    67  		expected = append(expected,
    68  			"remove-aliases",
    69  			"unlink-current-snap",
    70  		)
    71  	}
    72  	if opts&updatesGadget != 0 {
    73  		expected = append(expected, "update-gadget-assets")
    74  	}
    75  	expected = append(expected,
    76  		"copy-snap-data",
    77  		"setup-profiles",
    78  		"link-snap",
    79  	)
    80  	if opts&maybeCore != 0 {
    81  		expected = append(expected, "setup-profiles")
    82  	}
    83  	expected = append(expected,
    84  		"auto-connect",
    85  		"set-auto-aliases",
    86  		"setup-aliases",
    87  		"run-hook[post-refresh]",
    88  		"start-snap-services")
    89  
    90  	c.Assert(ts.Tasks()[len(expected)-2].Summary(), Matches, `Run post-refresh hook of .*`)
    91  	for i := 0; i < discards; i++ {
    92  		expected = append(expected,
    93  			"clear-snap",
    94  			"discard-snap",
    95  		)
    96  	}
    97  	if opts&cleanupAfter != 0 {
    98  		expected = append(expected,
    99  			"cleanup",
   100  		)
   101  	}
   102  	expected = append(expected,
   103  		"run-hook[configure]",
   104  		"run-hook[check-health]",
   105  	)
   106  	if opts&doesReRefresh != 0 {
   107  		expected = append(expected, "check-rerefresh")
   108  	}
   109  
   110  	c.Assert(kinds, DeepEquals, expected)
   111  }
   112  
   113  func (s *snapmgrTestSuite) TestUpdateDoesGC(c *C) {
   114  	s.state.Lock()
   115  	defer s.state.Unlock()
   116  	restore := release.MockOnClassic(false)
   117  	defer restore()
   118  
   119  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   120  		Active: true,
   121  		Sequence: []*snap.SideInfo{
   122  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   123  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
   124  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
   125  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
   126  		},
   127  		Current:  snap.R(4),
   128  		SnapType: "app",
   129  	})
   130  
   131  	chg := s.state.NewChange("update", "update a snap")
   132  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
   133  	c.Assert(err, IsNil)
   134  	chg.AddAll(ts)
   135  
   136  	s.state.Unlock()
   137  	defer s.se.Stop()
   138  	s.settle(c)
   139  	s.state.Lock()
   140  
   141  	// ensure garbage collection runs as the last tasks
   142  	expectedTail := fakeOps{
   143  		{
   144  			op:   "link-snap",
   145  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
   146  		},
   147  		{
   148  			op:    "auto-connect:Doing",
   149  			name:  "some-snap",
   150  			revno: snap.R(11),
   151  		},
   152  		{
   153  			op: "update-aliases",
   154  		},
   155  		{
   156  			op:   "remove-snap-data",
   157  			path: filepath.Join(dirs.SnapMountDir, "some-snap/1"),
   158  		},
   159  		{
   160  			op:    "remove-snap-files",
   161  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/1"),
   162  			stype: "app",
   163  		},
   164  		{
   165  			op:   "remove-snap-data",
   166  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
   167  		},
   168  		{
   169  			op:    "remove-snap-files",
   170  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/2"),
   171  			stype: "app",
   172  		},
   173  		{
   174  			op:    "cleanup-trash",
   175  			name:  "some-snap",
   176  			revno: snap.R(11),
   177  		},
   178  	}
   179  
   180  	opsTail := s.fakeBackend.ops[len(s.fakeBackend.ops)-len(expectedTail):]
   181  	c.Assert(opsTail.Ops(), DeepEquals, expectedTail.Ops())
   182  	c.Check(opsTail, DeepEquals, expectedTail)
   183  }
   184  
   185  func (s *snapmgrTestSuite) TestUpdateScenarios(c *C) {
   186  	// TODO: also use channel-for-7 or equiv to check updates that are switches
   187  	for k, t := range switchScenarios {
   188  		s.testUpdateScenario(c, k, t)
   189  	}
   190  }
   191  
   192  func (s *snapmgrTestSuite) testUpdateScenario(c *C, desc string, t switchScenario) {
   193  	// reset
   194  	s.fakeBackend.ops = nil
   195  
   196  	comment := Commentf("%q (%+v)", desc, t)
   197  	si := snap.SideInfo{
   198  		RealName: "some-snap",
   199  		Revision: snap.R(7),
   200  		Channel:  t.chanFrom,
   201  		SnapID:   "some-snap-id",
   202  	}
   203  
   204  	s.state.Lock()
   205  	defer s.state.Unlock()
   206  
   207  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   208  		Sequence:        []*snap.SideInfo{&si},
   209  		Active:          true,
   210  		Current:         si.Revision,
   211  		TrackingChannel: t.chanFrom,
   212  		CohortKey:       t.cohFrom,
   213  	})
   214  
   215  	chg := s.state.NewChange("update-snap", t.summary)
   216  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{
   217  		Channel:     t.chanTo,
   218  		CohortKey:   t.cohTo,
   219  		LeaveCohort: t.cohFrom != "" && t.cohTo == "",
   220  	}, 0, snapstate.Flags{})
   221  	c.Assert(err, IsNil, comment)
   222  	chg.AddAll(ts)
   223  
   224  	s.state.Unlock()
   225  	s.settle(c)
   226  	s.state.Lock()
   227  
   228  	// switch is not really really doing anything backend related
   229  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, []string{
   230  		"storesvc-snap-action",
   231  		"storesvc-snap-action:action",
   232  		"storesvc-download",
   233  		"validate-snap:Doing",
   234  		"current",
   235  		"open-snap-file",
   236  		"setup-snap",
   237  		"remove-snap-aliases",
   238  		"unlink-snap",
   239  		"copy-data",
   240  		"setup-profiles:Doing",
   241  		"candidate",
   242  		"link-snap",
   243  		"auto-connect:Doing",
   244  		"update-aliases",
   245  		"cleanup-trash",
   246  	}, comment)
   247  
   248  	expectedChanTo := t.chanTo
   249  	if t.chanTo == "" {
   250  		expectedChanTo = t.chanFrom
   251  	}
   252  	expectedCohTo := t.cohTo
   253  
   254  	// ensure the desired channel/cohort has changed
   255  	var snapst snapstate.SnapState
   256  	err = snapstate.Get(s.state, "some-snap", &snapst)
   257  	c.Assert(err, IsNil, comment)
   258  	c.Assert(snapst.TrackingChannel, Equals, expectedChanTo, comment)
   259  	c.Assert(snapst.CohortKey, Equals, expectedCohTo, comment)
   260  
   261  	// ensure the current info *has* changed
   262  	info, err := snapst.CurrentInfo()
   263  	c.Assert(err, IsNil, comment)
   264  	c.Assert(info.Channel, Equals, expectedChanTo, comment)
   265  }
   266  
   267  func (s *snapmgrTestSuite) TestUpdateTasksWithOldCurrent(c *C) {
   268  	s.state.Lock()
   269  	defer s.state.Unlock()
   270  	restore := release.MockOnClassic(false)
   271  	defer restore()
   272  
   273  	si1 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}
   274  	si2 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}
   275  	si3 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}
   276  	si4 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}
   277  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   278  		Active:          true,
   279  		TrackingChannel: "latest/edge",
   280  		Sequence:        []*snap.SideInfo{si1, si2, si3, si4},
   281  		Current:         snap.R(2),
   282  		SnapType:        "app",
   283  	})
   284  
   285  	// run the update
   286  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
   287  	c.Assert(err, IsNil)
   288  
   289  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 2, ts, s.state)
   290  
   291  	// and ensure that it will remove the revisions after "current"
   292  	// (si3, si4)
   293  	var snapsup snapstate.SnapSetup
   294  	tasks := ts.Tasks()
   295  
   296  	i := len(tasks) - 8
   297  	c.Check(tasks[i].Kind(), Equals, "clear-snap")
   298  	err = tasks[i].Get("snap-setup", &snapsup)
   299  	c.Assert(err, IsNil)
   300  	c.Check(snapsup.Revision(), Equals, si3.Revision)
   301  
   302  	i = len(tasks) - 6
   303  	c.Check(tasks[i].Kind(), Equals, "clear-snap")
   304  	err = tasks[i].Get("snap-setup", &snapsup)
   305  	c.Assert(err, IsNil)
   306  	c.Check(snapsup.Revision(), Equals, si4.Revision)
   307  }
   308  
   309  func (s *snapmgrTestSuite) TestUpdateCanDoBackwards(c *C) {
   310  	si7 := snap.SideInfo{
   311  		RealName: "some-snap",
   312  		SnapID:   "some-snap-id",
   313  		Revision: snap.R(7),
   314  	}
   315  	si11 := snap.SideInfo{
   316  		RealName: "some-snap",
   317  		SnapID:   "some-snap-id",
   318  		Revision: snap.R(11),
   319  	}
   320  
   321  	s.state.Lock()
   322  	defer s.state.Unlock()
   323  
   324  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   325  		Active:   true,
   326  		Sequence: []*snap.SideInfo{&si7, &si11},
   327  		Current:  si11.Revision,
   328  		SnapType: "app",
   329  	})
   330  
   331  	chg := s.state.NewChange("refresh", "refresh a snap")
   332  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(7)}, s.user.ID, snapstate.Flags{})
   333  	c.Assert(err, IsNil)
   334  	chg.AddAll(ts)
   335  
   336  	s.state.Unlock()
   337  	defer s.se.Stop()
   338  	s.settle(c)
   339  	s.state.Lock()
   340  	expected := fakeOps{
   341  		{
   342  			op:   "remove-snap-aliases",
   343  			name: "some-snap",
   344  		},
   345  		{
   346  			op:   "unlink-snap",
   347  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
   348  		},
   349  		{
   350  			op:   "copy-data",
   351  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
   352  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
   353  		},
   354  		{
   355  			op:    "setup-profiles:Doing",
   356  			name:  "some-snap",
   357  			revno: snap.R(7),
   358  		},
   359  		{
   360  			op: "candidate",
   361  			sinfo: snap.SideInfo{
   362  				RealName: "some-snap",
   363  				SnapID:   "some-snap-id",
   364  				Channel:  "",
   365  				Revision: snap.R(7),
   366  			},
   367  		},
   368  		{
   369  			op:   "link-snap",
   370  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
   371  		},
   372  		{
   373  			op:    "auto-connect:Doing",
   374  			name:  "some-snap",
   375  			revno: snap.R(7),
   376  		},
   377  		{
   378  			op: "update-aliases",
   379  		},
   380  		{
   381  			op:    "cleanup-trash",
   382  			name:  "some-snap",
   383  			revno: snap.R(7),
   384  		},
   385  	}
   386  	// start with an easier-to-read error if this fails:
   387  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
   388  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
   389  }
   390  
   391  func revs(seq []*snap.SideInfo) []int {
   392  	revs := make([]int, len(seq))
   393  	for i, si := range seq {
   394  		revs[i] = si.Revision.N
   395  	}
   396  
   397  	return revs
   398  }
   399  
   400  type opSeqOpts struct {
   401  	revert  bool
   402  	fail    bool
   403  	before  []int
   404  	current int
   405  	via     int
   406  	after   []int
   407  }
   408  
   409  // build a SnapState with a revision sequence given by `before` and a
   410  // current revision of `current`. Then refresh --revision via. Then
   411  // check the revision sequence is as in `after`.
   412  func (s *snapmgrTestSuite) testOpSequence(c *C, opts *opSeqOpts) (*snapstate.SnapState, *state.TaskSet) {
   413  	s.state.Lock()
   414  	defer s.state.Unlock()
   415  
   416  	seq := make([]*snap.SideInfo, len(opts.before))
   417  	for i, n := range opts.before {
   418  		seq[i] = &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(n)}
   419  	}
   420  
   421  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   422  		Active:          true,
   423  		TrackingChannel: "latest/edge",
   424  		Sequence:        seq,
   425  		Current:         snap.R(opts.current),
   426  		SnapType:        "app",
   427  	})
   428  
   429  	var chg *state.Change
   430  	var ts *state.TaskSet
   431  	var err error
   432  	if opts.revert {
   433  		chg = s.state.NewChange("revert", "revert a snap")
   434  		ts, err = snapstate.RevertToRevision(s.state, "some-snap", snap.R(opts.via), snapstate.Flags{})
   435  	} else {
   436  		chg = s.state.NewChange("refresh", "refresh a snap")
   437  		ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(opts.via)}, s.user.ID, snapstate.Flags{})
   438  	}
   439  	c.Assert(err, IsNil)
   440  	if opts.fail {
   441  		tasks := ts.Tasks()
   442  		var last *state.Task
   443  		// don't make a task wait on rerefresh, that's bad
   444  		for i := len(tasks) - 1; i > 0; i-- {
   445  			last = tasks[i]
   446  			if last.Kind() != "check-rerefresh" {
   447  				break
   448  			}
   449  		}
   450  		terr := s.state.NewTask("error-trigger", "provoking total undo")
   451  		terr.WaitFor(last)
   452  		if len(last.Lanes()) > 0 {
   453  			lanes := last.Lanes()
   454  			// sanity
   455  			c.Assert(lanes, HasLen, 1)
   456  			terr.JoinLane(lanes[0])
   457  		}
   458  		chg.AddTask(terr)
   459  	}
   460  	chg.AddAll(ts)
   461  
   462  	s.state.Unlock()
   463  	defer s.se.Stop()
   464  	s.settle(c)
   465  	s.state.Lock()
   466  
   467  	var snapst snapstate.SnapState
   468  	err = snapstate.Get(s.state, "some-snap", &snapst)
   469  	c.Assert(err, IsNil)
   470  	c.Check(revs(snapst.Sequence), DeepEquals, opts.after)
   471  
   472  	return &snapst, ts
   473  }
   474  
   475  func (s *snapmgrTestSuite) testUpdateSequence(c *C, opts *opSeqOpts) *state.TaskSet {
   476  	restore := release.MockOnClassic(false)
   477  	defer restore()
   478  
   479  	opts.revert = false
   480  	snapst, ts := s.testOpSequence(c, opts)
   481  	// update always ends with current==seq[-1]==via:
   482  	c.Check(snapst.Current.N, Equals, opts.after[len(opts.after)-1])
   483  	c.Check(snapst.Current.N, Equals, opts.via)
   484  
   485  	c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 1)
   486  	c.Check(s.fakeBackend.ops.First("copy-data"), DeepEquals, &fakeOp{
   487  		op:   "copy-data",
   488  		path: fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via),
   489  		old:  fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.current),
   490  	})
   491  
   492  	return ts
   493  }
   494  
   495  func (s *snapmgrTestSuite) testUpdateFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet {
   496  	restore := release.MockOnClassic(false)
   497  	defer restore()
   498  
   499  	opts.revert = false
   500  	opts.after = opts.before
   501  	s.fakeBackend.linkSnapFailTrigger = fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via)
   502  	snapst, ts := s.testOpSequence(c, opts)
   503  	// a failed update will always end with current unchanged
   504  	c.Check(snapst.Current.N, Equals, opts.current)
   505  
   506  	ops := s.fakeBackend.ops
   507  	c.Check(ops.Count("copy-data"), Equals, 1)
   508  	do := ops.First("copy-data")
   509  
   510  	c.Check(ops.Count("undo-copy-snap-data"), Equals, 1)
   511  	undo := ops.First("undo-copy-snap-data")
   512  
   513  	do.op = undo.op
   514  	c.Check(do, DeepEquals, undo) // i.e. they only differed in the op
   515  
   516  	return ts
   517  }
   518  
   519  // testTotal*Failure fails *after* link-snap
   520  func (s *snapmgrTestSuite) testTotalUpdateFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet {
   521  	restore := release.MockOnClassic(false)
   522  	defer restore()
   523  
   524  	opts.revert = false
   525  	opts.fail = true
   526  	snapst, ts := s.testOpSequence(c, opts)
   527  	// a failed update will always end with current unchanged
   528  	c.Check(snapst.Current.N, Equals, opts.current)
   529  
   530  	ops := s.fakeBackend.ops
   531  	c.Check(ops.Count("copy-data"), Equals, 1)
   532  	do := ops.First("copy-data")
   533  
   534  	c.Check(ops.Count("undo-copy-snap-data"), Equals, 1)
   535  	undo := ops.First("undo-copy-snap-data")
   536  
   537  	do.op = undo.op
   538  	c.Check(do, DeepEquals, undo) // i.e. they only differed in the op
   539  
   540  	return ts
   541  }
   542  
   543  func (s *snapmgrTestSuite) TestUpdateLayoutsChecksFeatureFlag(c *C) {
   544  	s.state.Lock()
   545  	defer s.state.Unlock()
   546  
   547  	// When layouts are disabled we cannot refresh to a snap depending on the feature.
   548  	tr := config.NewTransaction(s.state)
   549  	tr.Set("core", "experimental.layouts", false)
   550  	tr.Commit()
   551  
   552  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   553  		Active: true,
   554  		Sequence: []*snap.SideInfo{
   555  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   556  		},
   557  		Current:  snap.R(1),
   558  		SnapType: "app",
   559  	})
   560  
   561  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-layout/stable"}, s.user.ID, snapstate.Flags{})
   562  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true")
   563  
   564  	// When layouts are enabled we can refresh to a snap depending on the feature.
   565  	tr = config.NewTransaction(s.state)
   566  	tr.Set("core", "experimental.layouts", true)
   567  	tr.Commit()
   568  
   569  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-layout/stable"}, s.user.ID, snapstate.Flags{})
   570  	c.Assert(err, IsNil)
   571  }
   572  
   573  func (s *snapmgrTestSuite) TestUpdateManyExplicitLayoutsChecksFeatureFlag(c *C) {
   574  	s.state.Lock()
   575  	defer s.state.Unlock()
   576  
   577  	// When layouts are disabled we cannot refresh multiple snaps if one of them depends on the feature.
   578  	tr := config.NewTransaction(s.state)
   579  	tr.Set("core", "experimental.layouts", false)
   580  	tr.Commit()
   581  
   582  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   583  		Active:          true,
   584  		TrackingChannel: "channel-for-layout/stable",
   585  		Sequence: []*snap.SideInfo{
   586  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   587  		},
   588  		Current:  snap.R(1),
   589  		SnapType: "app",
   590  	})
   591  
   592  	_, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
   593  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true")
   594  
   595  	// When layouts are enabled we can refresh multiple snaps if one of them depends on the feature.
   596  	tr = config.NewTransaction(s.state)
   597  	tr.Set("core", "experimental.layouts", true)
   598  	tr.Commit()
   599  
   600  	_, _, err = snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
   601  	c.Assert(err, IsNil)
   602  }
   603  
   604  func (s *snapmgrTestSuite) TestUpdateManyLayoutsChecksFeatureFlag(c *C) {
   605  	s.state.Lock()
   606  	defer s.state.Unlock()
   607  
   608  	// When layouts are disabled we cannot refresh multiple snaps if one of them depends on the feature.
   609  	tr := config.NewTransaction(s.state)
   610  	tr.Set("core", "experimental.layouts", false)
   611  	tr.Commit()
   612  
   613  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   614  		Active:          true,
   615  		TrackingChannel: "channel-for-layout/stable",
   616  		Sequence: []*snap.SideInfo{
   617  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   618  		},
   619  		Current:  snap.R(1),
   620  		SnapType: "app",
   621  	})
   622  
   623  	refreshes, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil)
   624  	c.Assert(err, IsNil)
   625  	c.Assert(refreshes, HasLen, 0)
   626  
   627  	// When layouts are enabled we can refresh multiple snaps if one of them depends on the feature.
   628  	tr = config.NewTransaction(s.state)
   629  	tr.Set("core", "experimental.layouts", true)
   630  	tr.Commit()
   631  
   632  	refreshes, _, err = snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil)
   633  	c.Assert(err, IsNil)
   634  	c.Assert(refreshes, DeepEquals, []string{"some-snap"})
   635  }
   636  
   637  func (s *snapmgrTestSuite) TestUpdateFailsEarlyOnEpochMismatch(c *C) {
   638  	s.state.Lock()
   639  	defer s.state.Unlock()
   640  
   641  	snapstate.Set(s.state, "some-epoch-snap", &snapstate.SnapState{
   642  		Active: true,
   643  		Sequence: []*snap.SideInfo{
   644  			{RealName: "some-epoch-snap", SnapID: "some-epoch-snap-id", Revision: snap.R(1)},
   645  		},
   646  		Current:  snap.R(1),
   647  		SnapType: "app",
   648  	})
   649  
   650  	_, err := snapstate.Update(s.state, "some-epoch-snap", nil, 0, snapstate.Flags{})
   651  	c.Assert(err, ErrorMatches, `cannot refresh "some-epoch-snap" to new revision 11 with epoch 42, because it can't read the current epoch of 13`)
   652  }
   653  
   654  func (s *snapmgrTestSuite) TestUpdateTasksPropagatesErrors(c *C) {
   655  	s.state.Lock()
   656  	defer s.state.Unlock()
   657  
   658  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   659  		Active:          true,
   660  		TrackingChannel: "latest/edge",
   661  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "fakestore-please-error-on-refresh", Revision: snap.R(7)}},
   662  		Current:         snap.R(7),
   663  	})
   664  
   665  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
   666  	c.Assert(err, ErrorMatches, `failing as requested`)
   667  }
   668  
   669  func (s *snapmgrTestSuite) TestUpdateTasks(c *C) {
   670  	s.state.Lock()
   671  	defer s.state.Unlock()
   672  
   673  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   674  		Active:          true,
   675  		TrackingChannel: "latest/edge",
   676  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
   677  		Current:         snap.R(7),
   678  		SnapType:        "app",
   679  	})
   680  
   681  	validateCalled := false
   682  	happyValidateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
   683  		validateCalled = true
   684  		return refreshes, nil
   685  	}
   686  	// hook it up
   687  	snapstate.ValidateRefreshes = happyValidateRefreshes
   688  
   689  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
   690  	c.Assert(err, IsNil)
   691  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
   692  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   693  
   694  	c.Check(validateCalled, Equals, true)
   695  
   696  	var snapsup snapstate.SnapSetup
   697  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
   698  	c.Assert(err, IsNil)
   699  
   700  	c.Check(snapsup.Channel, Equals, "some-channel")
   701  }
   702  
   703  func (s *snapmgrTestSuite) TestUpdateAmendRunThrough(c *C) {
   704  	si := snap.SideInfo{
   705  		RealName: "some-snap",
   706  		Revision: snap.R(-42),
   707  	}
   708  	snaptest.MockSnap(c, `name: some-snap`, &si)
   709  
   710  	s.state.Lock()
   711  	defer s.state.Unlock()
   712  
   713  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   714  		Active:          true,
   715  		Sequence:        []*snap.SideInfo{&si},
   716  		Current:         si.Revision,
   717  		SnapType:        "app",
   718  		TrackingChannel: "latest/stable",
   719  	})
   720  
   721  	chg := s.state.NewChange("refresh", "refresh a snap")
   722  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
   723  	c.Assert(err, IsNil)
   724  	chg.AddAll(ts)
   725  
   726  	s.state.Unlock()
   727  	defer s.se.Stop()
   728  	s.settle(c)
   729  	s.state.Lock()
   730  
   731  	// ensure all our tasks ran
   732  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
   733  		macaroon: s.user.StoreMacaroon,
   734  		name:     "some-snap",
   735  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
   736  	}})
   737  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
   738  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, []string{
   739  		"storesvc-snap-action",
   740  		"storesvc-snap-action:action",
   741  		"storesvc-download",
   742  		"validate-snap:Doing",
   743  		"current",
   744  		"open-snap-file",
   745  		"setup-snap",
   746  		"remove-snap-aliases",
   747  		"unlink-snap",
   748  		"copy-data",
   749  		"setup-profiles:Doing",
   750  		"candidate",
   751  		"link-snap",
   752  		"auto-connect:Doing",
   753  		"update-aliases",
   754  		"cleanup-trash",
   755  	})
   756  	// just check the interesting op
   757  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
   758  		op: "storesvc-snap-action:action",
   759  		action: store.SnapAction{
   760  			Action:       "install", // we asked for an Update, but an amend is actually an Install
   761  			InstanceName: "some-snap",
   762  			Channel:      "some-channel",
   763  			Epoch:        snap.E("1*"), // in amend, epoch in the action is not nil!
   764  			Flags:        store.SnapActionEnforceValidation,
   765  		},
   766  		revno:  snap.R(11),
   767  		userID: 1,
   768  	})
   769  
   770  	task := ts.Tasks()[1]
   771  	// verify snapSetup info
   772  	var snapsup snapstate.SnapSetup
   773  	err = task.Get("snap-setup", &snapsup)
   774  	c.Assert(err, IsNil)
   775  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
   776  		Channel: "some-channel",
   777  		UserID:  s.user.ID,
   778  
   779  		SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
   780  		DownloadInfo: &snap.DownloadInfo{
   781  			DownloadURL: "https://some-server.com/some/path.snap",
   782  			Size:        5,
   783  		},
   784  		SideInfo:  snapsup.SideInfo,
   785  		Type:      snap.TypeApp,
   786  		PlugsOnly: true,
   787  		Flags:     snapstate.Flags{Amend: true},
   788  	})
   789  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
   790  		RealName: "some-snap",
   791  		Revision: snap.R(11),
   792  		Channel:  "some-channel",
   793  		SnapID:   "some-snap-id",
   794  	})
   795  
   796  	// verify services stop reason
   797  	verifyStopReason(c, ts, "refresh")
   798  
   799  	// check post-refresh hook
   800  	task = ts.Tasks()[14]
   801  	c.Assert(task.Kind(), Equals, "run-hook")
   802  	c.Assert(task.Summary(), Matches, `Run post-refresh hook of "some-snap" snap if present`)
   803  
   804  	// verify snaps in the system state
   805  	var snapst snapstate.SnapState
   806  	err = snapstate.Get(s.state, "some-snap", &snapst)
   807  	c.Assert(err, IsNil)
   808  
   809  	c.Assert(snapst.Active, Equals, true)
   810  	c.Assert(snapst.Sequence, HasLen, 2)
   811  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
   812  		RealName: "some-snap",
   813  		Channel:  "",
   814  		Revision: snap.R(-42),
   815  	})
   816  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
   817  		RealName: "some-snap",
   818  		Channel:  "some-channel",
   819  		SnapID:   "some-snap-id",
   820  		Revision: snap.R(11),
   821  	})
   822  }
   823  
   824  func (s *snapmgrTestSuite) TestUpdateRunThrough(c *C) {
   825  	// we start without the auxiliary store info (or with an older one)
   826  	c.Check(snapstate.AuxStoreInfoFilename("services-snap-id"), testutil.FileAbsent)
   827  
   828  	// use services-snap here to make sure services would be stopped/started appropriately
   829  	si := snap.SideInfo{
   830  		RealName: "services-snap",
   831  		Revision: snap.R(7),
   832  		SnapID:   "services-snap-id",
   833  	}
   834  	snaptest.MockSnap(c, `name: services-snap`, &si)
   835  	fi, err := os.Stat(snap.MountFile("services-snap", si.Revision))
   836  	c.Assert(err, IsNil)
   837  	refreshedDate := fi.ModTime()
   838  	// look at disk
   839  	r := snapstate.MockRevisionDate(nil)
   840  	defer r()
   841  
   842  	s.state.Lock()
   843  	defer s.state.Unlock()
   844  
   845  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
   846  		Active:          true,
   847  		Sequence:        []*snap.SideInfo{&si},
   848  		Current:         si.Revision,
   849  		SnapType:        "app",
   850  		TrackingChannel: "latest/stable",
   851  		CohortKey:       "embattled",
   852  	})
   853  
   854  	chg := s.state.NewChange("refresh", "refresh a snap")
   855  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{
   856  		Channel:   "some-channel",
   857  		CohortKey: "some-cohort",
   858  	}, s.user.ID, snapstate.Flags{})
   859  	c.Assert(err, IsNil)
   860  	chg.AddAll(ts)
   861  
   862  	s.state.Unlock()
   863  	defer s.se.Stop()
   864  	s.settle(c)
   865  	s.state.Lock()
   866  
   867  	expected := fakeOps{
   868  		{
   869  			op: "storesvc-snap-action",
   870  			curSnaps: []store.CurrentSnap{{
   871  				InstanceName:    "services-snap",
   872  				SnapID:          "services-snap-id",
   873  				Revision:        snap.R(7),
   874  				TrackingChannel: "latest/stable",
   875  				RefreshedDate:   refreshedDate,
   876  				Epoch:           snap.E("0"),
   877  				CohortKey:       "embattled",
   878  			}},
   879  			userID: 1,
   880  		},
   881  		{
   882  			op: "storesvc-snap-action:action",
   883  			action: store.SnapAction{
   884  				Action:       "refresh",
   885  				InstanceName: "services-snap",
   886  				SnapID:       "services-snap-id",
   887  				Channel:      "some-channel",
   888  				CohortKey:    "some-cohort",
   889  				Flags:        store.SnapActionEnforceValidation,
   890  			},
   891  			revno:  snap.R(11),
   892  			userID: 1,
   893  		},
   894  		{
   895  			op:   "storesvc-download",
   896  			name: "services-snap",
   897  		},
   898  		{
   899  			op:    "validate-snap:Doing",
   900  			name:  "services-snap",
   901  			revno: snap.R(11),
   902  		},
   903  		{
   904  			op:  "current",
   905  			old: filepath.Join(dirs.SnapMountDir, "services-snap/7"),
   906  		},
   907  		{
   908  			op:   "open-snap-file",
   909  			path: filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
   910  			sinfo: snap.SideInfo{
   911  				RealName: "services-snap",
   912  				SnapID:   "services-snap-id",
   913  				Channel:  "some-channel",
   914  				Revision: snap.R(11),
   915  			},
   916  		},
   917  		{
   918  			op:    "setup-snap",
   919  			name:  "services-snap",
   920  			path:  filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
   921  			revno: snap.R(11),
   922  		},
   923  		{
   924  			op:   "stop-snap-services:refresh",
   925  			path: filepath.Join(dirs.SnapMountDir, "services-snap/7"),
   926  		},
   927  		{
   928  			op: "current-snap-service-states",
   929  		},
   930  		{
   931  			op:   "remove-snap-aliases",
   932  			name: "services-snap",
   933  		},
   934  		{
   935  			op:   "unlink-snap",
   936  			path: filepath.Join(dirs.SnapMountDir, "services-snap/7"),
   937  		},
   938  		{
   939  			op:   "copy-data",
   940  			path: filepath.Join(dirs.SnapMountDir, "services-snap/11"),
   941  			old:  filepath.Join(dirs.SnapMountDir, "services-snap/7"),
   942  		},
   943  		{
   944  			op:    "setup-profiles:Doing",
   945  			name:  "services-snap",
   946  			revno: snap.R(11),
   947  		},
   948  		{
   949  			op: "candidate",
   950  			sinfo: snap.SideInfo{
   951  				RealName: "services-snap",
   952  				SnapID:   "services-snap-id",
   953  				Channel:  "some-channel",
   954  				Revision: snap.R(11),
   955  			},
   956  		},
   957  		{
   958  			op:   "link-snap",
   959  			path: filepath.Join(dirs.SnapMountDir, "services-snap/11"),
   960  		},
   961  		{
   962  			op:    "auto-connect:Doing",
   963  			name:  "services-snap",
   964  			revno: snap.R(11),
   965  		},
   966  		{
   967  			op: "update-aliases",
   968  		},
   969  		{
   970  			op:       "start-snap-services",
   971  			path:     filepath.Join(dirs.SnapMountDir, "services-snap/11"),
   972  			services: []string{"svc1", "svc3", "svc2"},
   973  		},
   974  		{
   975  			op:    "cleanup-trash",
   976  			name:  "services-snap",
   977  			revno: snap.R(11),
   978  		},
   979  	}
   980  
   981  	// ensure all our tasks ran
   982  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
   983  		macaroon: s.user.StoreMacaroon,
   984  		name:     "services-snap",
   985  		target:   filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
   986  	}})
   987  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
   988  	// start with an easier-to-read error if this fails:
   989  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
   990  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
   991  
   992  	// check progress
   993  	task := ts.Tasks()[1]
   994  	_, cur, total := task.Progress()
   995  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
   996  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
   997  
   998  	// verify snapSetup info
   999  	var snapsup snapstate.SnapSetup
  1000  	err = task.Get("snap-setup", &snapsup)
  1001  	c.Assert(err, IsNil)
  1002  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1003  		Channel:   "some-channel",
  1004  		CohortKey: "some-cohort",
  1005  		UserID:    s.user.ID,
  1006  
  1007  		SnapPath: filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
  1008  		DownloadInfo: &snap.DownloadInfo{
  1009  			DownloadURL: "https://some-server.com/some/path.snap",
  1010  		},
  1011  		SideInfo:  snapsup.SideInfo,
  1012  		Type:      snap.TypeApp,
  1013  		PlugsOnly: true,
  1014  	})
  1015  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1016  		RealName: "services-snap",
  1017  		Revision: snap.R(11),
  1018  		Channel:  "some-channel",
  1019  		SnapID:   "services-snap-id",
  1020  	})
  1021  
  1022  	// verify services stop reason
  1023  	verifyStopReason(c, ts, "refresh")
  1024  
  1025  	// check post-refresh hook
  1026  	task = ts.Tasks()[14]
  1027  	c.Assert(task.Kind(), Equals, "run-hook")
  1028  	c.Assert(task.Summary(), Matches, `Run post-refresh hook of "services-snap" snap if present`)
  1029  
  1030  	// verify snaps in the system state
  1031  	var snapst snapstate.SnapState
  1032  	err = snapstate.Get(s.state, "services-snap", &snapst)
  1033  	c.Assert(err, IsNil)
  1034  
  1035  	c.Assert(snapst.Active, Equals, true)
  1036  	c.Assert(snapst.Sequence, HasLen, 2)
  1037  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1038  		RealName: "services-snap",
  1039  		SnapID:   "services-snap-id",
  1040  		Channel:  "",
  1041  		Revision: snap.R(7),
  1042  	})
  1043  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  1044  		RealName: "services-snap",
  1045  		Channel:  "some-channel",
  1046  		SnapID:   "services-snap-id",
  1047  		Revision: snap.R(11),
  1048  	})
  1049  	c.Check(snapst.CohortKey, Equals, "some-cohort")
  1050  
  1051  	// we end up with the auxiliary store info
  1052  	c.Check(snapstate.AuxStoreInfoFilename("services-snap-id"), testutil.FilePresent)
  1053  }
  1054  
  1055  func (s *snapmgrTestSuite) TestParallelInstanceUpdateRunThrough(c *C) {
  1056  	// use services-snap here to make sure services would be stopped/started appropriately
  1057  	si := snap.SideInfo{
  1058  		RealName: "services-snap",
  1059  		Revision: snap.R(7),
  1060  		SnapID:   "services-snap-id",
  1061  	}
  1062  	snaptest.MockSnapInstance(c, "services-snap_instance", `name: services-snap`, &si)
  1063  	fi, err := os.Stat(snap.MountFile("services-snap_instance", si.Revision))
  1064  	c.Assert(err, IsNil)
  1065  	refreshedDate := fi.ModTime()
  1066  	// look at disk
  1067  	r := snapstate.MockRevisionDate(nil)
  1068  	defer r()
  1069  
  1070  	s.state.Lock()
  1071  	defer s.state.Unlock()
  1072  
  1073  	tr := config.NewTransaction(s.state)
  1074  	tr.Set("core", "experimental.parallel-instances", true)
  1075  	tr.Commit()
  1076  
  1077  	snapstate.Set(s.state, "services-snap_instance", &snapstate.SnapState{
  1078  		Active:          true,
  1079  		Sequence:        []*snap.SideInfo{&si},
  1080  		Current:         si.Revision,
  1081  		SnapType:        "app",
  1082  		TrackingChannel: "latest/stable",
  1083  		InstanceKey:     "instance",
  1084  	})
  1085  
  1086  	chg := s.state.NewChange("refresh", "refresh a snap")
  1087  	ts, err := snapstate.Update(s.state, "services-snap_instance", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  1088  	c.Assert(err, IsNil)
  1089  	chg.AddAll(ts)
  1090  
  1091  	s.state.Unlock()
  1092  	s.settle(c)
  1093  	s.state.Lock()
  1094  
  1095  	expected := fakeOps{
  1096  		{
  1097  			op: "storesvc-snap-action",
  1098  			curSnaps: []store.CurrentSnap{{
  1099  				InstanceName:    "services-snap_instance",
  1100  				SnapID:          "services-snap-id",
  1101  				Revision:        snap.R(7),
  1102  				TrackingChannel: "latest/stable",
  1103  				RefreshedDate:   refreshedDate,
  1104  				Epoch:           snap.E("0"),
  1105  			}},
  1106  			userID: 1,
  1107  		},
  1108  		{
  1109  			op: "storesvc-snap-action:action",
  1110  			action: store.SnapAction{
  1111  				Action:       "refresh",
  1112  				SnapID:       "services-snap-id",
  1113  				InstanceName: "services-snap_instance",
  1114  				Channel:      "some-channel",
  1115  				Flags:        store.SnapActionEnforceValidation,
  1116  			},
  1117  			revno:  snap.R(11),
  1118  			userID: 1,
  1119  		},
  1120  		{
  1121  			op:   "storesvc-download",
  1122  			name: "services-snap",
  1123  		},
  1124  		{
  1125  			op:    "validate-snap:Doing",
  1126  			name:  "services-snap_instance",
  1127  			revno: snap.R(11),
  1128  		},
  1129  		{
  1130  			op:  "current",
  1131  			old: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  1132  		},
  1133  		{
  1134  			op:   "open-snap-file",
  1135  			path: filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  1136  			sinfo: snap.SideInfo{
  1137  				RealName: "services-snap",
  1138  				SnapID:   "services-snap-id",
  1139  				Channel:  "some-channel",
  1140  				Revision: snap.R(11),
  1141  			},
  1142  		},
  1143  		{
  1144  			op:    "setup-snap",
  1145  			name:  "services-snap_instance",
  1146  			path:  filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  1147  			revno: snap.R(11),
  1148  		},
  1149  		{
  1150  			op:   "stop-snap-services:refresh",
  1151  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  1152  		},
  1153  		{
  1154  			op: "current-snap-service-states",
  1155  		},
  1156  		{
  1157  			op:   "remove-snap-aliases",
  1158  			name: "services-snap_instance",
  1159  		},
  1160  		{
  1161  			op:   "unlink-snap",
  1162  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  1163  		},
  1164  		{
  1165  			op:   "copy-data",
  1166  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"),
  1167  			old:  filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  1168  		},
  1169  		{
  1170  			op:    "setup-profiles:Doing",
  1171  			name:  "services-snap_instance",
  1172  			revno: snap.R(11),
  1173  		},
  1174  		{
  1175  			op: "candidate",
  1176  			sinfo: snap.SideInfo{
  1177  				RealName: "services-snap",
  1178  				SnapID:   "services-snap-id",
  1179  				Channel:  "some-channel",
  1180  				Revision: snap.R(11),
  1181  			},
  1182  		},
  1183  		{
  1184  			op:   "link-snap",
  1185  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"),
  1186  		},
  1187  		{
  1188  			op:    "auto-connect:Doing",
  1189  			name:  "services-snap_instance",
  1190  			revno: snap.R(11),
  1191  		},
  1192  		{
  1193  			op: "update-aliases",
  1194  		},
  1195  		{
  1196  			op:       "start-snap-services",
  1197  			path:     filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"),
  1198  			services: []string{"svc1", "svc3", "svc2"},
  1199  		},
  1200  		{
  1201  			op:    "cleanup-trash",
  1202  			name:  "services-snap_instance",
  1203  			revno: snap.R(11),
  1204  		},
  1205  	}
  1206  
  1207  	// ensure all our tasks ran
  1208  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  1209  		macaroon: s.user.StoreMacaroon,
  1210  		name:     "services-snap",
  1211  		target:   filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  1212  	}})
  1213  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
  1214  	// start with an easier-to-read error if this fails:
  1215  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1216  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  1217  
  1218  	// check progress
  1219  	task := ts.Tasks()[1]
  1220  	_, cur, total := task.Progress()
  1221  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  1222  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  1223  
  1224  	// verify snapSetup info
  1225  	var snapsup snapstate.SnapSetup
  1226  	err = task.Get("snap-setup", &snapsup)
  1227  	c.Assert(err, IsNil)
  1228  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1229  		Channel: "some-channel",
  1230  		UserID:  s.user.ID,
  1231  
  1232  		SnapPath: filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  1233  		DownloadInfo: &snap.DownloadInfo{
  1234  			DownloadURL: "https://some-server.com/some/path.snap",
  1235  		},
  1236  		SideInfo:    snapsup.SideInfo,
  1237  		Type:        snap.TypeApp,
  1238  		PlugsOnly:   true,
  1239  		InstanceKey: "instance",
  1240  	})
  1241  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1242  		RealName: "services-snap",
  1243  		Revision: snap.R(11),
  1244  		Channel:  "some-channel",
  1245  		SnapID:   "services-snap-id",
  1246  	})
  1247  
  1248  	// verify services stop reason
  1249  	verifyStopReason(c, ts, "refresh")
  1250  
  1251  	// check post-refresh hook
  1252  	task = ts.Tasks()[14]
  1253  	c.Assert(task.Kind(), Equals, "run-hook")
  1254  	c.Assert(task.Summary(), Matches, `Run post-refresh hook of "services-snap_instance" snap if present`)
  1255  
  1256  	// verify snaps in the system state
  1257  	var snapst snapstate.SnapState
  1258  	err = snapstate.Get(s.state, "services-snap_instance", &snapst)
  1259  	c.Assert(err, IsNil)
  1260  
  1261  	c.Assert(snapst.InstanceKey, Equals, "instance")
  1262  	c.Assert(snapst.Active, Equals, true)
  1263  	c.Assert(snapst.Sequence, HasLen, 2)
  1264  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1265  		RealName: "services-snap",
  1266  		SnapID:   "services-snap-id",
  1267  		Channel:  "",
  1268  		Revision: snap.R(7),
  1269  	})
  1270  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  1271  		RealName: "services-snap",
  1272  		Channel:  "some-channel",
  1273  		SnapID:   "services-snap-id",
  1274  		Revision: snap.R(11),
  1275  	})
  1276  }
  1277  
  1278  func (s *snapmgrTestSuite) TestUpdateWithNewBase(c *C) {
  1279  	si := &snap.SideInfo{
  1280  		RealName: "some-snap",
  1281  		SnapID:   "some-snap-id",
  1282  		Revision: snap.R(7),
  1283  	}
  1284  	snaptest.MockSnap(c, `name: some-snap`, si)
  1285  
  1286  	s.state.Lock()
  1287  	defer s.state.Unlock()
  1288  
  1289  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1290  		Active:          true,
  1291  		TrackingChannel: "latest/edge",
  1292  		Sequence:        []*snap.SideInfo{si},
  1293  		Current:         snap.R(7),
  1294  		SnapType:        "app",
  1295  	})
  1296  
  1297  	chg := s.state.NewChange("refresh", "refresh a snap")
  1298  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-base/stable"}, s.user.ID, snapstate.Flags{})
  1299  	c.Assert(err, IsNil)
  1300  	chg.AddAll(ts)
  1301  
  1302  	s.state.Unlock()
  1303  	defer s.se.Stop()
  1304  	s.settle(c)
  1305  	s.state.Lock()
  1306  
  1307  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  1308  		{macaroon: s.user.StoreMacaroon, name: "some-base", target: filepath.Join(dirs.SnapBlobDir, "some-base_11.snap")},
  1309  		{macaroon: s.user.StoreMacaroon, name: "some-snap", target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap")},
  1310  	})
  1311  }
  1312  
  1313  func (s *snapmgrTestSuite) TestUpdateWithAlreadyInstalledBase(c *C) {
  1314  	si := &snap.SideInfo{
  1315  		RealName: "some-snap",
  1316  		SnapID:   "some-snap-id",
  1317  		Revision: snap.R(7),
  1318  	}
  1319  	snaptest.MockSnap(c, `name: some-snap`, si)
  1320  
  1321  	s.state.Lock()
  1322  	defer s.state.Unlock()
  1323  
  1324  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1325  		Active:          true,
  1326  		TrackingChannel: "latest/edge",
  1327  		Sequence:        []*snap.SideInfo{si},
  1328  		Current:         snap.R(7),
  1329  		SnapType:        "app",
  1330  	})
  1331  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  1332  		Active:          true,
  1333  		TrackingChannel: "latest/stable",
  1334  		Sequence: []*snap.SideInfo{{
  1335  			RealName: "some-base",
  1336  			SnapID:   "some-base-id",
  1337  			Revision: snap.R(1),
  1338  		}},
  1339  		Current:  snap.R(1),
  1340  		SnapType: "base",
  1341  	})
  1342  
  1343  	chg := s.state.NewChange("refresh", "refresh a snap")
  1344  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-base"}, s.user.ID, snapstate.Flags{})
  1345  	c.Assert(err, IsNil)
  1346  	chg.AddAll(ts)
  1347  
  1348  	s.state.Unlock()
  1349  	defer s.se.Stop()
  1350  	s.settle(c)
  1351  	s.state.Lock()
  1352  
  1353  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  1354  		{macaroon: s.user.StoreMacaroon, name: "some-snap", target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap")},
  1355  	})
  1356  }
  1357  
  1358  func (s *snapmgrTestSuite) TestUpdateWithNewDefaultProvider(c *C) {
  1359  	s.state.Lock()
  1360  	defer s.state.Unlock()
  1361  
  1362  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
  1363  	repo := interfaces.NewRepository()
  1364  	ifacerepo.Replace(s.state, repo)
  1365  
  1366  	si := &snap.SideInfo{
  1367  		RealName: "snap-content-plug",
  1368  		SnapID:   "snap-content-plug-id",
  1369  		Revision: snap.R(7),
  1370  	}
  1371  	snaptest.MockSnap(c, `name: snap-content-plug`, si)
  1372  	snapstate.Set(s.state, "snap-content-plug", &snapstate.SnapState{
  1373  		Active:          true,
  1374  		TrackingChannel: "latest/edge",
  1375  		Sequence:        []*snap.SideInfo{si},
  1376  		Current:         snap.R(7),
  1377  		SnapType:        "app",
  1378  	})
  1379  
  1380  	chg := s.state.NewChange("refresh", "refresh a snap")
  1381  	ts, err := snapstate.Update(s.state, "snap-content-plug", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
  1382  	c.Assert(err, IsNil)
  1383  	chg.AddAll(ts)
  1384  
  1385  	s.state.Unlock()
  1386  	defer s.se.Stop()
  1387  	s.settle(c)
  1388  	s.state.Lock()
  1389  
  1390  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  1391  		{macaroon: s.user.StoreMacaroon, name: "snap-content-plug", target: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_11.snap")},
  1392  		{macaroon: s.user.StoreMacaroon, name: "snap-content-slot", target: filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap")},
  1393  	})
  1394  }
  1395  
  1396  func (s *snapmgrTestSuite) TestUpdateWithInstalledDefaultProvider(c *C) {
  1397  	s.state.Lock()
  1398  	defer s.state.Unlock()
  1399  
  1400  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
  1401  	repo := interfaces.NewRepository()
  1402  	ifacerepo.Replace(s.state, repo)
  1403  
  1404  	si := &snap.SideInfo{
  1405  		RealName: "snap-content-plug",
  1406  		SnapID:   "snap-content-plug-id",
  1407  		Revision: snap.R(7),
  1408  	}
  1409  	snaptest.MockSnap(c, `name: snap-content-plug`, si)
  1410  	snapstate.Set(s.state, "snap-content-plug", &snapstate.SnapState{
  1411  		Active:          true,
  1412  		TrackingChannel: "latest/edge",
  1413  		Sequence:        []*snap.SideInfo{si},
  1414  		Current:         snap.R(7),
  1415  		SnapType:        "app",
  1416  	})
  1417  	snapstate.Set(s.state, "snap-content-slot", &snapstate.SnapState{
  1418  		Active:          true,
  1419  		TrackingChannel: "latest/stable",
  1420  		Sequence: []*snap.SideInfo{{
  1421  			RealName: "snap-content-slot",
  1422  			SnapID:   "snap-content-slot-id",
  1423  			Revision: snap.R(1),
  1424  		}},
  1425  		Current:  snap.R(1),
  1426  		SnapType: "app",
  1427  	})
  1428  
  1429  	chg := s.state.NewChange("refresh", "refresh a snap")
  1430  	ts, err := snapstate.Update(s.state, "snap-content-plug", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
  1431  	c.Assert(err, IsNil)
  1432  	chg.AddAll(ts)
  1433  
  1434  	s.state.Unlock()
  1435  	defer s.se.Stop()
  1436  	s.settle(c)
  1437  	s.state.Lock()
  1438  
  1439  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  1440  		{macaroon: s.user.StoreMacaroon, name: "snap-content-plug", target: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_11.snap")},
  1441  	})
  1442  }
  1443  
  1444  func (s *snapmgrTestSuite) TestUpdateRememberedUserRunThrough(c *C) {
  1445  	s.state.Lock()
  1446  	defer s.state.Unlock()
  1447  
  1448  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1449  		Active: true,
  1450  		Sequence: []*snap.SideInfo{
  1451  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  1452  		},
  1453  		Current:  snap.R(5),
  1454  		SnapType: "app",
  1455  		UserID:   1,
  1456  	})
  1457  
  1458  	chg := s.state.NewChange("refresh", "refresh a snap")
  1459  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, 0, snapstate.Flags{})
  1460  	c.Assert(err, IsNil)
  1461  	chg.AddAll(ts)
  1462  
  1463  	s.state.Unlock()
  1464  	defer s.se.Stop()
  1465  	s.settle(c)
  1466  	s.state.Lock()
  1467  
  1468  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1469  	c.Assert(chg.Err(), IsNil)
  1470  
  1471  	for _, op := range s.fakeBackend.ops {
  1472  		switch op.op {
  1473  		case "storesvc-snap-action":
  1474  			c.Check(op.userID, Equals, 1)
  1475  		case "storesvc-download":
  1476  			snapName := op.name
  1477  			c.Check(s.fakeStore.downloads[0], DeepEquals, fakeDownload{
  1478  				macaroon: "macaroon",
  1479  				name:     "some-snap",
  1480  				target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  1481  			}, Commentf(snapName))
  1482  		}
  1483  	}
  1484  }
  1485  
  1486  func (s *snapmgrTestSuite) TestUpdateModelKernelSwitchTrackRunThrough(c *C) {
  1487  	// use services-snap here to make sure services would be stopped/started appropriately
  1488  	si := snap.SideInfo{
  1489  		RealName: "kernel",
  1490  		Revision: snap.R(7),
  1491  		SnapID:   "kernel-id",
  1492  	}
  1493  	snaptest.MockSnap(c, `name: kernel`, &si)
  1494  	fi, err := os.Stat(snap.MountFile("kernel", si.Revision))
  1495  	c.Assert(err, IsNil)
  1496  	refreshedDate := fi.ModTime()
  1497  	// look at disk
  1498  	r := snapstate.MockRevisionDate(nil)
  1499  	defer r()
  1500  
  1501  	s.state.Lock()
  1502  	defer s.state.Unlock()
  1503  
  1504  	r1 := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  1505  	defer r1()
  1506  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  1507  		Active:          true,
  1508  		Sequence:        []*snap.SideInfo{&si},
  1509  		Current:         si.Revision,
  1510  		TrackingChannel: "18/stable",
  1511  	})
  1512  
  1513  	chg := s.state.NewChange("refresh", "refresh a snap")
  1514  	ts, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "edge"}, s.user.ID, snapstate.Flags{})
  1515  	c.Assert(err, IsNil)
  1516  	chg.AddAll(ts)
  1517  
  1518  	s.state.Unlock()
  1519  	defer s.se.Stop()
  1520  	s.settle(c)
  1521  	s.state.Lock()
  1522  
  1523  	c.Check(chg.Status(), Equals, state.DoneStatus)
  1524  
  1525  	c.Assert(len(s.fakeBackend.ops) > 2, Equals, true)
  1526  	c.Assert(s.fakeBackend.ops[:2], DeepEquals, fakeOps{
  1527  		{
  1528  			op: "storesvc-snap-action",
  1529  			curSnaps: []store.CurrentSnap{{
  1530  				InstanceName:    "kernel",
  1531  				SnapID:          "kernel-id",
  1532  				Revision:        snap.R(7),
  1533  				TrackingChannel: "18/stable",
  1534  				RefreshedDate:   refreshedDate,
  1535  				Epoch:           snap.E("1*"),
  1536  			}},
  1537  			userID: 1,
  1538  		}, {
  1539  			op: "storesvc-snap-action:action",
  1540  			action: store.SnapAction{
  1541  				Action:       "refresh",
  1542  				InstanceName: "kernel",
  1543  				SnapID:       "kernel-id",
  1544  				Channel:      "18/edge",
  1545  				Flags:        store.SnapActionEnforceValidation,
  1546  			},
  1547  			revno:  snap.R(11),
  1548  			userID: 1,
  1549  		},
  1550  	})
  1551  
  1552  	// check progress
  1553  	task := ts.Tasks()[1]
  1554  	_, cur, total := task.Progress()
  1555  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  1556  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  1557  
  1558  	// verify snapSetup info
  1559  	var snapsup snapstate.SnapSetup
  1560  	err = task.Get("snap-setup", &snapsup)
  1561  	c.Assert(err, IsNil)
  1562  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1563  		Channel: "18/edge",
  1564  		UserID:  s.user.ID,
  1565  
  1566  		SnapPath: filepath.Join(dirs.SnapBlobDir, "kernel_11.snap"),
  1567  		DownloadInfo: &snap.DownloadInfo{
  1568  			DownloadURL: "https://some-server.com/some/path.snap",
  1569  		},
  1570  		SideInfo:  snapsup.SideInfo,
  1571  		Type:      snap.TypeKernel,
  1572  		PlugsOnly: true,
  1573  	})
  1574  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1575  		RealName: "kernel",
  1576  		Revision: snap.R(11),
  1577  		Channel:  "18/edge",
  1578  		SnapID:   "kernel-id",
  1579  	})
  1580  
  1581  	// verify snaps in the system state
  1582  	var snapst snapstate.SnapState
  1583  	err = snapstate.Get(s.state, "kernel", &snapst)
  1584  	c.Assert(err, IsNil)
  1585  
  1586  	c.Assert(snapst.Active, Equals, true)
  1587  	c.Assert(snapst.TrackingChannel, Equals, "18/edge")
  1588  	c.Assert(snapst.Sequence, HasLen, 2)
  1589  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1590  		RealName: "kernel",
  1591  		SnapID:   "kernel-id",
  1592  		Channel:  "",
  1593  		Revision: snap.R(7),
  1594  	})
  1595  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  1596  		RealName: "kernel",
  1597  		Channel:  "18/edge",
  1598  		SnapID:   "kernel-id",
  1599  		Revision: snap.R(11),
  1600  	})
  1601  }
  1602  
  1603  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsNoUserRunThrough(c *C) {
  1604  	s.state.Lock()
  1605  	defer s.state.Unlock()
  1606  
  1607  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1608  		Active: true,
  1609  		Sequence: []*snap.SideInfo{
  1610  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  1611  		},
  1612  		Current:  snap.R(1),
  1613  		SnapType: "os",
  1614  	})
  1615  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1616  		Active: true,
  1617  		Sequence: []*snap.SideInfo{
  1618  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  1619  		},
  1620  		Current:  snap.R(5),
  1621  		SnapType: "app",
  1622  		UserID:   1,
  1623  	})
  1624  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1625  		Active: true,
  1626  		Sequence: []*snap.SideInfo{
  1627  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  1628  		},
  1629  		Current:  snap.R(2),
  1630  		SnapType: "app",
  1631  		UserID:   2,
  1632  	})
  1633  
  1634  	chg := s.state.NewChange("refresh", "refresh all snaps")
  1635  	// no user is passed to use for UpdateMany
  1636  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1637  	c.Assert(err, IsNil)
  1638  	for _, ts := range tts {
  1639  		chg.AddAll(ts)
  1640  	}
  1641  	c.Check(updated, HasLen, 3)
  1642  
  1643  	s.state.Unlock()
  1644  	defer s.se.Stop()
  1645  	s.settle(c)
  1646  	s.state.Lock()
  1647  
  1648  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1649  	c.Assert(chg.Err(), IsNil)
  1650  
  1651  	macaroonMap := map[string]string{
  1652  		"core":          "",
  1653  		"some-snap":     "macaroon",
  1654  		"services-snap": "macaroon2",
  1655  	}
  1656  
  1657  	seen := make(map[string]int)
  1658  	ir := 0
  1659  	di := 0
  1660  	for _, op := range s.fakeBackend.ops {
  1661  		switch op.op {
  1662  		case "storesvc-snap-action":
  1663  			ir++
  1664  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  1665  				{
  1666  					InstanceName:  "core",
  1667  					SnapID:        "core-snap-id",
  1668  					Revision:      snap.R(1),
  1669  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  1670  					Epoch:         snap.E("1*"),
  1671  				},
  1672  				{
  1673  					InstanceName:  "services-snap",
  1674  					SnapID:        "services-snap-id",
  1675  					Revision:      snap.R(2),
  1676  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  1677  					Epoch:         snap.E("0"),
  1678  				},
  1679  				{
  1680  					InstanceName:  "some-snap",
  1681  					SnapID:        "some-snap-id",
  1682  					Revision:      snap.R(5),
  1683  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  1684  					Epoch:         snap.E("1*"),
  1685  				},
  1686  			})
  1687  		case "storesvc-snap-action:action":
  1688  			snapID := op.action.SnapID
  1689  			seen[snapID] = op.userID
  1690  		case "storesvc-download":
  1691  			snapName := op.name
  1692  			fakeDl := s.fakeStore.downloads[di]
  1693  			// check target path separately and clear it
  1694  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  1695  			fakeDl.target = ""
  1696  			c.Check(fakeDl, DeepEquals, fakeDownload{
  1697  				macaroon: macaroonMap[snapName],
  1698  				name:     snapName,
  1699  			}, Commentf(snapName))
  1700  			di++
  1701  		}
  1702  	}
  1703  	c.Check(ir, Equals, 2)
  1704  	// we check all snaps with each user
  1705  	c.Check(seen["some-snap-id"], Equals, 1)
  1706  	c.Check(seen["services-snap-id"], Equals, 2)
  1707  	// coalesced with one of the others
  1708  	c.Check(seen["core-snap-id"] > 0, Equals, true)
  1709  }
  1710  
  1711  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsUserRunThrough(c *C) {
  1712  	s.state.Lock()
  1713  	defer s.state.Unlock()
  1714  
  1715  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1716  		Active: true,
  1717  		Sequence: []*snap.SideInfo{
  1718  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  1719  		},
  1720  		Current:  snap.R(1),
  1721  		SnapType: "os",
  1722  	})
  1723  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1724  		Active: true,
  1725  		Sequence: []*snap.SideInfo{
  1726  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  1727  		},
  1728  		Current:  snap.R(5),
  1729  		SnapType: "app",
  1730  		UserID:   1,
  1731  	})
  1732  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1733  		Active: true,
  1734  		Sequence: []*snap.SideInfo{
  1735  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  1736  		},
  1737  		Current:  snap.R(2),
  1738  		SnapType: "app",
  1739  		UserID:   2,
  1740  	})
  1741  
  1742  	chg := s.state.NewChange("refresh", "refresh all snaps")
  1743  	// do UpdateMany using user 2 as fallback
  1744  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 2, nil)
  1745  	c.Assert(err, IsNil)
  1746  	for _, ts := range tts {
  1747  		chg.AddAll(ts)
  1748  	}
  1749  	c.Check(updated, HasLen, 3)
  1750  
  1751  	s.state.Unlock()
  1752  	defer s.se.Stop()
  1753  	s.settle(c)
  1754  	s.state.Lock()
  1755  
  1756  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1757  	c.Assert(chg.Err(), IsNil)
  1758  
  1759  	macaroonMap := map[string]string{
  1760  		"core":          "macaroon2",
  1761  		"some-snap":     "macaroon",
  1762  		"services-snap": "macaroon2",
  1763  	}
  1764  
  1765  	type snapIDuserID struct {
  1766  		snapID string
  1767  		userID int
  1768  	}
  1769  	seen := make(map[snapIDuserID]bool)
  1770  	ir := 0
  1771  	di := 0
  1772  	for _, op := range s.fakeBackend.ops {
  1773  		switch op.op {
  1774  		case "storesvc-snap-action":
  1775  			ir++
  1776  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  1777  				{
  1778  					InstanceName:  "core",
  1779  					SnapID:        "core-snap-id",
  1780  					Revision:      snap.R(1),
  1781  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  1782  					Epoch:         snap.E("1*"),
  1783  				},
  1784  				{
  1785  					InstanceName:  "services-snap",
  1786  					SnapID:        "services-snap-id",
  1787  					Revision:      snap.R(2),
  1788  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  1789  					Epoch:         snap.E("0"),
  1790  				},
  1791  				{
  1792  					InstanceName:  "some-snap",
  1793  					SnapID:        "some-snap-id",
  1794  					Revision:      snap.R(5),
  1795  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  1796  					Epoch:         snap.E("1*"),
  1797  				},
  1798  			})
  1799  		case "storesvc-snap-action:action":
  1800  			snapID := op.action.SnapID
  1801  			seen[snapIDuserID{snapID: snapID, userID: op.userID}] = true
  1802  		case "storesvc-download":
  1803  			snapName := op.name
  1804  			fakeDl := s.fakeStore.downloads[di]
  1805  			// check target path separately and clear it
  1806  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  1807  			fakeDl.target = ""
  1808  			c.Check(fakeDl, DeepEquals, fakeDownload{
  1809  				macaroon: macaroonMap[snapName],
  1810  				name:     snapName,
  1811  			}, Commentf(snapName))
  1812  			di++
  1813  		}
  1814  	}
  1815  	c.Check(ir, Equals, 2)
  1816  	// we check all snaps with each user
  1817  	c.Check(seen, DeepEquals, map[snapIDuserID]bool{
  1818  		{snapID: "core-snap-id", userID: 2}:     true,
  1819  		{snapID: "some-snap-id", userID: 1}:     true,
  1820  		{snapID: "services-snap-id", userID: 2}: true,
  1821  	})
  1822  
  1823  	var coreState, snapState snapstate.SnapState
  1824  	// user in SnapState was preserved
  1825  	err = snapstate.Get(s.state, "some-snap", &snapState)
  1826  	c.Assert(err, IsNil)
  1827  	c.Check(snapState.UserID, Equals, 1)
  1828  	c.Check(snapState.Current, DeepEquals, snap.R(11))
  1829  
  1830  	// user in SnapState was set
  1831  	err = snapstate.Get(s.state, "core", &coreState)
  1832  	c.Assert(err, IsNil)
  1833  	c.Check(coreState.UserID, Equals, 2)
  1834  	c.Check(coreState.Current, DeepEquals, snap.R(11))
  1835  
  1836  }
  1837  
  1838  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsUserWithNoStoreAuthRunThrough(c *C) {
  1839  	s.state.Lock()
  1840  	defer s.state.Unlock()
  1841  
  1842  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1843  		Active: true,
  1844  		Sequence: []*snap.SideInfo{
  1845  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  1846  		},
  1847  		Current:  snap.R(1),
  1848  		SnapType: "os",
  1849  	})
  1850  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1851  		Active: true,
  1852  		Sequence: []*snap.SideInfo{
  1853  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  1854  		},
  1855  		Current:  snap.R(5),
  1856  		SnapType: "app",
  1857  		UserID:   1,
  1858  	})
  1859  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1860  		Active: true,
  1861  		Sequence: []*snap.SideInfo{
  1862  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  1863  		},
  1864  		Current:  snap.R(2),
  1865  		SnapType: "app",
  1866  		UserID:   3,
  1867  	})
  1868  
  1869  	chg := s.state.NewChange("refresh", "refresh all snaps")
  1870  	// no user is passed to use for UpdateMany
  1871  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1872  	c.Assert(err, IsNil)
  1873  	for _, ts := range tts {
  1874  		chg.AddAll(ts)
  1875  	}
  1876  	c.Check(updated, HasLen, 3)
  1877  
  1878  	s.state.Unlock()
  1879  	defer s.se.Stop()
  1880  	s.settle(c)
  1881  	s.state.Lock()
  1882  
  1883  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1884  	c.Assert(chg.Err(), IsNil)
  1885  
  1886  	macaroonMap := map[string]string{
  1887  		"core":          "",
  1888  		"some-snap":     "macaroon",
  1889  		"services-snap": "",
  1890  	}
  1891  
  1892  	seen := make(map[string]int)
  1893  	ir := 0
  1894  	di := 0
  1895  	for _, op := range s.fakeBackend.ops {
  1896  		switch op.op {
  1897  		case "storesvc-snap-action":
  1898  			ir++
  1899  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  1900  				{
  1901  					InstanceName:  "core",
  1902  					SnapID:        "core-snap-id",
  1903  					Revision:      snap.R(1),
  1904  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  1905  					Epoch:         snap.E("1*"),
  1906  				},
  1907  				{
  1908  					InstanceName:  "services-snap",
  1909  					SnapID:        "services-snap-id",
  1910  					Revision:      snap.R(2),
  1911  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  1912  					Epoch:         snap.E("0"),
  1913  				},
  1914  				{
  1915  					InstanceName:  "some-snap",
  1916  					SnapID:        "some-snap-id",
  1917  					Revision:      snap.R(5),
  1918  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  1919  					Epoch:         snap.E("1*"),
  1920  				},
  1921  			})
  1922  		case "storesvc-snap-action:action":
  1923  			snapID := op.action.SnapID
  1924  			if _, ok := seen[snapID]; !ok {
  1925  				seen[snapID] = op.userID
  1926  			}
  1927  		case "storesvc-download":
  1928  			snapName := op.name
  1929  			fakeDl := s.fakeStore.downloads[di]
  1930  			// check target path separately and clear it
  1931  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  1932  			fakeDl.target = ""
  1933  			c.Check(fakeDl, DeepEquals, fakeDownload{
  1934  				macaroon: macaroonMap[snapName],
  1935  				name:     snapName,
  1936  			}, Commentf(snapName))
  1937  			di++
  1938  		}
  1939  	}
  1940  	c.Check(ir, Equals, 1)
  1941  	// we check all snaps with each user
  1942  	c.Check(seen["some-snap-id"], Equals, 1)
  1943  	// coalesced with request for 1
  1944  	c.Check(seen["services-snap-id"], Equals, 1)
  1945  	c.Check(seen["core-snap-id"], Equals, 1)
  1946  }
  1947  
  1948  func (s *snapmgrTestSuite) TestUpdateUndoRunThrough(c *C) {
  1949  	si := snap.SideInfo{
  1950  		RealName: "some-snap",
  1951  		SnapID:   "some-snap-id",
  1952  		Revision: snap.R(7),
  1953  	}
  1954  
  1955  	s.state.Lock()
  1956  	defer s.state.Unlock()
  1957  
  1958  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1959  		Active:   true,
  1960  		Sequence: []*snap.SideInfo{&si},
  1961  		Current:  si.Revision,
  1962  		SnapType: "app",
  1963  	})
  1964  
  1965  	chg := s.state.NewChange("install", "install a snap")
  1966  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  1967  	c.Assert(err, IsNil)
  1968  	chg.AddAll(ts)
  1969  
  1970  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "/some-snap/11")
  1971  
  1972  	s.state.Unlock()
  1973  	defer s.se.Stop()
  1974  	s.settle(c)
  1975  	s.state.Lock()
  1976  
  1977  	expected := fakeOps{
  1978  		{
  1979  			op: "storesvc-snap-action",
  1980  			curSnaps: []store.CurrentSnap{{
  1981  				InstanceName:  "some-snap",
  1982  				SnapID:        "some-snap-id",
  1983  				Revision:      snap.R(7),
  1984  				RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  1985  				Epoch:         snap.E("1*"),
  1986  			}},
  1987  			userID: 1,
  1988  		},
  1989  		{
  1990  			op: "storesvc-snap-action:action",
  1991  			action: store.SnapAction{
  1992  				Action:       "refresh",
  1993  				InstanceName: "some-snap",
  1994  				SnapID:       "some-snap-id",
  1995  				Channel:      "some-channel",
  1996  				Flags:        store.SnapActionEnforceValidation,
  1997  			},
  1998  			revno:  snap.R(11),
  1999  			userID: 1,
  2000  		},
  2001  		{
  2002  			op:   "storesvc-download",
  2003  			name: "some-snap",
  2004  		},
  2005  		{
  2006  			op:    "validate-snap:Doing",
  2007  			name:  "some-snap",
  2008  			revno: snap.R(11),
  2009  		},
  2010  		{
  2011  			op:  "current",
  2012  			old: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2013  		},
  2014  		{
  2015  			op:   "open-snap-file",
  2016  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2017  			sinfo: snap.SideInfo{
  2018  				RealName: "some-snap",
  2019  				SnapID:   "some-snap-id",
  2020  				Channel:  "some-channel",
  2021  				Revision: snap.R(11),
  2022  			},
  2023  		},
  2024  		{
  2025  			op:    "setup-snap",
  2026  			name:  "some-snap",
  2027  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2028  			revno: snap.R(11),
  2029  		},
  2030  		{
  2031  			op:   "remove-snap-aliases",
  2032  			name: "some-snap",
  2033  		},
  2034  		{
  2035  			op:   "unlink-snap",
  2036  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2037  		},
  2038  		{
  2039  			op:   "copy-data",
  2040  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2041  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2042  		},
  2043  		{
  2044  			op:    "setup-profiles:Doing",
  2045  			name:  "some-snap",
  2046  			revno: snap.R(11),
  2047  		},
  2048  		{
  2049  			op: "candidate",
  2050  			sinfo: snap.SideInfo{
  2051  				RealName: "some-snap",
  2052  				SnapID:   "some-snap-id",
  2053  				Channel:  "some-channel",
  2054  				Revision: snap.R(11),
  2055  			},
  2056  		},
  2057  		{
  2058  			op:   "link-snap.failed",
  2059  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2060  		},
  2061  		{
  2062  			op:   "unlink-snap",
  2063  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2064  		},
  2065  		{
  2066  			op:    "setup-profiles:Undoing",
  2067  			name:  "some-snap",
  2068  			revno: snap.R(11),
  2069  		},
  2070  		{
  2071  			op:   "undo-copy-snap-data",
  2072  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2073  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2074  		},
  2075  		{
  2076  			op:   "link-snap",
  2077  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2078  		},
  2079  		{
  2080  			op: "update-aliases",
  2081  		},
  2082  		{
  2083  			op:    "undo-setup-snap",
  2084  			name:  "some-snap",
  2085  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2086  			stype: "app",
  2087  		},
  2088  		{
  2089  			op:   "remove-snap-dir",
  2090  			name: "some-snap",
  2091  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  2092  		},
  2093  	}
  2094  
  2095  	// ensure all our tasks ran
  2096  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  2097  		macaroon: s.user.StoreMacaroon,
  2098  		name:     "some-snap",
  2099  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2100  	}})
  2101  	// start with an easier-to-read error if this fails:
  2102  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2103  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2104  
  2105  	// verify snaps in the system state
  2106  	var snapst snapstate.SnapState
  2107  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2108  	c.Assert(err, IsNil)
  2109  
  2110  	c.Assert(snapst.Active, Equals, true)
  2111  	c.Assert(snapst.Sequence, HasLen, 1)
  2112  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2113  		RealName: "some-snap",
  2114  		SnapID:   "some-snap-id",
  2115  		Channel:  "",
  2116  		Revision: snap.R(7),
  2117  	})
  2118  }
  2119  
  2120  func lastWithLane(tasks []*state.Task) *state.Task {
  2121  	for i := len(tasks) - 1; i >= 0; i-- {
  2122  		if lanes := tasks[i].Lanes(); len(lanes) == 1 && lanes[0] != 0 {
  2123  			return tasks[i]
  2124  		}
  2125  	}
  2126  	return nil
  2127  }
  2128  
  2129  func (s *snapmgrTestSuite) TestUpdateUndoRestoresRevisionConfig(c *C) {
  2130  	var errorTaskExecuted bool
  2131  
  2132  	// overwrite error-trigger task handler with custom one for this test
  2133  	erroringHandler := func(task *state.Task, _ *tomb.Tomb) error {
  2134  		st := task.State()
  2135  		st.Lock()
  2136  		defer st.Unlock()
  2137  
  2138  		// modify current config of some-snap
  2139  		tr := config.NewTransaction(st)
  2140  		tr.Set("some-snap", "foo", "canary")
  2141  		tr.Commit()
  2142  
  2143  		errorTaskExecuted = true
  2144  		return errors.New("error out")
  2145  	}
  2146  	s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil)
  2147  
  2148  	si := snap.SideInfo{
  2149  		RealName: "some-snap",
  2150  		SnapID:   "some-snap-id",
  2151  		Revision: snap.R(7),
  2152  	}
  2153  	si2 := snap.SideInfo{
  2154  		RealName: "some-snap",
  2155  		SnapID:   "some-snap-id",
  2156  		Revision: snap.R(6),
  2157  	}
  2158  
  2159  	s.state.Lock()
  2160  	defer s.state.Unlock()
  2161  
  2162  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2163  		Active:          true,
  2164  		Sequence:        []*snap.SideInfo{&si2, &si},
  2165  		TrackingChannel: "latest/stable",
  2166  		Current:         si.Revision,
  2167  		SnapType:        "app",
  2168  	})
  2169  
  2170  	// set some configuration
  2171  	tr := config.NewTransaction(s.state)
  2172  	tr.Set("some-snap", "foo", "revision 7 value")
  2173  	tr.Commit()
  2174  	config.SaveRevisionConfig(s.state, "some-snap", snap.R(7))
  2175  
  2176  	chg := s.state.NewChange("install", "install a snap")
  2177  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2178  	c.Assert(err, IsNil)
  2179  	chg.AddAll(ts)
  2180  
  2181  	last := lastWithLane(ts.Tasks())
  2182  	c.Assert(last, NotNil)
  2183  
  2184  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  2185  	terr.WaitFor(last)
  2186  	terr.JoinLane(last.Lanes()[0])
  2187  	chg.AddTask(terr)
  2188  
  2189  	s.state.Unlock()
  2190  	defer s.se.Stop()
  2191  	s.settle(c)
  2192  	s.state.Lock()
  2193  
  2194  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  2195  	c.Check(errorTaskExecuted, Equals, true)
  2196  
  2197  	// after undoing the update some-snap config should be restored to that of rev.7
  2198  	var val string
  2199  	tr = config.NewTransaction(s.state)
  2200  	c.Assert(tr.Get("some-snap", "foo", &val), IsNil)
  2201  	c.Check(val, Equals, "revision 7 value")
  2202  }
  2203  
  2204  func (s *snapmgrTestSuite) TestUpdateMakesConfigSnapshot(c *C) {
  2205  	s.state.Lock()
  2206  	defer s.state.Unlock()
  2207  
  2208  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2209  		Active: true,
  2210  		Sequence: []*snap.SideInfo{
  2211  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  2212  		},
  2213  		Current:  snap.R(1),
  2214  		SnapType: "app",
  2215  	})
  2216  
  2217  	tr := config.NewTransaction(s.state)
  2218  	tr.Set("some-snap", "foo", "bar")
  2219  	tr.Commit()
  2220  
  2221  	var cfgs map[string]interface{}
  2222  	// we don't have config snapshots yet
  2223  	c.Assert(s.state.Get("revision-config", &cfgs), Equals, state.ErrNoState)
  2224  
  2225  	chg := s.state.NewChange("update", "update a snap")
  2226  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(2)}
  2227  	ts, err := snapstate.Update(s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2228  	c.Assert(err, IsNil)
  2229  	chg.AddAll(ts)
  2230  
  2231  	s.state.Unlock()
  2232  	defer s.se.Stop()
  2233  	s.settle(c)
  2234  
  2235  	s.state.Lock()
  2236  	cfgs = nil
  2237  	// config copy of rev. 1 has been made
  2238  	c.Assert(s.state.Get("revision-config", &cfgs), IsNil)
  2239  	c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{
  2240  		"1": map[string]interface{}{
  2241  			"foo": "bar",
  2242  		},
  2243  	})
  2244  }
  2245  
  2246  func (s *snapmgrTestSuite) TestUpdateTotalUndoRunThrough(c *C) {
  2247  	si := snap.SideInfo{
  2248  		RealName: "some-snap",
  2249  		SnapID:   "some-snap-id",
  2250  		Revision: snap.R(7),
  2251  	}
  2252  
  2253  	s.state.Lock()
  2254  	defer s.state.Unlock()
  2255  
  2256  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2257  		Active:          true,
  2258  		Sequence:        []*snap.SideInfo{&si},
  2259  		TrackingChannel: "latest/stable",
  2260  		Current:         si.Revision,
  2261  		SnapType:        "app",
  2262  	})
  2263  
  2264  	chg := s.state.NewChange("install", "install a snap")
  2265  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2266  	c.Assert(err, IsNil)
  2267  	chg.AddAll(ts)
  2268  
  2269  	// We need to make it not be rerefresh, and we could do just
  2270  	// that but instead we do the 'right' thing and attach it to
  2271  	// the last task that's on a lane.
  2272  	last := lastWithLane(ts.Tasks())
  2273  	c.Assert(last, NotNil)
  2274  
  2275  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  2276  	terr.WaitFor(last)
  2277  	terr.JoinLane(last.Lanes()[0])
  2278  	chg.AddTask(terr)
  2279  
  2280  	s.state.Unlock()
  2281  	defer s.se.Stop()
  2282  	s.settle(c)
  2283  	s.state.Lock()
  2284  
  2285  	expected := fakeOps{
  2286  		{
  2287  			op: "storesvc-snap-action",
  2288  			curSnaps: []store.CurrentSnap{{
  2289  				InstanceName:    "some-snap",
  2290  				SnapID:          "some-snap-id",
  2291  				Revision:        snap.R(7),
  2292  				TrackingChannel: "latest/stable",
  2293  				RefreshedDate:   fakeRevDateEpoch.AddDate(0, 0, 7),
  2294  				Epoch:           snap.E("1*"),
  2295  			}},
  2296  			userID: 1,
  2297  		},
  2298  		{
  2299  			op: "storesvc-snap-action:action",
  2300  			action: store.SnapAction{
  2301  				Action:       "refresh",
  2302  				InstanceName: "some-snap",
  2303  				SnapID:       "some-snap-id",
  2304  				Channel:      "some-channel",
  2305  				Flags:        store.SnapActionEnforceValidation,
  2306  			},
  2307  			revno:  snap.R(11),
  2308  			userID: 1,
  2309  		},
  2310  		{
  2311  			op:   "storesvc-download",
  2312  			name: "some-snap",
  2313  		},
  2314  		{
  2315  			op:    "validate-snap:Doing",
  2316  			name:  "some-snap",
  2317  			revno: snap.R(11),
  2318  		},
  2319  		{
  2320  			op:  "current",
  2321  			old: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2322  		},
  2323  		{
  2324  			op:   "open-snap-file",
  2325  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2326  			sinfo: snap.SideInfo{
  2327  				RealName: "some-snap",
  2328  				SnapID:   "some-snap-id",
  2329  				Channel:  "some-channel",
  2330  				Revision: snap.R(11),
  2331  			},
  2332  		},
  2333  		{
  2334  			op:    "setup-snap",
  2335  			name:  "some-snap",
  2336  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2337  			revno: snap.R(11),
  2338  		},
  2339  		{
  2340  			op:   "remove-snap-aliases",
  2341  			name: "some-snap",
  2342  		},
  2343  		{
  2344  			op:   "unlink-snap",
  2345  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2346  		},
  2347  		{
  2348  			op:   "copy-data",
  2349  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2350  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2351  		},
  2352  		{
  2353  			op:    "setup-profiles:Doing",
  2354  			name:  "some-snap",
  2355  			revno: snap.R(11),
  2356  		},
  2357  		{
  2358  			op: "candidate",
  2359  			sinfo: snap.SideInfo{
  2360  				RealName: "some-snap",
  2361  				SnapID:   "some-snap-id",
  2362  				Channel:  "some-channel",
  2363  				Revision: snap.R(11),
  2364  			},
  2365  		},
  2366  		{
  2367  			op:   "link-snap",
  2368  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2369  		},
  2370  		{
  2371  			op:    "auto-connect:Doing",
  2372  			name:  "some-snap",
  2373  			revno: snap.R(11),
  2374  		},
  2375  		{
  2376  			op: "update-aliases",
  2377  		},
  2378  		// undoing everything from here down...
  2379  		{
  2380  			op:   "remove-snap-aliases",
  2381  			name: "some-snap",
  2382  		},
  2383  		{
  2384  			op: "current-snap-service-states",
  2385  		},
  2386  		{
  2387  			op:   "unlink-snap",
  2388  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2389  		},
  2390  		{
  2391  			op:    "setup-profiles:Undoing",
  2392  			name:  "some-snap",
  2393  			revno: snap.R(11),
  2394  		},
  2395  		{
  2396  			op:   "undo-copy-snap-data",
  2397  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2398  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2399  		},
  2400  		{
  2401  			op:   "link-snap",
  2402  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2403  		},
  2404  		{
  2405  			op: "update-aliases",
  2406  		},
  2407  		{
  2408  			op:    "undo-setup-snap",
  2409  			name:  "some-snap",
  2410  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2411  			stype: "app",
  2412  		},
  2413  		{
  2414  			op:   "remove-snap-dir",
  2415  			name: "some-snap",
  2416  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  2417  		},
  2418  	}
  2419  
  2420  	// ensure all our tasks ran
  2421  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  2422  		macaroon: s.user.StoreMacaroon,
  2423  		name:     "some-snap",
  2424  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2425  	}})
  2426  	// friendlier failure first
  2427  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2428  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2429  
  2430  	// verify snaps in the system state
  2431  	var snapst snapstate.SnapState
  2432  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2433  	c.Assert(err, IsNil)
  2434  
  2435  	c.Assert(snapst.Active, Equals, true)
  2436  	c.Assert(snapst.TrackingChannel, Equals, "latest/stable")
  2437  	c.Assert(snapst.Sequence, HasLen, 1)
  2438  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2439  		RealName: "some-snap",
  2440  		SnapID:   "some-snap-id",
  2441  		Channel:  "",
  2442  		Revision: snap.R(7),
  2443  	})
  2444  }
  2445  
  2446  func (s *snapmgrTestSuite) TestUpdateSameRevision(c *C) {
  2447  	si := snap.SideInfo{
  2448  		RealName: "some-snap",
  2449  		SnapID:   "some-snap-id",
  2450  		Revision: snap.R(7),
  2451  	}
  2452  
  2453  	s.state.Lock()
  2454  	defer s.state.Unlock()
  2455  
  2456  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2457  		Active:          true,
  2458  		Sequence:        []*snap.SideInfo{&si},
  2459  		TrackingChannel: "channel-for-7/stable",
  2460  		Current:         si.Revision,
  2461  	})
  2462  
  2463  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7/stable"}, s.user.ID, snapstate.Flags{})
  2464  	c.Assert(err, Equals, store.ErrNoUpdateAvailable)
  2465  }
  2466  
  2467  func (s *snapmgrTestSuite) TestUpdateToRevisionRememberedUserRunThrough(c *C) {
  2468  	s.state.Lock()
  2469  	defer s.state.Unlock()
  2470  
  2471  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2472  		Active: true,
  2473  		Sequence: []*snap.SideInfo{
  2474  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  2475  		},
  2476  		Current:  snap.R(5),
  2477  		SnapType: "app",
  2478  		UserID:   1,
  2479  	})
  2480  
  2481  	chg := s.state.NewChange("refresh", "refresh a snap")
  2482  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(11)}, 0, snapstate.Flags{})
  2483  	c.Assert(err, IsNil)
  2484  	chg.AddAll(ts)
  2485  
  2486  	s.state.Unlock()
  2487  	defer s.se.Stop()
  2488  	s.settle(c)
  2489  	s.state.Lock()
  2490  
  2491  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  2492  	c.Assert(chg.Err(), IsNil)
  2493  
  2494  	for _, op := range s.fakeBackend.ops {
  2495  		switch op.op {
  2496  		case "storesvc-snap-action:action":
  2497  			c.Check(op.userID, Equals, 1)
  2498  		case "storesvc-download":
  2499  			snapName := op.name
  2500  			c.Check(s.fakeStore.downloads[0], DeepEquals, fakeDownload{
  2501  				macaroon: "macaroon",
  2502  				name:     "some-snap",
  2503  				target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2504  			}, Commentf(snapName))
  2505  		}
  2506  	}
  2507  }
  2508  
  2509  // A noResultsStore returns no results for install/refresh requests
  2510  type noResultsStore struct {
  2511  	*fakeStore
  2512  }
  2513  
  2514  func (n noResultsStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) {
  2515  	if assertQuery != nil {
  2516  		panic("no assertion query support")
  2517  	}
  2518  	return nil, nil, &store.SnapActionError{NoResults: true}
  2519  }
  2520  
  2521  func (s *snapmgrTestSuite) TestUpdateNoStoreResults(c *C) {
  2522  	s.state.Lock()
  2523  	defer s.state.Unlock()
  2524  
  2525  	snapstate.ReplaceStore(s.state, noResultsStore{fakeStore: s.fakeStore})
  2526  
  2527  	// this is an atypical case in which the store didn't return
  2528  	// an error nor a result, we are defensive and return
  2529  	// a reasonable error
  2530  	si := snap.SideInfo{
  2531  		RealName: "some-snap",
  2532  		SnapID:   "some-snap-id",
  2533  		Revision: snap.R(7),
  2534  	}
  2535  
  2536  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2537  		Active:          true,
  2538  		Sequence:        []*snap.SideInfo{&si},
  2539  		TrackingChannel: "channel-for-7/stable",
  2540  		Current:         si.Revision,
  2541  	})
  2542  
  2543  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  2544  	c.Assert(err, Equals, snapstate.ErrMissingExpectedResult)
  2545  }
  2546  
  2547  func (s *snapmgrTestSuite) TestUpdateNoStoreResultsWithChannelChange(c *C) {
  2548  	s.state.Lock()
  2549  	defer s.state.Unlock()
  2550  
  2551  	snapstate.ReplaceStore(s.state, noResultsStore{fakeStore: s.fakeStore})
  2552  
  2553  	// this is an atypical case in which the store didn't return
  2554  	// an error nor a result, we are defensive and return
  2555  	// a reasonable error
  2556  	si := snap.SideInfo{
  2557  		RealName: "some-snap",
  2558  		SnapID:   "some-snap-id",
  2559  		Revision: snap.R(7),
  2560  	}
  2561  
  2562  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2563  		Active:          true,
  2564  		Sequence:        []*snap.SideInfo{&si},
  2565  		TrackingChannel: "channel-for-9/stable",
  2566  		Current:         si.Revision,
  2567  	})
  2568  
  2569  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  2570  	c.Assert(err, Equals, snapstate.ErrMissingExpectedResult)
  2571  }
  2572  
  2573  func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchesChannel(c *C) {
  2574  	si := snap.SideInfo{
  2575  		RealName: "some-snap",
  2576  		SnapID:   "some-snap-id",
  2577  		Revision: snap.R(7),
  2578  	}
  2579  
  2580  	s.state.Lock()
  2581  	defer s.state.Unlock()
  2582  
  2583  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2584  		Active:          true,
  2585  		Sequence:        []*snap.SideInfo{&si},
  2586  		TrackingChannel: "other-chanenl/stable",
  2587  		Current:         si.Revision,
  2588  	})
  2589  
  2590  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7/stable"}, s.user.ID, snapstate.Flags{})
  2591  	c.Assert(err, IsNil)
  2592  	c.Check(ts.Tasks(), HasLen, 1)
  2593  	c.Check(ts.Tasks()[0].Kind(), Equals, "switch-snap-channel")
  2594  }
  2595  
  2596  func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchesChannelConflict(c *C) {
  2597  	si := snap.SideInfo{
  2598  		RealName: "some-snap",
  2599  		SnapID:   "some-snap-id",
  2600  		Revision: snap.R(7),
  2601  	}
  2602  
  2603  	s.state.Lock()
  2604  	defer s.state.Unlock()
  2605  
  2606  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2607  		Active:          true,
  2608  		Sequence:        []*snap.SideInfo{&si},
  2609  		TrackingChannel: "other-channel/stable",
  2610  		Current:         si.Revision,
  2611  	})
  2612  
  2613  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  2614  	c.Assert(err, IsNil)
  2615  	// make it visible
  2616  	s.state.NewChange("refresh", "refresh a snap").AddAll(ts)
  2617  
  2618  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  2619  	c.Check(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`)
  2620  }
  2621  
  2622  func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchChannelRunThrough(c *C) {
  2623  	si := snap.SideInfo{
  2624  		RealName: "some-snap",
  2625  		SnapID:   "some-snap-id",
  2626  		Channel:  "other-channel",
  2627  		Revision: snap.R(7),
  2628  	}
  2629  
  2630  	s.state.Lock()
  2631  	defer s.state.Unlock()
  2632  
  2633  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2634  		Active:          true,
  2635  		Sequence:        []*snap.SideInfo{&si},
  2636  		TrackingChannel: "other-channel/stable",
  2637  		Current:         si.Revision,
  2638  	})
  2639  
  2640  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7/stable"}, s.user.ID, snapstate.Flags{})
  2641  	c.Assert(err, IsNil)
  2642  	chg := s.state.NewChange("refresh", "refresh a snap")
  2643  	chg.AddAll(ts)
  2644  
  2645  	s.state.Unlock()
  2646  	defer s.se.Stop()
  2647  	s.settle(c)
  2648  	s.state.Lock()
  2649  
  2650  	expected := fakeOps{
  2651  		// we just expect the "storesvc-snap-action" ops, we
  2652  		// don't have a fakeOp for switchChannel because it has
  2653  		// not a backend method, it just manipulates the state
  2654  		{
  2655  			op: "storesvc-snap-action",
  2656  			curSnaps: []store.CurrentSnap{{
  2657  				InstanceName:    "some-snap",
  2658  				SnapID:          "some-snap-id",
  2659  				Revision:        snap.R(7),
  2660  				TrackingChannel: "other-channel/stable",
  2661  				RefreshedDate:   fakeRevDateEpoch.AddDate(0, 0, 7),
  2662  				Epoch:           snap.E("1*"),
  2663  			}},
  2664  			userID: 1,
  2665  		},
  2666  
  2667  		{
  2668  			op: "storesvc-snap-action:action",
  2669  			action: store.SnapAction{
  2670  				Action:       "refresh",
  2671  				InstanceName: "some-snap",
  2672  				SnapID:       "some-snap-id",
  2673  				Channel:      "channel-for-7/stable",
  2674  				Flags:        store.SnapActionEnforceValidation,
  2675  			},
  2676  			userID: 1,
  2677  		},
  2678  	}
  2679  
  2680  	// start with an easier-to-read error if this fails:
  2681  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2682  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2683  
  2684  	// verify snapSetup info
  2685  	var snapsup snapstate.SnapSetup
  2686  	task := ts.Tasks()[0]
  2687  	err = task.Get("snap-setup", &snapsup)
  2688  	c.Assert(err, IsNil)
  2689  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  2690  		Channel:  "channel-for-7/stable",
  2691  		SideInfo: snapsup.SideInfo,
  2692  	})
  2693  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  2694  		RealName: "some-snap",
  2695  		SnapID:   "some-snap-id",
  2696  		Revision: snap.R(7),
  2697  		Channel:  "channel-for-7/stable",
  2698  	})
  2699  
  2700  	// verify snaps in the system state
  2701  	var snapst snapstate.SnapState
  2702  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2703  	c.Assert(err, IsNil)
  2704  
  2705  	c.Assert(snapst.Active, Equals, true)
  2706  	c.Assert(snapst.Sequence, HasLen, 1)
  2707  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2708  		RealName: "some-snap",
  2709  		SnapID:   "some-snap-id",
  2710  		Channel:  "channel-for-7/stable",
  2711  		Revision: snap.R(7),
  2712  	})
  2713  }
  2714  
  2715  func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidation(c *C) {
  2716  	si := snap.SideInfo{
  2717  		RealName: "some-snap",
  2718  		SnapID:   "some-snap-id",
  2719  		Revision: snap.R(7),
  2720  	}
  2721  
  2722  	s.state.Lock()
  2723  	defer s.state.Unlock()
  2724  
  2725  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2726  		Active:          true,
  2727  		Sequence:        []*snap.SideInfo{&si},
  2728  		TrackingChannel: "channel-for-7/stable",
  2729  		Current:         si.Revision,
  2730  	})
  2731  
  2732  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7/stable"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  2733  	c.Assert(err, IsNil)
  2734  	c.Check(ts.Tasks(), HasLen, 1)
  2735  	c.Check(ts.Tasks()[0].Kind(), Equals, "toggle-snap-flags")
  2736  }
  2737  
  2738  func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidationConflict(c *C) {
  2739  	si := snap.SideInfo{
  2740  		RealName: "some-snap",
  2741  		SnapID:   "some-snap-id",
  2742  		Revision: snap.R(7),
  2743  	}
  2744  
  2745  	s.state.Lock()
  2746  	defer s.state.Unlock()
  2747  
  2748  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2749  		Active:          true,
  2750  		Sequence:        []*snap.SideInfo{&si},
  2751  		TrackingChannel: "channel-for-7/stable",
  2752  		Current:         si.Revision,
  2753  	})
  2754  
  2755  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  2756  	c.Assert(err, IsNil)
  2757  	// make it visible
  2758  	s.state.NewChange("refresh", "refresh a snap").AddAll(ts)
  2759  
  2760  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  2761  	c.Check(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`)
  2762  
  2763  }
  2764  
  2765  func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidationRunThrough(c *C) {
  2766  	si := snap.SideInfo{
  2767  		RealName: "some-snap",
  2768  		SnapID:   "some-snap-id",
  2769  		Revision: snap.R(7),
  2770  		Channel:  "channel-for-7",
  2771  	}
  2772  
  2773  	s.state.Lock()
  2774  	defer s.state.Unlock()
  2775  
  2776  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2777  		Active:          true,
  2778  		Sequence:        []*snap.SideInfo{&si},
  2779  		TrackingChannel: "channel-for-7/stable",
  2780  		Current:         si.Revision,
  2781  	})
  2782  
  2783  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7/stable"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  2784  	c.Assert(err, IsNil)
  2785  
  2786  	chg := s.state.NewChange("refresh", "refresh a snap")
  2787  	chg.AddAll(ts)
  2788  
  2789  	s.state.Unlock()
  2790  	defer s.se.Stop()
  2791  	s.settle(c)
  2792  	s.state.Lock()
  2793  
  2794  	// verify snapSetup info
  2795  	var snapsup snapstate.SnapSetup
  2796  	task := ts.Tasks()[0]
  2797  	err = task.Get("snap-setup", &snapsup)
  2798  	c.Assert(err, IsNil)
  2799  	c.Check(snapsup, DeepEquals, snapstate.SnapSetup{
  2800  		SideInfo: snapsup.SideInfo,
  2801  		Flags: snapstate.Flags{
  2802  			IgnoreValidation: true,
  2803  		},
  2804  	})
  2805  	c.Check(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  2806  		RealName: "some-snap",
  2807  		SnapID:   "some-snap-id",
  2808  		Revision: snap.R(7),
  2809  		Channel:  "channel-for-7",
  2810  	})
  2811  
  2812  	// verify snaps in the system state
  2813  	var snapst snapstate.SnapState
  2814  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2815  	c.Assert(err, IsNil)
  2816  
  2817  	c.Check(snapst.Active, Equals, true)
  2818  	c.Check(snapst.Sequence, HasLen, 1)
  2819  	c.Check(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2820  		RealName: "some-snap",
  2821  		SnapID:   "some-snap-id",
  2822  		Channel:  "channel-for-7",
  2823  		Revision: snap.R(7),
  2824  	})
  2825  	c.Check(snapst.IgnoreValidation, Equals, true)
  2826  }
  2827  
  2828  func (s *snapmgrTestSuite) TestUpdateValidateRefreshesSaysNo(c *C) {
  2829  	si := snap.SideInfo{
  2830  		RealName: "some-snap",
  2831  		SnapID:   "some-snap-id",
  2832  		Revision: snap.R(7),
  2833  	}
  2834  
  2835  	s.state.Lock()
  2836  	defer s.state.Unlock()
  2837  
  2838  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2839  		Active:   true,
  2840  		Sequence: []*snap.SideInfo{&si},
  2841  		Current:  si.Revision,
  2842  	})
  2843  
  2844  	validateErr := errors.New("refresh control error")
  2845  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  2846  		c.Check(refreshes, HasLen, 1)
  2847  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  2848  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  2849  		c.Check(ignoreValidation, HasLen, 0)
  2850  		return nil, validateErr
  2851  	}
  2852  	// hook it up
  2853  	snapstate.ValidateRefreshes = validateRefreshes
  2854  
  2855  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
  2856  	c.Assert(err, Equals, validateErr)
  2857  }
  2858  
  2859  func (s *snapmgrTestSuite) TestUpdateValidateRefreshesSaysNoButIgnoreValidationIsSet(c *C) {
  2860  	si := snap.SideInfo{
  2861  		RealName: "some-snap",
  2862  		SnapID:   "some-snap-id",
  2863  		Revision: snap.R(7),
  2864  	}
  2865  
  2866  	s.state.Lock()
  2867  	defer s.state.Unlock()
  2868  
  2869  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2870  		Active:   true,
  2871  		Sequence: []*snap.SideInfo{&si},
  2872  		Current:  si.Revision,
  2873  		SnapType: "app",
  2874  	})
  2875  
  2876  	validateErr := errors.New("refresh control error")
  2877  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  2878  		return nil, validateErr
  2879  	}
  2880  	// hook it up
  2881  	snapstate.ValidateRefreshes = validateRefreshes
  2882  
  2883  	flags := snapstate.Flags{JailMode: true, IgnoreValidation: true}
  2884  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  2885  	c.Assert(err, IsNil)
  2886  
  2887  	var snapsup snapstate.SnapSetup
  2888  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  2889  	c.Assert(err, IsNil)
  2890  	c.Check(snapsup.Flags, DeepEquals, flags.ForSnapSetup())
  2891  }
  2892  
  2893  func (s *snapmgrTestSuite) TestUpdateIgnoreValidationSticky(c *C) {
  2894  	si := snap.SideInfo{
  2895  		RealName: "some-snap",
  2896  		SnapID:   "some-snap-id",
  2897  		Revision: snap.R(7),
  2898  	}
  2899  
  2900  	s.state.Lock()
  2901  	defer s.state.Unlock()
  2902  
  2903  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2904  		Active:   true,
  2905  		Sequence: []*snap.SideInfo{&si},
  2906  		Current:  si.Revision,
  2907  		SnapType: "app",
  2908  	})
  2909  
  2910  	validateErr := errors.New("refresh control error")
  2911  	validateRefreshesFail := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  2912  		c.Check(refreshes, HasLen, 1)
  2913  		if len(ignoreValidation) == 0 {
  2914  			return nil, validateErr
  2915  		}
  2916  		c.Check(ignoreValidation, DeepEquals, map[string]bool{
  2917  			"some-snap": true,
  2918  		})
  2919  		return refreshes, nil
  2920  	}
  2921  	// hook it up
  2922  	snapstate.ValidateRefreshes = validateRefreshesFail
  2923  
  2924  	flags := snapstate.Flags{IgnoreValidation: true}
  2925  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  2926  	c.Assert(err, IsNil)
  2927  
  2928  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  2929  		op: "storesvc-snap-action",
  2930  		curSnaps: []store.CurrentSnap{{
  2931  			InstanceName:     "some-snap",
  2932  			SnapID:           "some-snap-id",
  2933  			Revision:         snap.R(7),
  2934  			IgnoreValidation: false,
  2935  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  2936  			Epoch:            snap.E("1*"),
  2937  		}},
  2938  		userID: 1,
  2939  	})
  2940  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  2941  		op:    "storesvc-snap-action:action",
  2942  		revno: snap.R(11),
  2943  		action: store.SnapAction{
  2944  			Action:       "refresh",
  2945  			InstanceName: "some-snap",
  2946  			SnapID:       "some-snap-id",
  2947  			Channel:      "stable",
  2948  			Flags:        store.SnapActionIgnoreValidation,
  2949  		},
  2950  		userID: 1,
  2951  	})
  2952  
  2953  	chg := s.state.NewChange("refresh", "refresh snap")
  2954  	chg.AddAll(ts)
  2955  
  2956  	s.state.Unlock()
  2957  	defer s.se.Stop()
  2958  	s.settle(c)
  2959  	s.state.Lock()
  2960  
  2961  	// verify snap has IgnoreValidation set
  2962  	var snapst snapstate.SnapState
  2963  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2964  	c.Assert(err, IsNil)
  2965  	c.Check(snapst.IgnoreValidation, Equals, true)
  2966  	c.Check(snapst.Current, Equals, snap.R(11))
  2967  
  2968  	s.fakeBackend.ops = nil
  2969  	s.fakeStore.refreshRevnos = map[string]snap.Revision{
  2970  		"some-snap-id": snap.R(12),
  2971  	}
  2972  	_, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  2973  	c.Assert(err, IsNil)
  2974  	c.Check(tts, HasLen, 2)
  2975  	verifyLastTasksetIsReRefresh(c, tts)
  2976  
  2977  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  2978  		op: "storesvc-snap-action",
  2979  		curSnaps: []store.CurrentSnap{{
  2980  			InstanceName:     "some-snap",
  2981  			SnapID:           "some-snap-id",
  2982  			Revision:         snap.R(11),
  2983  			TrackingChannel:  "latest/stable",
  2984  			IgnoreValidation: true,
  2985  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 11),
  2986  			Epoch:            snap.E("1*"),
  2987  		}},
  2988  		userID: 1,
  2989  	})
  2990  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  2991  		op:    "storesvc-snap-action:action",
  2992  		revno: snap.R(12),
  2993  		action: store.SnapAction{
  2994  			Action:       "refresh",
  2995  			InstanceName: "some-snap",
  2996  			SnapID:       "some-snap-id",
  2997  			Flags:        0,
  2998  		},
  2999  		userID: 1,
  3000  	})
  3001  
  3002  	chg = s.state.NewChange("refresh", "refresh snaps")
  3003  	chg.AddAll(tts[0])
  3004  
  3005  	s.state.Unlock()
  3006  	defer s.se.Stop()
  3007  	s.settle(c)
  3008  	s.state.Lock()
  3009  
  3010  	snapst = snapstate.SnapState{}
  3011  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3012  	c.Assert(err, IsNil)
  3013  	c.Check(snapst.IgnoreValidation, Equals, true)
  3014  	c.Check(snapst.Current, Equals, snap.R(12))
  3015  
  3016  	// reset ignore validation
  3017  	s.fakeBackend.ops = nil
  3018  	s.fakeStore.refreshRevnos = map[string]snap.Revision{
  3019  		"some-snap-id": snap.R(11),
  3020  	}
  3021  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  3022  		return refreshes, nil
  3023  	}
  3024  	// hook it up
  3025  	snapstate.ValidateRefreshes = validateRefreshes
  3026  	flags = snapstate.Flags{}
  3027  	ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  3028  	c.Assert(err, IsNil)
  3029  
  3030  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3031  		op: "storesvc-snap-action",
  3032  		curSnaps: []store.CurrentSnap{{
  3033  			InstanceName:     "some-snap",
  3034  			SnapID:           "some-snap-id",
  3035  			Revision:         snap.R(12),
  3036  			TrackingChannel:  "latest/stable",
  3037  			IgnoreValidation: true,
  3038  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 12),
  3039  			Epoch:            snap.E("1*"),
  3040  		}},
  3041  		userID: 1,
  3042  	})
  3043  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  3044  		op:    "storesvc-snap-action:action",
  3045  		revno: snap.R(11),
  3046  		action: store.SnapAction{
  3047  			Action:       "refresh",
  3048  			InstanceName: "some-snap",
  3049  			SnapID:       "some-snap-id",
  3050  			Channel:      "latest/stable",
  3051  			Flags:        store.SnapActionEnforceValidation,
  3052  		},
  3053  		userID: 1,
  3054  	})
  3055  
  3056  	chg = s.state.NewChange("refresh", "refresh snap")
  3057  	chg.AddAll(ts)
  3058  
  3059  	s.state.Unlock()
  3060  	defer s.se.Stop()
  3061  	s.settle(c)
  3062  	s.state.Lock()
  3063  
  3064  	snapst = snapstate.SnapState{}
  3065  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3066  	c.Assert(err, IsNil)
  3067  	c.Check(snapst.IgnoreValidation, Equals, false)
  3068  	c.Check(snapst.Current, Equals, snap.R(11))
  3069  }
  3070  
  3071  func (s *snapmgrTestSuite) TestParallelInstanceUpdateIgnoreValidationSticky(c *C) {
  3072  	si := snap.SideInfo{
  3073  		RealName: "some-snap",
  3074  		SnapID:   "some-snap-id",
  3075  		Revision: snap.R(7),
  3076  	}
  3077  
  3078  	s.state.Lock()
  3079  	defer s.state.Unlock()
  3080  
  3081  	tr := config.NewTransaction(s.state)
  3082  	tr.Set("core", "experimental.parallel-instances", true)
  3083  	tr.Commit()
  3084  
  3085  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3086  		Active:   true,
  3087  		Sequence: []*snap.SideInfo{&si},
  3088  		Current:  si.Revision,
  3089  		SnapType: "app",
  3090  	})
  3091  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  3092  		Active:      true,
  3093  		Sequence:    []*snap.SideInfo{&si},
  3094  		Current:     si.Revision,
  3095  		SnapType:    "app",
  3096  		InstanceKey: "instance",
  3097  	})
  3098  
  3099  	validateErr := errors.New("refresh control error")
  3100  	validateRefreshesFail := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  3101  		c.Check(refreshes, HasLen, 2)
  3102  		if len(ignoreValidation) == 0 {
  3103  			return nil, validateErr
  3104  		}
  3105  		c.Check(ignoreValidation, DeepEquals, map[string]bool{
  3106  			"some-snap_instance": true,
  3107  		})
  3108  		return refreshes, nil
  3109  	}
  3110  	// hook it up
  3111  	snapstate.ValidateRefreshes = validateRefreshesFail
  3112  
  3113  	flags := snapstate.Flags{IgnoreValidation: true}
  3114  	ts, err := snapstate.Update(s.state, "some-snap_instance", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  3115  	c.Assert(err, IsNil)
  3116  
  3117  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3118  		op: "storesvc-snap-action",
  3119  		curSnaps: []store.CurrentSnap{{
  3120  			InstanceName:     "some-snap",
  3121  			SnapID:           "some-snap-id",
  3122  			Revision:         snap.R(7),
  3123  			IgnoreValidation: false,
  3124  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  3125  			Epoch:            snap.E("1*"),
  3126  		}, {
  3127  			InstanceName:     "some-snap_instance",
  3128  			SnapID:           "some-snap-id",
  3129  			Revision:         snap.R(7),
  3130  			IgnoreValidation: false,
  3131  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  3132  			Epoch:            snap.E("1*"),
  3133  		}},
  3134  		userID: 1,
  3135  	})
  3136  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  3137  		op:    "storesvc-snap-action:action",
  3138  		revno: snap.R(11),
  3139  		action: store.SnapAction{
  3140  			Action:       "refresh",
  3141  			InstanceName: "some-snap_instance",
  3142  			SnapID:       "some-snap-id",
  3143  			Channel:      "stable",
  3144  			Flags:        store.SnapActionIgnoreValidation,
  3145  		},
  3146  		userID: 1,
  3147  	})
  3148  
  3149  	chg := s.state.NewChange("refresh", "refresh snaps")
  3150  	chg.AddAll(ts)
  3151  
  3152  	s.state.Unlock()
  3153  	defer s.se.Stop()
  3154  	s.settle(c)
  3155  	s.state.Lock()
  3156  
  3157  	// ensure all our tasks ran
  3158  	c.Assert(chg.Err(), IsNil)
  3159  	c.Assert(chg.IsReady(), Equals, true)
  3160  
  3161  	// verify snap 'instance' has IgnoreValidation set and the snap was
  3162  	// updated
  3163  	var snapst snapstate.SnapState
  3164  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  3165  	c.Assert(err, IsNil)
  3166  	c.Check(snapst.IgnoreValidation, Equals, true)
  3167  	c.Check(snapst.Current, Equals, snap.R(11))
  3168  	// and the other snap does not
  3169  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3170  	c.Assert(err, IsNil)
  3171  	c.Check(snapst.Current, Equals, snap.R(7))
  3172  	c.Check(snapst.IgnoreValidation, Equals, false)
  3173  
  3174  	s.fakeBackend.ops = nil
  3175  	s.fakeStore.refreshRevnos = map[string]snap.Revision{
  3176  		"some-snap-id": snap.R(12),
  3177  	}
  3178  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "some-snap_instance"}, s.user.ID, nil)
  3179  	c.Assert(err, IsNil)
  3180  	c.Check(tts, HasLen, 3)
  3181  	verifyLastTasksetIsReRefresh(c, tts)
  3182  	sort.Strings(updates)
  3183  	c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"})
  3184  
  3185  	chg = s.state.NewChange("refresh", "refresh snaps")
  3186  	for _, ts := range tts[:len(tts)-1] {
  3187  		chg.AddAll(ts)
  3188  	}
  3189  
  3190  	s.state.Unlock()
  3191  	s.settle(c)
  3192  	s.state.Lock()
  3193  
  3194  	// ensure all our tasks ran
  3195  	c.Assert(chg.Err(), IsNil)
  3196  	c.Assert(chg.IsReady(), Equals, true)
  3197  
  3198  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3199  	c.Assert(err, IsNil)
  3200  	c.Check(snapst.IgnoreValidation, Equals, false)
  3201  	c.Check(snapst.Current, Equals, snap.R(12))
  3202  
  3203  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  3204  	c.Assert(err, IsNil)
  3205  	c.Check(snapst.IgnoreValidation, Equals, true)
  3206  	c.Check(snapst.Current, Equals, snap.R(12))
  3207  
  3208  	for i := 0; i < 2; i++ {
  3209  		op := s.fakeBackend.ops[i]
  3210  		switch op.op {
  3211  		case "storesvc-snap-action":
  3212  			c.Check(op, DeepEquals, fakeOp{
  3213  				op: "storesvc-snap-action",
  3214  				curSnaps: []store.CurrentSnap{{
  3215  					InstanceName:     "some-snap",
  3216  					SnapID:           "some-snap-id",
  3217  					Revision:         snap.R(7),
  3218  					IgnoreValidation: false,
  3219  					RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  3220  					Epoch:            snap.E("1*"),
  3221  				}, {
  3222  					InstanceName:     "some-snap_instance",
  3223  					SnapID:           "some-snap-id",
  3224  					Revision:         snap.R(11),
  3225  					TrackingChannel:  "latest/stable",
  3226  					IgnoreValidation: true,
  3227  					RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 11),
  3228  					Epoch:            snap.E("1*"),
  3229  				}},
  3230  				userID: 1,
  3231  			})
  3232  		case "storesvc-snap-action:action":
  3233  			switch op.action.InstanceName {
  3234  			case "some-snap":
  3235  				c.Check(op, DeepEquals, fakeOp{
  3236  					op:    "storesvc-snap-action:action",
  3237  					revno: snap.R(12),
  3238  					action: store.SnapAction{
  3239  						Action:       "refresh",
  3240  						InstanceName: "some-snap",
  3241  						SnapID:       "some-snap-id",
  3242  						Flags:        0,
  3243  					},
  3244  					userID: 1,
  3245  				})
  3246  			case "some-snap_instance":
  3247  				c.Check(op, DeepEquals, fakeOp{
  3248  					op:    "storesvc-snap-action:action",
  3249  					revno: snap.R(12),
  3250  					action: store.SnapAction{
  3251  						Action:       "refresh",
  3252  						InstanceName: "some-snap_instance",
  3253  						SnapID:       "some-snap-id",
  3254  						Flags:        0,
  3255  					},
  3256  					userID: 1,
  3257  				})
  3258  			default:
  3259  				c.Fatalf("unexpected instance name %q", op.action.InstanceName)
  3260  			}
  3261  		default:
  3262  			c.Fatalf("unexpected action %q", op.op)
  3263  		}
  3264  	}
  3265  
  3266  }
  3267  
  3268  func (s *snapmgrTestSuite) TestUpdateFromLocal(c *C) {
  3269  	si := snap.SideInfo{
  3270  		RealName: "some-snap",
  3271  		Revision: snap.R("x1"),
  3272  	}
  3273  
  3274  	s.state.Lock()
  3275  	defer s.state.Unlock()
  3276  
  3277  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3278  		Active:          true,
  3279  		Sequence:        []*snap.SideInfo{&si},
  3280  		TrackingChannel: "channel-for-7/stable",
  3281  		Current:         si.Revision,
  3282  	})
  3283  
  3284  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  3285  	c.Assert(err, Equals, store.ErrLocalSnap)
  3286  }
  3287  
  3288  func (s *snapmgrTestSuite) TestUpdateAmend(c *C) {
  3289  	si := snap.SideInfo{
  3290  		RealName: "some-snap",
  3291  		Revision: snap.R("x1"),
  3292  	}
  3293  
  3294  	s.state.Lock()
  3295  	defer s.state.Unlock()
  3296  
  3297  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3298  		Active:          true,
  3299  		Sequence:        []*snap.SideInfo{&si},
  3300  		TrackingChannel: "channel-for-7/stable",
  3301  		Current:         si.Revision,
  3302  	})
  3303  
  3304  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{Amend: true})
  3305  	c.Assert(err, IsNil)
  3306  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  3307  
  3308  	// ensure we go from local to store revision-7
  3309  	var snapsup snapstate.SnapSetup
  3310  	tasks := ts.Tasks()
  3311  	c.Check(tasks[1].Kind(), Equals, "download-snap")
  3312  	err = tasks[1].Get("snap-setup", &snapsup)
  3313  	c.Assert(err, IsNil)
  3314  	c.Check(snapsup.Revision(), Equals, snap.R(7))
  3315  }
  3316  
  3317  func (s *snapmgrTestSuite) TestUpdateAmendSnapNotFound(c *C) {
  3318  	si := snap.SideInfo{
  3319  		RealName: "snap-unknown",
  3320  		Revision: snap.R("x1"),
  3321  	}
  3322  
  3323  	s.state.Lock()
  3324  	defer s.state.Unlock()
  3325  
  3326  	snapstate.Set(s.state, "snap-unknown", &snapstate.SnapState{
  3327  		Active:          true,
  3328  		Sequence:        []*snap.SideInfo{&si},
  3329  		TrackingChannel: "latest/stable",
  3330  		Current:         si.Revision,
  3331  	})
  3332  
  3333  	_, err := snapstate.Update(s.state, "snap-unknown", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{Amend: true})
  3334  	c.Assert(err, Equals, store.ErrSnapNotFound)
  3335  }
  3336  
  3337  func (s *snapmgrTestSuite) TestSingleUpdateBlockedRevision(c *C) {
  3338  	// single updates should *not* set the block list
  3339  	si7 := snap.SideInfo{
  3340  		RealName: "some-snap",
  3341  		SnapID:   "some-snap-id",
  3342  		Revision: snap.R(7),
  3343  	}
  3344  	si11 := snap.SideInfo{
  3345  		RealName: "some-snap",
  3346  		SnapID:   "some-snap-id",
  3347  		Revision: snap.R(11),
  3348  	}
  3349  
  3350  	s.state.Lock()
  3351  	defer s.state.Unlock()
  3352  
  3353  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3354  		Active:   true,
  3355  		Sequence: []*snap.SideInfo{&si7, &si11},
  3356  		Current:  si7.Revision,
  3357  		SnapType: "app",
  3358  	})
  3359  
  3360  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  3361  	c.Assert(err, IsNil)
  3362  
  3363  	c.Assert(s.fakeBackend.ops, HasLen, 2)
  3364  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3365  		op: "storesvc-snap-action",
  3366  		curSnaps: []store.CurrentSnap{{
  3367  			InstanceName:  "some-snap",
  3368  			SnapID:        "some-snap-id",
  3369  			Revision:      snap.R(7),
  3370  			RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  3371  			Epoch:         snap.E("1*"),
  3372  		}},
  3373  		userID: 1,
  3374  	})
  3375  }
  3376  
  3377  func (s *snapmgrTestSuite) TestMultiUpdateBlockedRevision(c *C) {
  3378  	// multi-updates should *not* set the block list
  3379  	si7 := snap.SideInfo{
  3380  		RealName: "some-snap",
  3381  		SnapID:   "some-snap-id",
  3382  		Revision: snap.R(7),
  3383  	}
  3384  	si11 := snap.SideInfo{
  3385  		RealName: "some-snap",
  3386  		SnapID:   "some-snap-id",
  3387  		Revision: snap.R(11),
  3388  	}
  3389  
  3390  	s.state.Lock()
  3391  	defer s.state.Unlock()
  3392  
  3393  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3394  		Active:   true,
  3395  		Sequence: []*snap.SideInfo{&si7, &si11},
  3396  		Current:  si7.Revision,
  3397  		SnapType: "app",
  3398  	})
  3399  
  3400  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  3401  	c.Assert(err, IsNil)
  3402  	c.Check(updates, DeepEquals, []string{"some-snap"})
  3403  
  3404  	c.Assert(s.fakeBackend.ops, HasLen, 2)
  3405  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3406  		op: "storesvc-snap-action",
  3407  		curSnaps: []store.CurrentSnap{{
  3408  			InstanceName:  "some-snap",
  3409  			SnapID:        "some-snap-id",
  3410  			Revision:      snap.R(7),
  3411  			RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  3412  			Epoch:         snap.E("1*"),
  3413  		}},
  3414  		userID: 1,
  3415  	})
  3416  }
  3417  
  3418  func (s *snapmgrTestSuite) TestAllUpdateBlockedRevision(c *C) {
  3419  	//  update-all *should* set the block list
  3420  	si7 := snap.SideInfo{
  3421  		RealName: "some-snap",
  3422  		SnapID:   "some-snap-id",
  3423  		Revision: snap.R(7),
  3424  	}
  3425  	si11 := snap.SideInfo{
  3426  		RealName: "some-snap",
  3427  		SnapID:   "some-snap-id",
  3428  		Revision: snap.R(11),
  3429  	}
  3430  
  3431  	s.state.Lock()
  3432  	defer s.state.Unlock()
  3433  
  3434  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3435  		Active:   true,
  3436  		Sequence: []*snap.SideInfo{&si7, &si11},
  3437  		Current:  si7.Revision,
  3438  	})
  3439  
  3440  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil)
  3441  	c.Check(err, IsNil)
  3442  	c.Check(updates, HasLen, 0)
  3443  
  3444  	c.Assert(s.fakeBackend.ops, HasLen, 2)
  3445  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3446  		op: "storesvc-snap-action",
  3447  		curSnaps: []store.CurrentSnap{{
  3448  			InstanceName:  "some-snap",
  3449  			SnapID:        "some-snap-id",
  3450  			Revision:      snap.R(7),
  3451  			RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  3452  			Block:         []snap.Revision{snap.R(11)},
  3453  			Epoch:         snap.E("1*"),
  3454  		}},
  3455  		userID: 1,
  3456  	})
  3457  }
  3458  
  3459  var orthogonalAutoAliasesScenarios = []struct {
  3460  	aliasesBefore map[string][]string
  3461  	names         []string
  3462  	prune         []string
  3463  	update        bool
  3464  	new           bool
  3465  }{
  3466  	{nil, nil, nil, true, true},
  3467  	{nil, []string{"some-snap"}, nil, true, false},
  3468  	{nil, []string{"other-snap"}, nil, false, true},
  3469  	{map[string][]string{"some-snap": {"aliasA", "aliasC"}}, []string{"some-snap"}, nil, true, false},
  3470  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, []string{"other-snap"}, []string{"other-snap"}, false, false},
  3471  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, nil, []string{"other-snap"}, true, false},
  3472  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, []string{"some-snap"}, nil, true, false},
  3473  	{map[string][]string{"other-snap": {"aliasC"}}, []string{"other-snap"}, []string{"other-snap"}, false, true},
  3474  	{map[string][]string{"other-snap": {"aliasC"}}, nil, []string{"other-snap"}, true, true},
  3475  	{map[string][]string{"other-snap": {"aliasC"}}, []string{"some-snap"}, nil, true, false},
  3476  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, []string{"some-snap"}, []string{"other-snap"}, true, false},
  3477  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, nil, []string{"other-snap", "some-snap"}, true, true},
  3478  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, []string{"other-snap"}, []string{"other-snap", "some-snap"}, false, true},
  3479  	{map[string][]string{"some-snap": {"aliasB"}}, nil, []string{"some-snap"}, true, true},
  3480  	{map[string][]string{"some-snap": {"aliasB"}}, []string{"other-snap"}, []string{"some-snap"}, false, true},
  3481  	{map[string][]string{"some-snap": {"aliasB"}}, []string{"some-snap"}, nil, true, false},
  3482  	{map[string][]string{"other-snap": {"aliasA"}}, nil, []string{"other-snap"}, true, true},
  3483  	{map[string][]string{"other-snap": {"aliasA"}}, []string{"other-snap"}, []string{"other-snap"}, false, true},
  3484  	{map[string][]string{"other-snap": {"aliasA"}}, []string{"some-snap"}, []string{"other-snap"}, true, false},
  3485  }
  3486  
  3487  func (s *snapmgrTestSuite) TestUpdateManyAutoAliasesScenarios(c *C) {
  3488  	s.state.Lock()
  3489  	defer s.state.Unlock()
  3490  
  3491  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
  3492  		Active: true,
  3493  		Sequence: []*snap.SideInfo{
  3494  			{RealName: "other-snap", SnapID: "other-snap-id", Revision: snap.R(2)},
  3495  		},
  3496  		Current:  snap.R(2),
  3497  		SnapType: "app",
  3498  	})
  3499  
  3500  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  3501  		switch info.InstanceName() {
  3502  		case "some-snap":
  3503  			return map[string]string{"aliasA": "cmdA"}, nil
  3504  		case "other-snap":
  3505  			return map[string]string{"aliasB": "cmdB"}, nil
  3506  		}
  3507  		return nil, nil
  3508  	}
  3509  
  3510  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3511  		Active: true,
  3512  		Sequence: []*snap.SideInfo{
  3513  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  3514  		},
  3515  		Current:  snap.R(4),
  3516  		SnapType: "app",
  3517  	})
  3518  
  3519  	expectedSet := func(aliases []string) map[string]bool {
  3520  		res := make(map[string]bool, len(aliases))
  3521  		for _, alias := range aliases {
  3522  			res[alias] = true
  3523  		}
  3524  		return res
  3525  	}
  3526  
  3527  	for _, scenario := range orthogonalAutoAliasesScenarios {
  3528  		for _, instanceName := range []string{"some-snap", "other-snap"} {
  3529  			var snapst snapstate.SnapState
  3530  			err := snapstate.Get(s.state, instanceName, &snapst)
  3531  			c.Assert(err, IsNil)
  3532  			snapst.Aliases = nil
  3533  			snapst.AutoAliasesDisabled = false
  3534  			if autoAliases := scenario.aliasesBefore[instanceName]; autoAliases != nil {
  3535  				targets := make(map[string]*snapstate.AliasTarget)
  3536  				for _, alias := range autoAliases {
  3537  					targets[alias] = &snapstate.AliasTarget{Auto: "cmd" + alias[len(alias)-1:]}
  3538  				}
  3539  
  3540  				snapst.Aliases = targets
  3541  			}
  3542  			snapstate.Set(s.state, instanceName, &snapst)
  3543  		}
  3544  
  3545  		updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, scenario.names, s.user.ID, nil)
  3546  		c.Check(err, IsNil)
  3547  		if scenario.update {
  3548  			verifyLastTasksetIsReRefresh(c, tts)
  3549  		}
  3550  
  3551  		_, dropped, err := snapstate.AutoAliasesDelta(s.state, []string{"some-snap", "other-snap"})
  3552  		c.Assert(err, IsNil)
  3553  
  3554  		j := 0
  3555  		expectedUpdatesSet := make(map[string]bool)
  3556  		var expectedPruned map[string]map[string]bool
  3557  		var pruneTs *state.TaskSet
  3558  		if len(scenario.prune) != 0 {
  3559  			pruneTs = tts[0]
  3560  			j++
  3561  			taskAliases := make(map[string]map[string]bool)
  3562  			for _, aliasTask := range pruneTs.Tasks() {
  3563  				c.Check(aliasTask.Kind(), Equals, "prune-auto-aliases")
  3564  				var aliases []string
  3565  				err := aliasTask.Get("aliases", &aliases)
  3566  				c.Assert(err, IsNil)
  3567  				snapsup, err := snapstate.TaskSnapSetup(aliasTask)
  3568  				c.Assert(err, IsNil)
  3569  				taskAliases[snapsup.InstanceName()] = expectedSet(aliases)
  3570  			}
  3571  			expectedPruned = make(map[string]map[string]bool)
  3572  			for _, instanceName := range scenario.prune {
  3573  				expectedPruned[instanceName] = expectedSet(dropped[instanceName])
  3574  				if instanceName == "other-snap" && !scenario.new && !scenario.update {
  3575  					expectedUpdatesSet["other-snap"] = true
  3576  				}
  3577  			}
  3578  			c.Check(taskAliases, DeepEquals, expectedPruned)
  3579  		}
  3580  		if scenario.update {
  3581  			updateTs := tts[j]
  3582  			j++
  3583  			expectedUpdatesSet["some-snap"] = true
  3584  			first := updateTs.Tasks()[0]
  3585  			c.Check(first.Kind(), Equals, "prerequisites")
  3586  			wait := false
  3587  			if expectedPruned["other-snap"]["aliasA"] {
  3588  				wait = true
  3589  			} else if expectedPruned["some-snap"] != nil {
  3590  				wait = true
  3591  			}
  3592  			if wait {
  3593  				c.Check(first.WaitTasks(), DeepEquals, pruneTs.Tasks())
  3594  			} else {
  3595  				c.Check(first.WaitTasks(), HasLen, 0)
  3596  			}
  3597  		}
  3598  		if scenario.new {
  3599  			newTs := tts[j]
  3600  			j++
  3601  			expectedUpdatesSet["other-snap"] = true
  3602  			tasks := newTs.Tasks()
  3603  			c.Check(tasks, HasLen, 1)
  3604  			aliasTask := tasks[0]
  3605  			c.Check(aliasTask.Kind(), Equals, "refresh-aliases")
  3606  
  3607  			wait := false
  3608  			if expectedPruned["some-snap"]["aliasB"] {
  3609  				wait = true
  3610  			} else if expectedPruned["other-snap"] != nil {
  3611  				wait = true
  3612  			}
  3613  			if wait {
  3614  				c.Check(aliasTask.WaitTasks(), DeepEquals, pruneTs.Tasks())
  3615  			} else {
  3616  				c.Check(aliasTask.WaitTasks(), HasLen, 0)
  3617  			}
  3618  		}
  3619  		l := len(tts)
  3620  		if scenario.update {
  3621  			l--
  3622  		}
  3623  		c.Assert(j, Equals, l, Commentf("%#v", scenario))
  3624  
  3625  		// check reported updated names
  3626  		c.Check(len(updates) > 0, Equals, true)
  3627  		sort.Strings(updates)
  3628  		expectedUpdates := make([]string, 0, len(expectedUpdatesSet))
  3629  		for x := range expectedUpdatesSet {
  3630  			expectedUpdates = append(expectedUpdates, x)
  3631  		}
  3632  		sort.Strings(expectedUpdates)
  3633  		c.Check(updates, DeepEquals, expectedUpdates)
  3634  	}
  3635  }
  3636  
  3637  func (s *snapmgrTestSuite) TestUpdateOneAutoAliasesScenarios(c *C) {
  3638  	s.state.Lock()
  3639  	defer s.state.Unlock()
  3640  
  3641  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
  3642  		Active: true,
  3643  		Sequence: []*snap.SideInfo{
  3644  			{RealName: "other-snap", SnapID: "other-snap-id", Revision: snap.R(2)},
  3645  		},
  3646  		Current:  snap.R(2),
  3647  		SnapType: "app",
  3648  	})
  3649  
  3650  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  3651  		switch info.InstanceName() {
  3652  		case "some-snap":
  3653  			return map[string]string{"aliasA": "cmdA"}, nil
  3654  		case "other-snap":
  3655  			return map[string]string{"aliasB": "cmdB"}, nil
  3656  		}
  3657  		return nil, nil
  3658  	}
  3659  
  3660  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3661  		Active: true,
  3662  		Sequence: []*snap.SideInfo{
  3663  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  3664  		},
  3665  		Current:  snap.R(4),
  3666  		SnapType: "app",
  3667  	})
  3668  
  3669  	expectedSet := func(aliases []string) map[string]bool {
  3670  		res := make(map[string]bool, len(aliases))
  3671  		for _, alias := range aliases {
  3672  			res[alias] = true
  3673  		}
  3674  		return res
  3675  	}
  3676  
  3677  	for _, scenario := range orthogonalAutoAliasesScenarios {
  3678  		if len(scenario.names) != 1 {
  3679  			continue
  3680  		}
  3681  
  3682  		for _, instanceName := range []string{"some-snap", "other-snap"} {
  3683  			var snapst snapstate.SnapState
  3684  			err := snapstate.Get(s.state, instanceName, &snapst)
  3685  			c.Assert(err, IsNil)
  3686  			snapst.Aliases = nil
  3687  			snapst.AutoAliasesDisabled = false
  3688  			if autoAliases := scenario.aliasesBefore[instanceName]; autoAliases != nil {
  3689  				targets := make(map[string]*snapstate.AliasTarget)
  3690  				for _, alias := range autoAliases {
  3691  					targets[alias] = &snapstate.AliasTarget{Auto: "cmd" + alias[len(alias)-1:]}
  3692  				}
  3693  
  3694  				snapst.Aliases = targets
  3695  			}
  3696  			snapstate.Set(s.state, instanceName, &snapst)
  3697  		}
  3698  
  3699  		ts, err := snapstate.Update(s.state, scenario.names[0], nil, s.user.ID, snapstate.Flags{})
  3700  		c.Assert(err, IsNil)
  3701  		_, dropped, err := snapstate.AutoAliasesDelta(s.state, []string{"some-snap", "other-snap"})
  3702  		c.Assert(err, IsNil)
  3703  
  3704  		j := 0
  3705  
  3706  		tasks := ts.Tasks()
  3707  		// make sure the last task from Update is the rerefresh
  3708  		if scenario.update {
  3709  			reRefresh := tasks[len(tasks)-1]
  3710  			c.Check(reRefresh.Kind(), Equals, "check-rerefresh")
  3711  			// nothing should wait on it
  3712  			c.Check(reRefresh.NumHaltTasks(), Equals, 0)
  3713  			tasks = tasks[:len(tasks)-1] // and now forget about it
  3714  		}
  3715  
  3716  		var expectedPruned map[string]map[string]bool
  3717  		var pruneTasks []*state.Task
  3718  		if len(scenario.prune) != 0 {
  3719  			nprune := len(scenario.prune)
  3720  			pruneTasks = tasks[:nprune]
  3721  			j += nprune
  3722  			taskAliases := make(map[string]map[string]bool)
  3723  			for _, aliasTask := range pruneTasks {
  3724  				c.Check(aliasTask.Kind(), Equals, "prune-auto-aliases")
  3725  				var aliases []string
  3726  				err := aliasTask.Get("aliases", &aliases)
  3727  				c.Assert(err, IsNil)
  3728  				snapsup, err := snapstate.TaskSnapSetup(aliasTask)
  3729  				c.Assert(err, IsNil)
  3730  				taskAliases[snapsup.InstanceName()] = expectedSet(aliases)
  3731  			}
  3732  			expectedPruned = make(map[string]map[string]bool)
  3733  			for _, instanceName := range scenario.prune {
  3734  				expectedPruned[instanceName] = expectedSet(dropped[instanceName])
  3735  			}
  3736  			c.Check(taskAliases, DeepEquals, expectedPruned)
  3737  		}
  3738  		if scenario.update {
  3739  			first := tasks[j]
  3740  			j += 19
  3741  			c.Check(first.Kind(), Equals, "prerequisites")
  3742  			wait := false
  3743  			if expectedPruned["other-snap"]["aliasA"] {
  3744  				wait = true
  3745  			} else if expectedPruned["some-snap"] != nil {
  3746  				wait = true
  3747  			}
  3748  			if wait {
  3749  				c.Check(first.WaitTasks(), DeepEquals, pruneTasks)
  3750  			} else {
  3751  				c.Check(first.WaitTasks(), HasLen, 0)
  3752  			}
  3753  		}
  3754  		if scenario.new {
  3755  			aliasTask := tasks[j]
  3756  			j++
  3757  			c.Check(aliasTask.Kind(), Equals, "refresh-aliases")
  3758  			wait := false
  3759  			if expectedPruned["some-snap"]["aliasB"] {
  3760  				wait = true
  3761  			} else if expectedPruned["other-snap"] != nil {
  3762  				wait = true
  3763  			}
  3764  			if wait {
  3765  				c.Check(aliasTask.WaitTasks(), DeepEquals, pruneTasks)
  3766  			} else {
  3767  				c.Check(aliasTask.WaitTasks(), HasLen, 0)
  3768  			}
  3769  		}
  3770  		c.Assert(len(tasks), Equals, j, Commentf("%#v", scenario))
  3771  
  3772  		// conflict checks are triggered
  3773  		chg := s.state.NewChange("update", "...")
  3774  		chg.AddAll(ts)
  3775  		err = snapstate.CheckChangeConflict(s.state, scenario.names[0], nil)
  3776  		c.Check(err, ErrorMatches, `.* has "update" change in progress`)
  3777  		chg.SetStatus(state.DoneStatus)
  3778  	}
  3779  }
  3780  
  3781  func (s *snapmgrTestSuite) TestUpdateLocalSnapFails(c *C) {
  3782  	si := snap.SideInfo{
  3783  		RealName: "some-snap",
  3784  		Revision: snap.R(7),
  3785  	}
  3786  
  3787  	s.state.Lock()
  3788  	defer s.state.Unlock()
  3789  
  3790  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3791  		Active:   true,
  3792  		Sequence: []*snap.SideInfo{&si},
  3793  		Current:  si.Revision,
  3794  	})
  3795  
  3796  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  3797  	c.Assert(err, Equals, store.ErrLocalSnap)
  3798  }
  3799  
  3800  func (s *snapmgrTestSuite) TestUpdateDisabledUnsupported(c *C) {
  3801  	si := snap.SideInfo{
  3802  		RealName: "some-snap",
  3803  		SnapID:   "some-snap-id",
  3804  		Revision: snap.R(7),
  3805  	}
  3806  
  3807  	s.state.Lock()
  3808  	defer s.state.Unlock()
  3809  
  3810  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3811  		Active:   false,
  3812  		Sequence: []*snap.SideInfo{&si},
  3813  		Current:  si.Revision,
  3814  	})
  3815  
  3816  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  3817  	c.Assert(err, ErrorMatches, `refreshing disabled snap "some-snap" not supported`)
  3818  }
  3819  
  3820  func (s *snapmgrTestSuite) TestUpdateKernelTrackChecksSwitchingTracks(c *C) {
  3821  	si := snap.SideInfo{
  3822  		RealName: "kernel",
  3823  		SnapID:   "kernel-id",
  3824  		Revision: snap.R(7),
  3825  	}
  3826  
  3827  	s.state.Lock()
  3828  	defer s.state.Unlock()
  3829  
  3830  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  3831  	defer r()
  3832  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  3833  		Active:          true,
  3834  		Sequence:        []*snap.SideInfo{&si},
  3835  		Current:         si.Revision,
  3836  		TrackingChannel: "18/stable",
  3837  	})
  3838  
  3839  	// switching tracks is not ok
  3840  	_, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{})
  3841  	c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`)
  3842  
  3843  	// no change to the channel is ok
  3844  	_, err = snapstate.Update(s.state, "kernel", nil, s.user.ID, snapstate.Flags{})
  3845  	c.Assert(err, IsNil)
  3846  
  3847  	// switching risk level is ok
  3848  	_, err = snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "18/beta"}, s.user.ID, snapstate.Flags{})
  3849  	c.Assert(err, IsNil)
  3850  
  3851  	// switching just risk within the pinned track is ok
  3852  	_, err = snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "beta"}, s.user.ID, snapstate.Flags{})
  3853  	c.Assert(err, IsNil)
  3854  }
  3855  
  3856  func (s *snapmgrTestSuite) TestUpdateGadgetTrackChecksSwitchingTracks(c *C) {
  3857  	si := snap.SideInfo{
  3858  		RealName: "brand-gadget",
  3859  		SnapID:   "brand-gadget-id",
  3860  		Revision: snap.R(7),
  3861  	}
  3862  
  3863  	s.state.Lock()
  3864  	defer s.state.Unlock()
  3865  
  3866  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
  3867  	defer r()
  3868  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
  3869  		Active:          true,
  3870  		Sequence:        []*snap.SideInfo{&si},
  3871  		Current:         si.Revision,
  3872  		TrackingChannel: "18/stable",
  3873  	})
  3874  
  3875  	// switching tracks is not ok
  3876  	_, err := snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{})
  3877  	c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`)
  3878  
  3879  	// no change to the channel is ok
  3880  	_, err = snapstate.Update(s.state, "brand-gadget", nil, s.user.ID, snapstate.Flags{})
  3881  	c.Assert(err, IsNil)
  3882  
  3883  	// switching risk level is ok
  3884  	_, err = snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "18/beta"}, s.user.ID, snapstate.Flags{})
  3885  	c.Assert(err, IsNil)
  3886  
  3887  	// switching just risk within the pinned track is ok
  3888  	_, err = snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "beta"}, s.user.ID, snapstate.Flags{})
  3889  	c.Assert(err, IsNil)
  3890  
  3891  }
  3892  
  3893  func (s *snapmgrTestSuite) TestUpdateWithDeviceContext(c *C) {
  3894  	s.state.Lock()
  3895  	defer s.state.Unlock()
  3896  
  3897  	// unset the global store, it will need to come via the device context
  3898  	snapstate.ReplaceStore(s.state, nil)
  3899  
  3900  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  3901  		DeviceModel: DefaultModel(),
  3902  		CtxStore:    s.fakeStore,
  3903  	}
  3904  
  3905  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3906  		Active:          true,
  3907  		TrackingChannel: "latest/edge",
  3908  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  3909  		Current:         snap.R(7),
  3910  		SnapType:        "app",
  3911  	})
  3912  
  3913  	validateCalled := false
  3914  	happyValidateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx1 snapstate.DeviceContext) ([]*snap.Info, error) {
  3915  		c.Check(deviceCtx1, Equals, deviceCtx)
  3916  		validateCalled = true
  3917  		return refreshes, nil
  3918  	}
  3919  	// hook it up
  3920  	snapstate.ValidateRefreshes = happyValidateRefreshes
  3921  
  3922  	ts, err := snapstate.UpdateWithDeviceContext(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}, deviceCtx, "")
  3923  	c.Assert(err, IsNil)
  3924  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  3925  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  3926  
  3927  	c.Check(validateCalled, Equals, true)
  3928  }
  3929  
  3930  func (s *snapmgrTestSuite) TestUpdateWithDeviceContextToRevision(c *C) {
  3931  	s.state.Lock()
  3932  	defer s.state.Unlock()
  3933  
  3934  	// unset the global store, it will need to come via the device context
  3935  	snapstate.ReplaceStore(s.state, nil)
  3936  
  3937  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  3938  		DeviceModel: DefaultModel(),
  3939  		CtxStore:    s.fakeStore,
  3940  	}
  3941  
  3942  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3943  		Active: true,
  3944  		Sequence: []*snap.SideInfo{
  3945  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  3946  		},
  3947  		Current:  snap.R(5),
  3948  		SnapType: "app",
  3949  		UserID:   1,
  3950  	})
  3951  
  3952  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(11)}
  3953  	ts, err := snapstate.UpdateWithDeviceContext(s.state, "some-snap", opts, 0, snapstate.Flags{}, deviceCtx, "")
  3954  	c.Assert(err, IsNil)
  3955  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  3956  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  3957  }
  3958  
  3959  func (s *snapmgrTestSuite) TestUpdateTasksCoreSetsIgnoreOnConfigure(c *C) {
  3960  	s.state.Lock()
  3961  	defer s.state.Unlock()
  3962  
  3963  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  3964  		Active:          true,
  3965  		TrackingChannel: "latest/edge",
  3966  		Sequence:        []*snap.SideInfo{{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(7)}},
  3967  		Current:         snap.R(7),
  3968  		SnapType:        "os",
  3969  	})
  3970  
  3971  	oldConfigure := snapstate.Configure
  3972  	defer func() { snapstate.Configure = oldConfigure }()
  3973  
  3974  	var configureFlags int
  3975  	snapstate.Configure = func(st *state.State, snapName string, patch map[string]interface{}, flags int) *state.TaskSet {
  3976  		configureFlags = flags
  3977  		return state.NewTaskSet()
  3978  	}
  3979  
  3980  	_, err := snapstate.Update(s.state, "core", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  3981  	c.Assert(err, IsNil)
  3982  
  3983  	// ensure the core snap sets the "ignore-hook-error" flag
  3984  	c.Check(configureFlags&snapstate.IgnoreHookError, Equals, 1)
  3985  }
  3986  
  3987  func (s *snapmgrTestSuite) TestUpdateDevModeConfinementFiltering(c *C) {
  3988  	restore := maybeMockClassicSupport(c)
  3989  	defer restore()
  3990  
  3991  	s.state.Lock()
  3992  	defer s.state.Unlock()
  3993  
  3994  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3995  		Active:          true,
  3996  		TrackingChannel: "channel-for-devmode/stable",
  3997  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  3998  		Current:         snap.R(7),
  3999  		SnapType:        "app",
  4000  	})
  4001  
  4002  	// updated snap is devmode, refresh without --devmode, do nothing
  4003  	// TODO: better error message here
  4004  	_, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  4005  	c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`)
  4006  
  4007  	// updated snap is devmode, refresh with --devmode
  4008  	_, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{DevMode: true})
  4009  	c.Assert(err, IsNil)
  4010  }
  4011  
  4012  func (s *snapmgrTestSuite) TestUpdateClassicConfinementFiltering(c *C) {
  4013  	restore := maybeMockClassicSupport(c)
  4014  	defer restore()
  4015  
  4016  	s.state.Lock()
  4017  	defer s.state.Unlock()
  4018  
  4019  	snapstate.Set(s.state, "some-snap-now-classic", &snapstate.SnapState{
  4020  		Active:   true,
  4021  		Sequence: []*snap.SideInfo{{RealName: "some-snap-now-classic", SnapID: "some-snap-now-classic-id", Revision: snap.R(7)}},
  4022  		Current:  snap.R(7),
  4023  		SnapType: "app",
  4024  	})
  4025  
  4026  	// updated snap is classic, refresh without --classic, do nothing
  4027  	// TODO: better error message here
  4028  	_, err := snapstate.Update(s.state, "some-snap-now-classic", nil, s.user.ID, snapstate.Flags{})
  4029  	c.Assert(err, ErrorMatches, `.* requires classic confinement`)
  4030  
  4031  	// updated snap is classic, refresh with --classic
  4032  	ts, err := snapstate.Update(s.state, "some-snap-now-classic", nil, s.user.ID, snapstate.Flags{Classic: true})
  4033  	c.Assert(err, IsNil)
  4034  
  4035  	chg := s.state.NewChange("refresh", "refresh snap")
  4036  	chg.AddAll(ts)
  4037  
  4038  	s.state.Unlock()
  4039  	defer s.se.Stop()
  4040  	s.settle(c)
  4041  	s.state.Lock()
  4042  
  4043  	c.Assert(chg.Err(), IsNil)
  4044  	c.Assert(chg.IsReady(), Equals, true)
  4045  
  4046  	// verify snap is in classic
  4047  	var snapst snapstate.SnapState
  4048  	err = snapstate.Get(s.state, "some-snap-now-classic", &snapst)
  4049  	c.Assert(err, IsNil)
  4050  	c.Check(snapst.Classic, Equals, true)
  4051  }
  4052  
  4053  func (s *snapmgrTestSuite) TestUpdateClassicFromClassic(c *C) {
  4054  	restore := maybeMockClassicSupport(c)
  4055  	defer restore()
  4056  
  4057  	s.state.Lock()
  4058  	defer s.state.Unlock()
  4059  
  4060  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4061  		Active:          true,
  4062  		TrackingChannel: "channel-for-classic/stable",
  4063  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4064  		Current:         snap.R(7),
  4065  		SnapType:        "app",
  4066  		Flags:           snapstate.Flags{Classic: true},
  4067  	})
  4068  
  4069  	// snap installed with --classic, update needs classic, refresh with --classic works
  4070  	ts, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{Classic: true})
  4071  	c.Assert(err, IsNil)
  4072  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4073  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
  4074  	c.Assert(err, IsNil)
  4075  	c.Check(snapsup.Flags.Classic, Equals, true)
  4076  
  4077  	// devmode overrides the snapsetup classic flag
  4078  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{DevMode: true})
  4079  	c.Assert(err, IsNil)
  4080  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4081  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4082  	c.Assert(err, IsNil)
  4083  	c.Check(snapsup.Flags.Classic, Equals, false)
  4084  
  4085  	// jailmode overrides it too (you need to provide both)
  4086  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{JailMode: true})
  4087  	c.Assert(err, IsNil)
  4088  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4089  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4090  	c.Assert(err, IsNil)
  4091  	c.Check(snapsup.Flags.Classic, Equals, false)
  4092  
  4093  	// jailmode and classic together gets you both
  4094  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{JailMode: true, Classic: true})
  4095  	c.Assert(err, IsNil)
  4096  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4097  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4098  	c.Assert(err, IsNil)
  4099  	c.Check(snapsup.Flags.Classic, Equals, true)
  4100  
  4101  	// snap installed with --classic, update needs classic, refresh without --classic works
  4102  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  4103  	c.Assert(err, IsNil)
  4104  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4105  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4106  	c.Assert(err, IsNil)
  4107  	c.Check(snapsup.Flags.Classic, Equals, true)
  4108  
  4109  	chg := s.state.NewChange("refresh", "refresh snap")
  4110  	chg.AddAll(ts)
  4111  
  4112  	s.state.Unlock()
  4113  	defer s.se.Stop()
  4114  	s.settle(c)
  4115  	s.state.Lock()
  4116  
  4117  	// verify snap is in classic
  4118  	var snapst snapstate.SnapState
  4119  	err = snapstate.Get(s.state, "some-snap", &snapst)
  4120  	c.Assert(err, IsNil)
  4121  	c.Check(snapst.Classic, Equals, true)
  4122  }
  4123  
  4124  func (s *snapmgrTestSuite) TestUpdateStrictFromClassic(c *C) {
  4125  	restore := maybeMockClassicSupport(c)
  4126  	defer restore()
  4127  
  4128  	s.state.Lock()
  4129  	defer s.state.Unlock()
  4130  
  4131  	snapstate.Set(s.state, "some-snap-was-classic", &snapstate.SnapState{
  4132  		Active:          true,
  4133  		TrackingChannel: "channel/stable",
  4134  		Sequence:        []*snap.SideInfo{{RealName: "some-snap-was-classic", SnapID: "some-snap-was-classic-id", Revision: snap.R(7)}},
  4135  		Current:         snap.R(7),
  4136  		SnapType:        "app",
  4137  		Flags:           snapstate.Flags{Classic: true},
  4138  	})
  4139  
  4140  	// snap installed with --classic, update does not need classic, refresh works without --classic
  4141  	_, err := snapstate.Update(s.state, "some-snap-was-classic", nil, s.user.ID, snapstate.Flags{})
  4142  	c.Assert(err, IsNil)
  4143  
  4144  	// snap installed with --classic, update does not need classic, refresh works with --classic
  4145  	_, err = snapstate.Update(s.state, "some-snap-was-classic", nil, s.user.ID, snapstate.Flags{Classic: true})
  4146  	c.Assert(err, IsNil)
  4147  }
  4148  
  4149  func (s *snapmgrTestSuite) TestUpdateChannelFallback(c *C) {
  4150  	s.state.Lock()
  4151  	defer s.state.Unlock()
  4152  
  4153  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4154  		Active:          true,
  4155  		TrackingChannel: "latest/edge",
  4156  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4157  		Current:         snap.R(7),
  4158  		SnapType:        "app",
  4159  	})
  4160  
  4161  	ts, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  4162  	c.Assert(err, IsNil)
  4163  
  4164  	var snapsup snapstate.SnapSetup
  4165  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  4166  	c.Assert(err, IsNil)
  4167  
  4168  	c.Check(snapsup.Channel, Equals, "latest/edge")
  4169  }
  4170  
  4171  func (s *snapmgrTestSuite) TestUpdateTooEarly(c *C) {
  4172  	s.state.Lock()
  4173  	defer s.state.Unlock()
  4174  
  4175  	s.state.Set("seeded", nil)
  4176  
  4177  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4178  		Active:   true,
  4179  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4180  		Current:  snap.R(7),
  4181  		SnapType: "app",
  4182  	})
  4183  
  4184  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4185  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  4186  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
  4187  }
  4188  
  4189  func (s *snapmgrTestSuite) TestUpdateConflict(c *C) {
  4190  	s.state.Lock()
  4191  	defer s.state.Unlock()
  4192  
  4193  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4194  		Active:   true,
  4195  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4196  		Current:  snap.R(7),
  4197  		SnapType: "app",
  4198  	})
  4199  
  4200  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4201  	c.Assert(err, IsNil)
  4202  	// need a change to make the tasks visible
  4203  	s.state.NewChange("refresh", "...").AddAll(ts)
  4204  
  4205  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4206  	c.Assert(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`)
  4207  }
  4208  
  4209  func (s *snapmgrTestSuite) TestUpdateCreatesGCTasks(c *C) {
  4210  	restore := release.MockOnClassic(false)
  4211  	defer restore()
  4212  
  4213  	s.testUpdateCreatesGCTasks(c, 2)
  4214  }
  4215  
  4216  func (s *snapmgrTestSuite) TestUpdateCreatesGCTasksOnClassic(c *C) {
  4217  	restore := release.MockOnClassic(true)
  4218  	defer restore()
  4219  
  4220  	s.testUpdateCreatesGCTasks(c, 3)
  4221  }
  4222  
  4223  func (s *snapmgrTestSuite) testUpdateCreatesGCTasks(c *C, expectedDiscards int) {
  4224  	s.state.Lock()
  4225  	defer s.state.Unlock()
  4226  
  4227  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4228  		Active: true,
  4229  		Sequence: []*snap.SideInfo{
  4230  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4231  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4232  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4233  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4234  		},
  4235  		Current:  snap.R(4),
  4236  		SnapType: "app",
  4237  	})
  4238  
  4239  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{})
  4240  	c.Assert(err, IsNil)
  4241  
  4242  	// ensure edges information is still there
  4243  	te, err := ts.Edge(snapstate.DownloadAndChecksDoneEdge)
  4244  	c.Assert(te, NotNil)
  4245  	c.Assert(err, IsNil)
  4246  
  4247  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, expectedDiscards, ts, s.state)
  4248  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  4249  }
  4250  
  4251  func (s *snapmgrTestSuite) TestUpdateCreatesDiscardAfterCurrentTasks(c *C) {
  4252  	s.state.Lock()
  4253  	defer s.state.Unlock()
  4254  
  4255  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4256  		Active: true,
  4257  		Sequence: []*snap.SideInfo{
  4258  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4259  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4260  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4261  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4262  		},
  4263  		Current:  snap.R(1),
  4264  		SnapType: "app",
  4265  	})
  4266  
  4267  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{})
  4268  	c.Assert(err, IsNil)
  4269  
  4270  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 3, ts, s.state)
  4271  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  4272  }
  4273  
  4274  func (s *snapmgrTestSuite) TestUpdateManyTooEarly(c *C) {
  4275  	s.state.Lock()
  4276  	defer s.state.Unlock()
  4277  
  4278  	s.state.Set("seeded", nil)
  4279  
  4280  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4281  		Active:   true,
  4282  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4283  		Current:  snap.R(7),
  4284  		SnapType: "app",
  4285  	})
  4286  
  4287  	_, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4288  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  4289  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
  4290  }
  4291  
  4292  func (s *snapmgrTestSuite) TestUpdateMany(c *C) {
  4293  	s.state.Lock()
  4294  	defer s.state.Unlock()
  4295  
  4296  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4297  		Active: true,
  4298  		Sequence: []*snap.SideInfo{
  4299  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4300  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4301  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4302  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4303  		},
  4304  		Current:  snap.R(1),
  4305  		SnapType: "app",
  4306  	})
  4307  
  4308  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4309  	c.Assert(err, IsNil)
  4310  	c.Assert(tts, HasLen, 2)
  4311  	verifyLastTasksetIsReRefresh(c, tts)
  4312  	c.Check(updates, DeepEquals, []string{"some-snap"})
  4313  
  4314  	ts := tts[0]
  4315  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 3, ts, s.state)
  4316  
  4317  	// check that the tasks are in non-default lane
  4318  	for _, t := range ts.Tasks() {
  4319  		c.Assert(t.Lanes(), DeepEquals, []int{1})
  4320  	}
  4321  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())+1) // 1==rerefresh
  4322  
  4323  	// ensure edges information is still there
  4324  	te, err := ts.Edge(snapstate.DownloadAndChecksDoneEdge)
  4325  	c.Assert(te, NotNil)
  4326  	c.Assert(err, IsNil)
  4327  
  4328  	checkIsAutoRefresh(c, ts.Tasks(), false)
  4329  }
  4330  
  4331  func (s *snapmgrTestSuite) TestUpdateManyDevModeConfinementFiltering(c *C) {
  4332  	s.state.Lock()
  4333  	defer s.state.Unlock()
  4334  
  4335  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4336  		Active:          true,
  4337  		TrackingChannel: "channel-for-devmode/stable",
  4338  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4339  		Current:         snap.R(7),
  4340  		SnapType:        "app",
  4341  	})
  4342  
  4343  	// updated snap is devmode, updatemany doesn't update it
  4344  	_, tts, _ := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  4345  	// FIXME: UpdateMany will not error out in this case (daemon catches this case, with a weird error)
  4346  	c.Assert(tts, HasLen, 0)
  4347  }
  4348  
  4349  func (s *snapmgrTestSuite) TestUpdateManyClassicConfinementFiltering(c *C) {
  4350  	restore := maybeMockClassicSupport(c)
  4351  	defer restore()
  4352  
  4353  	s.state.Lock()
  4354  	defer s.state.Unlock()
  4355  
  4356  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4357  		Active:          true,
  4358  		TrackingChannel: "channel-for-classic/stable",
  4359  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4360  		Current:         snap.R(7),
  4361  		SnapType:        "app",
  4362  	})
  4363  
  4364  	// if a snap installed without --classic gets a classic update it isn't installed
  4365  	_, tts, _ := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  4366  	// FIXME: UpdateMany will not error out in this case (daemon catches this case, with a weird error)
  4367  	c.Assert(tts, HasLen, 0)
  4368  }
  4369  
  4370  func (s *snapmgrTestSuite) TestUpdateManyClassic(c *C) {
  4371  	restore := maybeMockClassicSupport(c)
  4372  	defer restore()
  4373  
  4374  	s.state.Lock()
  4375  	defer s.state.Unlock()
  4376  
  4377  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4378  		Active:          true,
  4379  		TrackingChannel: "channel-for-classic/stable",
  4380  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4381  		Current:         snap.R(7),
  4382  		SnapType:        "app",
  4383  		Flags:           snapstate.Flags{Classic: true},
  4384  	})
  4385  
  4386  	// snap installed with classic: refresh gets classic
  4387  	_, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  4388  	c.Assert(err, IsNil)
  4389  	c.Assert(tts, HasLen, 2)
  4390  	verifyLastTasksetIsReRefresh(c, tts)
  4391  }
  4392  
  4393  func (s *snapmgrTestSuite) TestUpdateManyClassicToStrict(c *C) {
  4394  	restore := maybeMockClassicSupport(c)
  4395  	defer restore()
  4396  
  4397  	s.state.Lock()
  4398  	defer s.state.Unlock()
  4399  
  4400  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4401  		Active:          true,
  4402  		TrackingChannel: "stable",
  4403  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4404  		Current:         snap.R(7),
  4405  		SnapType:        "app",
  4406  		Flags:           snapstate.Flags{Classic: true},
  4407  	})
  4408  
  4409  	// snap installed with classic: refresh gets classic
  4410  	_, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, &snapstate.Flags{Classic: true})
  4411  	c.Assert(err, IsNil)
  4412  	c.Assert(tts, HasLen, 2)
  4413  	// ensure we clear the classic flag
  4414  	snapsup, err := snapstate.TaskSnapSetup(tts[0].Tasks()[0])
  4415  	c.Assert(err, IsNil)
  4416  	c.Assert(snapsup.Flags.Classic, Equals, false)
  4417  
  4418  	verifyLastTasksetIsReRefresh(c, tts)
  4419  }
  4420  
  4421  func (s *snapmgrTestSuite) TestUpdateManyDevMode(c *C) {
  4422  	s.state.Lock()
  4423  	defer s.state.Unlock()
  4424  
  4425  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4426  		Active: true,
  4427  		Flags:  snapstate.Flags{DevMode: true},
  4428  		Sequence: []*snap.SideInfo{
  4429  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4430  		},
  4431  		Current:  snap.R(1),
  4432  		SnapType: "app",
  4433  	})
  4434  
  4435  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, 0, nil)
  4436  	c.Assert(err, IsNil)
  4437  	c.Check(updates, HasLen, 1)
  4438  }
  4439  
  4440  func (s *snapmgrTestSuite) TestUpdateAllDevMode(c *C) {
  4441  	s.state.Lock()
  4442  	defer s.state.Unlock()
  4443  
  4444  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4445  		Active: true,
  4446  		Flags:  snapstate.Flags{DevMode: true},
  4447  		Sequence: []*snap.SideInfo{
  4448  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4449  		},
  4450  		Current:  snap.R(1),
  4451  		SnapType: "app",
  4452  	})
  4453  
  4454  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4455  	c.Assert(err, IsNil)
  4456  	c.Check(updates, HasLen, 0)
  4457  }
  4458  
  4459  func (s *snapmgrTestSuite) TestUpdateManyWaitForBasesUC16(c *C) {
  4460  	s.state.Lock()
  4461  	defer s.state.Unlock()
  4462  
  4463  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4464  		Active: true,
  4465  		Sequence: []*snap.SideInfo{
  4466  			{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(1)},
  4467  		},
  4468  		Current:  snap.R(1),
  4469  		SnapType: "os",
  4470  	})
  4471  
  4472  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  4473  		Active: true,
  4474  		Sequence: []*snap.SideInfo{
  4475  			{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)},
  4476  		},
  4477  		Current:  snap.R(1),
  4478  		SnapType: "base",
  4479  	})
  4480  
  4481  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4482  		Active: true,
  4483  		Sequence: []*snap.SideInfo{
  4484  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4485  		},
  4486  		Current:         snap.R(1),
  4487  		SnapType:        "app",
  4488  		TrackingChannel: "channel-for-base/stable",
  4489  	})
  4490  
  4491  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "core", "some-base"}, 0, nil)
  4492  	c.Assert(err, IsNil)
  4493  	c.Assert(tts, HasLen, 4)
  4494  	verifyLastTasksetIsReRefresh(c, tts)
  4495  	c.Check(updates, HasLen, 3)
  4496  
  4497  	// to make TaskSnapSetup work
  4498  	chg := s.state.NewChange("refresh", "...")
  4499  	for _, ts := range tts {
  4500  		chg.AddAll(ts)
  4501  	}
  4502  
  4503  	prereqTotal := len(tts[0].Tasks()) + len(tts[1].Tasks())
  4504  	prereqs := map[string]bool{}
  4505  	for i, task := range tts[2].Tasks() {
  4506  		waitTasks := task.WaitTasks()
  4507  		if i == 0 {
  4508  			c.Check(len(waitTasks), Equals, prereqTotal)
  4509  		} else if task.Kind() == "link-snap" {
  4510  			c.Check(len(waitTasks), Equals, prereqTotal+1)
  4511  			for _, pre := range waitTasks {
  4512  				if pre.Kind() == "link-snap" {
  4513  					snapsup, err := snapstate.TaskSnapSetup(pre)
  4514  					c.Assert(err, IsNil)
  4515  					prereqs[snapsup.InstanceName()] = true
  4516  				}
  4517  			}
  4518  		}
  4519  	}
  4520  
  4521  	c.Check(prereqs, DeepEquals, map[string]bool{
  4522  		"core":      true,
  4523  		"some-base": true,
  4524  	})
  4525  }
  4526  
  4527  func (s *snapmgrTestSuite) TestUpdateManyWaitForBasesUC18(c *C) {
  4528  	r := snapstatetest.MockDeviceModel(ModelWithBase("core18"))
  4529  	defer r()
  4530  
  4531  	s.state.Lock()
  4532  	defer s.state.Unlock()
  4533  
  4534  	snapstate.Set(s.state, "core18", &snapstate.SnapState{
  4535  		Active: true,
  4536  		Sequence: []*snap.SideInfo{
  4537  			{RealName: "core18", SnapID: "core18-snap-id", Revision: snap.R(1)},
  4538  		},
  4539  		Current:  snap.R(1),
  4540  		SnapType: "base",
  4541  	})
  4542  
  4543  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  4544  		Active: true,
  4545  		Sequence: []*snap.SideInfo{
  4546  			{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)},
  4547  		},
  4548  		Current:  snap.R(1),
  4549  		SnapType: "base",
  4550  	})
  4551  
  4552  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
  4553  		Active: true,
  4554  		Sequence: []*snap.SideInfo{
  4555  			{RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)},
  4556  		},
  4557  		Current:  snap.R(1),
  4558  		SnapType: "app",
  4559  	})
  4560  
  4561  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4562  		Active: true,
  4563  		Sequence: []*snap.SideInfo{
  4564  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4565  		},
  4566  		Current:         snap.R(1),
  4567  		SnapType:        "app",
  4568  		TrackingChannel: "channel-for-base/stable",
  4569  	})
  4570  
  4571  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "core18", "some-base", "snapd"}, 0, nil)
  4572  	c.Assert(err, IsNil)
  4573  	c.Assert(tts, HasLen, 5)
  4574  	verifyLastTasksetIsReRefresh(c, tts)
  4575  	c.Check(updates, HasLen, 4)
  4576  
  4577  	// to make TaskSnapSetup work
  4578  	chg := s.state.NewChange("refresh", "...")
  4579  	for _, ts := range tts {
  4580  		chg.AddAll(ts)
  4581  	}
  4582  
  4583  	// Note that some-app only waits for snapd+some-base. The core18
  4584  	// base is not special to this snap and not waited for
  4585  	prereqTotal := len(tts[0].Tasks()) + len(tts[1].Tasks())
  4586  	prereqs := map[string]bool{}
  4587  	for i, task := range tts[3].Tasks() {
  4588  		waitTasks := task.WaitTasks()
  4589  		if i == 0 {
  4590  			c.Check(len(waitTasks), Equals, prereqTotal)
  4591  		} else if task.Kind() == "link-snap" {
  4592  			c.Check(len(waitTasks), Equals, prereqTotal+1)
  4593  			for _, pre := range waitTasks {
  4594  				if pre.Kind() == "link-snap" {
  4595  					snapsup, err := snapstate.TaskSnapSetup(pre)
  4596  					c.Assert(err, IsNil)
  4597  					prereqs[snapsup.InstanceName()] = true
  4598  				}
  4599  			}
  4600  		}
  4601  	}
  4602  
  4603  	// Note that "core18" is not part of the prereqs for some-app
  4604  	// as it does not use this base.
  4605  	c.Check(prereqs, DeepEquals, map[string]bool{
  4606  		"some-base": true,
  4607  		"snapd":     true,
  4608  	})
  4609  }
  4610  
  4611  func (s *snapmgrTestSuite) TestUpdateManyValidateRefreshes(c *C) {
  4612  	s.state.Lock()
  4613  	defer s.state.Unlock()
  4614  
  4615  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4616  		Active: true,
  4617  		Sequence: []*snap.SideInfo{
  4618  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4619  		},
  4620  		Current:  snap.R(1),
  4621  		SnapType: "app",
  4622  	})
  4623  
  4624  	validateCalled := false
  4625  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  4626  		validateCalled = true
  4627  		c.Check(refreshes, HasLen, 1)
  4628  		c.Check(refreshes[0].InstanceName(), Equals, "some-snap")
  4629  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  4630  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  4631  		c.Check(ignoreValidation, HasLen, 0)
  4632  		return refreshes, nil
  4633  	}
  4634  	// hook it up
  4635  	snapstate.ValidateRefreshes = validateRefreshes
  4636  
  4637  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4638  	c.Assert(err, IsNil)
  4639  	c.Assert(tts, HasLen, 2)
  4640  	verifyLastTasksetIsReRefresh(c, tts)
  4641  	c.Check(updates, DeepEquals, []string{"some-snap"})
  4642  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[0], s.state)
  4643  
  4644  	c.Check(validateCalled, Equals, true)
  4645  }
  4646  
  4647  func (s *snapmgrTestSuite) TestParallelInstanceUpdateMany(c *C) {
  4648  	restore := release.MockOnClassic(false)
  4649  	defer restore()
  4650  
  4651  	s.state.Lock()
  4652  	defer s.state.Unlock()
  4653  
  4654  	tr := config.NewTransaction(s.state)
  4655  	tr.Set("core", "experimental.parallel-instances", true)
  4656  	tr.Commit()
  4657  
  4658  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4659  		Active: true,
  4660  		Sequence: []*snap.SideInfo{
  4661  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4662  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4663  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4664  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4665  		},
  4666  		Current:  snap.R(1),
  4667  		SnapType: "app",
  4668  	})
  4669  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  4670  		Active: true,
  4671  		Sequence: []*snap.SideInfo{
  4672  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4673  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4674  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4675  		},
  4676  		Current:     snap.R(3),
  4677  		SnapType:    "app",
  4678  		InstanceKey: "instance",
  4679  	})
  4680  
  4681  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4682  	c.Assert(err, IsNil)
  4683  	c.Assert(tts, HasLen, 3)
  4684  	verifyLastTasksetIsReRefresh(c, tts)
  4685  	// ensure stable ordering of updates list
  4686  	if updates[0] != "some-snap" {
  4687  		updates[1], updates[0] = updates[0], updates[1]
  4688  	}
  4689  
  4690  	c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"})
  4691  
  4692  	var snapsup, snapsupInstance *snapstate.SnapSetup
  4693  
  4694  	// ensure stable ordering of task sets list
  4695  	snapsup, err = snapstate.TaskSnapSetup(tts[0].Tasks()[0])
  4696  	c.Assert(err, IsNil)
  4697  	if snapsup.InstanceName() != "some-snap" {
  4698  		tts[0], tts[1] = tts[1], tts[0]
  4699  		snapsup, err = snapstate.TaskSnapSetup(tts[0].Tasks()[0])
  4700  		c.Assert(err, IsNil)
  4701  	}
  4702  	snapsupInstance, err = snapstate.TaskSnapSetup(tts[1].Tasks()[0])
  4703  	c.Assert(err, IsNil)
  4704  
  4705  	c.Assert(snapsup.InstanceName(), Equals, "some-snap")
  4706  	c.Assert(snapsupInstance.InstanceName(), Equals, "some-snap_instance")
  4707  
  4708  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 3, tts[0], s.state)
  4709  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 1, tts[1], s.state)
  4710  }
  4711  
  4712  func (s *snapmgrTestSuite) TestParallelInstanceUpdateManyValidateRefreshes(c *C) {
  4713  	s.state.Lock()
  4714  	defer s.state.Unlock()
  4715  
  4716  	tr := config.NewTransaction(s.state)
  4717  	tr.Set("core", "experimental.parallel-instances", true)
  4718  	tr.Commit()
  4719  
  4720  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4721  		Active: true,
  4722  		Sequence: []*snap.SideInfo{
  4723  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4724  		},
  4725  		Current:  snap.R(1),
  4726  		SnapType: "app",
  4727  	})
  4728  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  4729  		Active: true,
  4730  		Sequence: []*snap.SideInfo{
  4731  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4732  		},
  4733  		Current:     snap.R(1),
  4734  		SnapType:    "app",
  4735  		InstanceKey: "instance",
  4736  	})
  4737  
  4738  	validateCalled := false
  4739  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  4740  		validateCalled = true
  4741  		c.Check(refreshes, HasLen, 2)
  4742  		instanceIdx := 0
  4743  		someIdx := 1
  4744  		if refreshes[0].InstanceName() != "some-snap_instance" {
  4745  			instanceIdx = 1
  4746  			someIdx = 0
  4747  		}
  4748  		c.Check(refreshes[someIdx].InstanceName(), Equals, "some-snap")
  4749  		c.Check(refreshes[instanceIdx].InstanceName(), Equals, "some-snap_instance")
  4750  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  4751  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  4752  		c.Check(refreshes[1].SnapID, Equals, "some-snap-id")
  4753  		c.Check(refreshes[1].Revision, Equals, snap.R(11))
  4754  		c.Check(ignoreValidation, HasLen, 0)
  4755  		return refreshes, nil
  4756  	}
  4757  	// hook it up
  4758  	snapstate.ValidateRefreshes = validateRefreshes
  4759  
  4760  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4761  	c.Assert(err, IsNil)
  4762  	c.Assert(tts, HasLen, 3)
  4763  	verifyLastTasksetIsReRefresh(c, tts)
  4764  	sort.Strings(updates)
  4765  	c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"})
  4766  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[0], s.state)
  4767  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[1], s.state)
  4768  
  4769  	c.Check(validateCalled, Equals, true)
  4770  }
  4771  
  4772  func (s *snapmgrTestSuite) TestUpdateManyValidateRefreshesUnhappy(c *C) {
  4773  	s.state.Lock()
  4774  	defer s.state.Unlock()
  4775  
  4776  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4777  		Active: true,
  4778  		Sequence: []*snap.SideInfo{
  4779  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4780  		},
  4781  		Current: snap.R(1),
  4782  	})
  4783  
  4784  	validateErr := errors.New("refresh control error")
  4785  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  4786  		c.Check(refreshes, HasLen, 1)
  4787  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  4788  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  4789  		c.Check(ignoreValidation, HasLen, 0)
  4790  		return nil, validateErr
  4791  	}
  4792  	// hook it up
  4793  	snapstate.ValidateRefreshes = validateRefreshes
  4794  
  4795  	// refresh all => no error
  4796  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4797  	c.Assert(err, IsNil)
  4798  	c.Check(tts, HasLen, 0)
  4799  	c.Check(updates, HasLen, 0)
  4800  
  4801  	// refresh some-snap => report error
  4802  	updates, tts, err = snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, 0, nil)
  4803  	c.Assert(err, Equals, validateErr)
  4804  	c.Check(tts, HasLen, 0)
  4805  	c.Check(updates, HasLen, 0)
  4806  
  4807  }
  4808  
  4809  func (s *snapmgrTestSuite) TestUnlinkCurrentSnapLastActiveDisabledServicesSet(c *C) {
  4810  	si := snap.SideInfo{
  4811  		RealName: "services-snap",
  4812  		Revision: snap.R(-42),
  4813  	}
  4814  	snaptest.MockSnap(c, `name: services-snap`, &si)
  4815  
  4816  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  4817  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  4818  
  4819  	// reset the services to what they were before after the test is done
  4820  	defer func() {
  4821  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  4822  	}()
  4823  
  4824  	s.state.Lock()
  4825  	defer s.state.Unlock()
  4826  
  4827  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  4828  		Active:                     true,
  4829  		Sequence:                   []*snap.SideInfo{&si},
  4830  		Current:                    si.Revision,
  4831  		SnapType:                   "app",
  4832  		TrackingChannel:            "stable",
  4833  		LastActiveDisabledServices: []string{},
  4834  	})
  4835  
  4836  	chg := s.state.NewChange("refresh", "refresh a snap")
  4837  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
  4838  
  4839  	c.Assert(err, IsNil)
  4840  	// only add up to unlink-current-snap task
  4841  	for _, t := range ts.Tasks() {
  4842  		chg.AddTask(t)
  4843  		if t.Kind() == "unlink-current-snap" {
  4844  			// don't add any more from this point on
  4845  			break
  4846  		}
  4847  	}
  4848  
  4849  	s.state.Unlock()
  4850  	defer s.se.Stop()
  4851  	s.settle(c)
  4852  	s.state.Lock()
  4853  
  4854  	c.Assert(chg.Err(), IsNil)
  4855  	c.Assert(chg.IsReady(), Equals, true)
  4856  
  4857  	// get the snap state
  4858  	var snapst snapstate.SnapState
  4859  	err = snapstate.Get(s.state, "services-snap", &snapst)
  4860  	c.Assert(err, IsNil)
  4861  
  4862  	// make sure that the disabled services in this snap's state is what we
  4863  	// provided
  4864  	sort.Strings(snapst.LastActiveDisabledServices)
  4865  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  4866  }
  4867  
  4868  func (s *snapmgrTestSuite) TestUnlinkCurrentSnapMergedLastActiveDisabledServicesSet(c *C) {
  4869  	si := snap.SideInfo{
  4870  		RealName: "services-snap",
  4871  		Revision: snap.R(-42),
  4872  	}
  4873  	snaptest.MockSnap(c, `name: services-snap`, &si)
  4874  
  4875  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  4876  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  4877  
  4878  	// reset the services to what they were before after the test is done
  4879  	defer func() {
  4880  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  4881  	}()
  4882  
  4883  	s.state.Lock()
  4884  	defer s.state.Unlock()
  4885  
  4886  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  4887  		Active:                     true,
  4888  		Sequence:                   []*snap.SideInfo{&si},
  4889  		Current:                    si.Revision,
  4890  		SnapType:                   "app",
  4891  		TrackingChannel:            "stable",
  4892  		LastActiveDisabledServices: []string{"missing-svc3"},
  4893  	})
  4894  
  4895  	chg := s.state.NewChange("refresh", "refresh a snap")
  4896  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
  4897  
  4898  	c.Assert(err, IsNil)
  4899  	// only add up to unlink-current-snap task
  4900  	for _, t := range ts.Tasks() {
  4901  		chg.AddTask(t)
  4902  		if t.Kind() == "unlink-current-snap" {
  4903  			// don't add any more from this point on
  4904  			break
  4905  		}
  4906  	}
  4907  
  4908  	s.state.Unlock()
  4909  	defer s.se.Stop()
  4910  	s.settle(c)
  4911  	s.state.Lock()
  4912  
  4913  	c.Assert(chg.Err(), IsNil)
  4914  	c.Assert(chg.IsReady(), Equals, true)
  4915  
  4916  	// get the snap state
  4917  	var snapst snapstate.SnapState
  4918  	err = snapstate.Get(s.state, "services-snap", &snapst)
  4919  	c.Assert(err, IsNil)
  4920  
  4921  	// make sure that the disabled services in this snap's state is what we
  4922  	// provided
  4923  	sort.Strings(snapst.LastActiveDisabledServices)
  4924  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3", "svc1", "svc2"})
  4925  }
  4926  
  4927  func (s *snapmgrTestSuite) TestUnlinkCurrentSnapPassthroughLastActiveDisabledServicesSet(c *C) {
  4928  	si := snap.SideInfo{
  4929  		RealName: "services-snap",
  4930  		Revision: snap.R(-42),
  4931  	}
  4932  	snaptest.MockSnap(c, `name: services-snap`, &si)
  4933  
  4934  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  4935  	s.fakeBackend.servicesCurrentlyDisabled = []string{}
  4936  
  4937  	// reset the services to what they were before after the test is done
  4938  	defer func() {
  4939  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  4940  	}()
  4941  
  4942  	s.state.Lock()
  4943  	defer s.state.Unlock()
  4944  
  4945  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  4946  		Active:                     true,
  4947  		Sequence:                   []*snap.SideInfo{&si},
  4948  		Current:                    si.Revision,
  4949  		SnapType:                   "app",
  4950  		TrackingChannel:            "stable",
  4951  		LastActiveDisabledServices: []string{"missing-svc3"},
  4952  	})
  4953  
  4954  	chg := s.state.NewChange("refresh", "refresh a snap")
  4955  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
  4956  
  4957  	c.Assert(err, IsNil)
  4958  	// only add up to unlink-current-snap task
  4959  	for _, t := range ts.Tasks() {
  4960  		chg.AddTask(t)
  4961  		if t.Kind() == "unlink-current-snap" {
  4962  			// don't add any more from this point on
  4963  			break
  4964  		}
  4965  	}
  4966  
  4967  	s.state.Unlock()
  4968  	defer s.se.Stop()
  4969  	s.settle(c)
  4970  	s.state.Lock()
  4971  
  4972  	c.Assert(chg.Err(), IsNil)
  4973  	c.Assert(chg.IsReady(), Equals, true)
  4974  
  4975  	// get the snap state
  4976  	var snapst snapstate.SnapState
  4977  	err = snapstate.Get(s.state, "services-snap", &snapst)
  4978  	c.Assert(err, IsNil)
  4979  
  4980  	// make sure that the disabled services in this snap's state is what we
  4981  	// provided
  4982  	sort.Strings(snapst.LastActiveDisabledServices)
  4983  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3"})
  4984  }
  4985  
  4986  func (s *snapmgrTestSuite) TestStopSnapServicesSavesSnapSetupLastActiveDisabledServices(c *C) {
  4987  	s.state.Lock()
  4988  	defer s.state.Unlock()
  4989  
  4990  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  4991  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  4992  
  4993  	// reset the services to what they were before after the test is done
  4994  	defer func() {
  4995  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  4996  	}()
  4997  
  4998  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  4999  		Sequence: []*snap.SideInfo{
  5000  			{RealName: "services-snap", Revision: snap.R(11)},
  5001  		},
  5002  		Current: snap.R(11),
  5003  		Active:  true,
  5004  	})
  5005  
  5006  	snapsup := &snapstate.SnapSetup{
  5007  		SideInfo: &snap.SideInfo{
  5008  			RealName: "services-snap",
  5009  			Revision: snap.R(11),
  5010  			SnapID:   "services-snap-id",
  5011  		},
  5012  	}
  5013  
  5014  	chg := s.state.NewChange("stop-services", "stop the services")
  5015  	t1 := s.state.NewTask("prerequisites", "...")
  5016  	t1.Set("snap-setup", snapsup)
  5017  	t2 := s.state.NewTask("stop-snap-services", "...")
  5018  	t2.Set("stop-reason", snap.StopReasonDisable)
  5019  	t2.Set("snap-setup-task", t1.ID())
  5020  	t2.WaitFor(t1)
  5021  	chg.AddTask(t1)
  5022  	chg.AddTask(t2)
  5023  
  5024  	s.state.Unlock()
  5025  	defer s.se.Stop()
  5026  	s.settle(c)
  5027  	s.state.Lock()
  5028  
  5029  	c.Assert(chg.Err(), IsNil)
  5030  	c.Assert(chg.IsReady(), Equals, true)
  5031  
  5032  	// get the snap setup from the task from state
  5033  	endT := s.state.Task(t1.ID())
  5034  	finalsnapsup := &snapstate.SnapSetup{}
  5035  	endT.Get("snap-setup", finalsnapsup)
  5036  
  5037  	// make sure that the disabled services in this snap's state is what we
  5038  	// provided
  5039  	sort.Strings(finalsnapsup.LastActiveDisabledServices)
  5040  	c.Assert(finalsnapsup.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  5041  
  5042  }
  5043  
  5044  func (s *snapmgrTestSuite) TestStopSnapServicesFirstSavesSnapSetupLastActiveDisabledServices(c *C) {
  5045  	s.state.Lock()
  5046  	defer s.state.Unlock()
  5047  
  5048  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  5049  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  5050  
  5051  	// reset the services to what they were before after the test is done
  5052  	defer func() {
  5053  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  5054  	}()
  5055  
  5056  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  5057  		Sequence: []*snap.SideInfo{
  5058  			{RealName: "services-snap", Revision: snap.R(11)},
  5059  		},
  5060  		Current: snap.R(11),
  5061  		Active:  true,
  5062  	})
  5063  
  5064  	snapsup := &snapstate.SnapSetup{
  5065  		SideInfo: &snap.SideInfo{
  5066  			RealName: "services-snap",
  5067  			Revision: snap.R(11),
  5068  			SnapID:   "services-snap-id",
  5069  		},
  5070  	}
  5071  
  5072  	chg := s.state.NewChange("stop-services", "stop the services")
  5073  	t := s.state.NewTask("stop-snap-services", "...")
  5074  	t.Set("stop-reason", snap.StopReasonDisable)
  5075  	t.Set("snap-setup", snapsup)
  5076  	chg.AddTask(t)
  5077  
  5078  	s.state.Unlock()
  5079  	defer s.se.Stop()
  5080  	s.settle(c)
  5081  	s.state.Lock()
  5082  
  5083  	c.Assert(chg.Err(), IsNil)
  5084  	c.Assert(chg.IsReady(), Equals, true)
  5085  
  5086  	// get the snap setup from the task from state
  5087  	endT := s.state.Task(t.ID())
  5088  	finalsnapsup := &snapstate.SnapSetup{}
  5089  	endT.Get("snap-setup", finalsnapsup)
  5090  
  5091  	// make sure that the disabled services in this snap's state is what we
  5092  	// provided
  5093  	sort.Strings(finalsnapsup.LastActiveDisabledServices)
  5094  	c.Assert(finalsnapsup.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  5095  }
  5096  
  5097  func (s *snapmgrTestSuite) TestRefreshDoesntRestoreRevisionConfig(c *C) {
  5098  	restore := release.MockOnClassic(false)
  5099  	defer restore()
  5100  
  5101  	s.state.Lock()
  5102  	defer s.state.Unlock()
  5103  
  5104  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5105  		Active: true,
  5106  		Sequence: []*snap.SideInfo{
  5107  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  5108  		},
  5109  		Current:  snap.R(1),
  5110  		SnapType: "app",
  5111  	})
  5112  
  5113  	// set global configuration (affecting current snap)
  5114  	tr := config.NewTransaction(s.state)
  5115  	tr.Set("some-snap", "foo", "100")
  5116  	tr.Commit()
  5117  
  5118  	// set per-revision config for the upcoming rev. 2, we don't expect it restored though
  5119  	// since only revert restores revision configs.
  5120  	s.state.Set("revision-config", map[string]interface{}{
  5121  		"some-snap": map[string]interface{}{
  5122  			"2": map[string]interface{}{"foo": "200"},
  5123  		},
  5124  	})
  5125  
  5126  	// simulate a refresh to rev. 2
  5127  	chg := s.state.NewChange("update", "update some-snap")
  5128  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(2)}, s.user.ID, snapstate.Flags{})
  5129  	c.Assert(err, IsNil)
  5130  	chg.AddAll(ts)
  5131  
  5132  	s.state.Unlock()
  5133  	defer s.se.Stop()
  5134  	s.settle(c)
  5135  
  5136  	s.state.Lock()
  5137  	// config of rev. 1 has been stored in per-revision map
  5138  	var cfgs map[string]interface{}
  5139  	c.Assert(s.state.Get("revision-config", &cfgs), IsNil)
  5140  	c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{
  5141  		"1": map[string]interface{}{"foo": "100"},
  5142  		"2": map[string]interface{}{"foo": "200"},
  5143  	})
  5144  
  5145  	// config of rev. 2 hasn't been restored by refresh, old value returned
  5146  	tr = config.NewTransaction(s.state)
  5147  	var res string
  5148  	c.Assert(tr.Get("some-snap", "foo", &res), IsNil)
  5149  	c.Assert(res, Equals, "100")
  5150  }
  5151  
  5152  func (s *snapmgrTestSuite) TestRefreshFailureCausesErrorReport(c *C) {
  5153  	var errSnap, errMsg, errSig string
  5154  	var errExtra map[string]string
  5155  	var n int
  5156  	restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) {
  5157  		errSnap = aSnap
  5158  		errMsg = aErrMsg
  5159  		errSig = aDupSig
  5160  		errExtra = extra
  5161  		n += 1
  5162  		return "oopsid", nil
  5163  	})
  5164  	defer restore()
  5165  
  5166  	si := snap.SideInfo{
  5167  		RealName: "some-snap",
  5168  		SnapID:   "some-snap-id",
  5169  		Revision: snap.R(7),
  5170  	}
  5171  
  5172  	s.state.Lock()
  5173  	defer s.state.Unlock()
  5174  
  5175  	s.state.Set("ubuntu-core-transition-retry", 7)
  5176  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5177  		Active:   true,
  5178  		Sequence: []*snap.SideInfo{&si},
  5179  		Current:  si.Revision,
  5180  		SnapType: "app",
  5181  	})
  5182  
  5183  	chg := s.state.NewChange("install", "install a snap")
  5184  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  5185  	c.Assert(err, IsNil)
  5186  	chg.AddAll(ts)
  5187  
  5188  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11")
  5189  
  5190  	s.state.Unlock()
  5191  	defer s.se.Stop()
  5192  	s.settle(c)
  5193  	s.state.Lock()
  5194  
  5195  	// verify we generated a failure report
  5196  	c.Check(n, Equals, 1)
  5197  	c.Check(errSnap, Equals, "some-snap")
  5198  	c.Check(errExtra, DeepEquals, map[string]string{
  5199  		"UbuntuCoreTransitionCount": "7",
  5200  		"Channel":                   "some-channel",
  5201  		"Revision":                  "11",
  5202  	})
  5203  	c.Check(errMsg, Matches, `(?sm)change "install": "install a snap"
  5204  prerequisites: Undo
  5205   snap-setup: "some-snap" \(11\) "some-channel"
  5206  download-snap: Undoing
  5207  validate-snap: Done
  5208  .*
  5209  link-snap: Error
  5210   INFO unlink
  5211   ERROR fail
  5212  auto-connect: Hold
  5213  set-auto-aliases: Hold
  5214  setup-aliases: Hold
  5215  run-hook: Hold
  5216  start-snap-services: Hold
  5217  cleanup: Hold
  5218  run-hook: Hold`)
  5219  	c.Check(errSig, Matches, `(?sm)snap-install:
  5220  prerequisites: Undo
  5221   snap-setup: "some-snap"
  5222  download-snap: Undoing
  5223  validate-snap: Done
  5224  .*
  5225  link-snap: Error
  5226   INFO unlink
  5227   ERROR fail
  5228  auto-connect: Hold
  5229  set-auto-aliases: Hold
  5230  setup-aliases: Hold
  5231  run-hook: Hold
  5232  start-snap-services: Hold
  5233  cleanup: Hold
  5234  run-hook: Hold`)
  5235  
  5236  	// run again with empty "ubuntu-core-transition-retry"
  5237  	s.state.Set("ubuntu-core-transition-retry", 0)
  5238  	chg = s.state.NewChange("install", "install a snap")
  5239  	ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  5240  	c.Assert(err, IsNil)
  5241  	chg.AddAll(ts)
  5242  	s.state.Unlock()
  5243  	defer s.se.Stop()
  5244  	s.settle(c)
  5245  	s.state.Lock()
  5246  	// verify that we excluded this field from the bugreport
  5247  	c.Check(n, Equals, 2)
  5248  	c.Check(errExtra, DeepEquals, map[string]string{
  5249  		"Channel":  "some-channel",
  5250  		"Revision": "11",
  5251  	})
  5252  }
  5253  
  5254  func (s *snapmgrTestSuite) TestNoReRefreshInUpdate(c *C) {
  5255  	s.state.Lock()
  5256  	defer s.state.Unlock()
  5257  
  5258  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5259  		Active: true,
  5260  		Sequence: []*snap.SideInfo{
  5261  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  5262  		},
  5263  		Current:  snap.R(1),
  5264  		SnapType: "app",
  5265  	})
  5266  
  5267  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{NoReRefresh: true})
  5268  	c.Assert(err, IsNil)
  5269  
  5270  	// ensure we have no re-refresh task
  5271  	for _, t := range ts.Tasks() {
  5272  		c.Assert(t.Kind(), Not(Equals), "check-rerefresh")
  5273  	}
  5274  
  5275  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
  5276  	c.Assert(err, IsNil)
  5277  	// NoReRefresh is consumed and consulted when creating the taskset
  5278  	// but is not copied into SnapSetup
  5279  	c.Check(snapsup.Flags.NoReRefresh, Equals, false)
  5280  }
  5281  
  5282  func (s *snapmgrTestSuite) TestEmptyUpdateWithChannelChangeAndAutoAlias(c *C) {
  5283  	// this reproduces the cause behind lp:1860324,
  5284  	// namely an empty refresh with a channel change on a snap
  5285  	// with changed aliases
  5286  
  5287  	s.state.Lock()
  5288  	defer s.state.Unlock()
  5289  
  5290  	n := 0
  5291  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  5292  		if info.InstanceName() == "alias-snap" {
  5293  			if n > 0 {
  5294  				return map[string]string{
  5295  					"alias1": "cmd1",
  5296  					"alias2": "cmd2",
  5297  				}, nil
  5298  			}
  5299  			n++
  5300  		}
  5301  		return nil, nil
  5302  	}
  5303  
  5304  	snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{
  5305  		TrackingChannel: "latest/stable",
  5306  		Sequence: []*snap.SideInfo{
  5307  			{RealName: "alias-snap", Revision: snap.R(11), SnapID: "alias-snap-id"},
  5308  		},
  5309  		Current: snap.R(11),
  5310  		Active:  true,
  5311  	})
  5312  
  5313  	s.state.Set("aliases", map[string]map[string]string{
  5314  		"alias-snap": {
  5315  			"alias1": "auto",
  5316  		},
  5317  	})
  5318  
  5319  	s.state.Unlock()
  5320  	err := s.snapmgr.Ensure()
  5321  	s.state.Lock()
  5322  	c.Assert(err, IsNil)
  5323  
  5324  	ts, err := snapstate.Update(s.state, "alias-snap", &snapstate.RevisionOptions{Channel: "latest/candidate"}, s.user.ID, snapstate.Flags{})
  5325  	c.Assert(err, IsNil)
  5326  
  5327  	chg := s.state.NewChange("refresh", "refresh snap")
  5328  	chg.AddAll(ts)
  5329  
  5330  	s.state.Unlock()
  5331  	defer s.se.Stop()
  5332  	s.settle(c)
  5333  	s.state.Lock()
  5334  
  5335  	c.Assert(chg.Err(), IsNil)
  5336  	c.Assert(chg.IsReady(), Equals, true)
  5337  }