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