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