github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/snapstate/snapstate_update_test.go (about)

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