github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/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/osutil"
    36  	"github.com/snapcore/snapd/overlord/auth"
    37  
    38  	// So it registers Configure.
    39  	_ "github.com/snapcore/snapd/overlord/configstate"
    40  	"github.com/snapcore/snapd/overlord/configstate/config"
    41  	"github.com/snapcore/snapd/overlord/ifacestate/ifacerepo"
    42  	"github.com/snapcore/snapd/overlord/snapstate"
    43  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    44  	"github.com/snapcore/snapd/overlord/state"
    45  	"github.com/snapcore/snapd/release"
    46  	"github.com/snapcore/snapd/snap"
    47  	"github.com/snapcore/snapd/snap/snaptest"
    48  	"github.com/snapcore/snapd/store"
    49  	"github.com/snapcore/snapd/testutil"
    50  )
    51  
    52  func verifyUpdateTasks(c *C, opts, discards int, ts *state.TaskSet, st *state.State) {
    53  	kinds := taskKinds(ts.Tasks())
    54  
    55  	expected := []string{
    56  		"prerequisites",
    57  		"download-snap",
    58  		"validate-snap",
    59  		"mount-snap",
    60  	}
    61  	expected = append(expected, "run-hook[pre-refresh]")
    62  	if opts&unlinkBefore != 0 {
    63  		expected = append(expected,
    64  			"stop-snap-services",
    65  		)
    66  	}
    67  	if opts&unlinkBefore != 0 {
    68  		expected = append(expected,
    69  			"remove-aliases",
    70  			"unlink-current-snap",
    71  		)
    72  	}
    73  	if opts&updatesGadget != 0 {
    74  		expected = append(expected,
    75  			"update-gadget-assets",
    76  			"update-gadget-cmdline")
    77  	}
    78  	expected = append(expected,
    79  		"copy-snap-data",
    80  		"setup-profiles",
    81  		"link-snap",
    82  	)
    83  	if opts&maybeCore != 0 {
    84  		expected = append(expected, "setup-profiles")
    85  	}
    86  	expected = append(expected,
    87  		"auto-connect",
    88  		"set-auto-aliases",
    89  		"setup-aliases",
    90  		"run-hook[post-refresh]",
    91  		"start-snap-services")
    92  
    93  	c.Assert(ts.Tasks()[len(expected)-2].Summary(), Matches, `Run post-refresh hook of .*`)
    94  	for i := 0; i < discards; i++ {
    95  		expected = append(expected,
    96  			"clear-snap",
    97  			"discard-snap",
    98  		)
    99  	}
   100  	if opts&cleanupAfter != 0 {
   101  		expected = append(expected,
   102  			"cleanup",
   103  		)
   104  	}
   105  	expected = append(expected,
   106  		"run-hook[configure]",
   107  		"run-hook[check-health]",
   108  	)
   109  	if opts&doesReRefresh != 0 {
   110  		expected = append(expected, "check-rerefresh")
   111  	}
   112  
   113  	c.Assert(kinds, DeepEquals, expected)
   114  }
   115  
   116  func (s *snapmgrTestSuite) TestUpdateDoesGC(c *C) {
   117  	s.state.Lock()
   118  	defer s.state.Unlock()
   119  	restore := release.MockOnClassic(false)
   120  	defer restore()
   121  
   122  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   123  		Active: true,
   124  		Sequence: []*snap.SideInfo{
   125  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   126  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
   127  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
   128  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
   129  		},
   130  		Current:  snap.R(4),
   131  		SnapType: "app",
   132  	})
   133  
   134  	chg := s.state.NewChange("update", "update a snap")
   135  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
   136  	c.Assert(err, IsNil)
   137  	chg.AddAll(ts)
   138  
   139  	s.state.Unlock()
   140  	defer s.se.Stop()
   141  	s.settle(c)
   142  	s.state.Lock()
   143  
   144  	// ensure garbage collection runs as the last tasks
   145  	expectedTail := fakeOps{
   146  		{
   147  			op:   "link-snap",
   148  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
   149  		},
   150  		{
   151  			op:    "auto-connect:Doing",
   152  			name:  "some-snap",
   153  			revno: snap.R(11),
   154  		},
   155  		{
   156  			op: "update-aliases",
   157  		},
   158  		{
   159  			op:   "remove-snap-data",
   160  			path: filepath.Join(dirs.SnapMountDir, "some-snap/1"),
   161  		},
   162  		{
   163  			op:    "remove-snap-files",
   164  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/1"),
   165  			stype: "app",
   166  		},
   167  		{
   168  			op:   "remove-snap-data",
   169  			path: filepath.Join(dirs.SnapMountDir, "some-snap/2"),
   170  		},
   171  		{
   172  			op:    "remove-snap-files",
   173  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/2"),
   174  			stype: "app",
   175  		},
   176  		{
   177  			op:    "cleanup-trash",
   178  			name:  "some-snap",
   179  			revno: snap.R(11),
   180  		},
   181  	}
   182  
   183  	opsTail := s.fakeBackend.ops[len(s.fakeBackend.ops)-len(expectedTail):]
   184  	c.Assert(opsTail.Ops(), DeepEquals, expectedTail.Ops())
   185  	c.Check(opsTail, DeepEquals, expectedTail)
   186  }
   187  
   188  func (s *snapmgrTestSuite) TestUpdateScenarios(c *C) {
   189  	// TODO: also use channel-for-7 or equiv to check updates that are switches
   190  	for k, t := range switchScenarios {
   191  		s.testUpdateScenario(c, k, t)
   192  	}
   193  }
   194  
   195  func (s *snapmgrTestSuite) testUpdateScenario(c *C, desc string, t switchScenario) {
   196  	// reset
   197  	s.fakeBackend.ops = nil
   198  
   199  	comment := Commentf("%q (%+v)", desc, t)
   200  	si := snap.SideInfo{
   201  		RealName: "some-snap",
   202  		Revision: snap.R(7),
   203  		Channel:  t.chanFrom,
   204  		SnapID:   "some-snap-id",
   205  	}
   206  
   207  	s.state.Lock()
   208  	defer s.state.Unlock()
   209  
   210  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   211  		Sequence:        []*snap.SideInfo{&si},
   212  		Active:          true,
   213  		Current:         si.Revision,
   214  		TrackingChannel: t.chanFrom,
   215  		CohortKey:       t.cohFrom,
   216  	})
   217  
   218  	chg := s.state.NewChange("update-snap", t.summary)
   219  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{
   220  		Channel:     t.chanTo,
   221  		CohortKey:   t.cohTo,
   222  		LeaveCohort: t.cohFrom != "" && t.cohTo == "",
   223  	}, 0, snapstate.Flags{})
   224  	c.Assert(err, IsNil, comment)
   225  	chg.AddAll(ts)
   226  
   227  	s.state.Unlock()
   228  	s.settle(c)
   229  	s.state.Lock()
   230  
   231  	// switch is not really really doing anything backend related
   232  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, []string{
   233  		"storesvc-snap-action",
   234  		"storesvc-snap-action:action",
   235  		"storesvc-download",
   236  		"validate-snap:Doing",
   237  		"current",
   238  		"open-snap-file",
   239  		"setup-snap",
   240  		"remove-snap-aliases",
   241  		"unlink-snap",
   242  		"copy-data",
   243  		"setup-profiles:Doing",
   244  		"candidate",
   245  		"link-snap",
   246  		"auto-connect:Doing",
   247  		"update-aliases",
   248  		"cleanup-trash",
   249  	}, comment)
   250  
   251  	expectedChanTo := t.chanTo
   252  	if t.chanTo == "" {
   253  		expectedChanTo = t.chanFrom
   254  	}
   255  	expectedCohTo := t.cohTo
   256  
   257  	// ensure the desired channel/cohort has changed
   258  	var snapst snapstate.SnapState
   259  	err = snapstate.Get(s.state, "some-snap", &snapst)
   260  	c.Assert(err, IsNil, comment)
   261  	c.Assert(snapst.TrackingChannel, Equals, expectedChanTo, comment)
   262  	c.Assert(snapst.CohortKey, Equals, expectedCohTo, comment)
   263  
   264  	// ensure the current info *has* changed
   265  	info, err := snapst.CurrentInfo()
   266  	c.Assert(err, IsNil, comment)
   267  	c.Assert(info.Channel, Equals, expectedChanTo, comment)
   268  }
   269  
   270  func (s *snapmgrTestSuite) TestUpdateTasksWithOldCurrent(c *C) {
   271  	s.state.Lock()
   272  	defer s.state.Unlock()
   273  	restore := release.MockOnClassic(false)
   274  	defer restore()
   275  
   276  	si1 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}
   277  	si2 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}
   278  	si3 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)}
   279  	si4 := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)}
   280  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   281  		Active:          true,
   282  		TrackingChannel: "latest/edge",
   283  		Sequence:        []*snap.SideInfo{si1, si2, si3, si4},
   284  		Current:         snap.R(2),
   285  		SnapType:        "app",
   286  	})
   287  
   288  	// run the update
   289  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
   290  	c.Assert(err, IsNil)
   291  
   292  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 2, ts, s.state)
   293  
   294  	// and ensure that it will remove the revisions after "current"
   295  	// (si3, si4)
   296  	var snapsup snapstate.SnapSetup
   297  	tasks := ts.Tasks()
   298  
   299  	i := len(tasks) - 8
   300  	c.Check(tasks[i].Kind(), Equals, "clear-snap")
   301  	err = tasks[i].Get("snap-setup", &snapsup)
   302  	c.Assert(err, IsNil)
   303  	c.Check(snapsup.Revision(), Equals, si3.Revision)
   304  
   305  	i = len(tasks) - 6
   306  	c.Check(tasks[i].Kind(), Equals, "clear-snap")
   307  	err = tasks[i].Get("snap-setup", &snapsup)
   308  	c.Assert(err, IsNil)
   309  	c.Check(snapsup.Revision(), Equals, si4.Revision)
   310  }
   311  
   312  func (s *snapmgrTestSuite) TestUpdateCanDoBackwards(c *C) {
   313  	si7 := snap.SideInfo{
   314  		RealName: "some-snap",
   315  		SnapID:   "some-snap-id",
   316  		Revision: snap.R(7),
   317  	}
   318  	si11 := snap.SideInfo{
   319  		RealName: "some-snap",
   320  		SnapID:   "some-snap-id",
   321  		Revision: snap.R(11),
   322  	}
   323  
   324  	s.state.Lock()
   325  	defer s.state.Unlock()
   326  
   327  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   328  		Active:   true,
   329  		Sequence: []*snap.SideInfo{&si7, &si11},
   330  		Current:  si11.Revision,
   331  		SnapType: "app",
   332  	})
   333  
   334  	chg := s.state.NewChange("refresh", "refresh a snap")
   335  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(7)}, s.user.ID, snapstate.Flags{})
   336  	c.Assert(err, IsNil)
   337  	chg.AddAll(ts)
   338  
   339  	s.state.Unlock()
   340  	defer s.se.Stop()
   341  	s.settle(c)
   342  	s.state.Lock()
   343  	expected := fakeOps{
   344  		{
   345  			op:   "remove-snap-aliases",
   346  			name: "some-snap",
   347  		},
   348  		{
   349  			op:   "unlink-snap",
   350  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
   351  		},
   352  		{
   353  			op:   "copy-data",
   354  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
   355  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
   356  		},
   357  		{
   358  			op:    "setup-profiles:Doing",
   359  			name:  "some-snap",
   360  			revno: snap.R(7),
   361  		},
   362  		{
   363  			op: "candidate",
   364  			sinfo: snap.SideInfo{
   365  				RealName: "some-snap",
   366  				SnapID:   "some-snap-id",
   367  				Channel:  "",
   368  				Revision: snap.R(7),
   369  			},
   370  		},
   371  		{
   372  			op:   "link-snap",
   373  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
   374  		},
   375  		{
   376  			op:    "auto-connect:Doing",
   377  			name:  "some-snap",
   378  			revno: snap.R(7),
   379  		},
   380  		{
   381  			op: "update-aliases",
   382  		},
   383  		{
   384  			op:    "cleanup-trash",
   385  			name:  "some-snap",
   386  			revno: snap.R(7),
   387  		},
   388  	}
   389  	// start with an easier-to-read error if this fails:
   390  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
   391  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
   392  }
   393  
   394  func revs(seq []*snap.SideInfo) []int {
   395  	revs := make([]int, len(seq))
   396  	for i, si := range seq {
   397  		revs[i] = si.Revision.N
   398  	}
   399  
   400  	return revs
   401  }
   402  
   403  type opSeqOpts struct {
   404  	revert  bool
   405  	fail    bool
   406  	before  []int
   407  	current int
   408  	via     int
   409  	after   []int
   410  }
   411  
   412  // build a SnapState with a revision sequence given by `before` and a
   413  // current revision of `current`. Then refresh --revision via. Then
   414  // check the revision sequence is as in `after`.
   415  func (s *snapmgrTestSuite) testOpSequence(c *C, opts *opSeqOpts) (*snapstate.SnapState, *state.TaskSet) {
   416  	s.state.Lock()
   417  	defer s.state.Unlock()
   418  
   419  	seq := make([]*snap.SideInfo, len(opts.before))
   420  	for i, n := range opts.before {
   421  		seq[i] = &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(n)}
   422  	}
   423  
   424  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   425  		Active:          true,
   426  		TrackingChannel: "latest/edge",
   427  		Sequence:        seq,
   428  		Current:         snap.R(opts.current),
   429  		SnapType:        "app",
   430  	})
   431  
   432  	var chg *state.Change
   433  	var ts *state.TaskSet
   434  	var err error
   435  	if opts.revert {
   436  		chg = s.state.NewChange("revert", "revert a snap")
   437  		ts, err = snapstate.RevertToRevision(s.state, "some-snap", snap.R(opts.via), snapstate.Flags{})
   438  	} else {
   439  		chg = s.state.NewChange("refresh", "refresh a snap")
   440  		ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Revision: snap.R(opts.via)}, s.user.ID, snapstate.Flags{})
   441  	}
   442  	c.Assert(err, IsNil)
   443  	if opts.fail {
   444  		tasks := ts.Tasks()
   445  		var last *state.Task
   446  		// don't make a task wait on rerefresh, that's bad
   447  		for i := len(tasks) - 1; i > 0; i-- {
   448  			last = tasks[i]
   449  			if last.Kind() != "check-rerefresh" {
   450  				break
   451  			}
   452  		}
   453  		terr := s.state.NewTask("error-trigger", "provoking total undo")
   454  		terr.WaitFor(last)
   455  		if len(last.Lanes()) > 0 {
   456  			lanes := last.Lanes()
   457  			// sanity
   458  			c.Assert(lanes, HasLen, 1)
   459  			terr.JoinLane(lanes[0])
   460  		}
   461  		chg.AddTask(terr)
   462  	}
   463  	chg.AddAll(ts)
   464  
   465  	s.state.Unlock()
   466  	defer s.se.Stop()
   467  	s.settle(c)
   468  	s.state.Lock()
   469  
   470  	var snapst snapstate.SnapState
   471  	err = snapstate.Get(s.state, "some-snap", &snapst)
   472  	c.Assert(err, IsNil)
   473  	c.Check(revs(snapst.Sequence), DeepEquals, opts.after)
   474  
   475  	return &snapst, ts
   476  }
   477  
   478  func (s *snapmgrTestSuite) testUpdateSequence(c *C, opts *opSeqOpts) *state.TaskSet {
   479  	restore := release.MockOnClassic(false)
   480  	defer restore()
   481  
   482  	opts.revert = false
   483  	snapst, ts := s.testOpSequence(c, opts)
   484  	// update always ends with current==seq[-1]==via:
   485  	c.Check(snapst.Current.N, Equals, opts.after[len(opts.after)-1])
   486  	c.Check(snapst.Current.N, Equals, opts.via)
   487  
   488  	c.Check(s.fakeBackend.ops.Count("copy-data"), Equals, 1)
   489  	c.Check(s.fakeBackend.ops.First("copy-data"), DeepEquals, &fakeOp{
   490  		op:   "copy-data",
   491  		path: fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via),
   492  		old:  fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.current),
   493  	})
   494  
   495  	return ts
   496  }
   497  
   498  func (s *snapmgrTestSuite) testUpdateFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet {
   499  	restore := release.MockOnClassic(false)
   500  	defer restore()
   501  
   502  	opts.revert = false
   503  	opts.after = opts.before
   504  	s.fakeBackend.linkSnapFailTrigger = fmt.Sprintf(filepath.Join(dirs.SnapMountDir, "some-snap/%d"), opts.via)
   505  	snapst, ts := s.testOpSequence(c, opts)
   506  	// a failed update will always end with current unchanged
   507  	c.Check(snapst.Current.N, Equals, opts.current)
   508  
   509  	ops := s.fakeBackend.ops
   510  	c.Check(ops.Count("copy-data"), Equals, 1)
   511  	do := ops.First("copy-data")
   512  
   513  	c.Check(ops.Count("undo-copy-snap-data"), Equals, 1)
   514  	undo := ops.First("undo-copy-snap-data")
   515  
   516  	do.op = undo.op
   517  	c.Check(do, DeepEquals, undo) // i.e. they only differed in the op
   518  
   519  	return ts
   520  }
   521  
   522  // testTotal*Failure fails *after* link-snap
   523  func (s *snapmgrTestSuite) testTotalUpdateFailureSequence(c *C, opts *opSeqOpts) *state.TaskSet {
   524  	restore := release.MockOnClassic(false)
   525  	defer restore()
   526  
   527  	opts.revert = false
   528  	opts.fail = true
   529  	snapst, ts := s.testOpSequence(c, opts)
   530  	// a failed update will always end with current unchanged
   531  	c.Check(snapst.Current.N, Equals, opts.current)
   532  
   533  	ops := s.fakeBackend.ops
   534  	c.Check(ops.Count("copy-data"), Equals, 1)
   535  	do := ops.First("copy-data")
   536  
   537  	c.Check(ops.Count("undo-copy-snap-data"), Equals, 1)
   538  	undo := ops.First("undo-copy-snap-data")
   539  
   540  	do.op = undo.op
   541  	c.Check(do, DeepEquals, undo) // i.e. they only differed in the op
   542  
   543  	return ts
   544  }
   545  
   546  func (s *snapmgrTestSuite) TestUpdateLayoutsChecksFeatureFlag(c *C) {
   547  	s.state.Lock()
   548  	defer s.state.Unlock()
   549  
   550  	// When layouts are disabled we cannot refresh to a snap depending on the feature.
   551  	tr := config.NewTransaction(s.state)
   552  	tr.Set("core", "experimental.layouts", false)
   553  	tr.Commit()
   554  
   555  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   556  		Active: true,
   557  		Sequence: []*snap.SideInfo{
   558  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   559  		},
   560  		Current:  snap.R(1),
   561  		SnapType: "app",
   562  	})
   563  
   564  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-layout/stable"}, s.user.ID, snapstate.Flags{})
   565  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true")
   566  
   567  	// When layouts are enabled we can refresh to a snap depending on the feature.
   568  	tr = config.NewTransaction(s.state)
   569  	tr.Set("core", "experimental.layouts", true)
   570  	tr.Commit()
   571  
   572  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-layout/stable"}, s.user.ID, snapstate.Flags{})
   573  	c.Assert(err, IsNil)
   574  }
   575  
   576  func (s *snapmgrTestSuite) TestUpdateManyExplicitLayoutsChecksFeatureFlag(c *C) {
   577  	s.state.Lock()
   578  	defer s.state.Unlock()
   579  
   580  	// When layouts are disabled we cannot refresh multiple snaps if one of them depends on the feature.
   581  	tr := config.NewTransaction(s.state)
   582  	tr.Set("core", "experimental.layouts", false)
   583  	tr.Commit()
   584  
   585  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   586  		Active:          true,
   587  		TrackingChannel: "channel-for-layout/stable",
   588  		Sequence: []*snap.SideInfo{
   589  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   590  		},
   591  		Current:  snap.R(1),
   592  		SnapType: "app",
   593  	})
   594  
   595  	_, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
   596  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true")
   597  
   598  	// When layouts are enabled we can refresh multiple snaps if one of them depends on the feature.
   599  	tr = config.NewTransaction(s.state)
   600  	tr.Set("core", "experimental.layouts", true)
   601  	tr.Commit()
   602  
   603  	_, _, err = snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
   604  	c.Assert(err, IsNil)
   605  }
   606  
   607  func (s *snapmgrTestSuite) TestUpdateManyLayoutsChecksFeatureFlag(c *C) {
   608  	s.state.Lock()
   609  	defer s.state.Unlock()
   610  
   611  	// When layouts are disabled we cannot refresh multiple snaps if one of them depends on the feature.
   612  	tr := config.NewTransaction(s.state)
   613  	tr.Set("core", "experimental.layouts", false)
   614  	tr.Commit()
   615  
   616  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   617  		Active:          true,
   618  		TrackingChannel: "channel-for-layout/stable",
   619  		Sequence: []*snap.SideInfo{
   620  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   621  		},
   622  		Current:  snap.R(1),
   623  		SnapType: "app",
   624  	})
   625  
   626  	refreshes, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil)
   627  	c.Assert(err, IsNil)
   628  	c.Assert(refreshes, HasLen, 0)
   629  
   630  	// When layouts are enabled we can refresh multiple snaps if one of them depends on the feature.
   631  	tr = config.NewTransaction(s.state)
   632  	tr.Set("core", "experimental.layouts", true)
   633  	tr.Commit()
   634  
   635  	refreshes, _, err = snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil)
   636  	c.Assert(err, IsNil)
   637  	c.Assert(refreshes, DeepEquals, []string{"some-snap"})
   638  }
   639  
   640  func (s *snapmgrTestSuite) TestUpdateFailsEarlyOnEpochMismatch(c *C) {
   641  	s.state.Lock()
   642  	defer s.state.Unlock()
   643  
   644  	snapstate.Set(s.state, "some-epoch-snap", &snapstate.SnapState{
   645  		Active: true,
   646  		Sequence: []*snap.SideInfo{
   647  			{RealName: "some-epoch-snap", SnapID: "some-epoch-snap-id", Revision: snap.R(1)},
   648  		},
   649  		Current:  snap.R(1),
   650  		SnapType: "app",
   651  	})
   652  
   653  	_, err := snapstate.Update(s.state, "some-epoch-snap", nil, 0, snapstate.Flags{})
   654  	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`)
   655  }
   656  
   657  func (s *snapmgrTestSuite) TestUpdateTasksPropagatesErrors(c *C) {
   658  	s.state.Lock()
   659  	defer s.state.Unlock()
   660  
   661  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   662  		Active:          true,
   663  		TrackingChannel: "latest/edge",
   664  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "fakestore-please-error-on-refresh", Revision: snap.R(7)}},
   665  		Current:         snap.R(7),
   666  	})
   667  
   668  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
   669  	c.Assert(err, ErrorMatches, `failing as requested`)
   670  }
   671  
   672  func (s *snapmgrTestSuite) TestUpdateTasks(c *C) {
   673  	s.state.Lock()
   674  	defer s.state.Unlock()
   675  
   676  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   677  		Active:          true,
   678  		TrackingChannel: "latest/edge",
   679  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
   680  		Current:         snap.R(7),
   681  		SnapType:        "app",
   682  	})
   683  
   684  	validateCalled := false
   685  	happyValidateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
   686  		validateCalled = true
   687  		return refreshes, nil
   688  	}
   689  	// hook it up
   690  	snapstate.ValidateRefreshes = happyValidateRefreshes
   691  
   692  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
   693  	c.Assert(err, IsNil)
   694  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
   695  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   696  
   697  	c.Check(validateCalled, Equals, true)
   698  
   699  	var snapsup snapstate.SnapSetup
   700  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
   701  	c.Assert(err, IsNil)
   702  
   703  	c.Check(snapsup.Channel, Equals, "some-channel")
   704  }
   705  
   706  func (s *snapmgrTestSuite) TestUpdateAmendRunThrough(c *C) {
   707  	si := snap.SideInfo{
   708  		RealName: "some-snap",
   709  		Revision: snap.R(-42),
   710  	}
   711  	snaptest.MockSnap(c, `name: some-snap`, &si)
   712  
   713  	s.state.Lock()
   714  	defer s.state.Unlock()
   715  
   716  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   717  		Active:          true,
   718  		Sequence:        []*snap.SideInfo{&si},
   719  		Current:         si.Revision,
   720  		SnapType:        "app",
   721  		TrackingChannel: "latest/stable",
   722  	})
   723  
   724  	chg := s.state.NewChange("refresh", "refresh a snap")
   725  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
   726  	c.Assert(err, IsNil)
   727  	chg.AddAll(ts)
   728  
   729  	s.state.Unlock()
   730  	defer s.se.Stop()
   731  	s.settle(c)
   732  	s.state.Lock()
   733  
   734  	// ensure all our tasks ran
   735  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
   736  		macaroon: s.user.StoreMacaroon,
   737  		name:     "some-snap",
   738  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
   739  	}})
   740  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
   741  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, []string{
   742  		"storesvc-snap-action",
   743  		"storesvc-snap-action:action",
   744  		"storesvc-download",
   745  		"validate-snap:Doing",
   746  		"current",
   747  		"open-snap-file",
   748  		"setup-snap",
   749  		"remove-snap-aliases",
   750  		"unlink-snap",
   751  		"copy-data",
   752  		"setup-profiles:Doing",
   753  		"candidate",
   754  		"link-snap",
   755  		"auto-connect:Doing",
   756  		"update-aliases",
   757  		"cleanup-trash",
   758  	})
   759  	// just check the interesting op
   760  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
   761  		op: "storesvc-snap-action:action",
   762  		action: store.SnapAction{
   763  			Action:       "install", // we asked for an Update, but an amend is actually an Install
   764  			InstanceName: "some-snap",
   765  			Channel:      "some-channel",
   766  			Epoch:        snap.E("1*"), // in amend, epoch in the action is not nil!
   767  			Flags:        store.SnapActionEnforceValidation,
   768  		},
   769  		revno:  snap.R(11),
   770  		userID: 1,
   771  	})
   772  
   773  	task := ts.Tasks()[1]
   774  	// verify snapSetup info
   775  	var snapsup snapstate.SnapSetup
   776  	err = task.Get("snap-setup", &snapsup)
   777  	c.Assert(err, IsNil)
   778  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
   779  		Channel: "some-channel",
   780  		UserID:  s.user.ID,
   781  
   782  		SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
   783  		DownloadInfo: &snap.DownloadInfo{
   784  			DownloadURL: "https://some-server.com/some/path.snap",
   785  			Size:        5,
   786  		},
   787  		SideInfo:  snapsup.SideInfo,
   788  		Type:      snap.TypeApp,
   789  		PlugsOnly: true,
   790  		Flags:     snapstate.Flags{Amend: true},
   791  	})
   792  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
   793  		RealName: "some-snap",
   794  		Revision: snap.R(11),
   795  		Channel:  "some-channel",
   796  		SnapID:   "some-snap-id",
   797  	})
   798  
   799  	// verify services stop reason
   800  	verifyStopReason(c, ts, "refresh")
   801  
   802  	// check post-refresh hook
   803  	task = ts.Tasks()[14]
   804  	c.Assert(task.Kind(), Equals, "run-hook")
   805  	c.Assert(task.Summary(), Matches, `Run post-refresh hook of "some-snap" snap if present`)
   806  
   807  	// verify snaps in the system state
   808  	var snapst snapstate.SnapState
   809  	err = snapstate.Get(s.state, "some-snap", &snapst)
   810  	c.Assert(err, IsNil)
   811  
   812  	c.Assert(snapst.Active, Equals, true)
   813  	c.Assert(snapst.Sequence, HasLen, 2)
   814  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
   815  		RealName: "some-snap",
   816  		Channel:  "",
   817  		Revision: snap.R(-42),
   818  	})
   819  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
   820  		RealName: "some-snap",
   821  		Channel:  "some-channel",
   822  		SnapID:   "some-snap-id",
   823  		Revision: snap.R(11),
   824  	})
   825  }
   826  
   827  func (s *snapmgrTestSuite) TestUpdateRunThrough(c *C) {
   828  	// we start without the auxiliary store info (or with an older one)
   829  	c.Check(snapstate.AuxStoreInfoFilename("services-snap-id"), testutil.FileAbsent)
   830  
   831  	// use services-snap here to make sure services would be stopped/started appropriately
   832  	si := snap.SideInfo{
   833  		RealName: "services-snap",
   834  		Revision: snap.R(7),
   835  		SnapID:   "services-snap-id",
   836  	}
   837  	snaptest.MockSnap(c, `name: services-snap`, &si)
   838  	fi, err := os.Stat(snap.MountFile("services-snap", si.Revision))
   839  	c.Assert(err, IsNil)
   840  	refreshedDate := fi.ModTime()
   841  	// look at disk
   842  	r := snapstate.MockRevisionDate(nil)
   843  	defer r()
   844  
   845  	s.state.Lock()
   846  	defer s.state.Unlock()
   847  
   848  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
   849  		Active:          true,
   850  		Sequence:        []*snap.SideInfo{&si},
   851  		Current:         si.Revision,
   852  		SnapType:        "app",
   853  		TrackingChannel: "latest/stable",
   854  		CohortKey:       "embattled",
   855  	})
   856  
   857  	chg := s.state.NewChange("refresh", "refresh a snap")
   858  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{
   859  		Channel:   "some-channel",
   860  		CohortKey: "some-cohort",
   861  	}, s.user.ID, snapstate.Flags{})
   862  	c.Assert(err, IsNil)
   863  	chg.AddAll(ts)
   864  
   865  	s.state.Unlock()
   866  	defer s.se.Stop()
   867  	s.settle(c)
   868  	s.state.Lock()
   869  
   870  	expected := fakeOps{
   871  		{
   872  			op: "storesvc-snap-action",
   873  			curSnaps: []store.CurrentSnap{{
   874  				InstanceName:    "services-snap",
   875  				SnapID:          "services-snap-id",
   876  				Revision:        snap.R(7),
   877  				TrackingChannel: "latest/stable",
   878  				RefreshedDate:   refreshedDate,
   879  				Epoch:           snap.E("0"),
   880  				CohortKey:       "embattled",
   881  			}},
   882  			userID: 1,
   883  		},
   884  		{
   885  			op: "storesvc-snap-action:action",
   886  			action: store.SnapAction{
   887  				Action:       "refresh",
   888  				InstanceName: "services-snap",
   889  				SnapID:       "services-snap-id",
   890  				Channel:      "some-channel",
   891  				CohortKey:    "some-cohort",
   892  				Flags:        store.SnapActionEnforceValidation,
   893  			},
   894  			revno:  snap.R(11),
   895  			userID: 1,
   896  		},
   897  		{
   898  			op:   "storesvc-download",
   899  			name: "services-snap",
   900  		},
   901  		{
   902  			op:    "validate-snap:Doing",
   903  			name:  "services-snap",
   904  			revno: snap.R(11),
   905  		},
   906  		{
   907  			op:  "current",
   908  			old: filepath.Join(dirs.SnapMountDir, "services-snap/7"),
   909  		},
   910  		{
   911  			op:   "open-snap-file",
   912  			path: filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
   913  			sinfo: snap.SideInfo{
   914  				RealName: "services-snap",
   915  				SnapID:   "services-snap-id",
   916  				Channel:  "some-channel",
   917  				Revision: snap.R(11),
   918  			},
   919  		},
   920  		{
   921  			op:    "setup-snap",
   922  			name:  "services-snap",
   923  			path:  filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
   924  			revno: snap.R(11),
   925  		},
   926  		{
   927  			op:   "stop-snap-services:refresh",
   928  			path: filepath.Join(dirs.SnapMountDir, "services-snap/7"),
   929  		},
   930  		{
   931  			op: "current-snap-service-states",
   932  		},
   933  		{
   934  			op:   "remove-snap-aliases",
   935  			name: "services-snap",
   936  		},
   937  		{
   938  			op:   "unlink-snap",
   939  			path: filepath.Join(dirs.SnapMountDir, "services-snap/7"),
   940  		},
   941  		{
   942  			op:   "copy-data",
   943  			path: filepath.Join(dirs.SnapMountDir, "services-snap/11"),
   944  			old:  filepath.Join(dirs.SnapMountDir, "services-snap/7"),
   945  		},
   946  		{
   947  			op:    "setup-profiles:Doing",
   948  			name:  "services-snap",
   949  			revno: snap.R(11),
   950  		},
   951  		{
   952  			op: "candidate",
   953  			sinfo: snap.SideInfo{
   954  				RealName: "services-snap",
   955  				SnapID:   "services-snap-id",
   956  				Channel:  "some-channel",
   957  				Revision: snap.R(11),
   958  			},
   959  		},
   960  		{
   961  			op:   "link-snap",
   962  			path: filepath.Join(dirs.SnapMountDir, "services-snap/11"),
   963  		},
   964  		{
   965  			op:    "auto-connect:Doing",
   966  			name:  "services-snap",
   967  			revno: snap.R(11),
   968  		},
   969  		{
   970  			op: "update-aliases",
   971  		},
   972  		{
   973  			op:       "start-snap-services",
   974  			path:     filepath.Join(dirs.SnapMountDir, "services-snap/11"),
   975  			services: []string{"svc1", "svc3", "svc2"},
   976  		},
   977  		{
   978  			op:    "cleanup-trash",
   979  			name:  "services-snap",
   980  			revno: snap.R(11),
   981  		},
   982  	}
   983  
   984  	// ensure all our tasks ran
   985  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
   986  		macaroon: s.user.StoreMacaroon,
   987  		name:     "services-snap",
   988  		target:   filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
   989  	}})
   990  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
   991  	// start with an easier-to-read error if this fails:
   992  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
   993  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
   994  
   995  	// check progress
   996  	task := ts.Tasks()[1]
   997  	_, cur, total := task.Progress()
   998  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
   999  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  1000  
  1001  	// verify snapSetup info
  1002  	var snapsup snapstate.SnapSetup
  1003  	err = task.Get("snap-setup", &snapsup)
  1004  	c.Assert(err, IsNil)
  1005  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1006  		Channel:   "some-channel",
  1007  		CohortKey: "some-cohort",
  1008  		UserID:    s.user.ID,
  1009  
  1010  		SnapPath: filepath.Join(dirs.SnapBlobDir, "services-snap_11.snap"),
  1011  		DownloadInfo: &snap.DownloadInfo{
  1012  			DownloadURL: "https://some-server.com/some/path.snap",
  1013  		},
  1014  		SideInfo:  snapsup.SideInfo,
  1015  		Type:      snap.TypeApp,
  1016  		PlugsOnly: true,
  1017  	})
  1018  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1019  		RealName: "services-snap",
  1020  		Revision: snap.R(11),
  1021  		Channel:  "some-channel",
  1022  		SnapID:   "services-snap-id",
  1023  	})
  1024  
  1025  	// verify services stop reason
  1026  	verifyStopReason(c, ts, "refresh")
  1027  
  1028  	// check post-refresh hook
  1029  	task = ts.Tasks()[14]
  1030  	c.Assert(task.Kind(), Equals, "run-hook")
  1031  	c.Assert(task.Summary(), Matches, `Run post-refresh hook of "services-snap" snap if present`)
  1032  
  1033  	// verify snaps in the system state
  1034  	var snapst snapstate.SnapState
  1035  	err = snapstate.Get(s.state, "services-snap", &snapst)
  1036  	c.Assert(err, IsNil)
  1037  
  1038  	c.Assert(snapst.Active, Equals, true)
  1039  	c.Assert(snapst.Sequence, HasLen, 2)
  1040  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1041  		RealName: "services-snap",
  1042  		SnapID:   "services-snap-id",
  1043  		Channel:  "",
  1044  		Revision: snap.R(7),
  1045  	})
  1046  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  1047  		RealName: "services-snap",
  1048  		Channel:  "some-channel",
  1049  		SnapID:   "services-snap-id",
  1050  		Revision: snap.R(11),
  1051  	})
  1052  	c.Check(snapst.CohortKey, Equals, "some-cohort")
  1053  
  1054  	// we end up with the auxiliary store info
  1055  	c.Check(snapstate.AuxStoreInfoFilename("services-snap-id"), testutil.FilePresent)
  1056  }
  1057  
  1058  func (s *snapmgrTestSuite) TestParallelInstanceUpdateRunThrough(c *C) {
  1059  	// use services-snap here to make sure services would be stopped/started appropriately
  1060  	si := snap.SideInfo{
  1061  		RealName: "services-snap",
  1062  		Revision: snap.R(7),
  1063  		SnapID:   "services-snap-id",
  1064  	}
  1065  	snaptest.MockSnapInstance(c, "services-snap_instance", `name: services-snap`, &si)
  1066  	fi, err := os.Stat(snap.MountFile("services-snap_instance", si.Revision))
  1067  	c.Assert(err, IsNil)
  1068  	refreshedDate := fi.ModTime()
  1069  	// look at disk
  1070  	r := snapstate.MockRevisionDate(nil)
  1071  	defer r()
  1072  
  1073  	s.state.Lock()
  1074  	defer s.state.Unlock()
  1075  
  1076  	tr := config.NewTransaction(s.state)
  1077  	tr.Set("core", "experimental.parallel-instances", true)
  1078  	tr.Commit()
  1079  
  1080  	snapstate.Set(s.state, "services-snap_instance", &snapstate.SnapState{
  1081  		Active:          true,
  1082  		Sequence:        []*snap.SideInfo{&si},
  1083  		Current:         si.Revision,
  1084  		SnapType:        "app",
  1085  		TrackingChannel: "latest/stable",
  1086  		InstanceKey:     "instance",
  1087  	})
  1088  
  1089  	chg := s.state.NewChange("refresh", "refresh a snap")
  1090  	ts, err := snapstate.Update(s.state, "services-snap_instance", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  1091  	c.Assert(err, IsNil)
  1092  	chg.AddAll(ts)
  1093  
  1094  	s.state.Unlock()
  1095  	s.settle(c)
  1096  	s.state.Lock()
  1097  
  1098  	expected := fakeOps{
  1099  		{
  1100  			op: "storesvc-snap-action",
  1101  			curSnaps: []store.CurrentSnap{{
  1102  				InstanceName:    "services-snap_instance",
  1103  				SnapID:          "services-snap-id",
  1104  				Revision:        snap.R(7),
  1105  				TrackingChannel: "latest/stable",
  1106  				RefreshedDate:   refreshedDate,
  1107  				Epoch:           snap.E("0"),
  1108  			}},
  1109  			userID: 1,
  1110  		},
  1111  		{
  1112  			op: "storesvc-snap-action:action",
  1113  			action: store.SnapAction{
  1114  				Action:       "refresh",
  1115  				SnapID:       "services-snap-id",
  1116  				InstanceName: "services-snap_instance",
  1117  				Channel:      "some-channel",
  1118  				Flags:        store.SnapActionEnforceValidation,
  1119  			},
  1120  			revno:  snap.R(11),
  1121  			userID: 1,
  1122  		},
  1123  		{
  1124  			op:   "storesvc-download",
  1125  			name: "services-snap",
  1126  		},
  1127  		{
  1128  			op:    "validate-snap:Doing",
  1129  			name:  "services-snap_instance",
  1130  			revno: snap.R(11),
  1131  		},
  1132  		{
  1133  			op:  "current",
  1134  			old: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  1135  		},
  1136  		{
  1137  			op:   "open-snap-file",
  1138  			path: filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  1139  			sinfo: snap.SideInfo{
  1140  				RealName: "services-snap",
  1141  				SnapID:   "services-snap-id",
  1142  				Channel:  "some-channel",
  1143  				Revision: snap.R(11),
  1144  			},
  1145  		},
  1146  		{
  1147  			op:    "setup-snap",
  1148  			name:  "services-snap_instance",
  1149  			path:  filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  1150  			revno: snap.R(11),
  1151  		},
  1152  		{
  1153  			op:   "stop-snap-services:refresh",
  1154  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  1155  		},
  1156  		{
  1157  			op: "current-snap-service-states",
  1158  		},
  1159  		{
  1160  			op:   "remove-snap-aliases",
  1161  			name: "services-snap_instance",
  1162  		},
  1163  		{
  1164  			op:   "unlink-snap",
  1165  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  1166  		},
  1167  		{
  1168  			op:   "copy-data",
  1169  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"),
  1170  			old:  filepath.Join(dirs.SnapMountDir, "services-snap_instance/7"),
  1171  		},
  1172  		{
  1173  			op:    "setup-profiles:Doing",
  1174  			name:  "services-snap_instance",
  1175  			revno: snap.R(11),
  1176  		},
  1177  		{
  1178  			op: "candidate",
  1179  			sinfo: snap.SideInfo{
  1180  				RealName: "services-snap",
  1181  				SnapID:   "services-snap-id",
  1182  				Channel:  "some-channel",
  1183  				Revision: snap.R(11),
  1184  			},
  1185  		},
  1186  		{
  1187  			op:   "link-snap",
  1188  			path: filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"),
  1189  		},
  1190  		{
  1191  			op:    "auto-connect:Doing",
  1192  			name:  "services-snap_instance",
  1193  			revno: snap.R(11),
  1194  		},
  1195  		{
  1196  			op: "update-aliases",
  1197  		},
  1198  		{
  1199  			op:       "start-snap-services",
  1200  			path:     filepath.Join(dirs.SnapMountDir, "services-snap_instance/11"),
  1201  			services: []string{"svc1", "svc3", "svc2"},
  1202  		},
  1203  		{
  1204  			op:    "cleanup-trash",
  1205  			name:  "services-snap_instance",
  1206  			revno: snap.R(11),
  1207  		},
  1208  	}
  1209  
  1210  	// ensure all our tasks ran
  1211  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  1212  		macaroon: s.user.StoreMacaroon,
  1213  		name:     "services-snap",
  1214  		target:   filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  1215  	}})
  1216  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
  1217  	// start with an easier-to-read error if this fails:
  1218  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1219  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  1220  
  1221  	// check progress
  1222  	task := ts.Tasks()[1]
  1223  	_, cur, total := task.Progress()
  1224  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  1225  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  1226  
  1227  	// verify snapSetup info
  1228  	var snapsup snapstate.SnapSetup
  1229  	err = task.Get("snap-setup", &snapsup)
  1230  	c.Assert(err, IsNil)
  1231  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1232  		Channel: "some-channel",
  1233  		UserID:  s.user.ID,
  1234  
  1235  		SnapPath: filepath.Join(dirs.SnapBlobDir, "services-snap_instance_11.snap"),
  1236  		DownloadInfo: &snap.DownloadInfo{
  1237  			DownloadURL: "https://some-server.com/some/path.snap",
  1238  		},
  1239  		SideInfo:    snapsup.SideInfo,
  1240  		Type:        snap.TypeApp,
  1241  		PlugsOnly:   true,
  1242  		InstanceKey: "instance",
  1243  	})
  1244  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1245  		RealName: "services-snap",
  1246  		Revision: snap.R(11),
  1247  		Channel:  "some-channel",
  1248  		SnapID:   "services-snap-id",
  1249  	})
  1250  
  1251  	// verify services stop reason
  1252  	verifyStopReason(c, ts, "refresh")
  1253  
  1254  	// check post-refresh hook
  1255  	task = ts.Tasks()[14]
  1256  	c.Assert(task.Kind(), Equals, "run-hook")
  1257  	c.Assert(task.Summary(), Matches, `Run post-refresh hook of "services-snap_instance" snap if present`)
  1258  
  1259  	// verify snaps in the system state
  1260  	var snapst snapstate.SnapState
  1261  	err = snapstate.Get(s.state, "services-snap_instance", &snapst)
  1262  	c.Assert(err, IsNil)
  1263  
  1264  	c.Assert(snapst.InstanceKey, Equals, "instance")
  1265  	c.Assert(snapst.Active, Equals, true)
  1266  	c.Assert(snapst.Sequence, HasLen, 2)
  1267  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1268  		RealName: "services-snap",
  1269  		SnapID:   "services-snap-id",
  1270  		Channel:  "",
  1271  		Revision: snap.R(7),
  1272  	})
  1273  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  1274  		RealName: "services-snap",
  1275  		Channel:  "some-channel",
  1276  		SnapID:   "services-snap-id",
  1277  		Revision: snap.R(11),
  1278  	})
  1279  }
  1280  
  1281  func (s *snapmgrTestSuite) TestUpdateWithNewBase(c *C) {
  1282  	si := &snap.SideInfo{
  1283  		RealName: "some-snap",
  1284  		SnapID:   "some-snap-id",
  1285  		Revision: snap.R(7),
  1286  	}
  1287  	snaptest.MockSnap(c, `name: some-snap`, si)
  1288  
  1289  	s.state.Lock()
  1290  	defer s.state.Unlock()
  1291  
  1292  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1293  		Active:          true,
  1294  		TrackingChannel: "latest/edge",
  1295  		Sequence:        []*snap.SideInfo{si},
  1296  		Current:         snap.R(7),
  1297  		SnapType:        "app",
  1298  	})
  1299  
  1300  	chg := s.state.NewChange("refresh", "refresh a snap")
  1301  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-base/stable"}, s.user.ID, snapstate.Flags{})
  1302  	c.Assert(err, IsNil)
  1303  	chg.AddAll(ts)
  1304  
  1305  	s.state.Unlock()
  1306  	defer s.se.Stop()
  1307  	s.settle(c)
  1308  	s.state.Lock()
  1309  
  1310  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  1311  		{macaroon: s.user.StoreMacaroon, name: "some-base", target: filepath.Join(dirs.SnapBlobDir, "some-base_11.snap")},
  1312  		{macaroon: s.user.StoreMacaroon, name: "some-snap", target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap")},
  1313  	})
  1314  }
  1315  
  1316  func (s *snapmgrTestSuite) TestUpdateWithAlreadyInstalledBase(c *C) {
  1317  	si := &snap.SideInfo{
  1318  		RealName: "some-snap",
  1319  		SnapID:   "some-snap-id",
  1320  		Revision: snap.R(7),
  1321  	}
  1322  	snaptest.MockSnap(c, `name: some-snap`, si)
  1323  
  1324  	s.state.Lock()
  1325  	defer s.state.Unlock()
  1326  
  1327  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1328  		Active:          true,
  1329  		TrackingChannel: "latest/edge",
  1330  		Sequence:        []*snap.SideInfo{si},
  1331  		Current:         snap.R(7),
  1332  		SnapType:        "app",
  1333  	})
  1334  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  1335  		Active:          true,
  1336  		TrackingChannel: "latest/stable",
  1337  		Sequence: []*snap.SideInfo{{
  1338  			RealName: "some-base",
  1339  			SnapID:   "some-base-id",
  1340  			Revision: snap.R(1),
  1341  		}},
  1342  		Current:  snap.R(1),
  1343  		SnapType: "base",
  1344  	})
  1345  
  1346  	chg := s.state.NewChange("refresh", "refresh a snap")
  1347  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-base"}, s.user.ID, snapstate.Flags{})
  1348  	c.Assert(err, IsNil)
  1349  	chg.AddAll(ts)
  1350  
  1351  	s.state.Unlock()
  1352  	defer s.se.Stop()
  1353  	s.settle(c)
  1354  	s.state.Lock()
  1355  
  1356  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  1357  		{macaroon: s.user.StoreMacaroon, name: "some-snap", target: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap")},
  1358  	})
  1359  }
  1360  
  1361  func (s *snapmgrTestSuite) TestUpdateWithNewDefaultProvider(c *C) {
  1362  	s.state.Lock()
  1363  	defer s.state.Unlock()
  1364  
  1365  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
  1366  	repo := interfaces.NewRepository()
  1367  	ifacerepo.Replace(s.state, repo)
  1368  
  1369  	si := &snap.SideInfo{
  1370  		RealName: "snap-content-plug",
  1371  		SnapID:   "snap-content-plug-id",
  1372  		Revision: snap.R(7),
  1373  	}
  1374  	snaptest.MockSnap(c, `name: snap-content-plug`, si)
  1375  	snapstate.Set(s.state, "snap-content-plug", &snapstate.SnapState{
  1376  		Active:          true,
  1377  		TrackingChannel: "latest/edge",
  1378  		Sequence:        []*snap.SideInfo{si},
  1379  		Current:         snap.R(7),
  1380  		SnapType:        "app",
  1381  	})
  1382  
  1383  	chg := s.state.NewChange("refresh", "refresh a snap")
  1384  	ts, err := snapstate.Update(s.state, "snap-content-plug", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
  1385  	c.Assert(err, IsNil)
  1386  	chg.AddAll(ts)
  1387  
  1388  	s.state.Unlock()
  1389  	defer s.se.Stop()
  1390  	s.settle(c)
  1391  	s.state.Lock()
  1392  
  1393  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  1394  		{macaroon: s.user.StoreMacaroon, name: "snap-content-plug", target: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_11.snap")},
  1395  		{macaroon: s.user.StoreMacaroon, name: "snap-content-slot", target: filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap")},
  1396  	})
  1397  }
  1398  
  1399  func (s *snapmgrTestSuite) TestUpdateWithInstalledDefaultProvider(c *C) {
  1400  	s.state.Lock()
  1401  	defer s.state.Unlock()
  1402  
  1403  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
  1404  	repo := interfaces.NewRepository()
  1405  	ifacerepo.Replace(s.state, repo)
  1406  
  1407  	si := &snap.SideInfo{
  1408  		RealName: "snap-content-plug",
  1409  		SnapID:   "snap-content-plug-id",
  1410  		Revision: snap.R(7),
  1411  	}
  1412  	snaptest.MockSnap(c, `name: snap-content-plug`, si)
  1413  	snapstate.Set(s.state, "snap-content-plug", &snapstate.SnapState{
  1414  		Active:          true,
  1415  		TrackingChannel: "latest/edge",
  1416  		Sequence:        []*snap.SideInfo{si},
  1417  		Current:         snap.R(7),
  1418  		SnapType:        "app",
  1419  	})
  1420  	snapstate.Set(s.state, "snap-content-slot", &snapstate.SnapState{
  1421  		Active:          true,
  1422  		TrackingChannel: "latest/stable",
  1423  		Sequence: []*snap.SideInfo{{
  1424  			RealName: "snap-content-slot",
  1425  			SnapID:   "snap-content-slot-id",
  1426  			Revision: snap.R(1),
  1427  		}},
  1428  		Current:  snap.R(1),
  1429  		SnapType: "app",
  1430  	})
  1431  
  1432  	chg := s.state.NewChange("refresh", "refresh a snap")
  1433  	ts, err := snapstate.Update(s.state, "snap-content-plug", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
  1434  	c.Assert(err, IsNil)
  1435  	chg.AddAll(ts)
  1436  
  1437  	s.state.Unlock()
  1438  	defer s.se.Stop()
  1439  	s.settle(c)
  1440  	s.state.Lock()
  1441  
  1442  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  1443  		{macaroon: s.user.StoreMacaroon, name: "snap-content-plug", target: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_11.snap")},
  1444  	})
  1445  }
  1446  
  1447  func (s *snapmgrTestSuite) TestUpdateRememberedUserRunThrough(c *C) {
  1448  	s.state.Lock()
  1449  	defer s.state.Unlock()
  1450  
  1451  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1452  		Active: true,
  1453  		Sequence: []*snap.SideInfo{
  1454  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  1455  		},
  1456  		Current:  snap.R(5),
  1457  		SnapType: "app",
  1458  		UserID:   1,
  1459  	})
  1460  
  1461  	chg := s.state.NewChange("refresh", "refresh a snap")
  1462  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, 0, snapstate.Flags{})
  1463  	c.Assert(err, IsNil)
  1464  	chg.AddAll(ts)
  1465  
  1466  	s.state.Unlock()
  1467  	defer s.se.Stop()
  1468  	s.settle(c)
  1469  	s.state.Lock()
  1470  
  1471  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1472  	c.Assert(chg.Err(), IsNil)
  1473  
  1474  	for _, op := range s.fakeBackend.ops {
  1475  		switch op.op {
  1476  		case "storesvc-snap-action":
  1477  			c.Check(op.userID, Equals, 1)
  1478  		case "storesvc-download":
  1479  			snapName := op.name
  1480  			c.Check(s.fakeStore.downloads[0], DeepEquals, fakeDownload{
  1481  				macaroon: "macaroon",
  1482  				name:     "some-snap",
  1483  				target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  1484  			}, Commentf(snapName))
  1485  		}
  1486  	}
  1487  }
  1488  
  1489  func (s *snapmgrTestSuite) TestUpdateModelKernelSwitchTrackRunThrough(c *C) {
  1490  	// use services-snap here to make sure services would be stopped/started appropriately
  1491  	si := snap.SideInfo{
  1492  		RealName: "kernel",
  1493  		Revision: snap.R(7),
  1494  		SnapID:   "kernel-id",
  1495  	}
  1496  	snaptest.MockSnap(c, `name: kernel`, &si)
  1497  	fi, err := os.Stat(snap.MountFile("kernel", si.Revision))
  1498  	c.Assert(err, IsNil)
  1499  	refreshedDate := fi.ModTime()
  1500  	// look at disk
  1501  	r := snapstate.MockRevisionDate(nil)
  1502  	defer r()
  1503  
  1504  	s.state.Lock()
  1505  	defer s.state.Unlock()
  1506  
  1507  	r1 := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  1508  	defer r1()
  1509  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  1510  		Active:          true,
  1511  		Sequence:        []*snap.SideInfo{&si},
  1512  		Current:         si.Revision,
  1513  		TrackingChannel: "18/stable",
  1514  	})
  1515  
  1516  	chg := s.state.NewChange("refresh", "refresh a snap")
  1517  	ts, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "edge"}, s.user.ID, snapstate.Flags{})
  1518  	c.Assert(err, IsNil)
  1519  	chg.AddAll(ts)
  1520  
  1521  	s.state.Unlock()
  1522  	defer s.se.Stop()
  1523  	s.settle(c)
  1524  	s.state.Lock()
  1525  
  1526  	c.Check(chg.Status(), Equals, state.DoneStatus)
  1527  
  1528  	c.Assert(len(s.fakeBackend.ops) > 2, Equals, true)
  1529  	c.Assert(s.fakeBackend.ops[:2], DeepEquals, fakeOps{
  1530  		{
  1531  			op: "storesvc-snap-action",
  1532  			curSnaps: []store.CurrentSnap{{
  1533  				InstanceName:    "kernel",
  1534  				SnapID:          "kernel-id",
  1535  				Revision:        snap.R(7),
  1536  				TrackingChannel: "18/stable",
  1537  				RefreshedDate:   refreshedDate,
  1538  				Epoch:           snap.E("1*"),
  1539  			}},
  1540  			userID: 1,
  1541  		}, {
  1542  			op: "storesvc-snap-action:action",
  1543  			action: store.SnapAction{
  1544  				Action:       "refresh",
  1545  				InstanceName: "kernel",
  1546  				SnapID:       "kernel-id",
  1547  				Channel:      "18/edge",
  1548  				Flags:        store.SnapActionEnforceValidation,
  1549  			},
  1550  			revno:  snap.R(11),
  1551  			userID: 1,
  1552  		},
  1553  	})
  1554  
  1555  	// check progress
  1556  	task := ts.Tasks()[1]
  1557  	_, cur, total := task.Progress()
  1558  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  1559  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  1560  
  1561  	// verify snapSetup info
  1562  	var snapsup snapstate.SnapSetup
  1563  	err = task.Get("snap-setup", &snapsup)
  1564  	c.Assert(err, IsNil)
  1565  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1566  		Channel: "18/edge",
  1567  		UserID:  s.user.ID,
  1568  
  1569  		SnapPath: filepath.Join(dirs.SnapBlobDir, "kernel_11.snap"),
  1570  		DownloadInfo: &snap.DownloadInfo{
  1571  			DownloadURL: "https://some-server.com/some/path.snap",
  1572  		},
  1573  		SideInfo:  snapsup.SideInfo,
  1574  		Type:      snap.TypeKernel,
  1575  		PlugsOnly: true,
  1576  	})
  1577  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1578  		RealName: "kernel",
  1579  		Revision: snap.R(11),
  1580  		Channel:  "18/edge",
  1581  		SnapID:   "kernel-id",
  1582  	})
  1583  
  1584  	// verify snaps in the system state
  1585  	var snapst snapstate.SnapState
  1586  	err = snapstate.Get(s.state, "kernel", &snapst)
  1587  	c.Assert(err, IsNil)
  1588  
  1589  	c.Assert(snapst.Active, Equals, true)
  1590  	c.Assert(snapst.TrackingChannel, Equals, "18/edge")
  1591  	c.Assert(snapst.Sequence, HasLen, 2)
  1592  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1593  		RealName: "kernel",
  1594  		SnapID:   "kernel-id",
  1595  		Channel:  "",
  1596  		Revision: snap.R(7),
  1597  	})
  1598  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  1599  		RealName: "kernel",
  1600  		Channel:  "18/edge",
  1601  		SnapID:   "kernel-id",
  1602  		Revision: snap.R(11),
  1603  	})
  1604  }
  1605  
  1606  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsNoUserRunThrough(c *C) {
  1607  	s.state.Lock()
  1608  	defer s.state.Unlock()
  1609  
  1610  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1611  		Active: true,
  1612  		Sequence: []*snap.SideInfo{
  1613  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  1614  		},
  1615  		Current:  snap.R(1),
  1616  		SnapType: "os",
  1617  	})
  1618  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1619  		Active: true,
  1620  		Sequence: []*snap.SideInfo{
  1621  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  1622  		},
  1623  		Current:  snap.R(5),
  1624  		SnapType: "app",
  1625  		UserID:   1,
  1626  	})
  1627  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1628  		Active: true,
  1629  		Sequence: []*snap.SideInfo{
  1630  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  1631  		},
  1632  		Current:  snap.R(2),
  1633  		SnapType: "app",
  1634  		UserID:   2,
  1635  	})
  1636  
  1637  	chg := s.state.NewChange("refresh", "refresh all snaps")
  1638  	// no user is passed to use for UpdateMany
  1639  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1640  	c.Assert(err, IsNil)
  1641  	for _, ts := range tts {
  1642  		chg.AddAll(ts)
  1643  	}
  1644  	c.Check(updated, HasLen, 3)
  1645  
  1646  	s.state.Unlock()
  1647  	defer s.se.Stop()
  1648  	s.settle(c)
  1649  	s.state.Lock()
  1650  
  1651  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1652  	c.Assert(chg.Err(), IsNil)
  1653  
  1654  	macaroonMap := map[string]string{
  1655  		"core":          "",
  1656  		"some-snap":     "macaroon",
  1657  		"services-snap": "macaroon2",
  1658  	}
  1659  
  1660  	seen := make(map[string]int)
  1661  	ir := 0
  1662  	di := 0
  1663  	for _, op := range s.fakeBackend.ops {
  1664  		switch op.op {
  1665  		case "storesvc-snap-action":
  1666  			ir++
  1667  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  1668  				{
  1669  					InstanceName:  "core",
  1670  					SnapID:        "core-snap-id",
  1671  					Revision:      snap.R(1),
  1672  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  1673  					Epoch:         snap.E("1*"),
  1674  				},
  1675  				{
  1676  					InstanceName:  "services-snap",
  1677  					SnapID:        "services-snap-id",
  1678  					Revision:      snap.R(2),
  1679  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  1680  					Epoch:         snap.E("0"),
  1681  				},
  1682  				{
  1683  					InstanceName:  "some-snap",
  1684  					SnapID:        "some-snap-id",
  1685  					Revision:      snap.R(5),
  1686  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  1687  					Epoch:         snap.E("1*"),
  1688  				},
  1689  			})
  1690  		case "storesvc-snap-action:action":
  1691  			snapID := op.action.SnapID
  1692  			seen[snapID] = op.userID
  1693  		case "storesvc-download":
  1694  			snapName := op.name
  1695  			fakeDl := s.fakeStore.downloads[di]
  1696  			// check target path separately and clear it
  1697  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  1698  			fakeDl.target = ""
  1699  			c.Check(fakeDl, DeepEquals, fakeDownload{
  1700  				macaroon: macaroonMap[snapName],
  1701  				name:     snapName,
  1702  			}, Commentf(snapName))
  1703  			di++
  1704  		}
  1705  	}
  1706  	c.Check(ir, Equals, 2)
  1707  	// we check all snaps with each user
  1708  	c.Check(seen["some-snap-id"], Equals, 1)
  1709  	c.Check(seen["services-snap-id"], Equals, 2)
  1710  	// coalesced with one of the others
  1711  	c.Check(seen["core-snap-id"] > 0, Equals, true)
  1712  }
  1713  
  1714  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsUserRunThrough(c *C) {
  1715  	s.state.Lock()
  1716  	defer s.state.Unlock()
  1717  
  1718  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1719  		Active: true,
  1720  		Sequence: []*snap.SideInfo{
  1721  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  1722  		},
  1723  		Current:  snap.R(1),
  1724  		SnapType: "os",
  1725  	})
  1726  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1727  		Active: true,
  1728  		Sequence: []*snap.SideInfo{
  1729  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  1730  		},
  1731  		Current:  snap.R(5),
  1732  		SnapType: "app",
  1733  		UserID:   1,
  1734  	})
  1735  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1736  		Active: true,
  1737  		Sequence: []*snap.SideInfo{
  1738  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  1739  		},
  1740  		Current:  snap.R(2),
  1741  		SnapType: "app",
  1742  		UserID:   2,
  1743  	})
  1744  
  1745  	chg := s.state.NewChange("refresh", "refresh all snaps")
  1746  	// do UpdateMany using user 2 as fallback
  1747  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 2, nil)
  1748  	c.Assert(err, IsNil)
  1749  	for _, ts := range tts {
  1750  		chg.AddAll(ts)
  1751  	}
  1752  	c.Check(updated, HasLen, 3)
  1753  
  1754  	s.state.Unlock()
  1755  	defer s.se.Stop()
  1756  	s.settle(c)
  1757  	s.state.Lock()
  1758  
  1759  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1760  	c.Assert(chg.Err(), IsNil)
  1761  
  1762  	macaroonMap := map[string]string{
  1763  		"core":          "macaroon2",
  1764  		"some-snap":     "macaroon",
  1765  		"services-snap": "macaroon2",
  1766  	}
  1767  
  1768  	type snapIDuserID struct {
  1769  		snapID string
  1770  		userID int
  1771  	}
  1772  	seen := make(map[snapIDuserID]bool)
  1773  	ir := 0
  1774  	di := 0
  1775  	for _, op := range s.fakeBackend.ops {
  1776  		switch op.op {
  1777  		case "storesvc-snap-action":
  1778  			ir++
  1779  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  1780  				{
  1781  					InstanceName:  "core",
  1782  					SnapID:        "core-snap-id",
  1783  					Revision:      snap.R(1),
  1784  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  1785  					Epoch:         snap.E("1*"),
  1786  				},
  1787  				{
  1788  					InstanceName:  "services-snap",
  1789  					SnapID:        "services-snap-id",
  1790  					Revision:      snap.R(2),
  1791  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  1792  					Epoch:         snap.E("0"),
  1793  				},
  1794  				{
  1795  					InstanceName:  "some-snap",
  1796  					SnapID:        "some-snap-id",
  1797  					Revision:      snap.R(5),
  1798  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  1799  					Epoch:         snap.E("1*"),
  1800  				},
  1801  			})
  1802  		case "storesvc-snap-action:action":
  1803  			snapID := op.action.SnapID
  1804  			seen[snapIDuserID{snapID: snapID, userID: op.userID}] = true
  1805  		case "storesvc-download":
  1806  			snapName := op.name
  1807  			fakeDl := s.fakeStore.downloads[di]
  1808  			// check target path separately and clear it
  1809  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  1810  			fakeDl.target = ""
  1811  			c.Check(fakeDl, DeepEquals, fakeDownload{
  1812  				macaroon: macaroonMap[snapName],
  1813  				name:     snapName,
  1814  			}, Commentf(snapName))
  1815  			di++
  1816  		}
  1817  	}
  1818  	c.Check(ir, Equals, 2)
  1819  	// we check all snaps with each user
  1820  	c.Check(seen, DeepEquals, map[snapIDuserID]bool{
  1821  		{snapID: "core-snap-id", userID: 2}:     true,
  1822  		{snapID: "some-snap-id", userID: 1}:     true,
  1823  		{snapID: "services-snap-id", userID: 2}: true,
  1824  	})
  1825  
  1826  	var coreState, snapState snapstate.SnapState
  1827  	// user in SnapState was preserved
  1828  	err = snapstate.Get(s.state, "some-snap", &snapState)
  1829  	c.Assert(err, IsNil)
  1830  	c.Check(snapState.UserID, Equals, 1)
  1831  	c.Check(snapState.Current, DeepEquals, snap.R(11))
  1832  
  1833  	// user in SnapState was set
  1834  	err = snapstate.Get(s.state, "core", &coreState)
  1835  	c.Assert(err, IsNil)
  1836  	c.Check(coreState.UserID, Equals, 2)
  1837  	c.Check(coreState.Current, DeepEquals, snap.R(11))
  1838  
  1839  }
  1840  
  1841  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsUserWithNoStoreAuthRunThrough(c *C) {
  1842  	s.state.Lock()
  1843  	defer s.state.Unlock()
  1844  
  1845  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1846  		Active: true,
  1847  		Sequence: []*snap.SideInfo{
  1848  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  1849  		},
  1850  		Current:  snap.R(1),
  1851  		SnapType: "os",
  1852  	})
  1853  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1854  		Active: true,
  1855  		Sequence: []*snap.SideInfo{
  1856  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  1857  		},
  1858  		Current:  snap.R(5),
  1859  		SnapType: "app",
  1860  		UserID:   1,
  1861  	})
  1862  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1863  		Active: true,
  1864  		Sequence: []*snap.SideInfo{
  1865  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  1866  		},
  1867  		Current:  snap.R(2),
  1868  		SnapType: "app",
  1869  		UserID:   3,
  1870  	})
  1871  
  1872  	chg := s.state.NewChange("refresh", "refresh all snaps")
  1873  	// no user is passed to use for UpdateMany
  1874  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1875  	c.Assert(err, IsNil)
  1876  	for _, ts := range tts {
  1877  		chg.AddAll(ts)
  1878  	}
  1879  	c.Check(updated, HasLen, 3)
  1880  
  1881  	s.state.Unlock()
  1882  	defer s.se.Stop()
  1883  	s.settle(c)
  1884  	s.state.Lock()
  1885  
  1886  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1887  	c.Assert(chg.Err(), IsNil)
  1888  
  1889  	macaroonMap := map[string]string{
  1890  		"core":          "",
  1891  		"some-snap":     "macaroon",
  1892  		"services-snap": "",
  1893  	}
  1894  
  1895  	seen := make(map[string]int)
  1896  	ir := 0
  1897  	di := 0
  1898  	for _, op := range s.fakeBackend.ops {
  1899  		switch op.op {
  1900  		case "storesvc-snap-action":
  1901  			ir++
  1902  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  1903  				{
  1904  					InstanceName:  "core",
  1905  					SnapID:        "core-snap-id",
  1906  					Revision:      snap.R(1),
  1907  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  1908  					Epoch:         snap.E("1*"),
  1909  				},
  1910  				{
  1911  					InstanceName:  "services-snap",
  1912  					SnapID:        "services-snap-id",
  1913  					Revision:      snap.R(2),
  1914  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  1915  					Epoch:         snap.E("0"),
  1916  				},
  1917  				{
  1918  					InstanceName:  "some-snap",
  1919  					SnapID:        "some-snap-id",
  1920  					Revision:      snap.R(5),
  1921  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  1922  					Epoch:         snap.E("1*"),
  1923  				},
  1924  			})
  1925  		case "storesvc-snap-action:action":
  1926  			snapID := op.action.SnapID
  1927  			if _, ok := seen[snapID]; !ok {
  1928  				seen[snapID] = op.userID
  1929  			}
  1930  		case "storesvc-download":
  1931  			snapName := op.name
  1932  			fakeDl := s.fakeStore.downloads[di]
  1933  			// check target path separately and clear it
  1934  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  1935  			fakeDl.target = ""
  1936  			c.Check(fakeDl, DeepEquals, fakeDownload{
  1937  				macaroon: macaroonMap[snapName],
  1938  				name:     snapName,
  1939  			}, Commentf(snapName))
  1940  			di++
  1941  		}
  1942  	}
  1943  	c.Check(ir, Equals, 1)
  1944  	// we check all snaps with each user
  1945  	c.Check(seen["some-snap-id"], Equals, 1)
  1946  	// coalesced with request for 1
  1947  	c.Check(seen["services-snap-id"], Equals, 1)
  1948  	c.Check(seen["core-snap-id"], Equals, 1)
  1949  }
  1950  
  1951  func (s *snapmgrTestSuite) TestUpdateUndoRunThrough(c *C) {
  1952  	si := snap.SideInfo{
  1953  		RealName: "some-snap",
  1954  		SnapID:   "some-snap-id",
  1955  		Revision: snap.R(7),
  1956  	}
  1957  
  1958  	s.state.Lock()
  1959  	defer s.state.Unlock()
  1960  
  1961  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1962  		Active:   true,
  1963  		Sequence: []*snap.SideInfo{&si},
  1964  		Current:  si.Revision,
  1965  		SnapType: "app",
  1966  	})
  1967  
  1968  	chg := s.state.NewChange("install", "install a snap")
  1969  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  1970  	c.Assert(err, IsNil)
  1971  	chg.AddAll(ts)
  1972  
  1973  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "/some-snap/11")
  1974  
  1975  	s.state.Unlock()
  1976  	defer s.se.Stop()
  1977  	s.settle(c)
  1978  	s.state.Lock()
  1979  
  1980  	expected := fakeOps{
  1981  		{
  1982  			op: "storesvc-snap-action",
  1983  			curSnaps: []store.CurrentSnap{{
  1984  				InstanceName:  "some-snap",
  1985  				SnapID:        "some-snap-id",
  1986  				Revision:      snap.R(7),
  1987  				RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  1988  				Epoch:         snap.E("1*"),
  1989  			}},
  1990  			userID: 1,
  1991  		},
  1992  		{
  1993  			op: "storesvc-snap-action:action",
  1994  			action: store.SnapAction{
  1995  				Action:       "refresh",
  1996  				InstanceName: "some-snap",
  1997  				SnapID:       "some-snap-id",
  1998  				Channel:      "some-channel",
  1999  				Flags:        store.SnapActionEnforceValidation,
  2000  			},
  2001  			revno:  snap.R(11),
  2002  			userID: 1,
  2003  		},
  2004  		{
  2005  			op:   "storesvc-download",
  2006  			name: "some-snap",
  2007  		},
  2008  		{
  2009  			op:    "validate-snap:Doing",
  2010  			name:  "some-snap",
  2011  			revno: snap.R(11),
  2012  		},
  2013  		{
  2014  			op:  "current",
  2015  			old: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2016  		},
  2017  		{
  2018  			op:   "open-snap-file",
  2019  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2020  			sinfo: snap.SideInfo{
  2021  				RealName: "some-snap",
  2022  				SnapID:   "some-snap-id",
  2023  				Channel:  "some-channel",
  2024  				Revision: snap.R(11),
  2025  			},
  2026  		},
  2027  		{
  2028  			op:    "setup-snap",
  2029  			name:  "some-snap",
  2030  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2031  			revno: snap.R(11),
  2032  		},
  2033  		{
  2034  			op:   "remove-snap-aliases",
  2035  			name: "some-snap",
  2036  		},
  2037  		{
  2038  			op:   "unlink-snap",
  2039  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2040  		},
  2041  		{
  2042  			op:   "copy-data",
  2043  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2044  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2045  		},
  2046  		{
  2047  			op:    "setup-profiles:Doing",
  2048  			name:  "some-snap",
  2049  			revno: snap.R(11),
  2050  		},
  2051  		{
  2052  			op: "candidate",
  2053  			sinfo: snap.SideInfo{
  2054  				RealName: "some-snap",
  2055  				SnapID:   "some-snap-id",
  2056  				Channel:  "some-channel",
  2057  				Revision: snap.R(11),
  2058  			},
  2059  		},
  2060  		{
  2061  			op:   "link-snap.failed",
  2062  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2063  		},
  2064  		{
  2065  			op:   "unlink-snap",
  2066  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2067  		},
  2068  		{
  2069  			op:    "setup-profiles:Undoing",
  2070  			name:  "some-snap",
  2071  			revno: snap.R(11),
  2072  		},
  2073  		{
  2074  			op:   "undo-copy-snap-data",
  2075  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2076  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2077  		},
  2078  		{
  2079  			op:   "link-snap",
  2080  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2081  		},
  2082  		{
  2083  			op: "update-aliases",
  2084  		},
  2085  		{
  2086  			op:    "undo-setup-snap",
  2087  			name:  "some-snap",
  2088  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2089  			stype: "app",
  2090  		},
  2091  		{
  2092  			op:   "remove-snap-dir",
  2093  			name: "some-snap",
  2094  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  2095  		},
  2096  	}
  2097  
  2098  	// ensure all our tasks ran
  2099  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  2100  		macaroon: s.user.StoreMacaroon,
  2101  		name:     "some-snap",
  2102  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2103  	}})
  2104  	// start with an easier-to-read error if this fails:
  2105  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2106  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2107  
  2108  	// verify snaps in the system state
  2109  	var snapst snapstate.SnapState
  2110  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2111  	c.Assert(err, IsNil)
  2112  
  2113  	c.Assert(snapst.Active, Equals, true)
  2114  	c.Assert(snapst.Sequence, HasLen, 1)
  2115  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2116  		RealName: "some-snap",
  2117  		SnapID:   "some-snap-id",
  2118  		Channel:  "",
  2119  		Revision: snap.R(7),
  2120  	})
  2121  }
  2122  
  2123  func lastWithLane(tasks []*state.Task) *state.Task {
  2124  	for i := len(tasks) - 1; i >= 0; i-- {
  2125  		if lanes := tasks[i].Lanes(); len(lanes) == 1 && lanes[0] != 0 {
  2126  			return tasks[i]
  2127  		}
  2128  	}
  2129  	return nil
  2130  }
  2131  
  2132  func (s *snapmgrTestSuite) TestUpdateUndoRestoresRevisionConfig(c *C) {
  2133  	var errorTaskExecuted bool
  2134  
  2135  	// overwrite error-trigger task handler with custom one for this test
  2136  	erroringHandler := func(task *state.Task, _ *tomb.Tomb) error {
  2137  		st := task.State()
  2138  		st.Lock()
  2139  		defer st.Unlock()
  2140  
  2141  		// modify current config of some-snap
  2142  		tr := config.NewTransaction(st)
  2143  		tr.Set("some-snap", "foo", "canary")
  2144  		tr.Commit()
  2145  
  2146  		errorTaskExecuted = true
  2147  		return errors.New("error out")
  2148  	}
  2149  	s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil)
  2150  
  2151  	si := snap.SideInfo{
  2152  		RealName: "some-snap",
  2153  		SnapID:   "some-snap-id",
  2154  		Revision: snap.R(7),
  2155  	}
  2156  	si2 := snap.SideInfo{
  2157  		RealName: "some-snap",
  2158  		SnapID:   "some-snap-id",
  2159  		Revision: snap.R(6),
  2160  	}
  2161  
  2162  	s.state.Lock()
  2163  	defer s.state.Unlock()
  2164  
  2165  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2166  		Active:          true,
  2167  		Sequence:        []*snap.SideInfo{&si2, &si},
  2168  		TrackingChannel: "latest/stable",
  2169  		Current:         si.Revision,
  2170  		SnapType:        "app",
  2171  	})
  2172  
  2173  	// set some configuration
  2174  	tr := config.NewTransaction(s.state)
  2175  	tr.Set("some-snap", "foo", "revision 7 value")
  2176  	tr.Commit()
  2177  	config.SaveRevisionConfig(s.state, "some-snap", snap.R(7))
  2178  
  2179  	chg := s.state.NewChange("install", "install a snap")
  2180  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2181  	c.Assert(err, IsNil)
  2182  	chg.AddAll(ts)
  2183  
  2184  	last := lastWithLane(ts.Tasks())
  2185  	c.Assert(last, NotNil)
  2186  
  2187  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  2188  	terr.WaitFor(last)
  2189  	terr.JoinLane(last.Lanes()[0])
  2190  	chg.AddTask(terr)
  2191  
  2192  	s.state.Unlock()
  2193  	defer s.se.Stop()
  2194  	s.settle(c)
  2195  	s.state.Lock()
  2196  
  2197  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  2198  	c.Check(errorTaskExecuted, Equals, true)
  2199  
  2200  	// after undoing the update some-snap config should be restored to that of rev.7
  2201  	var val string
  2202  	tr = config.NewTransaction(s.state)
  2203  	c.Assert(tr.Get("some-snap", "foo", &val), IsNil)
  2204  	c.Check(val, Equals, "revision 7 value")
  2205  }
  2206  
  2207  func (s *snapmgrTestSuite) TestUpdateMakesConfigSnapshot(c *C) {
  2208  	s.state.Lock()
  2209  	defer s.state.Unlock()
  2210  
  2211  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2212  		Active: true,
  2213  		Sequence: []*snap.SideInfo{
  2214  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  2215  		},
  2216  		Current:  snap.R(1),
  2217  		SnapType: "app",
  2218  	})
  2219  
  2220  	tr := config.NewTransaction(s.state)
  2221  	tr.Set("some-snap", "foo", "bar")
  2222  	tr.Commit()
  2223  
  2224  	var cfgs map[string]interface{}
  2225  	// we don't have config snapshots yet
  2226  	c.Assert(s.state.Get("revision-config", &cfgs), Equals, state.ErrNoState)
  2227  
  2228  	chg := s.state.NewChange("update", "update a snap")
  2229  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(2)}
  2230  	ts, err := snapstate.Update(s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2231  	c.Assert(err, IsNil)
  2232  	chg.AddAll(ts)
  2233  
  2234  	s.state.Unlock()
  2235  	defer s.se.Stop()
  2236  	s.settle(c)
  2237  
  2238  	s.state.Lock()
  2239  	cfgs = nil
  2240  	// config copy of rev. 1 has been made
  2241  	c.Assert(s.state.Get("revision-config", &cfgs), IsNil)
  2242  	c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{
  2243  		"1": map[string]interface{}{
  2244  			"foo": "bar",
  2245  		},
  2246  	})
  2247  }
  2248  
  2249  func (s *snapmgrTestSuite) TestUpdateTotalUndoRunThrough(c *C) {
  2250  	si := snap.SideInfo{
  2251  		RealName: "some-snap",
  2252  		SnapID:   "some-snap-id",
  2253  		Revision: snap.R(7),
  2254  	}
  2255  
  2256  	s.state.Lock()
  2257  	defer s.state.Unlock()
  2258  
  2259  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2260  		Active:          true,
  2261  		Sequence:        []*snap.SideInfo{&si},
  2262  		TrackingChannel: "latest/stable",
  2263  		Current:         si.Revision,
  2264  		SnapType:        "app",
  2265  	})
  2266  
  2267  	chg := s.state.NewChange("install", "install a snap")
  2268  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2269  	c.Assert(err, IsNil)
  2270  	chg.AddAll(ts)
  2271  
  2272  	// We need to make it not be rerefresh, and we could do just
  2273  	// that but instead we do the 'right' thing and attach it to
  2274  	// the last task that's on a lane.
  2275  	last := lastWithLane(ts.Tasks())
  2276  	c.Assert(last, NotNil)
  2277  
  2278  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  2279  	terr.WaitFor(last)
  2280  	terr.JoinLane(last.Lanes()[0])
  2281  	chg.AddTask(terr)
  2282  
  2283  	s.state.Unlock()
  2284  	defer s.se.Stop()
  2285  	s.settle(c)
  2286  	s.state.Lock()
  2287  
  2288  	expected := fakeOps{
  2289  		{
  2290  			op: "storesvc-snap-action",
  2291  			curSnaps: []store.CurrentSnap{{
  2292  				InstanceName:    "some-snap",
  2293  				SnapID:          "some-snap-id",
  2294  				Revision:        snap.R(7),
  2295  				TrackingChannel: "latest/stable",
  2296  				RefreshedDate:   fakeRevDateEpoch.AddDate(0, 0, 7),
  2297  				Epoch:           snap.E("1*"),
  2298  			}},
  2299  			userID: 1,
  2300  		},
  2301  		{
  2302  			op: "storesvc-snap-action:action",
  2303  			action: store.SnapAction{
  2304  				Action:       "refresh",
  2305  				InstanceName: "some-snap",
  2306  				SnapID:       "some-snap-id",
  2307  				Channel:      "some-channel",
  2308  				Flags:        store.SnapActionEnforceValidation,
  2309  			},
  2310  			revno:  snap.R(11),
  2311  			userID: 1,
  2312  		},
  2313  		{
  2314  			op:   "storesvc-download",
  2315  			name: "some-snap",
  2316  		},
  2317  		{
  2318  			op:    "validate-snap:Doing",
  2319  			name:  "some-snap",
  2320  			revno: snap.R(11),
  2321  		},
  2322  		{
  2323  			op:  "current",
  2324  			old: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2325  		},
  2326  		{
  2327  			op:   "open-snap-file",
  2328  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2329  			sinfo: snap.SideInfo{
  2330  				RealName: "some-snap",
  2331  				SnapID:   "some-snap-id",
  2332  				Channel:  "some-channel",
  2333  				Revision: snap.R(11),
  2334  			},
  2335  		},
  2336  		{
  2337  			op:    "setup-snap",
  2338  			name:  "some-snap",
  2339  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2340  			revno: snap.R(11),
  2341  		},
  2342  		{
  2343  			op:   "remove-snap-aliases",
  2344  			name: "some-snap",
  2345  		},
  2346  		{
  2347  			op:   "unlink-snap",
  2348  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2349  		},
  2350  		{
  2351  			op:   "copy-data",
  2352  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2353  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2354  		},
  2355  		{
  2356  			op:    "setup-profiles:Doing",
  2357  			name:  "some-snap",
  2358  			revno: snap.R(11),
  2359  		},
  2360  		{
  2361  			op: "candidate",
  2362  			sinfo: snap.SideInfo{
  2363  				RealName: "some-snap",
  2364  				SnapID:   "some-snap-id",
  2365  				Channel:  "some-channel",
  2366  				Revision: snap.R(11),
  2367  			},
  2368  		},
  2369  		{
  2370  			op:   "link-snap",
  2371  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2372  		},
  2373  		{
  2374  			op:    "auto-connect:Doing",
  2375  			name:  "some-snap",
  2376  			revno: snap.R(11),
  2377  		},
  2378  		{
  2379  			op: "update-aliases",
  2380  		},
  2381  		// undoing everything from here down...
  2382  		{
  2383  			op:   "remove-snap-aliases",
  2384  			name: "some-snap",
  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  func (s *snapmgrTestSuite) TestUpdateManyPartialFailureCheckRerefreshDone(c *C) {
  3460  	s.state.Lock()
  3461  	defer s.state.Unlock()
  3462  
  3463  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  3464  	makeTestRefreshConfig(s.state)
  3465  
  3466  	var someSnapValidation bool
  3467  
  3468  	// override validate-snap handler set by AddForeignTaskHandlers.
  3469  	s.o.TaskRunner().AddHandler("validate-snap", func(t *state.Task, _ *tomb.Tomb) error {
  3470  		t.State().Lock()
  3471  		defer t.State().Unlock()
  3472  		snapsup, err := snapstate.TaskSnapSetup(t)
  3473  		c.Assert(err, IsNil)
  3474  		if snapsup.SnapName() == "some-snap" {
  3475  			someSnapValidation = true
  3476  			return fmt.Errorf("boom")
  3477  		}
  3478  		return nil
  3479  	}, nil)
  3480  
  3481  	snapstate.Set(s.state, "some-other-snap", &snapstate.SnapState{
  3482  		Active: true,
  3483  		Sequence: []*snap.SideInfo{
  3484  			{RealName: "some-other-snap", SnapID: "some-other-snap-id", Revision: snap.R(1)},
  3485  		},
  3486  		Current:  snap.R(1),
  3487  		SnapType: "app",
  3488  	})
  3489  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3490  		Active: true,
  3491  		Sequence: []*snap.SideInfo{
  3492  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  3493  		},
  3494  		Current:  snap.R(1),
  3495  		SnapType: "app",
  3496  	})
  3497  
  3498  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  3499  		c.Check(refreshes, HasLen, 2)
  3500  		c.Check(ignoreValidation, HasLen, 0)
  3501  		return refreshes, nil
  3502  	}
  3503  	// hook it up
  3504  	snapstate.ValidateRefreshes = validateRefreshes
  3505  
  3506  	s.state.Unlock()
  3507  	s.snapmgr.Ensure()
  3508  	s.state.Lock()
  3509  
  3510  	c.Assert(s.state.Changes(), HasLen, 1)
  3511  	chg := s.state.Changes()[0]
  3512  	c.Check(chg.Kind(), Equals, "auto-refresh")
  3513  	c.Check(chg.IsReady(), Equals, false)
  3514  	s.verifyRefreshLast(c)
  3515  
  3516  	checkIsAutoRefresh(c, chg.Tasks(), true)
  3517  
  3518  	s.state.Unlock()
  3519  	defer s.se.Stop()
  3520  	s.settle(c)
  3521  	s.state.Lock()
  3522  
  3523  	// not updated
  3524  	var snapst snapstate.SnapState
  3525  	c.Assert(snapstate.Get(s.state, "some-snap", &snapst), IsNil)
  3526  	c.Check(snapst.Current, Equals, snap.Revision{N: 1})
  3527  
  3528  	// updated
  3529  	c.Assert(snapstate.Get(s.state, "some-other-snap", &snapst), IsNil)
  3530  	c.Check(snapst.Current, Equals, snap.Revision{N: 11})
  3531  
  3532  	c.Assert(chg.Err(), ErrorMatches, "cannot perform the following tasks:\n.*Fetch and check assertions for snap \"some-snap\" \\(11\\) \\(boom\\)")
  3533  	c.Assert(chg.IsReady(), Equals, true)
  3534  
  3535  	// check-rerefresh is last
  3536  	tasks := chg.Tasks()
  3537  	checkRerefresh := tasks[len(tasks)-1]
  3538  	c.Check(checkRerefresh.Kind(), Equals, "check-rerefresh")
  3539  	c.Check(checkRerefresh.Status(), Equals, state.DoneStatus)
  3540  
  3541  	// sanity
  3542  	c.Check(someSnapValidation, Equals, true)
  3543  }
  3544  
  3545  var orthogonalAutoAliasesScenarios = []struct {
  3546  	aliasesBefore map[string][]string
  3547  	names         []string
  3548  	prune         []string
  3549  	update        bool
  3550  	new           bool
  3551  }{
  3552  	{nil, nil, nil, true, true},
  3553  	{nil, []string{"some-snap"}, nil, true, false},
  3554  	{nil, []string{"other-snap"}, nil, false, true},
  3555  	{map[string][]string{"some-snap": {"aliasA", "aliasC"}}, []string{"some-snap"}, nil, true, false},
  3556  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, []string{"other-snap"}, []string{"other-snap"}, false, false},
  3557  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, nil, []string{"other-snap"}, true, false},
  3558  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, []string{"some-snap"}, nil, true, false},
  3559  	{map[string][]string{"other-snap": {"aliasC"}}, []string{"other-snap"}, []string{"other-snap"}, false, true},
  3560  	{map[string][]string{"other-snap": {"aliasC"}}, nil, []string{"other-snap"}, true, true},
  3561  	{map[string][]string{"other-snap": {"aliasC"}}, []string{"some-snap"}, nil, true, false},
  3562  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, []string{"some-snap"}, []string{"other-snap"}, true, false},
  3563  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, nil, []string{"other-snap", "some-snap"}, true, true},
  3564  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, []string{"other-snap"}, []string{"other-snap", "some-snap"}, false, true},
  3565  	{map[string][]string{"some-snap": {"aliasB"}}, nil, []string{"some-snap"}, true, true},
  3566  	{map[string][]string{"some-snap": {"aliasB"}}, []string{"other-snap"}, []string{"some-snap"}, false, true},
  3567  	{map[string][]string{"some-snap": {"aliasB"}}, []string{"some-snap"}, nil, true, false},
  3568  	{map[string][]string{"other-snap": {"aliasA"}}, nil, []string{"other-snap"}, true, true},
  3569  	{map[string][]string{"other-snap": {"aliasA"}}, []string{"other-snap"}, []string{"other-snap"}, false, true},
  3570  	{map[string][]string{"other-snap": {"aliasA"}}, []string{"some-snap"}, []string{"other-snap"}, true, false},
  3571  }
  3572  
  3573  func (s *snapmgrTestSuite) TestUpdateManyAutoAliasesScenarios(c *C) {
  3574  	s.state.Lock()
  3575  	defer s.state.Unlock()
  3576  
  3577  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
  3578  		Active: true,
  3579  		Sequence: []*snap.SideInfo{
  3580  			{RealName: "other-snap", SnapID: "other-snap-id", Revision: snap.R(2)},
  3581  		},
  3582  		Current:  snap.R(2),
  3583  		SnapType: "app",
  3584  	})
  3585  
  3586  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  3587  		switch info.InstanceName() {
  3588  		case "some-snap":
  3589  			return map[string]string{"aliasA": "cmdA"}, nil
  3590  		case "other-snap":
  3591  			return map[string]string{"aliasB": "cmdB"}, nil
  3592  		}
  3593  		return nil, nil
  3594  	}
  3595  
  3596  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3597  		Active: true,
  3598  		Sequence: []*snap.SideInfo{
  3599  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  3600  		},
  3601  		Current:  snap.R(4),
  3602  		SnapType: "app",
  3603  	})
  3604  
  3605  	expectedSet := func(aliases []string) map[string]bool {
  3606  		res := make(map[string]bool, len(aliases))
  3607  		for _, alias := range aliases {
  3608  			res[alias] = true
  3609  		}
  3610  		return res
  3611  	}
  3612  
  3613  	for _, scenario := range orthogonalAutoAliasesScenarios {
  3614  		for _, instanceName := range []string{"some-snap", "other-snap"} {
  3615  			var snapst snapstate.SnapState
  3616  			err := snapstate.Get(s.state, instanceName, &snapst)
  3617  			c.Assert(err, IsNil)
  3618  			snapst.Aliases = nil
  3619  			snapst.AutoAliasesDisabled = false
  3620  			if autoAliases := scenario.aliasesBefore[instanceName]; autoAliases != nil {
  3621  				targets := make(map[string]*snapstate.AliasTarget)
  3622  				for _, alias := range autoAliases {
  3623  					targets[alias] = &snapstate.AliasTarget{Auto: "cmd" + alias[len(alias)-1:]}
  3624  				}
  3625  
  3626  				snapst.Aliases = targets
  3627  			}
  3628  			snapstate.Set(s.state, instanceName, &snapst)
  3629  		}
  3630  
  3631  		updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, scenario.names, s.user.ID, nil)
  3632  		c.Check(err, IsNil)
  3633  		if scenario.update {
  3634  			verifyLastTasksetIsReRefresh(c, tts)
  3635  		}
  3636  
  3637  		_, dropped, err := snapstate.AutoAliasesDelta(s.state, []string{"some-snap", "other-snap"})
  3638  		c.Assert(err, IsNil)
  3639  
  3640  		j := 0
  3641  		expectedUpdatesSet := make(map[string]bool)
  3642  		var expectedPruned map[string]map[string]bool
  3643  		var pruneTs *state.TaskSet
  3644  		if len(scenario.prune) != 0 {
  3645  			pruneTs = tts[0]
  3646  			j++
  3647  			taskAliases := make(map[string]map[string]bool)
  3648  			for _, aliasTask := range pruneTs.Tasks() {
  3649  				c.Check(aliasTask.Kind(), Equals, "prune-auto-aliases")
  3650  				var aliases []string
  3651  				err := aliasTask.Get("aliases", &aliases)
  3652  				c.Assert(err, IsNil)
  3653  				snapsup, err := snapstate.TaskSnapSetup(aliasTask)
  3654  				c.Assert(err, IsNil)
  3655  				taskAliases[snapsup.InstanceName()] = expectedSet(aliases)
  3656  			}
  3657  			expectedPruned = make(map[string]map[string]bool)
  3658  			for _, instanceName := range scenario.prune {
  3659  				expectedPruned[instanceName] = expectedSet(dropped[instanceName])
  3660  				if instanceName == "other-snap" && !scenario.new && !scenario.update {
  3661  					expectedUpdatesSet["other-snap"] = true
  3662  				}
  3663  			}
  3664  			c.Check(taskAliases, DeepEquals, expectedPruned)
  3665  		}
  3666  		if scenario.update {
  3667  			updateTs := tts[j]
  3668  			j++
  3669  			expectedUpdatesSet["some-snap"] = true
  3670  			first := updateTs.Tasks()[0]
  3671  			c.Check(first.Kind(), Equals, "prerequisites")
  3672  			wait := false
  3673  			if expectedPruned["other-snap"]["aliasA"] {
  3674  				wait = true
  3675  			} else if expectedPruned["some-snap"] != nil {
  3676  				wait = true
  3677  			}
  3678  			if wait {
  3679  				c.Check(first.WaitTasks(), DeepEquals, pruneTs.Tasks())
  3680  			} else {
  3681  				c.Check(first.WaitTasks(), HasLen, 0)
  3682  			}
  3683  		}
  3684  		if scenario.new {
  3685  			newTs := tts[j]
  3686  			j++
  3687  			expectedUpdatesSet["other-snap"] = true
  3688  			tasks := newTs.Tasks()
  3689  			c.Check(tasks, HasLen, 1)
  3690  			aliasTask := tasks[0]
  3691  			c.Check(aliasTask.Kind(), Equals, "refresh-aliases")
  3692  
  3693  			wait := false
  3694  			if expectedPruned["some-snap"]["aliasB"] {
  3695  				wait = true
  3696  			} else if expectedPruned["other-snap"] != nil {
  3697  				wait = true
  3698  			}
  3699  			if wait {
  3700  				c.Check(aliasTask.WaitTasks(), DeepEquals, pruneTs.Tasks())
  3701  			} else {
  3702  				c.Check(aliasTask.WaitTasks(), HasLen, 0)
  3703  			}
  3704  		}
  3705  		l := len(tts)
  3706  		if scenario.update {
  3707  			l--
  3708  		}
  3709  		c.Assert(j, Equals, l, Commentf("%#v", scenario))
  3710  
  3711  		// check reported updated names
  3712  		c.Check(len(updates) > 0, Equals, true)
  3713  		sort.Strings(updates)
  3714  		expectedUpdates := make([]string, 0, len(expectedUpdatesSet))
  3715  		for x := range expectedUpdatesSet {
  3716  			expectedUpdates = append(expectedUpdates, x)
  3717  		}
  3718  		sort.Strings(expectedUpdates)
  3719  		c.Check(updates, DeepEquals, expectedUpdates)
  3720  	}
  3721  }
  3722  
  3723  func (s *snapmgrTestSuite) TestUpdateOneAutoAliasesScenarios(c *C) {
  3724  	s.state.Lock()
  3725  	defer s.state.Unlock()
  3726  
  3727  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
  3728  		Active: true,
  3729  		Sequence: []*snap.SideInfo{
  3730  			{RealName: "other-snap", SnapID: "other-snap-id", Revision: snap.R(2)},
  3731  		},
  3732  		Current:  snap.R(2),
  3733  		SnapType: "app",
  3734  	})
  3735  
  3736  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  3737  		switch info.InstanceName() {
  3738  		case "some-snap":
  3739  			return map[string]string{"aliasA": "cmdA"}, nil
  3740  		case "other-snap":
  3741  			return map[string]string{"aliasB": "cmdB"}, nil
  3742  		}
  3743  		return nil, nil
  3744  	}
  3745  
  3746  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3747  		Active: true,
  3748  		Sequence: []*snap.SideInfo{
  3749  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  3750  		},
  3751  		Current:  snap.R(4),
  3752  		SnapType: "app",
  3753  	})
  3754  
  3755  	expectedSet := func(aliases []string) map[string]bool {
  3756  		res := make(map[string]bool, len(aliases))
  3757  		for _, alias := range aliases {
  3758  			res[alias] = true
  3759  		}
  3760  		return res
  3761  	}
  3762  
  3763  	for _, scenario := range orthogonalAutoAliasesScenarios {
  3764  		if len(scenario.names) != 1 {
  3765  			continue
  3766  		}
  3767  
  3768  		for _, instanceName := range []string{"some-snap", "other-snap"} {
  3769  			var snapst snapstate.SnapState
  3770  			err := snapstate.Get(s.state, instanceName, &snapst)
  3771  			c.Assert(err, IsNil)
  3772  			snapst.Aliases = nil
  3773  			snapst.AutoAliasesDisabled = false
  3774  			if autoAliases := scenario.aliasesBefore[instanceName]; autoAliases != nil {
  3775  				targets := make(map[string]*snapstate.AliasTarget)
  3776  				for _, alias := range autoAliases {
  3777  					targets[alias] = &snapstate.AliasTarget{Auto: "cmd" + alias[len(alias)-1:]}
  3778  				}
  3779  
  3780  				snapst.Aliases = targets
  3781  			}
  3782  			snapstate.Set(s.state, instanceName, &snapst)
  3783  		}
  3784  
  3785  		ts, err := snapstate.Update(s.state, scenario.names[0], nil, s.user.ID, snapstate.Flags{})
  3786  		c.Assert(err, IsNil)
  3787  		_, dropped, err := snapstate.AutoAliasesDelta(s.state, []string{"some-snap", "other-snap"})
  3788  		c.Assert(err, IsNil)
  3789  
  3790  		j := 0
  3791  
  3792  		tasks := ts.Tasks()
  3793  		// make sure the last task from Update is the rerefresh
  3794  		if scenario.update {
  3795  			reRefresh := tasks[len(tasks)-1]
  3796  			c.Check(reRefresh.Kind(), Equals, "check-rerefresh")
  3797  			// nothing should wait on it
  3798  			c.Check(reRefresh.NumHaltTasks(), Equals, 0)
  3799  			tasks = tasks[:len(tasks)-1] // and now forget about it
  3800  		}
  3801  
  3802  		var expectedPruned map[string]map[string]bool
  3803  		var pruneTasks []*state.Task
  3804  		if len(scenario.prune) != 0 {
  3805  			nprune := len(scenario.prune)
  3806  			pruneTasks = tasks[:nprune]
  3807  			j += nprune
  3808  			taskAliases := make(map[string]map[string]bool)
  3809  			for _, aliasTask := range pruneTasks {
  3810  				c.Check(aliasTask.Kind(), Equals, "prune-auto-aliases")
  3811  				var aliases []string
  3812  				err := aliasTask.Get("aliases", &aliases)
  3813  				c.Assert(err, IsNil)
  3814  				snapsup, err := snapstate.TaskSnapSetup(aliasTask)
  3815  				c.Assert(err, IsNil)
  3816  				taskAliases[snapsup.InstanceName()] = expectedSet(aliases)
  3817  			}
  3818  			expectedPruned = make(map[string]map[string]bool)
  3819  			for _, instanceName := range scenario.prune {
  3820  				expectedPruned[instanceName] = expectedSet(dropped[instanceName])
  3821  			}
  3822  			c.Check(taskAliases, DeepEquals, expectedPruned)
  3823  		}
  3824  		if scenario.update {
  3825  			first := tasks[j]
  3826  			j += 19
  3827  			c.Check(first.Kind(), Equals, "prerequisites")
  3828  			wait := false
  3829  			if expectedPruned["other-snap"]["aliasA"] {
  3830  				wait = true
  3831  			} else if expectedPruned["some-snap"] != nil {
  3832  				wait = true
  3833  			}
  3834  			if wait {
  3835  				c.Check(first.WaitTasks(), DeepEquals, pruneTasks)
  3836  			} else {
  3837  				c.Check(first.WaitTasks(), HasLen, 0)
  3838  			}
  3839  		}
  3840  		if scenario.new {
  3841  			aliasTask := tasks[j]
  3842  			j++
  3843  			c.Check(aliasTask.Kind(), Equals, "refresh-aliases")
  3844  			wait := false
  3845  			if expectedPruned["some-snap"]["aliasB"] {
  3846  				wait = true
  3847  			} else if expectedPruned["other-snap"] != nil {
  3848  				wait = true
  3849  			}
  3850  			if wait {
  3851  				c.Check(aliasTask.WaitTasks(), DeepEquals, pruneTasks)
  3852  			} else {
  3853  				c.Check(aliasTask.WaitTasks(), HasLen, 0)
  3854  			}
  3855  		}
  3856  		c.Assert(len(tasks), Equals, j, Commentf("%#v", scenario))
  3857  
  3858  		// conflict checks are triggered
  3859  		chg := s.state.NewChange("update", "...")
  3860  		chg.AddAll(ts)
  3861  		err = snapstate.CheckChangeConflict(s.state, scenario.names[0], nil)
  3862  		c.Check(err, ErrorMatches, `.* has "update" change in progress`)
  3863  		chg.SetStatus(state.DoneStatus)
  3864  	}
  3865  }
  3866  
  3867  func (s *snapmgrTestSuite) TestUpdateLocalSnapFails(c *C) {
  3868  	si := snap.SideInfo{
  3869  		RealName: "some-snap",
  3870  		Revision: snap.R(7),
  3871  	}
  3872  
  3873  	s.state.Lock()
  3874  	defer s.state.Unlock()
  3875  
  3876  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3877  		Active:   true,
  3878  		Sequence: []*snap.SideInfo{&si},
  3879  		Current:  si.Revision,
  3880  	})
  3881  
  3882  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  3883  	c.Assert(err, Equals, store.ErrLocalSnap)
  3884  }
  3885  
  3886  func (s *snapmgrTestSuite) TestUpdateDisabledUnsupported(c *C) {
  3887  	si := snap.SideInfo{
  3888  		RealName: "some-snap",
  3889  		SnapID:   "some-snap-id",
  3890  		Revision: snap.R(7),
  3891  	}
  3892  
  3893  	s.state.Lock()
  3894  	defer s.state.Unlock()
  3895  
  3896  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3897  		Active:   false,
  3898  		Sequence: []*snap.SideInfo{&si},
  3899  		Current:  si.Revision,
  3900  	})
  3901  
  3902  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  3903  	c.Assert(err, ErrorMatches, `refreshing disabled snap "some-snap" not supported`)
  3904  }
  3905  
  3906  func (s *snapmgrTestSuite) TestUpdateKernelTrackChecksSwitchingTracks(c *C) {
  3907  	si := snap.SideInfo{
  3908  		RealName: "kernel",
  3909  		SnapID:   "kernel-id",
  3910  		Revision: snap.R(7),
  3911  	}
  3912  
  3913  	s.state.Lock()
  3914  	defer s.state.Unlock()
  3915  
  3916  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  3917  	defer r()
  3918  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  3919  		Active:          true,
  3920  		Sequence:        []*snap.SideInfo{&si},
  3921  		Current:         si.Revision,
  3922  		TrackingChannel: "18/stable",
  3923  	})
  3924  
  3925  	// switching tracks is not ok
  3926  	_, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{})
  3927  	c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`)
  3928  
  3929  	// no change to the channel is ok
  3930  	_, err = snapstate.Update(s.state, "kernel", nil, s.user.ID, snapstate.Flags{})
  3931  	c.Assert(err, IsNil)
  3932  
  3933  	// switching risk level is ok
  3934  	_, err = snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "18/beta"}, s.user.ID, snapstate.Flags{})
  3935  	c.Assert(err, IsNil)
  3936  
  3937  	// switching just risk within the pinned track is ok
  3938  	_, err = snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "beta"}, s.user.ID, snapstate.Flags{})
  3939  	c.Assert(err, IsNil)
  3940  }
  3941  
  3942  func (s *snapmgrTestSuite) TestUpdateGadgetTrackChecksSwitchingTracks(c *C) {
  3943  	si := snap.SideInfo{
  3944  		RealName: "brand-gadget",
  3945  		SnapID:   "brand-gadget-id",
  3946  		Revision: snap.R(7),
  3947  	}
  3948  
  3949  	s.state.Lock()
  3950  	defer s.state.Unlock()
  3951  
  3952  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
  3953  	defer r()
  3954  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
  3955  		Active:          true,
  3956  		Sequence:        []*snap.SideInfo{&si},
  3957  		Current:         si.Revision,
  3958  		TrackingChannel: "18/stable",
  3959  	})
  3960  
  3961  	// switching tracks is not ok
  3962  	_, err := snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{})
  3963  	c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`)
  3964  
  3965  	// no change to the channel is ok
  3966  	_, err = snapstate.Update(s.state, "brand-gadget", nil, s.user.ID, snapstate.Flags{})
  3967  	c.Assert(err, IsNil)
  3968  
  3969  	// switching risk level is ok
  3970  	_, err = snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "18/beta"}, s.user.ID, snapstate.Flags{})
  3971  	c.Assert(err, IsNil)
  3972  
  3973  	// switching just risk within the pinned track is ok
  3974  	_, err = snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "beta"}, s.user.ID, snapstate.Flags{})
  3975  	c.Assert(err, IsNil)
  3976  
  3977  }
  3978  
  3979  func (s *snapmgrTestSuite) TestUpdateWithDeviceContext(c *C) {
  3980  	s.state.Lock()
  3981  	defer s.state.Unlock()
  3982  
  3983  	// unset the global store, it will need to come via the device context
  3984  	snapstate.ReplaceStore(s.state, nil)
  3985  
  3986  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  3987  		DeviceModel: DefaultModel(),
  3988  		CtxStore:    s.fakeStore,
  3989  	}
  3990  
  3991  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3992  		Active:          true,
  3993  		TrackingChannel: "latest/edge",
  3994  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  3995  		Current:         snap.R(7),
  3996  		SnapType:        "app",
  3997  	})
  3998  
  3999  	validateCalled := false
  4000  	happyValidateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx1 snapstate.DeviceContext) ([]*snap.Info, error) {
  4001  		c.Check(deviceCtx1, Equals, deviceCtx)
  4002  		validateCalled = true
  4003  		return refreshes, nil
  4004  	}
  4005  	// hook it up
  4006  	snapstate.ValidateRefreshes = happyValidateRefreshes
  4007  
  4008  	ts, err := snapstate.UpdateWithDeviceContext(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}, deviceCtx, "")
  4009  	c.Assert(err, IsNil)
  4010  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  4011  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  4012  
  4013  	c.Check(validateCalled, Equals, true)
  4014  }
  4015  
  4016  func (s *snapmgrTestSuite) TestUpdateWithDeviceContextToRevision(c *C) {
  4017  	s.state.Lock()
  4018  	defer s.state.Unlock()
  4019  
  4020  	// unset the global store, it will need to come via the device context
  4021  	snapstate.ReplaceStore(s.state, nil)
  4022  
  4023  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  4024  		DeviceModel: DefaultModel(),
  4025  		CtxStore:    s.fakeStore,
  4026  	}
  4027  
  4028  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4029  		Active: true,
  4030  		Sequence: []*snap.SideInfo{
  4031  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  4032  		},
  4033  		Current:  snap.R(5),
  4034  		SnapType: "app",
  4035  		UserID:   1,
  4036  	})
  4037  
  4038  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(11)}
  4039  	ts, err := snapstate.UpdateWithDeviceContext(s.state, "some-snap", opts, 0, snapstate.Flags{}, deviceCtx, "")
  4040  	c.Assert(err, IsNil)
  4041  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  4042  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  4043  }
  4044  
  4045  func (s *snapmgrTestSuite) TestUpdateTasksCoreSetsIgnoreOnConfigure(c *C) {
  4046  	s.state.Lock()
  4047  	defer s.state.Unlock()
  4048  
  4049  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4050  		Active:          true,
  4051  		TrackingChannel: "latest/edge",
  4052  		Sequence:        []*snap.SideInfo{{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(7)}},
  4053  		Current:         snap.R(7),
  4054  		SnapType:        "os",
  4055  	})
  4056  
  4057  	oldConfigure := snapstate.Configure
  4058  	defer func() { snapstate.Configure = oldConfigure }()
  4059  
  4060  	var configureFlags int
  4061  	snapstate.Configure = func(st *state.State, snapName string, patch map[string]interface{}, flags int) *state.TaskSet {
  4062  		configureFlags = flags
  4063  		return state.NewTaskSet()
  4064  	}
  4065  
  4066  	_, err := snapstate.Update(s.state, "core", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4067  	c.Assert(err, IsNil)
  4068  
  4069  	// ensure the core snap sets the "ignore-hook-error" flag
  4070  	c.Check(configureFlags&snapstate.IgnoreHookError, Equals, 1)
  4071  }
  4072  
  4073  func (s *snapmgrTestSuite) TestUpdateDevModeConfinementFiltering(c *C) {
  4074  	restore := maybeMockClassicSupport(c)
  4075  	defer restore()
  4076  
  4077  	s.state.Lock()
  4078  	defer s.state.Unlock()
  4079  
  4080  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4081  		Active:          true,
  4082  		TrackingChannel: "channel-for-devmode/stable",
  4083  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4084  		Current:         snap.R(7),
  4085  		SnapType:        "app",
  4086  	})
  4087  
  4088  	// updated snap is devmode, refresh without --devmode, do nothing
  4089  	// TODO: better error message here
  4090  	_, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  4091  	c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`)
  4092  
  4093  	// updated snap is devmode, refresh with --devmode
  4094  	_, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{DevMode: true})
  4095  	c.Assert(err, IsNil)
  4096  }
  4097  
  4098  func (s *snapmgrTestSuite) TestUpdateClassicConfinementFiltering(c *C) {
  4099  	restore := maybeMockClassicSupport(c)
  4100  	defer restore()
  4101  
  4102  	s.state.Lock()
  4103  	defer s.state.Unlock()
  4104  
  4105  	snapstate.Set(s.state, "some-snap-now-classic", &snapstate.SnapState{
  4106  		Active:   true,
  4107  		Sequence: []*snap.SideInfo{{RealName: "some-snap-now-classic", SnapID: "some-snap-now-classic-id", Revision: snap.R(7)}},
  4108  		Current:  snap.R(7),
  4109  		SnapType: "app",
  4110  	})
  4111  
  4112  	// updated snap is classic, refresh without --classic, do nothing
  4113  	// TODO: better error message here
  4114  	_, err := snapstate.Update(s.state, "some-snap-now-classic", nil, s.user.ID, snapstate.Flags{})
  4115  	c.Assert(err, ErrorMatches, `.* requires classic confinement`)
  4116  
  4117  	// updated snap is classic, refresh with --classic
  4118  	ts, err := snapstate.Update(s.state, "some-snap-now-classic", nil, s.user.ID, snapstate.Flags{Classic: true})
  4119  	c.Assert(err, IsNil)
  4120  
  4121  	chg := s.state.NewChange("refresh", "refresh snap")
  4122  	chg.AddAll(ts)
  4123  
  4124  	s.state.Unlock()
  4125  	defer s.se.Stop()
  4126  	s.settle(c)
  4127  	s.state.Lock()
  4128  
  4129  	c.Assert(chg.Err(), IsNil)
  4130  	c.Assert(chg.IsReady(), Equals, true)
  4131  
  4132  	// verify snap is in classic
  4133  	var snapst snapstate.SnapState
  4134  	err = snapstate.Get(s.state, "some-snap-now-classic", &snapst)
  4135  	c.Assert(err, IsNil)
  4136  	c.Check(snapst.Classic, Equals, true)
  4137  }
  4138  
  4139  func (s *snapmgrTestSuite) TestUpdateClassicFromClassic(c *C) {
  4140  	restore := maybeMockClassicSupport(c)
  4141  	defer restore()
  4142  
  4143  	s.state.Lock()
  4144  	defer s.state.Unlock()
  4145  
  4146  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4147  		Active:          true,
  4148  		TrackingChannel: "channel-for-classic/stable",
  4149  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4150  		Current:         snap.R(7),
  4151  		SnapType:        "app",
  4152  		Flags:           snapstate.Flags{Classic: true},
  4153  	})
  4154  
  4155  	// snap installed with --classic, update needs classic, refresh with --classic works
  4156  	ts, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{Classic: true})
  4157  	c.Assert(err, IsNil)
  4158  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4159  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
  4160  	c.Assert(err, IsNil)
  4161  	c.Check(snapsup.Flags.Classic, Equals, true)
  4162  
  4163  	// devmode overrides the snapsetup classic flag
  4164  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{DevMode: true})
  4165  	c.Assert(err, IsNil)
  4166  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4167  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4168  	c.Assert(err, IsNil)
  4169  	c.Check(snapsup.Flags.Classic, Equals, false)
  4170  
  4171  	// jailmode overrides it too (you need to provide both)
  4172  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{JailMode: true})
  4173  	c.Assert(err, IsNil)
  4174  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4175  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4176  	c.Assert(err, IsNil)
  4177  	c.Check(snapsup.Flags.Classic, Equals, false)
  4178  
  4179  	// jailmode and classic together gets you both
  4180  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{JailMode: true, Classic: true})
  4181  	c.Assert(err, IsNil)
  4182  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4183  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4184  	c.Assert(err, IsNil)
  4185  	c.Check(snapsup.Flags.Classic, Equals, true)
  4186  
  4187  	// snap installed with --classic, update needs classic, refresh without --classic works
  4188  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  4189  	c.Assert(err, IsNil)
  4190  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4191  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4192  	c.Assert(err, IsNil)
  4193  	c.Check(snapsup.Flags.Classic, Equals, true)
  4194  
  4195  	chg := s.state.NewChange("refresh", "refresh snap")
  4196  	chg.AddAll(ts)
  4197  
  4198  	s.state.Unlock()
  4199  	defer s.se.Stop()
  4200  	s.settle(c)
  4201  	s.state.Lock()
  4202  
  4203  	// verify snap is in classic
  4204  	var snapst snapstate.SnapState
  4205  	err = snapstate.Get(s.state, "some-snap", &snapst)
  4206  	c.Assert(err, IsNil)
  4207  	c.Check(snapst.Classic, Equals, true)
  4208  }
  4209  
  4210  func (s *snapmgrTestSuite) TestUpdateStrictFromClassic(c *C) {
  4211  	restore := maybeMockClassicSupport(c)
  4212  	defer restore()
  4213  
  4214  	s.state.Lock()
  4215  	defer s.state.Unlock()
  4216  
  4217  	snapstate.Set(s.state, "some-snap-was-classic", &snapstate.SnapState{
  4218  		Active:          true,
  4219  		TrackingChannel: "channel/stable",
  4220  		Sequence:        []*snap.SideInfo{{RealName: "some-snap-was-classic", SnapID: "some-snap-was-classic-id", Revision: snap.R(7)}},
  4221  		Current:         snap.R(7),
  4222  		SnapType:        "app",
  4223  		Flags:           snapstate.Flags{Classic: true},
  4224  	})
  4225  
  4226  	// snap installed with --classic, update does not need classic, refresh works without --classic
  4227  	_, err := snapstate.Update(s.state, "some-snap-was-classic", nil, s.user.ID, snapstate.Flags{})
  4228  	c.Assert(err, IsNil)
  4229  
  4230  	// snap installed with --classic, update does not need classic, refresh works with --classic
  4231  	_, err = snapstate.Update(s.state, "some-snap-was-classic", nil, s.user.ID, snapstate.Flags{Classic: true})
  4232  	c.Assert(err, IsNil)
  4233  }
  4234  
  4235  func (s *snapmgrTestSuite) TestUpdateChannelFallback(c *C) {
  4236  	s.state.Lock()
  4237  	defer s.state.Unlock()
  4238  
  4239  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4240  		Active:          true,
  4241  		TrackingChannel: "latest/edge",
  4242  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4243  		Current:         snap.R(7),
  4244  		SnapType:        "app",
  4245  	})
  4246  
  4247  	ts, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  4248  	c.Assert(err, IsNil)
  4249  
  4250  	var snapsup snapstate.SnapSetup
  4251  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  4252  	c.Assert(err, IsNil)
  4253  
  4254  	c.Check(snapsup.Channel, Equals, "latest/edge")
  4255  }
  4256  
  4257  func (s *snapmgrTestSuite) TestUpdateTooEarly(c *C) {
  4258  	s.state.Lock()
  4259  	defer s.state.Unlock()
  4260  
  4261  	s.state.Set("seeded", nil)
  4262  
  4263  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4264  		Active:   true,
  4265  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4266  		Current:  snap.R(7),
  4267  		SnapType: "app",
  4268  	})
  4269  
  4270  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4271  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  4272  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
  4273  }
  4274  
  4275  func (s *snapmgrTestSuite) TestUpdateConflict(c *C) {
  4276  	s.state.Lock()
  4277  	defer s.state.Unlock()
  4278  
  4279  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4280  		Active:   true,
  4281  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4282  		Current:  snap.R(7),
  4283  		SnapType: "app",
  4284  	})
  4285  
  4286  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4287  	c.Assert(err, IsNil)
  4288  	// need a change to make the tasks visible
  4289  	s.state.NewChange("refresh", "...").AddAll(ts)
  4290  
  4291  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4292  	c.Assert(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`)
  4293  }
  4294  
  4295  func (s *snapmgrTestSuite) TestUpdateCreatesGCTasks(c *C) {
  4296  	restore := release.MockOnClassic(false)
  4297  	defer restore()
  4298  
  4299  	s.testUpdateCreatesGCTasks(c, 2)
  4300  }
  4301  
  4302  func (s *snapmgrTestSuite) TestUpdateCreatesGCTasksOnClassic(c *C) {
  4303  	restore := release.MockOnClassic(true)
  4304  	defer restore()
  4305  
  4306  	s.testUpdateCreatesGCTasks(c, 3)
  4307  }
  4308  
  4309  func (s *snapmgrTestSuite) testUpdateCreatesGCTasks(c *C, expectedDiscards int) {
  4310  	s.state.Lock()
  4311  	defer s.state.Unlock()
  4312  
  4313  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4314  		Active: true,
  4315  		Sequence: []*snap.SideInfo{
  4316  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4317  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4318  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4319  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4320  		},
  4321  		Current:  snap.R(4),
  4322  		SnapType: "app",
  4323  	})
  4324  
  4325  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{})
  4326  	c.Assert(err, IsNil)
  4327  
  4328  	// ensure edges information is still there
  4329  	te, err := ts.Edge(snapstate.DownloadAndChecksDoneEdge)
  4330  	c.Assert(te, NotNil)
  4331  	c.Assert(err, IsNil)
  4332  
  4333  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, expectedDiscards, ts, s.state)
  4334  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  4335  }
  4336  
  4337  func (s *snapmgrTestSuite) TestUpdateCreatesDiscardAfterCurrentTasks(c *C) {
  4338  	s.state.Lock()
  4339  	defer s.state.Unlock()
  4340  
  4341  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4342  		Active: true,
  4343  		Sequence: []*snap.SideInfo{
  4344  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4345  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4346  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4347  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4348  		},
  4349  		Current:  snap.R(1),
  4350  		SnapType: "app",
  4351  	})
  4352  
  4353  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{})
  4354  	c.Assert(err, IsNil)
  4355  
  4356  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 3, ts, s.state)
  4357  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  4358  }
  4359  
  4360  func (s *snapmgrTestSuite) TestUpdateManyTooEarly(c *C) {
  4361  	s.state.Lock()
  4362  	defer s.state.Unlock()
  4363  
  4364  	s.state.Set("seeded", nil)
  4365  
  4366  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4367  		Active:   true,
  4368  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4369  		Current:  snap.R(7),
  4370  		SnapType: "app",
  4371  	})
  4372  
  4373  	_, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4374  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  4375  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
  4376  }
  4377  
  4378  func (s *snapmgrTestSuite) TestUpdateMany(c *C) {
  4379  	s.state.Lock()
  4380  	defer s.state.Unlock()
  4381  
  4382  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4383  		Active: true,
  4384  		Sequence: []*snap.SideInfo{
  4385  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4386  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4387  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4388  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4389  		},
  4390  		Current:  snap.R(1),
  4391  		SnapType: "app",
  4392  	})
  4393  
  4394  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4395  	c.Assert(err, IsNil)
  4396  	c.Assert(tts, HasLen, 2)
  4397  	verifyLastTasksetIsReRefresh(c, tts)
  4398  	c.Check(updates, DeepEquals, []string{"some-snap"})
  4399  
  4400  	ts := tts[0]
  4401  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 3, ts, s.state)
  4402  
  4403  	// check that the tasks are in non-default lane
  4404  	for _, t := range ts.Tasks() {
  4405  		c.Assert(t.Lanes(), DeepEquals, []int{1})
  4406  	}
  4407  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())+1) // 1==rerefresh
  4408  
  4409  	// ensure edges information is still there
  4410  	te, err := ts.Edge(snapstate.DownloadAndChecksDoneEdge)
  4411  	c.Assert(te, NotNil)
  4412  	c.Assert(err, IsNil)
  4413  
  4414  	checkIsAutoRefresh(c, ts.Tasks(), false)
  4415  }
  4416  
  4417  func (s *snapmgrTestSuite) TestUpdateManyFailureDoesntUndoSnapdRefresh(c *C) {
  4418  	s.state.Lock()
  4419  	defer s.state.Unlock()
  4420  
  4421  	r := snapstatetest.MockDeviceModel(ModelWithBase("core18"))
  4422  	defer r()
  4423  
  4424  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4425  		Active: true,
  4426  		Sequence: []*snap.SideInfo{
  4427  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4428  		},
  4429  		Current:         snap.R(1),
  4430  		SnapType:        "app",
  4431  		TrackingChannel: "channel-for-base/stable",
  4432  	})
  4433  
  4434  	snapstate.Set(s.state, "core18", &snapstate.SnapState{
  4435  		Active: true,
  4436  		Sequence: []*snap.SideInfo{
  4437  			{RealName: "core18", SnapID: "core18-snap-id", Revision: snap.R(1)},
  4438  		},
  4439  		Current:  snap.R(1),
  4440  		SnapType: "base",
  4441  	})
  4442  
  4443  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  4444  		Active: true,
  4445  		Sequence: []*snap.SideInfo{
  4446  			{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)},
  4447  		},
  4448  		Current:  snap.R(1),
  4449  		SnapType: "base",
  4450  	})
  4451  
  4452  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
  4453  		Active: true,
  4454  		Sequence: []*snap.SideInfo{
  4455  			{RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)},
  4456  		},
  4457  		Current:  snap.R(1),
  4458  		SnapType: "app",
  4459  	})
  4460  
  4461  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "some-base", "snapd"}, 0, nil)
  4462  	c.Assert(err, IsNil)
  4463  	c.Assert(tts, HasLen, 4)
  4464  	c.Assert(updates, HasLen, 3)
  4465  
  4466  	chg := s.state.NewChange("refresh", "...")
  4467  	for _, ts := range tts {
  4468  		chg.AddAll(ts)
  4469  	}
  4470  
  4471  	// refresh of some-snap fails on link-snap
  4472  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "/some-snap/11")
  4473  
  4474  	s.state.Unlock()
  4475  	defer s.se.Stop()
  4476  	s.settle(c)
  4477  	s.state.Lock()
  4478  
  4479  	c.Check(chg.Err(), ErrorMatches, ".*cannot perform the following tasks:\n- Make snap \"some-snap\" \\(11\\) available to the system.*")
  4480  	c.Check(chg.IsReady(), Equals, true)
  4481  
  4482  	var snapst snapstate.SnapState
  4483  
  4484  	// failed snap remains at the old revision, snapd and some-base are refreshed.
  4485  	c.Assert(snapstate.Get(s.state, "some-snap", &snapst), IsNil)
  4486  	c.Check(snapst.Current, Equals, snap.Revision{N: 1})
  4487  
  4488  	c.Assert(snapstate.Get(s.state, "snapd", &snapst), IsNil)
  4489  	c.Check(snapst.Current, Equals, snap.Revision{N: 11})
  4490  
  4491  	c.Assert(snapstate.Get(s.state, "some-base", &snapst), IsNil)
  4492  	c.Check(snapst.Current, Equals, snap.Revision{N: 11})
  4493  
  4494  	var undoneDownloads, doneDownloads int
  4495  	for _, ts := range tts {
  4496  		for _, t := range ts.Tasks() {
  4497  			if t.Kind() == "download-snap" {
  4498  				sup, err := snapstate.TaskSnapSetup(t)
  4499  				c.Assert(err, IsNil)
  4500  				switch sup.SnapName() {
  4501  				case "some-snap":
  4502  					undoneDownloads++
  4503  					c.Check(t.Status(), Equals, state.UndoneStatus)
  4504  				case "snapd", "some-base":
  4505  					doneDownloads++
  4506  					c.Check(t.Status(), Equals, state.DoneStatus)
  4507  				default:
  4508  					c.Errorf("unexpected snap %s", sup.SnapName())
  4509  				}
  4510  			}
  4511  		}
  4512  	}
  4513  	c.Assert(undoneDownloads, Equals, 1)
  4514  	c.Assert(doneDownloads, Equals, 2)
  4515  }
  4516  
  4517  func (s *snapmgrTestSuite) TestUpdateManyDevModeConfinementFiltering(c *C) {
  4518  	s.state.Lock()
  4519  	defer s.state.Unlock()
  4520  
  4521  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4522  		Active:          true,
  4523  		TrackingChannel: "channel-for-devmode/stable",
  4524  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4525  		Current:         snap.R(7),
  4526  		SnapType:        "app",
  4527  	})
  4528  
  4529  	// updated snap is devmode, updatemany doesn't update it
  4530  	_, tts, _ := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  4531  	// FIXME: UpdateMany will not error out in this case (daemon catches this case, with a weird error)
  4532  	c.Assert(tts, HasLen, 0)
  4533  }
  4534  
  4535  func (s *snapmgrTestSuite) TestUpdateManyClassicConfinementFiltering(c *C) {
  4536  	restore := maybeMockClassicSupport(c)
  4537  	defer restore()
  4538  
  4539  	s.state.Lock()
  4540  	defer s.state.Unlock()
  4541  
  4542  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4543  		Active:          true,
  4544  		TrackingChannel: "channel-for-classic/stable",
  4545  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4546  		Current:         snap.R(7),
  4547  		SnapType:        "app",
  4548  	})
  4549  
  4550  	// if a snap installed without --classic gets a classic update it isn't installed
  4551  	_, tts, _ := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  4552  	// FIXME: UpdateMany will not error out in this case (daemon catches this case, with a weird error)
  4553  	c.Assert(tts, HasLen, 0)
  4554  }
  4555  
  4556  func (s *snapmgrTestSuite) TestUpdateManyClassic(c *C) {
  4557  	restore := maybeMockClassicSupport(c)
  4558  	defer restore()
  4559  
  4560  	s.state.Lock()
  4561  	defer s.state.Unlock()
  4562  
  4563  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4564  		Active:          true,
  4565  		TrackingChannel: "channel-for-classic/stable",
  4566  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4567  		Current:         snap.R(7),
  4568  		SnapType:        "app",
  4569  		Flags:           snapstate.Flags{Classic: true},
  4570  	})
  4571  
  4572  	// snap installed with classic: refresh gets classic
  4573  	_, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  4574  	c.Assert(err, IsNil)
  4575  	c.Assert(tts, HasLen, 2)
  4576  	verifyLastTasksetIsReRefresh(c, tts)
  4577  }
  4578  
  4579  func (s *snapmgrTestSuite) TestUpdateManyClassicToStrict(c *C) {
  4580  	restore := maybeMockClassicSupport(c)
  4581  	defer restore()
  4582  
  4583  	s.state.Lock()
  4584  	defer s.state.Unlock()
  4585  
  4586  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4587  		Active:          true,
  4588  		TrackingChannel: "stable",
  4589  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4590  		Current:         snap.R(7),
  4591  		SnapType:        "app",
  4592  		Flags:           snapstate.Flags{Classic: true},
  4593  	})
  4594  
  4595  	// snap installed with classic: refresh gets classic
  4596  	_, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, &snapstate.Flags{Classic: true})
  4597  	c.Assert(err, IsNil)
  4598  	c.Assert(tts, HasLen, 2)
  4599  	// ensure we clear the classic flag
  4600  	snapsup, err := snapstate.TaskSnapSetup(tts[0].Tasks()[0])
  4601  	c.Assert(err, IsNil)
  4602  	c.Assert(snapsup.Flags.Classic, Equals, false)
  4603  
  4604  	verifyLastTasksetIsReRefresh(c, tts)
  4605  }
  4606  
  4607  func (s *snapmgrTestSuite) TestUpdateManyDevMode(c *C) {
  4608  	s.state.Lock()
  4609  	defer s.state.Unlock()
  4610  
  4611  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4612  		Active: true,
  4613  		Flags:  snapstate.Flags{DevMode: true},
  4614  		Sequence: []*snap.SideInfo{
  4615  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4616  		},
  4617  		Current:  snap.R(1),
  4618  		SnapType: "app",
  4619  	})
  4620  
  4621  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, 0, nil)
  4622  	c.Assert(err, IsNil)
  4623  	c.Check(updates, HasLen, 1)
  4624  }
  4625  
  4626  func (s *snapmgrTestSuite) TestUpdateAllDevMode(c *C) {
  4627  	s.state.Lock()
  4628  	defer s.state.Unlock()
  4629  
  4630  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4631  		Active: true,
  4632  		Flags:  snapstate.Flags{DevMode: true},
  4633  		Sequence: []*snap.SideInfo{
  4634  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4635  		},
  4636  		Current:  snap.R(1),
  4637  		SnapType: "app",
  4638  	})
  4639  
  4640  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4641  	c.Assert(err, IsNil)
  4642  	c.Check(updates, HasLen, 0)
  4643  }
  4644  
  4645  func (s *snapmgrTestSuite) TestUpdateManyWaitForBasesUC16(c *C) {
  4646  	s.state.Lock()
  4647  	defer s.state.Unlock()
  4648  
  4649  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4650  		Active: true,
  4651  		Sequence: []*snap.SideInfo{
  4652  			{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(1)},
  4653  		},
  4654  		Current:  snap.R(1),
  4655  		SnapType: "os",
  4656  	})
  4657  
  4658  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  4659  		Active: true,
  4660  		Sequence: []*snap.SideInfo{
  4661  			{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)},
  4662  		},
  4663  		Current:  snap.R(1),
  4664  		SnapType: "base",
  4665  	})
  4666  
  4667  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4668  		Active: true,
  4669  		Sequence: []*snap.SideInfo{
  4670  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4671  		},
  4672  		Current:         snap.R(1),
  4673  		SnapType:        "app",
  4674  		TrackingChannel: "channel-for-base/stable",
  4675  	})
  4676  
  4677  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "core", "some-base"}, 0, nil)
  4678  	c.Assert(err, IsNil)
  4679  	c.Assert(tts, HasLen, 4)
  4680  	verifyLastTasksetIsReRefresh(c, tts)
  4681  	c.Check(updates, HasLen, 3)
  4682  
  4683  	// to make TaskSnapSetup work
  4684  	chg := s.state.NewChange("refresh", "...")
  4685  	for _, ts := range tts {
  4686  		chg.AddAll(ts)
  4687  	}
  4688  
  4689  	prereqTotal := len(tts[0].Tasks()) + len(tts[1].Tasks())
  4690  	prereqs := map[string]bool{}
  4691  	for i, task := range tts[2].Tasks() {
  4692  		waitTasks := task.WaitTasks()
  4693  		if i == 0 {
  4694  			c.Check(len(waitTasks), Equals, prereqTotal)
  4695  		} else if task.Kind() == "link-snap" {
  4696  			c.Check(len(waitTasks), Equals, prereqTotal+1)
  4697  			for _, pre := range waitTasks {
  4698  				if pre.Kind() == "link-snap" {
  4699  					snapsup, err := snapstate.TaskSnapSetup(pre)
  4700  					c.Assert(err, IsNil)
  4701  					prereqs[snapsup.InstanceName()] = true
  4702  				}
  4703  			}
  4704  		}
  4705  	}
  4706  
  4707  	c.Check(prereqs, DeepEquals, map[string]bool{
  4708  		"core":      true,
  4709  		"some-base": true,
  4710  	})
  4711  }
  4712  
  4713  func (s *snapmgrTestSuite) TestUpdateManyWaitForBasesUC18(c *C) {
  4714  	r := snapstatetest.MockDeviceModel(ModelWithBase("core18"))
  4715  	defer r()
  4716  
  4717  	s.state.Lock()
  4718  	defer s.state.Unlock()
  4719  
  4720  	snapstate.Set(s.state, "core18", &snapstate.SnapState{
  4721  		Active: true,
  4722  		Sequence: []*snap.SideInfo{
  4723  			{RealName: "core18", SnapID: "core18-snap-id", Revision: snap.R(1)},
  4724  		},
  4725  		Current:  snap.R(1),
  4726  		SnapType: "base",
  4727  	})
  4728  
  4729  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  4730  		Active: true,
  4731  		Sequence: []*snap.SideInfo{
  4732  			{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)},
  4733  		},
  4734  		Current:  snap.R(1),
  4735  		SnapType: "base",
  4736  	})
  4737  
  4738  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
  4739  		Active: true,
  4740  		Sequence: []*snap.SideInfo{
  4741  			{RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)},
  4742  		},
  4743  		Current:  snap.R(1),
  4744  		SnapType: "app",
  4745  	})
  4746  
  4747  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4748  		Active: true,
  4749  		Sequence: []*snap.SideInfo{
  4750  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4751  		},
  4752  		Current:         snap.R(1),
  4753  		SnapType:        "app",
  4754  		TrackingChannel: "channel-for-base/stable",
  4755  	})
  4756  
  4757  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "core18", "some-base", "snapd"}, 0, nil)
  4758  	c.Assert(err, IsNil)
  4759  	c.Assert(tts, HasLen, 5)
  4760  	verifyLastTasksetIsReRefresh(c, tts)
  4761  	c.Check(updates, HasLen, 4)
  4762  
  4763  	// to make TaskSnapSetup work
  4764  	chg := s.state.NewChange("refresh", "...")
  4765  	for _, ts := range tts {
  4766  		chg.AddAll(ts)
  4767  	}
  4768  
  4769  	// Note that some-app only waits for snapd+some-base. The core18
  4770  	// base is not special to this snap and not waited for
  4771  	prereqTotal := len(tts[0].Tasks()) + len(tts[1].Tasks())
  4772  	prereqs := map[string]bool{}
  4773  	for i, task := range tts[3].Tasks() {
  4774  		waitTasks := task.WaitTasks()
  4775  		if i == 0 {
  4776  			c.Check(len(waitTasks), Equals, prereqTotal)
  4777  		} else if task.Kind() == "link-snap" {
  4778  			c.Check(len(waitTasks), Equals, prereqTotal+1)
  4779  			for _, pre := range waitTasks {
  4780  				if pre.Kind() == "link-snap" {
  4781  					snapsup, err := snapstate.TaskSnapSetup(pre)
  4782  					c.Assert(err, IsNil)
  4783  					prereqs[snapsup.InstanceName()] = true
  4784  				}
  4785  			}
  4786  		}
  4787  	}
  4788  
  4789  	// Note that "core18" is not part of the prereqs for some-app
  4790  	// as it does not use this base.
  4791  	c.Check(prereqs, DeepEquals, map[string]bool{
  4792  		"some-base": true,
  4793  		"snapd":     true,
  4794  	})
  4795  }
  4796  
  4797  func (s *snapmgrTestSuite) TestUpdateManyValidateRefreshes(c *C) {
  4798  	s.state.Lock()
  4799  	defer s.state.Unlock()
  4800  
  4801  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4802  		Active: true,
  4803  		Sequence: []*snap.SideInfo{
  4804  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4805  		},
  4806  		Current:  snap.R(1),
  4807  		SnapType: "app",
  4808  	})
  4809  
  4810  	validateCalled := false
  4811  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  4812  		validateCalled = true
  4813  		c.Check(refreshes, HasLen, 1)
  4814  		c.Check(refreshes[0].InstanceName(), Equals, "some-snap")
  4815  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  4816  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  4817  		c.Check(ignoreValidation, HasLen, 0)
  4818  		return refreshes, nil
  4819  	}
  4820  	// hook it up
  4821  	snapstate.ValidateRefreshes = validateRefreshes
  4822  
  4823  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4824  	c.Assert(err, IsNil)
  4825  	c.Assert(tts, HasLen, 2)
  4826  	verifyLastTasksetIsReRefresh(c, tts)
  4827  	c.Check(updates, DeepEquals, []string{"some-snap"})
  4828  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[0], s.state)
  4829  
  4830  	c.Check(validateCalled, Equals, true)
  4831  }
  4832  
  4833  func (s *snapmgrTestSuite) TestParallelInstanceUpdateMany(c *C) {
  4834  	restore := release.MockOnClassic(false)
  4835  	defer restore()
  4836  
  4837  	s.state.Lock()
  4838  	defer s.state.Unlock()
  4839  
  4840  	tr := config.NewTransaction(s.state)
  4841  	tr.Set("core", "experimental.parallel-instances", true)
  4842  	tr.Commit()
  4843  
  4844  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4845  		Active: true,
  4846  		Sequence: []*snap.SideInfo{
  4847  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4848  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4849  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4850  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4851  		},
  4852  		Current:  snap.R(1),
  4853  		SnapType: "app",
  4854  	})
  4855  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  4856  		Active: true,
  4857  		Sequence: []*snap.SideInfo{
  4858  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4859  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4860  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4861  		},
  4862  		Current:     snap.R(3),
  4863  		SnapType:    "app",
  4864  		InstanceKey: "instance",
  4865  	})
  4866  
  4867  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4868  	c.Assert(err, IsNil)
  4869  	c.Assert(tts, HasLen, 3)
  4870  	verifyLastTasksetIsReRefresh(c, tts)
  4871  	// ensure stable ordering of updates list
  4872  	if updates[0] != "some-snap" {
  4873  		updates[1], updates[0] = updates[0], updates[1]
  4874  	}
  4875  
  4876  	c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"})
  4877  
  4878  	var snapsup, snapsupInstance *snapstate.SnapSetup
  4879  
  4880  	// ensure stable ordering of task sets list
  4881  	snapsup, err = snapstate.TaskSnapSetup(tts[0].Tasks()[0])
  4882  	c.Assert(err, IsNil)
  4883  	if snapsup.InstanceName() != "some-snap" {
  4884  		tts[0], tts[1] = tts[1], tts[0]
  4885  		snapsup, err = snapstate.TaskSnapSetup(tts[0].Tasks()[0])
  4886  		c.Assert(err, IsNil)
  4887  	}
  4888  	snapsupInstance, err = snapstate.TaskSnapSetup(tts[1].Tasks()[0])
  4889  	c.Assert(err, IsNil)
  4890  
  4891  	c.Assert(snapsup.InstanceName(), Equals, "some-snap")
  4892  	c.Assert(snapsupInstance.InstanceName(), Equals, "some-snap_instance")
  4893  
  4894  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 3, tts[0], s.state)
  4895  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 1, tts[1], s.state)
  4896  }
  4897  
  4898  func (s *snapmgrTestSuite) TestParallelInstanceUpdateManyValidateRefreshes(c *C) {
  4899  	s.state.Lock()
  4900  	defer s.state.Unlock()
  4901  
  4902  	tr := config.NewTransaction(s.state)
  4903  	tr.Set("core", "experimental.parallel-instances", true)
  4904  	tr.Commit()
  4905  
  4906  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4907  		Active: true,
  4908  		Sequence: []*snap.SideInfo{
  4909  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4910  		},
  4911  		Current:  snap.R(1),
  4912  		SnapType: "app",
  4913  	})
  4914  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  4915  		Active: true,
  4916  		Sequence: []*snap.SideInfo{
  4917  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4918  		},
  4919  		Current:     snap.R(1),
  4920  		SnapType:    "app",
  4921  		InstanceKey: "instance",
  4922  	})
  4923  
  4924  	validateCalled := false
  4925  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  4926  		validateCalled = true
  4927  		c.Check(refreshes, HasLen, 2)
  4928  		instanceIdx := 0
  4929  		someIdx := 1
  4930  		if refreshes[0].InstanceName() != "some-snap_instance" {
  4931  			instanceIdx = 1
  4932  			someIdx = 0
  4933  		}
  4934  		c.Check(refreshes[someIdx].InstanceName(), Equals, "some-snap")
  4935  		c.Check(refreshes[instanceIdx].InstanceName(), Equals, "some-snap_instance")
  4936  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  4937  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  4938  		c.Check(refreshes[1].SnapID, Equals, "some-snap-id")
  4939  		c.Check(refreshes[1].Revision, Equals, snap.R(11))
  4940  		c.Check(ignoreValidation, HasLen, 0)
  4941  		return refreshes, nil
  4942  	}
  4943  	// hook it up
  4944  	snapstate.ValidateRefreshes = validateRefreshes
  4945  
  4946  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4947  	c.Assert(err, IsNil)
  4948  	c.Assert(tts, HasLen, 3)
  4949  	verifyLastTasksetIsReRefresh(c, tts)
  4950  	sort.Strings(updates)
  4951  	c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"})
  4952  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[0], s.state)
  4953  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[1], s.state)
  4954  
  4955  	c.Check(validateCalled, Equals, true)
  4956  }
  4957  
  4958  func (s *snapmgrTestSuite) TestUpdateManyValidateRefreshesUnhappy(c *C) {
  4959  	s.state.Lock()
  4960  	defer s.state.Unlock()
  4961  
  4962  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4963  		Active: true,
  4964  		Sequence: []*snap.SideInfo{
  4965  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4966  		},
  4967  		Current: snap.R(1),
  4968  	})
  4969  
  4970  	validateErr := errors.New("refresh control error")
  4971  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  4972  		c.Check(refreshes, HasLen, 1)
  4973  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  4974  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  4975  		c.Check(ignoreValidation, HasLen, 0)
  4976  		return nil, validateErr
  4977  	}
  4978  	// hook it up
  4979  	snapstate.ValidateRefreshes = validateRefreshes
  4980  
  4981  	// refresh all => no error
  4982  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4983  	c.Assert(err, IsNil)
  4984  	c.Check(tts, HasLen, 0)
  4985  	c.Check(updates, HasLen, 0)
  4986  
  4987  	// refresh some-snap => report error
  4988  	updates, tts, err = snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, 0, nil)
  4989  	c.Assert(err, Equals, validateErr)
  4990  	c.Check(tts, HasLen, 0)
  4991  	c.Check(updates, HasLen, 0)
  4992  
  4993  }
  4994  
  4995  func (s *snapmgrTestSuite) testUpdateManyDiskSpaceCheck(c *C, featureFlag, failDiskCheck, failInstallSize bool) error {
  4996  	var diskCheckCalled, installSizeCalled bool
  4997  	restore := snapstate.MockOsutilCheckFreeSpace(func(path string, sz uint64) error {
  4998  		diskCheckCalled = true
  4999  		c.Check(path, Equals, filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd"))
  5000  		c.Check(sz, Equals, snapstate.SafetyMarginDiskSpace(123))
  5001  		if failDiskCheck {
  5002  			return &osutil.NotEnoughDiskSpaceError{}
  5003  		}
  5004  		return nil
  5005  	})
  5006  	defer restore()
  5007  
  5008  	restoreInstallSize := snapstate.MockInstallSize(func(st *state.State, snaps []snapstate.MinimalInstallInfo, userID int) (uint64, error) {
  5009  		installSizeCalled = true
  5010  		if failInstallSize {
  5011  			return 0, fmt.Errorf("boom")
  5012  		}
  5013  		c.Assert(snaps, HasLen, 2)
  5014  		c.Check(snaps[0].InstanceName(), Equals, "snapd")
  5015  		c.Check(snaps[1].InstanceName(), Equals, "some-snap")
  5016  		return 123, nil
  5017  	})
  5018  	defer restoreInstallSize()
  5019  
  5020  	s.state.Lock()
  5021  	defer s.state.Unlock()
  5022  
  5023  	tr := config.NewTransaction(s.state)
  5024  	tr.Set("core", "experimental.check-disk-space-refresh", featureFlag)
  5025  	tr.Commit()
  5026  
  5027  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5028  		Active: true,
  5029  		Sequence: []*snap.SideInfo{
  5030  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  5031  		},
  5032  		Current:  snap.R(1),
  5033  		SnapType: "app",
  5034  	})
  5035  
  5036  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
  5037  		Active: true,
  5038  		Sequence: []*snap.SideInfo{
  5039  			{RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)},
  5040  		},
  5041  		Current:  snap.R(1),
  5042  		SnapType: "app",
  5043  	})
  5044  
  5045  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  5046  	if featureFlag {
  5047  		c.Check(installSizeCalled, Equals, true)
  5048  		if failInstallSize {
  5049  			c.Check(diskCheckCalled, Equals, false)
  5050  		} else {
  5051  			c.Check(diskCheckCalled, Equals, true)
  5052  			if failDiskCheck {
  5053  				c.Check(updates, HasLen, 0)
  5054  			} else {
  5055  				c.Check(updates, HasLen, 2)
  5056  			}
  5057  		}
  5058  	} else {
  5059  		c.Check(installSizeCalled, Equals, false)
  5060  		c.Check(diskCheckCalled, Equals, false)
  5061  	}
  5062  
  5063  	return err
  5064  }
  5065  
  5066  func (s *snapmgrTestSuite) TestUpdateManyDiskSpaceCheckError(c *C) {
  5067  	featureFlag := true
  5068  	failDiskCheck := true
  5069  	failInstallSize := false
  5070  	err := s.testUpdateManyDiskSpaceCheck(c, featureFlag, failDiskCheck, failInstallSize)
  5071  	diskSpaceErr := err.(*snapstate.InsufficientSpaceError)
  5072  	c.Assert(diskSpaceErr, ErrorMatches, `insufficient space in .* to perform "refresh" change for the following snaps: snapd, some-snap`)
  5073  	c.Check(diskSpaceErr.Path, Equals, filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd"))
  5074  	c.Check(diskSpaceErr.Snaps, DeepEquals, []string{"snapd", "some-snap"})
  5075  }
  5076  
  5077  func (s *snapmgrTestSuite) TestUpdateManyDiskSpaceSkippedIfFeatureDisabled(c *C) {
  5078  	featureFlag := false
  5079  	failDiskCheck := true
  5080  	failInstallSize := false
  5081  	err := s.testUpdateManyDiskSpaceCheck(c, featureFlag, failDiskCheck, failInstallSize)
  5082  	c.Assert(err, IsNil)
  5083  }
  5084  
  5085  func (s *snapmgrTestSuite) TestUpdateManyDiskSpaceFailInstallSize(c *C) {
  5086  	featureFlag := true
  5087  	failDiskCheck := false
  5088  	failInstallSize := true
  5089  	err := s.testUpdateManyDiskSpaceCheck(c, featureFlag, failDiskCheck, failInstallSize)
  5090  	c.Assert(err, ErrorMatches, "boom")
  5091  }
  5092  
  5093  func (s *snapmgrTestSuite) TestUnlinkCurrentSnapLastActiveDisabledServicesSet(c *C) {
  5094  	si := snap.SideInfo{
  5095  		RealName: "services-snap",
  5096  		Revision: snap.R(-42),
  5097  	}
  5098  	snaptest.MockSnap(c, `name: services-snap`, &si)
  5099  
  5100  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  5101  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  5102  
  5103  	// reset the services to what they were before after the test is done
  5104  	defer func() {
  5105  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  5106  	}()
  5107  
  5108  	s.state.Lock()
  5109  	defer s.state.Unlock()
  5110  
  5111  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  5112  		Active:                     true,
  5113  		Sequence:                   []*snap.SideInfo{&si},
  5114  		Current:                    si.Revision,
  5115  		SnapType:                   "app",
  5116  		TrackingChannel:            "stable",
  5117  		LastActiveDisabledServices: []string{},
  5118  	})
  5119  
  5120  	chg := s.state.NewChange("refresh", "refresh a snap")
  5121  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
  5122  
  5123  	c.Assert(err, IsNil)
  5124  	// only add up to unlink-current-snap task
  5125  	for _, t := range ts.Tasks() {
  5126  		chg.AddTask(t)
  5127  		if t.Kind() == "unlink-current-snap" {
  5128  			// don't add any more from this point on
  5129  			break
  5130  		}
  5131  	}
  5132  
  5133  	s.state.Unlock()
  5134  	defer s.se.Stop()
  5135  	s.settle(c)
  5136  	s.state.Lock()
  5137  
  5138  	c.Assert(chg.Err(), IsNil)
  5139  	c.Assert(chg.IsReady(), Equals, true)
  5140  
  5141  	// get the snap state
  5142  	var snapst snapstate.SnapState
  5143  	err = snapstate.Get(s.state, "services-snap", &snapst)
  5144  	c.Assert(err, IsNil)
  5145  
  5146  	// make sure that the disabled services in this snap's state is what we
  5147  	// provided
  5148  	sort.Strings(snapst.LastActiveDisabledServices)
  5149  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  5150  }
  5151  
  5152  func (s *snapmgrTestSuite) TestUnlinkCurrentSnapMergedLastActiveDisabledServicesSet(c *C) {
  5153  	si := snap.SideInfo{
  5154  		RealName: "services-snap",
  5155  		Revision: snap.R(-42),
  5156  	}
  5157  	snaptest.MockSnap(c, `name: services-snap`, &si)
  5158  
  5159  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  5160  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  5161  
  5162  	// reset the services to what they were before after the test is done
  5163  	defer func() {
  5164  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  5165  	}()
  5166  
  5167  	s.state.Lock()
  5168  	defer s.state.Unlock()
  5169  
  5170  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  5171  		Active:                     true,
  5172  		Sequence:                   []*snap.SideInfo{&si},
  5173  		Current:                    si.Revision,
  5174  		SnapType:                   "app",
  5175  		TrackingChannel:            "stable",
  5176  		LastActiveDisabledServices: []string{"missing-svc3"},
  5177  	})
  5178  
  5179  	chg := s.state.NewChange("refresh", "refresh a snap")
  5180  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
  5181  
  5182  	c.Assert(err, IsNil)
  5183  	// only add up to unlink-current-snap task
  5184  	for _, t := range ts.Tasks() {
  5185  		chg.AddTask(t)
  5186  		if t.Kind() == "unlink-current-snap" {
  5187  			// don't add any more from this point on
  5188  			break
  5189  		}
  5190  	}
  5191  
  5192  	s.state.Unlock()
  5193  	defer s.se.Stop()
  5194  	s.settle(c)
  5195  	s.state.Lock()
  5196  
  5197  	c.Assert(chg.Err(), IsNil)
  5198  	c.Assert(chg.IsReady(), Equals, true)
  5199  
  5200  	// get the snap state
  5201  	var snapst snapstate.SnapState
  5202  	err = snapstate.Get(s.state, "services-snap", &snapst)
  5203  	c.Assert(err, IsNil)
  5204  
  5205  	// make sure that the disabled services in this snap's state is what we
  5206  	// provided
  5207  	sort.Strings(snapst.LastActiveDisabledServices)
  5208  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3", "svc1", "svc2"})
  5209  }
  5210  
  5211  func (s *snapmgrTestSuite) TestUnlinkCurrentSnapPassthroughLastActiveDisabledServicesSet(c *C) {
  5212  	si := snap.SideInfo{
  5213  		RealName: "services-snap",
  5214  		Revision: snap.R(-42),
  5215  	}
  5216  	snaptest.MockSnap(c, `name: services-snap`, &si)
  5217  
  5218  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  5219  	s.fakeBackend.servicesCurrentlyDisabled = []string{}
  5220  
  5221  	// reset the services to what they were before after the test is done
  5222  	defer func() {
  5223  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  5224  	}()
  5225  
  5226  	s.state.Lock()
  5227  	defer s.state.Unlock()
  5228  
  5229  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  5230  		Active:                     true,
  5231  		Sequence:                   []*snap.SideInfo{&si},
  5232  		Current:                    si.Revision,
  5233  		SnapType:                   "app",
  5234  		TrackingChannel:            "stable",
  5235  		LastActiveDisabledServices: []string{"missing-svc3"},
  5236  	})
  5237  
  5238  	chg := s.state.NewChange("refresh", "refresh a snap")
  5239  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
  5240  
  5241  	c.Assert(err, IsNil)
  5242  	// only add up to unlink-current-snap task
  5243  	for _, t := range ts.Tasks() {
  5244  		chg.AddTask(t)
  5245  		if t.Kind() == "unlink-current-snap" {
  5246  			// don't add any more from this point on
  5247  			break
  5248  		}
  5249  	}
  5250  
  5251  	s.state.Unlock()
  5252  	defer s.se.Stop()
  5253  	s.settle(c)
  5254  	s.state.Lock()
  5255  
  5256  	c.Assert(chg.Err(), IsNil)
  5257  	c.Assert(chg.IsReady(), Equals, true)
  5258  
  5259  	// get the snap state
  5260  	var snapst snapstate.SnapState
  5261  	err = snapstate.Get(s.state, "services-snap", &snapst)
  5262  	c.Assert(err, IsNil)
  5263  
  5264  	// make sure that the disabled services in this snap's state is what we
  5265  	// provided
  5266  	sort.Strings(snapst.LastActiveDisabledServices)
  5267  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3"})
  5268  }
  5269  
  5270  func (s *snapmgrTestSuite) TestStopSnapServicesSavesSnapSetupLastActiveDisabledServices(c *C) {
  5271  	s.state.Lock()
  5272  	defer s.state.Unlock()
  5273  
  5274  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  5275  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  5276  
  5277  	// reset the services to what they were before after the test is done
  5278  	defer func() {
  5279  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  5280  	}()
  5281  
  5282  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  5283  		Sequence: []*snap.SideInfo{
  5284  			{RealName: "services-snap", Revision: snap.R(11)},
  5285  		},
  5286  		Current: snap.R(11),
  5287  		Active:  true,
  5288  	})
  5289  
  5290  	snapsup := &snapstate.SnapSetup{
  5291  		SideInfo: &snap.SideInfo{
  5292  			RealName: "services-snap",
  5293  			Revision: snap.R(11),
  5294  			SnapID:   "services-snap-id",
  5295  		},
  5296  	}
  5297  
  5298  	chg := s.state.NewChange("stop-services", "stop the services")
  5299  	t1 := s.state.NewTask("prerequisites", "...")
  5300  	t1.Set("snap-setup", snapsup)
  5301  	t2 := s.state.NewTask("stop-snap-services", "...")
  5302  	t2.Set("stop-reason", snap.StopReasonDisable)
  5303  	t2.Set("snap-setup-task", t1.ID())
  5304  	t2.WaitFor(t1)
  5305  	chg.AddTask(t1)
  5306  	chg.AddTask(t2)
  5307  
  5308  	s.state.Unlock()
  5309  	defer s.se.Stop()
  5310  	s.settle(c)
  5311  	s.state.Lock()
  5312  
  5313  	c.Assert(chg.Err(), IsNil)
  5314  	c.Assert(chg.IsReady(), Equals, true)
  5315  
  5316  	// get the snap state
  5317  	var snapst snapstate.SnapState
  5318  	c.Assert(snapstate.Get(s.state, "services-snap", &snapst), IsNil)
  5319  
  5320  	// make sure that the disabled services in this snap's state is what we
  5321  	// provided
  5322  	sort.Strings(snapst.LastActiveDisabledServices)
  5323  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  5324  }
  5325  
  5326  func (s *snapmgrTestSuite) TestStopSnapServicesFirstSavesSnapSetupLastActiveDisabledServices(c *C) {
  5327  	s.state.Lock()
  5328  	defer s.state.Unlock()
  5329  
  5330  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  5331  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1"}
  5332  
  5333  	// reset the services to what they were before after the test is done
  5334  	defer func() {
  5335  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  5336  	}()
  5337  
  5338  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  5339  		Sequence: []*snap.SideInfo{
  5340  			{RealName: "services-snap", Revision: snap.R(11)},
  5341  		},
  5342  		Current: snap.R(11),
  5343  		Active:  true,
  5344  		// leave this line to keep gofmt 1.10 happy
  5345  		LastActiveDisabledServices: []string{"svc2"},
  5346  	})
  5347  
  5348  	snapsup := &snapstate.SnapSetup{
  5349  		SideInfo: &snap.SideInfo{
  5350  			RealName: "services-snap",
  5351  			Revision: snap.R(11),
  5352  			SnapID:   "services-snap-id",
  5353  		},
  5354  	}
  5355  
  5356  	chg := s.state.NewChange("stop-services", "stop the services")
  5357  	t := s.state.NewTask("stop-snap-services", "...")
  5358  	t.Set("stop-reason", snap.StopReasonDisable)
  5359  	t.Set("snap-setup", snapsup)
  5360  	chg.AddTask(t)
  5361  
  5362  	s.state.Unlock()
  5363  	defer s.se.Stop()
  5364  	s.settle(c)
  5365  	s.state.Lock()
  5366  
  5367  	c.Assert(chg.Err(), IsNil)
  5368  	c.Assert(chg.IsReady(), Equals, true)
  5369  
  5370  	// get the snap state
  5371  	var snapst snapstate.SnapState
  5372  	c.Assert(snapstate.Get(s.state, "services-snap", &snapst), IsNil)
  5373  
  5374  	// make sure that the disabled services in this snap's state is what we
  5375  	// provided
  5376  	sort.Strings(snapst.LastActiveDisabledServices)
  5377  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  5378  }
  5379  
  5380  func (s *snapmgrTestSuite) TestRefreshDoesntRestoreRevisionConfig(c *C) {
  5381  	restore := release.MockOnClassic(false)
  5382  	defer restore()
  5383  
  5384  	s.state.Lock()
  5385  	defer s.state.Unlock()
  5386  
  5387  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5388  		Active: true,
  5389  		Sequence: []*snap.SideInfo{
  5390  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  5391  		},
  5392  		Current:  snap.R(1),
  5393  		SnapType: "app",
  5394  	})
  5395  
  5396  	// set global configuration (affecting current snap)
  5397  	tr := config.NewTransaction(s.state)
  5398  	tr.Set("some-snap", "foo", "100")
  5399  	tr.Commit()
  5400  
  5401  	// set per-revision config for the upcoming rev. 2, we don't expect it restored though
  5402  	// since only revert restores revision configs.
  5403  	s.state.Set("revision-config", map[string]interface{}{
  5404  		"some-snap": map[string]interface{}{
  5405  			"2": map[string]interface{}{"foo": "200"},
  5406  		},
  5407  	})
  5408  
  5409  	// simulate a refresh to rev. 2
  5410  	chg := s.state.NewChange("update", "update some-snap")
  5411  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(2)}, s.user.ID, snapstate.Flags{})
  5412  	c.Assert(err, IsNil)
  5413  	chg.AddAll(ts)
  5414  
  5415  	s.state.Unlock()
  5416  	defer s.se.Stop()
  5417  	s.settle(c)
  5418  
  5419  	s.state.Lock()
  5420  	// config of rev. 1 has been stored in per-revision map
  5421  	var cfgs map[string]interface{}
  5422  	c.Assert(s.state.Get("revision-config", &cfgs), IsNil)
  5423  	c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{
  5424  		"1": map[string]interface{}{"foo": "100"},
  5425  		"2": map[string]interface{}{"foo": "200"},
  5426  	})
  5427  
  5428  	// config of rev. 2 hasn't been restored by refresh, old value returned
  5429  	tr = config.NewTransaction(s.state)
  5430  	var res string
  5431  	c.Assert(tr.Get("some-snap", "foo", &res), IsNil)
  5432  	c.Assert(res, Equals, "100")
  5433  }
  5434  
  5435  func (s *snapmgrTestSuite) TestRefreshFailureCausesErrorReport(c *C) {
  5436  	var errSnap, errMsg, errSig string
  5437  	var errExtra map[string]string
  5438  	var n int
  5439  	restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) {
  5440  		errSnap = aSnap
  5441  		errMsg = aErrMsg
  5442  		errSig = aDupSig
  5443  		errExtra = extra
  5444  		n += 1
  5445  		return "oopsid", nil
  5446  	})
  5447  	defer restore()
  5448  
  5449  	si := snap.SideInfo{
  5450  		RealName: "some-snap",
  5451  		SnapID:   "some-snap-id",
  5452  		Revision: snap.R(7),
  5453  	}
  5454  
  5455  	s.state.Lock()
  5456  	defer s.state.Unlock()
  5457  
  5458  	s.state.Set("ubuntu-core-transition-retry", 7)
  5459  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5460  		Active:   true,
  5461  		Sequence: []*snap.SideInfo{&si},
  5462  		Current:  si.Revision,
  5463  		SnapType: "app",
  5464  	})
  5465  
  5466  	chg := s.state.NewChange("install", "install a snap")
  5467  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  5468  	c.Assert(err, IsNil)
  5469  	chg.AddAll(ts)
  5470  
  5471  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11")
  5472  
  5473  	s.state.Unlock()
  5474  	defer s.se.Stop()
  5475  	s.settle(c)
  5476  	s.state.Lock()
  5477  
  5478  	// verify we generated a failure report
  5479  	c.Check(n, Equals, 1)
  5480  	c.Check(errSnap, Equals, "some-snap")
  5481  	c.Check(errExtra, DeepEquals, map[string]string{
  5482  		"UbuntuCoreTransitionCount": "7",
  5483  		"Channel":                   "some-channel",
  5484  		"Revision":                  "11",
  5485  	})
  5486  	c.Check(errMsg, Matches, `(?sm)change "install": "install a snap"
  5487  prerequisites: Undo
  5488   snap-setup: "some-snap" \(11\) "some-channel"
  5489  download-snap: Undoing
  5490  validate-snap: Done
  5491  .*
  5492  link-snap: Error
  5493   INFO unlink
  5494   ERROR fail
  5495  auto-connect: Hold
  5496  set-auto-aliases: Hold
  5497  setup-aliases: Hold
  5498  run-hook: Hold
  5499  start-snap-services: Hold
  5500  cleanup: Hold
  5501  run-hook: Hold`)
  5502  	c.Check(errSig, Matches, `(?sm)snap-install:
  5503  prerequisites: Undo
  5504   snap-setup: "some-snap"
  5505  download-snap: Undoing
  5506  validate-snap: Done
  5507  .*
  5508  link-snap: Error
  5509   INFO unlink
  5510   ERROR fail
  5511  auto-connect: Hold
  5512  set-auto-aliases: Hold
  5513  setup-aliases: Hold
  5514  run-hook: Hold
  5515  start-snap-services: Hold
  5516  cleanup: Hold
  5517  run-hook: Hold`)
  5518  
  5519  	// run again with empty "ubuntu-core-transition-retry"
  5520  	s.state.Set("ubuntu-core-transition-retry", 0)
  5521  	chg = s.state.NewChange("install", "install a snap")
  5522  	ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  5523  	c.Assert(err, IsNil)
  5524  	chg.AddAll(ts)
  5525  	s.state.Unlock()
  5526  	defer s.se.Stop()
  5527  	s.settle(c)
  5528  	s.state.Lock()
  5529  	// verify that we excluded this field from the bugreport
  5530  	c.Check(n, Equals, 2)
  5531  	c.Check(errExtra, DeepEquals, map[string]string{
  5532  		"Channel":  "some-channel",
  5533  		"Revision": "11",
  5534  	})
  5535  }
  5536  
  5537  func (s *snapmgrTestSuite) TestNoReRefreshInUpdate(c *C) {
  5538  	s.state.Lock()
  5539  	defer s.state.Unlock()
  5540  
  5541  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5542  		Active: true,
  5543  		Sequence: []*snap.SideInfo{
  5544  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  5545  		},
  5546  		Current:  snap.R(1),
  5547  		SnapType: "app",
  5548  	})
  5549  
  5550  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{NoReRefresh: true})
  5551  	c.Assert(err, IsNil)
  5552  
  5553  	// ensure we have no re-refresh task
  5554  	for _, t := range ts.Tasks() {
  5555  		c.Assert(t.Kind(), Not(Equals), "check-rerefresh")
  5556  	}
  5557  
  5558  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
  5559  	c.Assert(err, IsNil)
  5560  	// NoReRefresh is consumed and consulted when creating the taskset
  5561  	// but is not copied into SnapSetup
  5562  	c.Check(snapsup.Flags.NoReRefresh, Equals, false)
  5563  }
  5564  
  5565  func (s *snapmgrTestSuite) TestEmptyUpdateWithChannelChangeAndAutoAlias(c *C) {
  5566  	// this reproduces the cause behind lp:1860324,
  5567  	// namely an empty refresh with a channel change on a snap
  5568  	// with changed aliases
  5569  
  5570  	s.state.Lock()
  5571  	defer s.state.Unlock()
  5572  
  5573  	n := 0
  5574  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  5575  		if info.InstanceName() == "alias-snap" {
  5576  			if n > 0 {
  5577  				return map[string]string{
  5578  					"alias1": "cmd1",
  5579  					"alias2": "cmd2",
  5580  				}, nil
  5581  			}
  5582  			n++
  5583  		}
  5584  		return nil, nil
  5585  	}
  5586  
  5587  	snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{
  5588  		TrackingChannel: "latest/stable",
  5589  		Sequence: []*snap.SideInfo{
  5590  			{RealName: "alias-snap", Revision: snap.R(11), SnapID: "alias-snap-id"},
  5591  		},
  5592  		Current: snap.R(11),
  5593  		Active:  true,
  5594  	})
  5595  
  5596  	s.state.Set("aliases", map[string]map[string]string{
  5597  		"alias-snap": {
  5598  			"alias1": "auto",
  5599  		},
  5600  	})
  5601  
  5602  	s.state.Unlock()
  5603  	err := s.snapmgr.Ensure()
  5604  	s.state.Lock()
  5605  	c.Assert(err, IsNil)
  5606  
  5607  	ts, err := snapstate.Update(s.state, "alias-snap", &snapstate.RevisionOptions{Channel: "latest/candidate"}, s.user.ID, snapstate.Flags{})
  5608  	c.Assert(err, IsNil)
  5609  
  5610  	chg := s.state.NewChange("refresh", "refresh snap")
  5611  	chg.AddAll(ts)
  5612  
  5613  	s.state.Unlock()
  5614  	defer s.se.Stop()
  5615  	s.settle(c)
  5616  	s.state.Lock()
  5617  
  5618  	c.Assert(chg.Err(), IsNil)
  5619  	c.Assert(chg.IsReady(), Equals, true)
  5620  }
  5621  
  5622  func (s *snapmgrTestSuite) testUpdateDiskSpaceCheck(c *C, featureFlag, failInstallSize, failDiskCheck bool) error {
  5623  	restore := snapstate.MockOsutilCheckFreeSpace(func(path string, sz uint64) error {
  5624  		c.Check(sz, Equals, snapstate.SafetyMarginDiskSpace(123))
  5625  		if failDiskCheck {
  5626  			return &osutil.NotEnoughDiskSpaceError{}
  5627  		}
  5628  		return nil
  5629  	})
  5630  	defer restore()
  5631  
  5632  	var installSizeCalled bool
  5633  
  5634  	restoreInstallSize := snapstate.MockInstallSize(func(st *state.State, snaps []snapstate.MinimalInstallInfo, userID int) (uint64, error) {
  5635  		installSizeCalled = true
  5636  		if failInstallSize {
  5637  			return 0, fmt.Errorf("boom")
  5638  		}
  5639  		c.Assert(snaps, HasLen, 1)
  5640  		c.Check(snaps[0].InstanceName(), Equals, "some-snap")
  5641  		return 123, nil
  5642  	})
  5643  	defer restoreInstallSize()
  5644  
  5645  	s.state.Lock()
  5646  	defer s.state.Unlock()
  5647  
  5648  	tr := config.NewTransaction(s.state)
  5649  	tr.Set("core", "experimental.check-disk-space-refresh", featureFlag)
  5650  	tr.Commit()
  5651  
  5652  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5653  		Active: true,
  5654  		Sequence: []*snap.SideInfo{
  5655  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  5656  		},
  5657  		Current:  snap.R(4),
  5658  		SnapType: "app",
  5659  	})
  5660  
  5661  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  5662  	_, err := snapstate.Update(s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  5663  
  5664  	if featureFlag {
  5665  		c.Check(installSizeCalled, Equals, true)
  5666  	} else {
  5667  		c.Check(installSizeCalled, Equals, false)
  5668  	}
  5669  
  5670  	return err
  5671  }
  5672  
  5673  func (s *snapmgrTestSuite) TestUpdateDiskSpaceError(c *C) {
  5674  	featureFlag := true
  5675  	failInstallSize := false
  5676  	failDiskCheck := true
  5677  	err := s.testUpdateDiskSpaceCheck(c, featureFlag, failInstallSize, failDiskCheck)
  5678  	diskSpaceErr := err.(*snapstate.InsufficientSpaceError)
  5679  	c.Assert(diskSpaceErr, ErrorMatches, `insufficient space in .* to perform "refresh" change for the following snaps: some-snap`)
  5680  	c.Check(diskSpaceErr.Path, Equals, filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd"))
  5681  	c.Check(diskSpaceErr.Snaps, DeepEquals, []string{"some-snap"})
  5682  }
  5683  
  5684  func (s *snapmgrTestSuite) TestUpdateDiskCheckSkippedIfDisabled(c *C) {
  5685  	featureFlag := false
  5686  	failInstallSize := false
  5687  	failDiskCheck := true
  5688  	err := s.testUpdateDiskSpaceCheck(c, featureFlag, failInstallSize, failDiskCheck)
  5689  	c.Check(err, IsNil)
  5690  }
  5691  
  5692  func (s *snapmgrTestSuite) TestUpdateDiskCheckInstallSizeError(c *C) {
  5693  	featureFlag := true
  5694  	failInstallSize := true
  5695  	failDiskCheck := false
  5696  	err := s.testUpdateDiskSpaceCheck(c, featureFlag, failInstallSize, failDiskCheck)
  5697  	c.Check(err, ErrorMatches, "boom")
  5698  }
  5699  
  5700  func (s *snapmgrTestSuite) TestUpdateDiskCheckHappy(c *C) {
  5701  	featureFlag := true
  5702  	failInstallSize := false
  5703  	failDiskCheck := false
  5704  	err := s.testUpdateDiskSpaceCheck(c, featureFlag, failInstallSize, failDiskCheck)
  5705  	c.Check(err, IsNil)
  5706  }