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

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