github.com/ubuntu-core/snappy@v0.0.0-20210827154228-9e584df982bb/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  	restore := release.MockOnClassic(false)
  1560  	defer restore()
  1561  
  1562  	// use services-snap here to make sure services would be stopped/started appropriately
  1563  	si := snap.SideInfo{
  1564  		RealName: "kernel",
  1565  		Revision: snap.R(7),
  1566  		SnapID:   "kernel-id",
  1567  	}
  1568  	snaptest.MockSnap(c, `name: kernel`, &si)
  1569  	fi, err := os.Stat(snap.MountFile("kernel", si.Revision))
  1570  	c.Assert(err, IsNil)
  1571  	refreshedDate := fi.ModTime()
  1572  	// look at disk
  1573  	r := snapstate.MockRevisionDate(nil)
  1574  	defer r()
  1575  
  1576  	s.state.Lock()
  1577  	defer s.state.Unlock()
  1578  
  1579  	r1 := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  1580  	defer r1()
  1581  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  1582  		Active:          true,
  1583  		Sequence:        []*snap.SideInfo{&si},
  1584  		Current:         si.Revision,
  1585  		TrackingChannel: "18/stable",
  1586  	})
  1587  
  1588  	chg := s.state.NewChange("refresh", "refresh a snap")
  1589  	ts, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "edge"}, s.user.ID, snapstate.Flags{})
  1590  	c.Assert(err, IsNil)
  1591  	chg.AddAll(ts)
  1592  
  1593  	s.state.Unlock()
  1594  	defer s.se.Stop()
  1595  	s.settle(c)
  1596  	s.state.Lock()
  1597  
  1598  	c.Check(chg.Status(), Equals, state.DoneStatus)
  1599  
  1600  	c.Assert(len(s.fakeBackend.ops) > 2, Equals, true)
  1601  	c.Assert(s.fakeBackend.ops[:2], DeepEquals, fakeOps{
  1602  		{
  1603  			op: "storesvc-snap-action",
  1604  			curSnaps: []store.CurrentSnap{{
  1605  				InstanceName:    "kernel",
  1606  				SnapID:          "kernel-id",
  1607  				Revision:        snap.R(7),
  1608  				TrackingChannel: "18/stable",
  1609  				RefreshedDate:   refreshedDate,
  1610  				Epoch:           snap.E("1*"),
  1611  			}},
  1612  			userID: 1,
  1613  		}, {
  1614  			op: "storesvc-snap-action:action",
  1615  			action: store.SnapAction{
  1616  				Action:       "refresh",
  1617  				InstanceName: "kernel",
  1618  				SnapID:       "kernel-id",
  1619  				Channel:      "18/edge",
  1620  				Flags:        store.SnapActionEnforceValidation,
  1621  			},
  1622  			revno:  snap.R(11),
  1623  			userID: 1,
  1624  		},
  1625  	})
  1626  
  1627  	// check progress
  1628  	task := ts.Tasks()[1]
  1629  	_, cur, total := task.Progress()
  1630  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  1631  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  1632  
  1633  	// verify snapSetup info
  1634  	var snapsup snapstate.SnapSetup
  1635  	err = task.Get("snap-setup", &snapsup)
  1636  	c.Assert(err, IsNil)
  1637  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1638  		Channel: "18/edge",
  1639  		UserID:  s.user.ID,
  1640  
  1641  		SnapPath: filepath.Join(dirs.SnapBlobDir, "kernel_11.snap"),
  1642  		DownloadInfo: &snap.DownloadInfo{
  1643  			DownloadURL: "https://some-server.com/some/path.snap",
  1644  		},
  1645  		SideInfo:  snapsup.SideInfo,
  1646  		Type:      snap.TypeKernel,
  1647  		PlugsOnly: true,
  1648  	})
  1649  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1650  		RealName: "kernel",
  1651  		Revision: snap.R(11),
  1652  		Channel:  "18/edge",
  1653  		SnapID:   "kernel-id",
  1654  	})
  1655  
  1656  	// verify snaps in the system state
  1657  	var snapst snapstate.SnapState
  1658  	err = snapstate.Get(s.state, "kernel", &snapst)
  1659  	c.Assert(err, IsNil)
  1660  
  1661  	c.Assert(snapst.Active, Equals, true)
  1662  	c.Assert(snapst.TrackingChannel, Equals, "18/edge")
  1663  	c.Assert(snapst.Sequence, HasLen, 2)
  1664  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1665  		RealName: "kernel",
  1666  		SnapID:   "kernel-id",
  1667  		Channel:  "",
  1668  		Revision: snap.R(7),
  1669  	})
  1670  	c.Assert(snapst.Sequence[1], DeepEquals, &snap.SideInfo{
  1671  		RealName: "kernel",
  1672  		Channel:  "18/edge",
  1673  		SnapID:   "kernel-id",
  1674  		Revision: snap.R(11),
  1675  	})
  1676  }
  1677  
  1678  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsNoUserRunThrough(c *C) {
  1679  	s.state.Lock()
  1680  	defer s.state.Unlock()
  1681  
  1682  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1683  		Active: true,
  1684  		Sequence: []*snap.SideInfo{
  1685  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  1686  		},
  1687  		Current:  snap.R(1),
  1688  		SnapType: "os",
  1689  	})
  1690  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1691  		Active: true,
  1692  		Sequence: []*snap.SideInfo{
  1693  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  1694  		},
  1695  		Current:  snap.R(5),
  1696  		SnapType: "app",
  1697  		UserID:   1,
  1698  	})
  1699  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1700  		Active: true,
  1701  		Sequence: []*snap.SideInfo{
  1702  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  1703  		},
  1704  		Current:  snap.R(2),
  1705  		SnapType: "app",
  1706  		UserID:   2,
  1707  	})
  1708  
  1709  	chg := s.state.NewChange("refresh", "refresh all snaps")
  1710  	// no user is passed to use for UpdateMany
  1711  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1712  	c.Assert(err, IsNil)
  1713  	for _, ts := range tts {
  1714  		chg.AddAll(ts)
  1715  	}
  1716  	c.Check(updated, HasLen, 3)
  1717  
  1718  	s.state.Unlock()
  1719  	defer s.se.Stop()
  1720  	s.settle(c)
  1721  	s.state.Lock()
  1722  
  1723  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1724  	c.Assert(chg.Err(), IsNil)
  1725  
  1726  	macaroonMap := map[string]string{
  1727  		"core":          "",
  1728  		"some-snap":     "macaroon",
  1729  		"services-snap": "macaroon2",
  1730  	}
  1731  
  1732  	seen := make(map[string]int)
  1733  	ir := 0
  1734  	di := 0
  1735  	for _, op := range s.fakeBackend.ops {
  1736  		switch op.op {
  1737  		case "storesvc-snap-action":
  1738  			ir++
  1739  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  1740  				{
  1741  					InstanceName:  "core",
  1742  					SnapID:        "core-snap-id",
  1743  					Revision:      snap.R(1),
  1744  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  1745  					Epoch:         snap.E("1*"),
  1746  				},
  1747  				{
  1748  					InstanceName:  "services-snap",
  1749  					SnapID:        "services-snap-id",
  1750  					Revision:      snap.R(2),
  1751  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  1752  					Epoch:         snap.E("0"),
  1753  				},
  1754  				{
  1755  					InstanceName:  "some-snap",
  1756  					SnapID:        "some-snap-id",
  1757  					Revision:      snap.R(5),
  1758  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  1759  					Epoch:         snap.E("1*"),
  1760  				},
  1761  			})
  1762  		case "storesvc-snap-action:action":
  1763  			snapID := op.action.SnapID
  1764  			seen[snapID] = op.userID
  1765  		case "storesvc-download":
  1766  			snapName := op.name
  1767  			fakeDl := s.fakeStore.downloads[di]
  1768  			// check target path separately and clear it
  1769  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  1770  			fakeDl.target = ""
  1771  			c.Check(fakeDl, DeepEquals, fakeDownload{
  1772  				macaroon: macaroonMap[snapName],
  1773  				name:     snapName,
  1774  			}, Commentf(snapName))
  1775  			di++
  1776  		}
  1777  	}
  1778  	c.Check(ir, Equals, 2)
  1779  	// we check all snaps with each user
  1780  	c.Check(seen["some-snap-id"], Equals, 1)
  1781  	c.Check(seen["services-snap-id"], Equals, 2)
  1782  	// coalesced with one of the others
  1783  	c.Check(seen["core-snap-id"] > 0, Equals, true)
  1784  }
  1785  
  1786  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsUserRunThrough(c *C) {
  1787  	s.state.Lock()
  1788  	defer s.state.Unlock()
  1789  
  1790  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1791  		Active: true,
  1792  		Sequence: []*snap.SideInfo{
  1793  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  1794  		},
  1795  		Current:  snap.R(1),
  1796  		SnapType: "os",
  1797  	})
  1798  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1799  		Active: true,
  1800  		Sequence: []*snap.SideInfo{
  1801  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  1802  		},
  1803  		Current:  snap.R(5),
  1804  		SnapType: "app",
  1805  		UserID:   1,
  1806  	})
  1807  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1808  		Active: true,
  1809  		Sequence: []*snap.SideInfo{
  1810  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  1811  		},
  1812  		Current:  snap.R(2),
  1813  		SnapType: "app",
  1814  		UserID:   2,
  1815  	})
  1816  
  1817  	chg := s.state.NewChange("refresh", "refresh all snaps")
  1818  	// do UpdateMany using user 2 as fallback
  1819  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 2, nil)
  1820  	c.Assert(err, IsNil)
  1821  	for _, ts := range tts {
  1822  		chg.AddAll(ts)
  1823  	}
  1824  	c.Check(updated, HasLen, 3)
  1825  
  1826  	s.state.Unlock()
  1827  	defer s.se.Stop()
  1828  	s.settle(c)
  1829  	s.state.Lock()
  1830  
  1831  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1832  	c.Assert(chg.Err(), IsNil)
  1833  
  1834  	macaroonMap := map[string]string{
  1835  		"core":          "macaroon2",
  1836  		"some-snap":     "macaroon",
  1837  		"services-snap": "macaroon2",
  1838  	}
  1839  
  1840  	type snapIDuserID struct {
  1841  		snapID string
  1842  		userID int
  1843  	}
  1844  	seen := make(map[snapIDuserID]bool)
  1845  	ir := 0
  1846  	di := 0
  1847  	for _, op := range s.fakeBackend.ops {
  1848  		switch op.op {
  1849  		case "storesvc-snap-action":
  1850  			ir++
  1851  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  1852  				{
  1853  					InstanceName:  "core",
  1854  					SnapID:        "core-snap-id",
  1855  					Revision:      snap.R(1),
  1856  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  1857  					Epoch:         snap.E("1*"),
  1858  				},
  1859  				{
  1860  					InstanceName:  "services-snap",
  1861  					SnapID:        "services-snap-id",
  1862  					Revision:      snap.R(2),
  1863  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  1864  					Epoch:         snap.E("0"),
  1865  				},
  1866  				{
  1867  					InstanceName:  "some-snap",
  1868  					SnapID:        "some-snap-id",
  1869  					Revision:      snap.R(5),
  1870  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  1871  					Epoch:         snap.E("1*"),
  1872  				},
  1873  			})
  1874  		case "storesvc-snap-action:action":
  1875  			snapID := op.action.SnapID
  1876  			seen[snapIDuserID{snapID: snapID, userID: op.userID}] = true
  1877  		case "storesvc-download":
  1878  			snapName := op.name
  1879  			fakeDl := s.fakeStore.downloads[di]
  1880  			// check target path separately and clear it
  1881  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  1882  			fakeDl.target = ""
  1883  			c.Check(fakeDl, DeepEquals, fakeDownload{
  1884  				macaroon: macaroonMap[snapName],
  1885  				name:     snapName,
  1886  			}, Commentf(snapName))
  1887  			di++
  1888  		}
  1889  	}
  1890  	c.Check(ir, Equals, 2)
  1891  	// we check all snaps with each user
  1892  	c.Check(seen, DeepEquals, map[snapIDuserID]bool{
  1893  		{snapID: "core-snap-id", userID: 2}:     true,
  1894  		{snapID: "some-snap-id", userID: 1}:     true,
  1895  		{snapID: "services-snap-id", userID: 2}: true,
  1896  	})
  1897  
  1898  	var coreState, snapState snapstate.SnapState
  1899  	// user in SnapState was preserved
  1900  	err = snapstate.Get(s.state, "some-snap", &snapState)
  1901  	c.Assert(err, IsNil)
  1902  	c.Check(snapState.UserID, Equals, 1)
  1903  	c.Check(snapState.Current, DeepEquals, snap.R(11))
  1904  
  1905  	// user in SnapState was set
  1906  	err = snapstate.Get(s.state, "core", &coreState)
  1907  	c.Assert(err, IsNil)
  1908  	c.Check(coreState.UserID, Equals, 2)
  1909  	c.Check(coreState.Current, DeepEquals, snap.R(11))
  1910  
  1911  }
  1912  
  1913  func (s *snapmgrTestSuite) TestUpdateManyMultipleCredsUserWithNoStoreAuthRunThrough(c *C) {
  1914  	s.state.Lock()
  1915  	defer s.state.Unlock()
  1916  
  1917  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  1918  		Active: true,
  1919  		Sequence: []*snap.SideInfo{
  1920  			{RealName: "core", Revision: snap.R(1), SnapID: "core-snap-id"},
  1921  		},
  1922  		Current:  snap.R(1),
  1923  		SnapType: "os",
  1924  	})
  1925  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  1926  		Active: true,
  1927  		Sequence: []*snap.SideInfo{
  1928  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  1929  		},
  1930  		Current:  snap.R(5),
  1931  		SnapType: "app",
  1932  		UserID:   1,
  1933  	})
  1934  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  1935  		Active: true,
  1936  		Sequence: []*snap.SideInfo{
  1937  			{RealName: "services-snap", Revision: snap.R(2), SnapID: "services-snap-id"},
  1938  		},
  1939  		Current:  snap.R(2),
  1940  		SnapType: "app",
  1941  		UserID:   3,
  1942  	})
  1943  
  1944  	chg := s.state.NewChange("refresh", "refresh all snaps")
  1945  	// no user is passed to use for UpdateMany
  1946  	updated, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  1947  	c.Assert(err, IsNil)
  1948  	for _, ts := range tts {
  1949  		chg.AddAll(ts)
  1950  	}
  1951  	c.Check(updated, HasLen, 3)
  1952  
  1953  	s.state.Unlock()
  1954  	defer s.se.Stop()
  1955  	s.settle(c)
  1956  	s.state.Lock()
  1957  
  1958  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1959  	c.Assert(chg.Err(), IsNil)
  1960  
  1961  	macaroonMap := map[string]string{
  1962  		"core":          "",
  1963  		"some-snap":     "macaroon",
  1964  		"services-snap": "",
  1965  	}
  1966  
  1967  	seen := make(map[string]int)
  1968  	ir := 0
  1969  	di := 0
  1970  	for _, op := range s.fakeBackend.ops {
  1971  		switch op.op {
  1972  		case "storesvc-snap-action":
  1973  			ir++
  1974  			c.Check(op.curSnaps, DeepEquals, []store.CurrentSnap{
  1975  				{
  1976  					InstanceName:  "core",
  1977  					SnapID:        "core-snap-id",
  1978  					Revision:      snap.R(1),
  1979  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 1),
  1980  					Epoch:         snap.E("1*"),
  1981  				},
  1982  				{
  1983  					InstanceName:  "services-snap",
  1984  					SnapID:        "services-snap-id",
  1985  					Revision:      snap.R(2),
  1986  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 2),
  1987  					Epoch:         snap.E("0"),
  1988  				},
  1989  				{
  1990  					InstanceName:  "some-snap",
  1991  					SnapID:        "some-snap-id",
  1992  					Revision:      snap.R(5),
  1993  					RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 5),
  1994  					Epoch:         snap.E("1*"),
  1995  				},
  1996  			})
  1997  		case "storesvc-snap-action:action":
  1998  			snapID := op.action.SnapID
  1999  			if _, ok := seen[snapID]; !ok {
  2000  				seen[snapID] = op.userID
  2001  			}
  2002  		case "storesvc-download":
  2003  			snapName := op.name
  2004  			fakeDl := s.fakeStore.downloads[di]
  2005  			// check target path separately and clear it
  2006  			c.Check(fakeDl.target, Matches, filepath.Join(dirs.SnapBlobDir, fmt.Sprintf("%s_[0-9]+.snap", snapName)))
  2007  			fakeDl.target = ""
  2008  			c.Check(fakeDl, DeepEquals, fakeDownload{
  2009  				macaroon: macaroonMap[snapName],
  2010  				name:     snapName,
  2011  			}, Commentf(snapName))
  2012  			di++
  2013  		}
  2014  	}
  2015  	c.Check(ir, Equals, 1)
  2016  	// we check all snaps with each user
  2017  	c.Check(seen["some-snap-id"], Equals, 1)
  2018  	// coalesced with request for 1
  2019  	c.Check(seen["services-snap-id"], Equals, 1)
  2020  	c.Check(seen["core-snap-id"], Equals, 1)
  2021  }
  2022  
  2023  func (s *snapmgrTestSuite) TestUpdateUndoRunThrough(c *C) {
  2024  	si := snap.SideInfo{
  2025  		RealName: "some-snap",
  2026  		SnapID:   "some-snap-id",
  2027  		Revision: snap.R(7),
  2028  	}
  2029  
  2030  	s.state.Lock()
  2031  	defer s.state.Unlock()
  2032  
  2033  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2034  		Active:   true,
  2035  		Sequence: []*snap.SideInfo{&si},
  2036  		Current:  si.Revision,
  2037  		SnapType: "app",
  2038  	})
  2039  
  2040  	chg := s.state.NewChange("install", "install a snap")
  2041  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2042  	c.Assert(err, IsNil)
  2043  	chg.AddAll(ts)
  2044  
  2045  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "/some-snap/11")
  2046  
  2047  	s.state.Unlock()
  2048  	defer s.se.Stop()
  2049  	s.settle(c)
  2050  	s.state.Lock()
  2051  
  2052  	expected := fakeOps{
  2053  		{
  2054  			op: "storesvc-snap-action",
  2055  			curSnaps: []store.CurrentSnap{{
  2056  				InstanceName:  "some-snap",
  2057  				SnapID:        "some-snap-id",
  2058  				Revision:      snap.R(7),
  2059  				RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  2060  				Epoch:         snap.E("1*"),
  2061  			}},
  2062  			userID: 1,
  2063  		},
  2064  		{
  2065  			op: "storesvc-snap-action:action",
  2066  			action: store.SnapAction{
  2067  				Action:       "refresh",
  2068  				InstanceName: "some-snap",
  2069  				SnapID:       "some-snap-id",
  2070  				Channel:      "some-channel",
  2071  				Flags:        store.SnapActionEnforceValidation,
  2072  			},
  2073  			revno:  snap.R(11),
  2074  			userID: 1,
  2075  		},
  2076  		{
  2077  			op:   "storesvc-download",
  2078  			name: "some-snap",
  2079  		},
  2080  		{
  2081  			op:    "validate-snap:Doing",
  2082  			name:  "some-snap",
  2083  			revno: snap.R(11),
  2084  		},
  2085  		{
  2086  			op:  "current",
  2087  			old: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2088  		},
  2089  		{
  2090  			op:   "open-snap-file",
  2091  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2092  			sinfo: snap.SideInfo{
  2093  				RealName: "some-snap",
  2094  				SnapID:   "some-snap-id",
  2095  				Channel:  "some-channel",
  2096  				Revision: snap.R(11),
  2097  			},
  2098  		},
  2099  		{
  2100  			op:    "setup-snap",
  2101  			name:  "some-snap",
  2102  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2103  			revno: snap.R(11),
  2104  		},
  2105  		{
  2106  			op:   "remove-snap-aliases",
  2107  			name: "some-snap",
  2108  		},
  2109  		{
  2110  			op:   "unlink-snap",
  2111  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2112  		},
  2113  		{
  2114  			op:   "copy-data",
  2115  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2116  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2117  		},
  2118  		{
  2119  			op:    "setup-profiles:Doing",
  2120  			name:  "some-snap",
  2121  			revno: snap.R(11),
  2122  		},
  2123  		{
  2124  			op: "candidate",
  2125  			sinfo: snap.SideInfo{
  2126  				RealName: "some-snap",
  2127  				SnapID:   "some-snap-id",
  2128  				Channel:  "some-channel",
  2129  				Revision: snap.R(11),
  2130  			},
  2131  		},
  2132  		{
  2133  			op:   "link-snap.failed",
  2134  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2135  		},
  2136  		{
  2137  			op:   "unlink-snap",
  2138  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2139  		},
  2140  		{
  2141  			op:    "setup-profiles:Undoing",
  2142  			name:  "some-snap",
  2143  			revno: snap.R(11),
  2144  		},
  2145  		{
  2146  			op:   "undo-copy-snap-data",
  2147  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2148  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2149  		},
  2150  		{
  2151  			op:   "link-snap",
  2152  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2153  		},
  2154  		{
  2155  			op: "update-aliases",
  2156  		},
  2157  		{
  2158  			op:    "undo-setup-snap",
  2159  			name:  "some-snap",
  2160  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2161  			stype: "app",
  2162  		},
  2163  		{
  2164  			op:   "remove-snap-dir",
  2165  			name: "some-snap",
  2166  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  2167  		},
  2168  	}
  2169  
  2170  	// ensure all our tasks ran
  2171  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  2172  		macaroon: s.user.StoreMacaroon,
  2173  		name:     "some-snap",
  2174  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2175  	}})
  2176  	// start with an easier-to-read error if this fails:
  2177  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2178  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2179  
  2180  	// verify snaps in the system state
  2181  	var snapst snapstate.SnapState
  2182  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2183  	c.Assert(err, IsNil)
  2184  
  2185  	c.Assert(snapst.Active, Equals, true)
  2186  	c.Assert(snapst.Sequence, HasLen, 1)
  2187  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2188  		RealName: "some-snap",
  2189  		SnapID:   "some-snap-id",
  2190  		Channel:  "",
  2191  		Revision: snap.R(7),
  2192  	})
  2193  }
  2194  
  2195  func lastWithLane(tasks []*state.Task) *state.Task {
  2196  	for i := len(tasks) - 1; i >= 0; i-- {
  2197  		if lanes := tasks[i].Lanes(); len(lanes) == 1 && lanes[0] != 0 {
  2198  			return tasks[i]
  2199  		}
  2200  	}
  2201  	return nil
  2202  }
  2203  
  2204  func (s *snapmgrTestSuite) TestUpdateUndoRestoresRevisionConfig(c *C) {
  2205  	var errorTaskExecuted bool
  2206  
  2207  	// overwrite error-trigger task handler with custom one for this test
  2208  	erroringHandler := func(task *state.Task, _ *tomb.Tomb) error {
  2209  		st := task.State()
  2210  		st.Lock()
  2211  		defer st.Unlock()
  2212  
  2213  		// modify current config of some-snap
  2214  		tr := config.NewTransaction(st)
  2215  		tr.Set("some-snap", "foo", "canary")
  2216  		tr.Commit()
  2217  
  2218  		errorTaskExecuted = true
  2219  		return errors.New("error out")
  2220  	}
  2221  	s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil)
  2222  
  2223  	si := snap.SideInfo{
  2224  		RealName: "some-snap",
  2225  		SnapID:   "some-snap-id",
  2226  		Revision: snap.R(7),
  2227  	}
  2228  	si2 := snap.SideInfo{
  2229  		RealName: "some-snap",
  2230  		SnapID:   "some-snap-id",
  2231  		Revision: snap.R(6),
  2232  	}
  2233  
  2234  	s.state.Lock()
  2235  	defer s.state.Unlock()
  2236  
  2237  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2238  		Active:          true,
  2239  		Sequence:        []*snap.SideInfo{&si2, &si},
  2240  		TrackingChannel: "latest/stable",
  2241  		Current:         si.Revision,
  2242  		SnapType:        "app",
  2243  	})
  2244  
  2245  	// set some configuration
  2246  	tr := config.NewTransaction(s.state)
  2247  	tr.Set("some-snap", "foo", "revision 7 value")
  2248  	tr.Commit()
  2249  	config.SaveRevisionConfig(s.state, "some-snap", snap.R(7))
  2250  
  2251  	chg := s.state.NewChange("install", "install a snap")
  2252  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2253  	c.Assert(err, IsNil)
  2254  	chg.AddAll(ts)
  2255  
  2256  	last := lastWithLane(ts.Tasks())
  2257  	c.Assert(last, NotNil)
  2258  
  2259  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  2260  	terr.WaitFor(last)
  2261  	terr.JoinLane(last.Lanes()[0])
  2262  	chg.AddTask(terr)
  2263  
  2264  	s.state.Unlock()
  2265  	defer s.se.Stop()
  2266  	s.settle(c)
  2267  	s.state.Lock()
  2268  
  2269  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  2270  	c.Check(errorTaskExecuted, Equals, true)
  2271  
  2272  	// after undoing the update some-snap config should be restored to that of rev.7
  2273  	var val string
  2274  	tr = config.NewTransaction(s.state)
  2275  	c.Assert(tr.Get("some-snap", "foo", &val), IsNil)
  2276  	c.Check(val, Equals, "revision 7 value")
  2277  }
  2278  
  2279  func (s *snapmgrTestSuite) TestUpdateMakesConfigSnapshot(c *C) {
  2280  	s.state.Lock()
  2281  	defer s.state.Unlock()
  2282  
  2283  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2284  		Active: true,
  2285  		Sequence: []*snap.SideInfo{
  2286  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  2287  		},
  2288  		Current:  snap.R(1),
  2289  		SnapType: "app",
  2290  	})
  2291  
  2292  	tr := config.NewTransaction(s.state)
  2293  	tr.Set("some-snap", "foo", "bar")
  2294  	tr.Commit()
  2295  
  2296  	var cfgs map[string]interface{}
  2297  	// we don't have config snapshots yet
  2298  	c.Assert(s.state.Get("revision-config", &cfgs), Equals, state.ErrNoState)
  2299  
  2300  	chg := s.state.NewChange("update", "update a snap")
  2301  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(2)}
  2302  	ts, err := snapstate.Update(s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2303  	c.Assert(err, IsNil)
  2304  	chg.AddAll(ts)
  2305  
  2306  	s.state.Unlock()
  2307  	defer s.se.Stop()
  2308  	s.settle(c)
  2309  
  2310  	s.state.Lock()
  2311  	cfgs = nil
  2312  	// config copy of rev. 1 has been made
  2313  	c.Assert(s.state.Get("revision-config", &cfgs), IsNil)
  2314  	c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{
  2315  		"1": map[string]interface{}{
  2316  			"foo": "bar",
  2317  		},
  2318  	})
  2319  }
  2320  
  2321  func (s *snapmgrTestSuite) TestUpdateTotalUndoRunThrough(c *C) {
  2322  	si := snap.SideInfo{
  2323  		RealName: "some-snap",
  2324  		SnapID:   "some-snap-id",
  2325  		Revision: snap.R(7),
  2326  	}
  2327  
  2328  	s.state.Lock()
  2329  	defer s.state.Unlock()
  2330  
  2331  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2332  		Active:          true,
  2333  		Sequence:        []*snap.SideInfo{&si},
  2334  		TrackingChannel: "latest/stable",
  2335  		Current:         si.Revision,
  2336  		SnapType:        "app",
  2337  	})
  2338  
  2339  	chg := s.state.NewChange("install", "install a snap")
  2340  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  2341  	c.Assert(err, IsNil)
  2342  	chg.AddAll(ts)
  2343  
  2344  	// We need to make it not be rerefresh, and we could do just
  2345  	// that but instead we do the 'right' thing and attach it to
  2346  	// the last task that's on a lane.
  2347  	last := lastWithLane(ts.Tasks())
  2348  	c.Assert(last, NotNil)
  2349  
  2350  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  2351  	terr.WaitFor(last)
  2352  	terr.JoinLane(last.Lanes()[0])
  2353  	chg.AddTask(terr)
  2354  
  2355  	s.state.Unlock()
  2356  	defer s.se.Stop()
  2357  	s.settle(c)
  2358  	s.state.Lock()
  2359  
  2360  	expected := fakeOps{
  2361  		{
  2362  			op: "storesvc-snap-action",
  2363  			curSnaps: []store.CurrentSnap{{
  2364  				InstanceName:    "some-snap",
  2365  				SnapID:          "some-snap-id",
  2366  				Revision:        snap.R(7),
  2367  				TrackingChannel: "latest/stable",
  2368  				RefreshedDate:   fakeRevDateEpoch.AddDate(0, 0, 7),
  2369  				Epoch:           snap.E("1*"),
  2370  			}},
  2371  			userID: 1,
  2372  		},
  2373  		{
  2374  			op: "storesvc-snap-action:action",
  2375  			action: store.SnapAction{
  2376  				Action:       "refresh",
  2377  				InstanceName: "some-snap",
  2378  				SnapID:       "some-snap-id",
  2379  				Channel:      "some-channel",
  2380  				Flags:        store.SnapActionEnforceValidation,
  2381  			},
  2382  			revno:  snap.R(11),
  2383  			userID: 1,
  2384  		},
  2385  		{
  2386  			op:   "storesvc-download",
  2387  			name: "some-snap",
  2388  		},
  2389  		{
  2390  			op:    "validate-snap:Doing",
  2391  			name:  "some-snap",
  2392  			revno: snap.R(11),
  2393  		},
  2394  		{
  2395  			op:  "current",
  2396  			old: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2397  		},
  2398  		{
  2399  			op:   "open-snap-file",
  2400  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2401  			sinfo: snap.SideInfo{
  2402  				RealName: "some-snap",
  2403  				SnapID:   "some-snap-id",
  2404  				Channel:  "some-channel",
  2405  				Revision: snap.R(11),
  2406  			},
  2407  		},
  2408  		{
  2409  			op:    "setup-snap",
  2410  			name:  "some-snap",
  2411  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2412  			revno: snap.R(11),
  2413  		},
  2414  		{
  2415  			op:   "remove-snap-aliases",
  2416  			name: "some-snap",
  2417  		},
  2418  		{
  2419  			op:   "unlink-snap",
  2420  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2421  		},
  2422  		{
  2423  			op:   "copy-data",
  2424  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2425  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2426  		},
  2427  		{
  2428  			op:    "setup-profiles:Doing",
  2429  			name:  "some-snap",
  2430  			revno: snap.R(11),
  2431  		},
  2432  		{
  2433  			op: "candidate",
  2434  			sinfo: snap.SideInfo{
  2435  				RealName: "some-snap",
  2436  				SnapID:   "some-snap-id",
  2437  				Channel:  "some-channel",
  2438  				Revision: snap.R(11),
  2439  			},
  2440  		},
  2441  		{
  2442  			op:   "link-snap",
  2443  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2444  		},
  2445  		{
  2446  			op:    "auto-connect:Doing",
  2447  			name:  "some-snap",
  2448  			revno: snap.R(11),
  2449  		},
  2450  		{
  2451  			op: "update-aliases",
  2452  		},
  2453  		// undoing everything from here down...
  2454  		{
  2455  			op:   "remove-snap-aliases",
  2456  			name: "some-snap",
  2457  		},
  2458  		{
  2459  			op:   "unlink-snap",
  2460  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2461  		},
  2462  		{
  2463  			op:    "setup-profiles:Undoing",
  2464  			name:  "some-snap",
  2465  			revno: snap.R(11),
  2466  		},
  2467  		{
  2468  			op:   "undo-copy-snap-data",
  2469  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2470  			old:  filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2471  		},
  2472  		{
  2473  			op:   "link-snap",
  2474  			path: filepath.Join(dirs.SnapMountDir, "some-snap/7"),
  2475  		},
  2476  		{
  2477  			op: "update-aliases",
  2478  		},
  2479  		{
  2480  			op:    "undo-setup-snap",
  2481  			name:  "some-snap",
  2482  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  2483  			stype: "app",
  2484  		},
  2485  		{
  2486  			op:   "remove-snap-dir",
  2487  			name: "some-snap",
  2488  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  2489  		},
  2490  	}
  2491  
  2492  	// ensure all our tasks ran
  2493  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  2494  		macaroon: s.user.StoreMacaroon,
  2495  		name:     "some-snap",
  2496  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2497  	}})
  2498  	// friendlier failure first
  2499  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2500  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2501  
  2502  	// verify snaps in the system state
  2503  	var snapst snapstate.SnapState
  2504  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2505  	c.Assert(err, IsNil)
  2506  
  2507  	c.Assert(snapst.Active, Equals, true)
  2508  	c.Assert(snapst.TrackingChannel, Equals, "latest/stable")
  2509  	c.Assert(snapst.Sequence, HasLen, 1)
  2510  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2511  		RealName: "some-snap",
  2512  		SnapID:   "some-snap-id",
  2513  		Channel:  "",
  2514  		Revision: snap.R(7),
  2515  	})
  2516  }
  2517  
  2518  func (s *snapmgrTestSuite) TestUpdateSameRevision(c *C) {
  2519  	si := snap.SideInfo{
  2520  		RealName: "some-snap",
  2521  		SnapID:   "some-snap-id",
  2522  		Revision: snap.R(7),
  2523  	}
  2524  
  2525  	s.state.Lock()
  2526  	defer s.state.Unlock()
  2527  
  2528  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2529  		Active:          true,
  2530  		Sequence:        []*snap.SideInfo{&si},
  2531  		TrackingChannel: "channel-for-7/stable",
  2532  		Current:         si.Revision,
  2533  	})
  2534  
  2535  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7/stable"}, s.user.ID, snapstate.Flags{})
  2536  	c.Assert(err, Equals, store.ErrNoUpdateAvailable)
  2537  }
  2538  
  2539  func (s *snapmgrTestSuite) TestUpdateToRevisionRememberedUserRunThrough(c *C) {
  2540  	s.state.Lock()
  2541  	defer s.state.Unlock()
  2542  
  2543  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2544  		Active: true,
  2545  		Sequence: []*snap.SideInfo{
  2546  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  2547  		},
  2548  		Current:  snap.R(5),
  2549  		SnapType: "app",
  2550  		UserID:   1,
  2551  	})
  2552  
  2553  	chg := s.state.NewChange("refresh", "refresh a snap")
  2554  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(11)}, 0, snapstate.Flags{})
  2555  	c.Assert(err, IsNil)
  2556  	chg.AddAll(ts)
  2557  
  2558  	s.state.Unlock()
  2559  	defer s.se.Stop()
  2560  	s.settle(c)
  2561  	s.state.Lock()
  2562  
  2563  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  2564  	c.Assert(chg.Err(), IsNil)
  2565  
  2566  	for _, op := range s.fakeBackend.ops {
  2567  		switch op.op {
  2568  		case "storesvc-snap-action:action":
  2569  			c.Check(op.userID, Equals, 1)
  2570  		case "storesvc-download":
  2571  			snapName := op.name
  2572  			c.Check(s.fakeStore.downloads[0], DeepEquals, fakeDownload{
  2573  				macaroon: "macaroon",
  2574  				name:     "some-snap",
  2575  				target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  2576  			}, Commentf(snapName))
  2577  		}
  2578  	}
  2579  }
  2580  
  2581  // A noResultsStore returns no results for install/refresh requests
  2582  type noResultsStore struct {
  2583  	*fakeStore
  2584  }
  2585  
  2586  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) {
  2587  	if assertQuery != nil {
  2588  		panic("no assertion query support")
  2589  	}
  2590  	return nil, nil, &store.SnapActionError{NoResults: true}
  2591  }
  2592  
  2593  func (s *snapmgrTestSuite) TestUpdateNoStoreResults(c *C) {
  2594  	s.state.Lock()
  2595  	defer s.state.Unlock()
  2596  
  2597  	snapstate.ReplaceStore(s.state, noResultsStore{fakeStore: s.fakeStore})
  2598  
  2599  	// this is an atypical case in which the store didn't return
  2600  	// an error nor a result, we are defensive and return
  2601  	// a reasonable error
  2602  	si := snap.SideInfo{
  2603  		RealName: "some-snap",
  2604  		SnapID:   "some-snap-id",
  2605  		Revision: snap.R(7),
  2606  	}
  2607  
  2608  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2609  		Active:          true,
  2610  		Sequence:        []*snap.SideInfo{&si},
  2611  		TrackingChannel: "channel-for-7/stable",
  2612  		Current:         si.Revision,
  2613  	})
  2614  
  2615  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  2616  	c.Assert(err, Equals, snapstate.ErrMissingExpectedResult)
  2617  }
  2618  
  2619  func (s *snapmgrTestSuite) TestUpdateNoStoreResultsWithChannelChange(c *C) {
  2620  	s.state.Lock()
  2621  	defer s.state.Unlock()
  2622  
  2623  	snapstate.ReplaceStore(s.state, noResultsStore{fakeStore: s.fakeStore})
  2624  
  2625  	// this is an atypical case in which the store didn't return
  2626  	// an error nor a result, we are defensive and return
  2627  	// a reasonable error
  2628  	si := snap.SideInfo{
  2629  		RealName: "some-snap",
  2630  		SnapID:   "some-snap-id",
  2631  		Revision: snap.R(7),
  2632  	}
  2633  
  2634  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2635  		Active:          true,
  2636  		Sequence:        []*snap.SideInfo{&si},
  2637  		TrackingChannel: "channel-for-9/stable",
  2638  		Current:         si.Revision,
  2639  	})
  2640  
  2641  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  2642  	c.Assert(err, Equals, snapstate.ErrMissingExpectedResult)
  2643  }
  2644  
  2645  func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchesChannel(c *C) {
  2646  	si := snap.SideInfo{
  2647  		RealName: "some-snap",
  2648  		SnapID:   "some-snap-id",
  2649  		Revision: snap.R(7),
  2650  	}
  2651  
  2652  	s.state.Lock()
  2653  	defer s.state.Unlock()
  2654  
  2655  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2656  		Active:          true,
  2657  		Sequence:        []*snap.SideInfo{&si},
  2658  		TrackingChannel: "other-chanenl/stable",
  2659  		Current:         si.Revision,
  2660  	})
  2661  
  2662  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7/stable"}, s.user.ID, snapstate.Flags{})
  2663  	c.Assert(err, IsNil)
  2664  	c.Check(ts.Tasks(), HasLen, 1)
  2665  	c.Check(ts.Tasks()[0].Kind(), Equals, "switch-snap-channel")
  2666  }
  2667  
  2668  func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchesChannelConflict(c *C) {
  2669  	si := snap.SideInfo{
  2670  		RealName: "some-snap",
  2671  		SnapID:   "some-snap-id",
  2672  		Revision: snap.R(7),
  2673  	}
  2674  
  2675  	s.state.Lock()
  2676  	defer s.state.Unlock()
  2677  
  2678  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2679  		Active:          true,
  2680  		Sequence:        []*snap.SideInfo{&si},
  2681  		TrackingChannel: "other-channel/stable",
  2682  		Current:         si.Revision,
  2683  	})
  2684  
  2685  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  2686  	c.Assert(err, IsNil)
  2687  	// make it visible
  2688  	s.state.NewChange("refresh", "refresh a snap").AddAll(ts)
  2689  
  2690  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  2691  	c.Check(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`)
  2692  }
  2693  
  2694  func (s *snapmgrTestSuite) TestUpdateSameRevisionSwitchChannelRunThrough(c *C) {
  2695  	si := snap.SideInfo{
  2696  		RealName: "some-snap",
  2697  		SnapID:   "some-snap-id",
  2698  		Channel:  "other-channel",
  2699  		Revision: snap.R(7),
  2700  	}
  2701  
  2702  	s.state.Lock()
  2703  	defer s.state.Unlock()
  2704  
  2705  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2706  		Active:          true,
  2707  		Sequence:        []*snap.SideInfo{&si},
  2708  		TrackingChannel: "other-channel/stable",
  2709  		Current:         si.Revision,
  2710  	})
  2711  
  2712  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7/stable"}, s.user.ID, snapstate.Flags{})
  2713  	c.Assert(err, IsNil)
  2714  	chg := s.state.NewChange("refresh", "refresh a snap")
  2715  	chg.AddAll(ts)
  2716  
  2717  	s.state.Unlock()
  2718  	defer s.se.Stop()
  2719  	s.settle(c)
  2720  	s.state.Lock()
  2721  
  2722  	expected := fakeOps{
  2723  		// we just expect the "storesvc-snap-action" ops, we
  2724  		// don't have a fakeOp for switchChannel because it has
  2725  		// not a backend method, it just manipulates the state
  2726  		{
  2727  			op: "storesvc-snap-action",
  2728  			curSnaps: []store.CurrentSnap{{
  2729  				InstanceName:    "some-snap",
  2730  				SnapID:          "some-snap-id",
  2731  				Revision:        snap.R(7),
  2732  				TrackingChannel: "other-channel/stable",
  2733  				RefreshedDate:   fakeRevDateEpoch.AddDate(0, 0, 7),
  2734  				Epoch:           snap.E("1*"),
  2735  			}},
  2736  			userID: 1,
  2737  		},
  2738  
  2739  		{
  2740  			op: "storesvc-snap-action:action",
  2741  			action: store.SnapAction{
  2742  				Action:       "refresh",
  2743  				InstanceName: "some-snap",
  2744  				SnapID:       "some-snap-id",
  2745  				Channel:      "channel-for-7/stable",
  2746  				Flags:        store.SnapActionEnforceValidation,
  2747  			},
  2748  			userID: 1,
  2749  		},
  2750  	}
  2751  
  2752  	// start with an easier-to-read error if this fails:
  2753  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2754  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  2755  
  2756  	// verify snapSetup info
  2757  	var snapsup snapstate.SnapSetup
  2758  	task := ts.Tasks()[0]
  2759  	err = task.Get("snap-setup", &snapsup)
  2760  	c.Assert(err, IsNil)
  2761  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  2762  		Channel:  "channel-for-7/stable",
  2763  		SideInfo: snapsup.SideInfo,
  2764  	})
  2765  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  2766  		RealName: "some-snap",
  2767  		SnapID:   "some-snap-id",
  2768  		Revision: snap.R(7),
  2769  		Channel:  "channel-for-7/stable",
  2770  	})
  2771  
  2772  	// verify snaps in the system state
  2773  	var snapst snapstate.SnapState
  2774  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2775  	c.Assert(err, IsNil)
  2776  
  2777  	c.Assert(snapst.Active, Equals, true)
  2778  	c.Assert(snapst.Sequence, HasLen, 1)
  2779  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2780  		RealName: "some-snap",
  2781  		SnapID:   "some-snap-id",
  2782  		Channel:  "channel-for-7/stable",
  2783  		Revision: snap.R(7),
  2784  	})
  2785  }
  2786  
  2787  func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidation(c *C) {
  2788  	si := snap.SideInfo{
  2789  		RealName: "some-snap",
  2790  		SnapID:   "some-snap-id",
  2791  		Revision: snap.R(7),
  2792  	}
  2793  
  2794  	s.state.Lock()
  2795  	defer s.state.Unlock()
  2796  
  2797  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2798  		Active:          true,
  2799  		Sequence:        []*snap.SideInfo{&si},
  2800  		TrackingChannel: "channel-for-7/stable",
  2801  		Current:         si.Revision,
  2802  	})
  2803  
  2804  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7/stable"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  2805  	c.Assert(err, IsNil)
  2806  	c.Check(ts.Tasks(), HasLen, 1)
  2807  	c.Check(ts.Tasks()[0].Kind(), Equals, "toggle-snap-flags")
  2808  }
  2809  
  2810  func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidationConflict(c *C) {
  2811  	si := snap.SideInfo{
  2812  		RealName: "some-snap",
  2813  		SnapID:   "some-snap-id",
  2814  		Revision: snap.R(7),
  2815  	}
  2816  
  2817  	s.state.Lock()
  2818  	defer s.state.Unlock()
  2819  
  2820  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2821  		Active:          true,
  2822  		Sequence:        []*snap.SideInfo{&si},
  2823  		TrackingChannel: "channel-for-7/stable",
  2824  		Current:         si.Revision,
  2825  	})
  2826  
  2827  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  2828  	c.Assert(err, IsNil)
  2829  	// make it visible
  2830  	s.state.NewChange("refresh", "refresh a snap").AddAll(ts)
  2831  
  2832  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  2833  	c.Check(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`)
  2834  
  2835  }
  2836  
  2837  func (s *snapmgrTestSuite) TestUpdateSameRevisionToggleIgnoreValidationRunThrough(c *C) {
  2838  	si := snap.SideInfo{
  2839  		RealName: "some-snap",
  2840  		SnapID:   "some-snap-id",
  2841  		Revision: snap.R(7),
  2842  		Channel:  "channel-for-7",
  2843  	}
  2844  
  2845  	s.state.Lock()
  2846  	defer s.state.Unlock()
  2847  
  2848  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2849  		Active:          true,
  2850  		Sequence:        []*snap.SideInfo{&si},
  2851  		TrackingChannel: "channel-for-7/stable",
  2852  		Current:         si.Revision,
  2853  	})
  2854  
  2855  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7/stable"}, s.user.ID, snapstate.Flags{IgnoreValidation: true})
  2856  	c.Assert(err, IsNil)
  2857  
  2858  	chg := s.state.NewChange("refresh", "refresh a snap")
  2859  	chg.AddAll(ts)
  2860  
  2861  	s.state.Unlock()
  2862  	defer s.se.Stop()
  2863  	s.settle(c)
  2864  	s.state.Lock()
  2865  
  2866  	// verify snapSetup info
  2867  	var snapsup snapstate.SnapSetup
  2868  	task := ts.Tasks()[0]
  2869  	err = task.Get("snap-setup", &snapsup)
  2870  	c.Assert(err, IsNil)
  2871  	c.Check(snapsup, DeepEquals, snapstate.SnapSetup{
  2872  		SideInfo: snapsup.SideInfo,
  2873  		Flags: snapstate.Flags{
  2874  			IgnoreValidation: true,
  2875  		},
  2876  	})
  2877  	c.Check(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  2878  		RealName: "some-snap",
  2879  		SnapID:   "some-snap-id",
  2880  		Revision: snap.R(7),
  2881  		Channel:  "channel-for-7",
  2882  	})
  2883  
  2884  	// verify snaps in the system state
  2885  	var snapst snapstate.SnapState
  2886  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2887  	c.Assert(err, IsNil)
  2888  
  2889  	c.Check(snapst.Active, Equals, true)
  2890  	c.Check(snapst.Sequence, HasLen, 1)
  2891  	c.Check(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2892  		RealName: "some-snap",
  2893  		SnapID:   "some-snap-id",
  2894  		Channel:  "channel-for-7",
  2895  		Revision: snap.R(7),
  2896  	})
  2897  	c.Check(snapst.IgnoreValidation, Equals, true)
  2898  }
  2899  
  2900  func (s *snapmgrTestSuite) TestUpdateValidateRefreshesSaysNo(c *C) {
  2901  	si := snap.SideInfo{
  2902  		RealName: "some-snap",
  2903  		SnapID:   "some-snap-id",
  2904  		Revision: snap.R(7),
  2905  	}
  2906  
  2907  	s.state.Lock()
  2908  	defer s.state.Unlock()
  2909  
  2910  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2911  		Active:   true,
  2912  		Sequence: []*snap.SideInfo{&si},
  2913  		Current:  si.Revision,
  2914  	})
  2915  
  2916  	validateErr := errors.New("refresh control error")
  2917  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  2918  		c.Check(refreshes, HasLen, 1)
  2919  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  2920  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  2921  		c.Check(ignoreValidation, HasLen, 0)
  2922  		return nil, validateErr
  2923  	}
  2924  	// hook it up
  2925  	snapstate.ValidateRefreshes = validateRefreshes
  2926  
  2927  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{})
  2928  	c.Assert(err, Equals, validateErr)
  2929  }
  2930  
  2931  func (s *snapmgrTestSuite) TestUpdateValidateRefreshesSaysNoButIgnoreValidationIsSet(c *C) {
  2932  	si := snap.SideInfo{
  2933  		RealName: "some-snap",
  2934  		SnapID:   "some-snap-id",
  2935  		Revision: snap.R(7),
  2936  	}
  2937  
  2938  	s.state.Lock()
  2939  	defer s.state.Unlock()
  2940  
  2941  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2942  		Active:   true,
  2943  		Sequence: []*snap.SideInfo{&si},
  2944  		Current:  si.Revision,
  2945  		SnapType: "app",
  2946  	})
  2947  
  2948  	validateErr := errors.New("refresh control error")
  2949  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  2950  		return nil, validateErr
  2951  	}
  2952  	// hook it up
  2953  	snapstate.ValidateRefreshes = validateRefreshes
  2954  
  2955  	flags := snapstate.Flags{JailMode: true, IgnoreValidation: true}
  2956  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  2957  	c.Assert(err, IsNil)
  2958  
  2959  	var snapsup snapstate.SnapSetup
  2960  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  2961  	c.Assert(err, IsNil)
  2962  	c.Check(snapsup.Flags, DeepEquals, flags.ForSnapSetup())
  2963  }
  2964  
  2965  func (s *snapmgrTestSuite) TestUpdateIgnoreValidationSticky(c *C) {
  2966  	si := snap.SideInfo{
  2967  		RealName: "some-snap",
  2968  		SnapID:   "some-snap-id",
  2969  		Revision: snap.R(7),
  2970  	}
  2971  
  2972  	s.state.Lock()
  2973  	defer s.state.Unlock()
  2974  
  2975  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  2976  		Active:   true,
  2977  		Sequence: []*snap.SideInfo{&si},
  2978  		Current:  si.Revision,
  2979  		SnapType: "app",
  2980  	})
  2981  
  2982  	validateErr := errors.New("refresh control error")
  2983  	validateRefreshesFail := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  2984  		c.Check(refreshes, HasLen, 1)
  2985  		if len(ignoreValidation) == 0 {
  2986  			return nil, validateErr
  2987  		}
  2988  		c.Check(ignoreValidation, DeepEquals, map[string]bool{
  2989  			"some-snap": true,
  2990  		})
  2991  		return refreshes, nil
  2992  	}
  2993  	// hook it up
  2994  	snapstate.ValidateRefreshes = validateRefreshesFail
  2995  
  2996  	flags := snapstate.Flags{IgnoreValidation: true}
  2997  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  2998  	c.Assert(err, IsNil)
  2999  
  3000  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3001  		op: "storesvc-snap-action",
  3002  		curSnaps: []store.CurrentSnap{{
  3003  			InstanceName:     "some-snap",
  3004  			SnapID:           "some-snap-id",
  3005  			Revision:         snap.R(7),
  3006  			IgnoreValidation: false,
  3007  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  3008  			Epoch:            snap.E("1*"),
  3009  		}},
  3010  		userID: 1,
  3011  	})
  3012  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  3013  		op:    "storesvc-snap-action:action",
  3014  		revno: snap.R(11),
  3015  		action: store.SnapAction{
  3016  			Action:       "refresh",
  3017  			InstanceName: "some-snap",
  3018  			SnapID:       "some-snap-id",
  3019  			Channel:      "stable",
  3020  			Flags:        store.SnapActionIgnoreValidation,
  3021  		},
  3022  		userID: 1,
  3023  	})
  3024  
  3025  	chg := s.state.NewChange("refresh", "refresh snap")
  3026  	chg.AddAll(ts)
  3027  
  3028  	s.state.Unlock()
  3029  	defer s.se.Stop()
  3030  	s.settle(c)
  3031  	s.state.Lock()
  3032  
  3033  	// verify snap has IgnoreValidation set
  3034  	var snapst snapstate.SnapState
  3035  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3036  	c.Assert(err, IsNil)
  3037  	c.Check(snapst.IgnoreValidation, Equals, true)
  3038  	c.Check(snapst.Current, Equals, snap.R(11))
  3039  
  3040  	s.fakeBackend.ops = nil
  3041  	s.fakeStore.refreshRevnos = map[string]snap.Revision{
  3042  		"some-snap-id": snap.R(12),
  3043  	}
  3044  	_, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  3045  	c.Assert(err, IsNil)
  3046  	c.Check(tts, HasLen, 2)
  3047  	verifyLastTasksetIsReRefresh(c, tts)
  3048  
  3049  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3050  		op: "storesvc-snap-action",
  3051  		curSnaps: []store.CurrentSnap{{
  3052  			InstanceName:     "some-snap",
  3053  			SnapID:           "some-snap-id",
  3054  			Revision:         snap.R(11),
  3055  			TrackingChannel:  "latest/stable",
  3056  			IgnoreValidation: true,
  3057  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 11),
  3058  			Epoch:            snap.E("1*"),
  3059  		}},
  3060  		userID: 1,
  3061  	})
  3062  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  3063  		op:    "storesvc-snap-action:action",
  3064  		revno: snap.R(12),
  3065  		action: store.SnapAction{
  3066  			Action:       "refresh",
  3067  			InstanceName: "some-snap",
  3068  			SnapID:       "some-snap-id",
  3069  			Flags:        0,
  3070  		},
  3071  		userID: 1,
  3072  	})
  3073  
  3074  	chg = s.state.NewChange("refresh", "refresh snaps")
  3075  	chg.AddAll(tts[0])
  3076  
  3077  	s.state.Unlock()
  3078  	defer s.se.Stop()
  3079  	s.settle(c)
  3080  	s.state.Lock()
  3081  
  3082  	snapst = snapstate.SnapState{}
  3083  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3084  	c.Assert(err, IsNil)
  3085  	c.Check(snapst.IgnoreValidation, Equals, true)
  3086  	c.Check(snapst.Current, Equals, snap.R(12))
  3087  
  3088  	// reset ignore validation
  3089  	s.fakeBackend.ops = nil
  3090  	s.fakeStore.refreshRevnos = map[string]snap.Revision{
  3091  		"some-snap-id": snap.R(11),
  3092  	}
  3093  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  3094  		return refreshes, nil
  3095  	}
  3096  	// hook it up
  3097  	snapstate.ValidateRefreshes = validateRefreshes
  3098  	flags = snapstate.Flags{}
  3099  	ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  3100  	c.Assert(err, IsNil)
  3101  
  3102  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3103  		op: "storesvc-snap-action",
  3104  		curSnaps: []store.CurrentSnap{{
  3105  			InstanceName:     "some-snap",
  3106  			SnapID:           "some-snap-id",
  3107  			Revision:         snap.R(12),
  3108  			TrackingChannel:  "latest/stable",
  3109  			IgnoreValidation: true,
  3110  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 12),
  3111  			Epoch:            snap.E("1*"),
  3112  		}},
  3113  		userID: 1,
  3114  	})
  3115  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  3116  		op:    "storesvc-snap-action:action",
  3117  		revno: snap.R(11),
  3118  		action: store.SnapAction{
  3119  			Action:       "refresh",
  3120  			InstanceName: "some-snap",
  3121  			SnapID:       "some-snap-id",
  3122  			Channel:      "latest/stable",
  3123  			Flags:        store.SnapActionEnforceValidation,
  3124  		},
  3125  		userID: 1,
  3126  	})
  3127  
  3128  	chg = s.state.NewChange("refresh", "refresh snap")
  3129  	chg.AddAll(ts)
  3130  
  3131  	s.state.Unlock()
  3132  	defer s.se.Stop()
  3133  	s.settle(c)
  3134  	s.state.Lock()
  3135  
  3136  	snapst = snapstate.SnapState{}
  3137  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3138  	c.Assert(err, IsNil)
  3139  	c.Check(snapst.IgnoreValidation, Equals, false)
  3140  	c.Check(snapst.Current, Equals, snap.R(11))
  3141  }
  3142  
  3143  func (s *snapmgrTestSuite) TestParallelInstanceUpdateIgnoreValidationSticky(c *C) {
  3144  	si := snap.SideInfo{
  3145  		RealName: "some-snap",
  3146  		SnapID:   "some-snap-id",
  3147  		Revision: snap.R(7),
  3148  	}
  3149  
  3150  	s.state.Lock()
  3151  	defer s.state.Unlock()
  3152  
  3153  	tr := config.NewTransaction(s.state)
  3154  	tr.Set("core", "experimental.parallel-instances", true)
  3155  	tr.Commit()
  3156  
  3157  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3158  		Active:   true,
  3159  		Sequence: []*snap.SideInfo{&si},
  3160  		Current:  si.Revision,
  3161  		SnapType: "app",
  3162  	})
  3163  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  3164  		Active:      true,
  3165  		Sequence:    []*snap.SideInfo{&si},
  3166  		Current:     si.Revision,
  3167  		SnapType:    "app",
  3168  		InstanceKey: "instance",
  3169  	})
  3170  
  3171  	validateErr := errors.New("refresh control error")
  3172  	validateRefreshesFail := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  3173  		c.Check(refreshes, HasLen, 2)
  3174  		if len(ignoreValidation) == 0 {
  3175  			return nil, validateErr
  3176  		}
  3177  		c.Check(ignoreValidation, DeepEquals, map[string]bool{
  3178  			"some-snap_instance": true,
  3179  		})
  3180  		return refreshes, nil
  3181  	}
  3182  	// hook it up
  3183  	snapstate.ValidateRefreshes = validateRefreshesFail
  3184  
  3185  	flags := snapstate.Flags{IgnoreValidation: true}
  3186  	ts, err := snapstate.Update(s.state, "some-snap_instance", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, flags)
  3187  	c.Assert(err, IsNil)
  3188  
  3189  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3190  		op: "storesvc-snap-action",
  3191  		curSnaps: []store.CurrentSnap{{
  3192  			InstanceName:     "some-snap",
  3193  			SnapID:           "some-snap-id",
  3194  			Revision:         snap.R(7),
  3195  			IgnoreValidation: false,
  3196  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  3197  			Epoch:            snap.E("1*"),
  3198  		}, {
  3199  			InstanceName:     "some-snap_instance",
  3200  			SnapID:           "some-snap-id",
  3201  			Revision:         snap.R(7),
  3202  			IgnoreValidation: false,
  3203  			RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  3204  			Epoch:            snap.E("1*"),
  3205  		}},
  3206  		userID: 1,
  3207  	})
  3208  	c.Check(s.fakeBackend.ops[1], DeepEquals, fakeOp{
  3209  		op:    "storesvc-snap-action:action",
  3210  		revno: snap.R(11),
  3211  		action: store.SnapAction{
  3212  			Action:       "refresh",
  3213  			InstanceName: "some-snap_instance",
  3214  			SnapID:       "some-snap-id",
  3215  			Channel:      "stable",
  3216  			Flags:        store.SnapActionIgnoreValidation,
  3217  		},
  3218  		userID: 1,
  3219  	})
  3220  
  3221  	chg := s.state.NewChange("refresh", "refresh snaps")
  3222  	chg.AddAll(ts)
  3223  
  3224  	s.state.Unlock()
  3225  	defer s.se.Stop()
  3226  	s.settle(c)
  3227  	s.state.Lock()
  3228  
  3229  	// ensure all our tasks ran
  3230  	c.Assert(chg.Err(), IsNil)
  3231  	c.Assert(chg.IsReady(), Equals, true)
  3232  
  3233  	// verify snap 'instance' has IgnoreValidation set and the snap was
  3234  	// updated
  3235  	var snapst snapstate.SnapState
  3236  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  3237  	c.Assert(err, IsNil)
  3238  	c.Check(snapst.IgnoreValidation, Equals, true)
  3239  	c.Check(snapst.Current, Equals, snap.R(11))
  3240  	// and the other snap does not
  3241  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3242  	c.Assert(err, IsNil)
  3243  	c.Check(snapst.Current, Equals, snap.R(7))
  3244  	c.Check(snapst.IgnoreValidation, Equals, false)
  3245  
  3246  	s.fakeBackend.ops = nil
  3247  	s.fakeStore.refreshRevnos = map[string]snap.Revision{
  3248  		"some-snap-id": snap.R(12),
  3249  	}
  3250  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "some-snap_instance"}, s.user.ID, nil)
  3251  	c.Assert(err, IsNil)
  3252  	c.Check(tts, HasLen, 3)
  3253  	verifyLastTasksetIsReRefresh(c, tts)
  3254  	sort.Strings(updates)
  3255  	c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"})
  3256  
  3257  	chg = s.state.NewChange("refresh", "refresh snaps")
  3258  	for _, ts := range tts[:len(tts)-1] {
  3259  		chg.AddAll(ts)
  3260  	}
  3261  
  3262  	s.state.Unlock()
  3263  	s.settle(c)
  3264  	s.state.Lock()
  3265  
  3266  	// ensure all our tasks ran
  3267  	c.Assert(chg.Err(), IsNil)
  3268  	c.Assert(chg.IsReady(), Equals, true)
  3269  
  3270  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3271  	c.Assert(err, IsNil)
  3272  	c.Check(snapst.IgnoreValidation, Equals, false)
  3273  	c.Check(snapst.Current, Equals, snap.R(12))
  3274  
  3275  	err = snapstate.Get(s.state, "some-snap_instance", &snapst)
  3276  	c.Assert(err, IsNil)
  3277  	c.Check(snapst.IgnoreValidation, Equals, true)
  3278  	c.Check(snapst.Current, Equals, snap.R(12))
  3279  
  3280  	for i := 0; i < 2; i++ {
  3281  		op := s.fakeBackend.ops[i]
  3282  		switch op.op {
  3283  		case "storesvc-snap-action":
  3284  			c.Check(op, DeepEquals, fakeOp{
  3285  				op: "storesvc-snap-action",
  3286  				curSnaps: []store.CurrentSnap{{
  3287  					InstanceName:     "some-snap",
  3288  					SnapID:           "some-snap-id",
  3289  					Revision:         snap.R(7),
  3290  					IgnoreValidation: false,
  3291  					RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 7),
  3292  					Epoch:            snap.E("1*"),
  3293  				}, {
  3294  					InstanceName:     "some-snap_instance",
  3295  					SnapID:           "some-snap-id",
  3296  					Revision:         snap.R(11),
  3297  					TrackingChannel:  "latest/stable",
  3298  					IgnoreValidation: true,
  3299  					RefreshedDate:    fakeRevDateEpoch.AddDate(0, 0, 11),
  3300  					Epoch:            snap.E("1*"),
  3301  				}},
  3302  				userID: 1,
  3303  			})
  3304  		case "storesvc-snap-action:action":
  3305  			switch op.action.InstanceName {
  3306  			case "some-snap":
  3307  				c.Check(op, DeepEquals, fakeOp{
  3308  					op:    "storesvc-snap-action:action",
  3309  					revno: snap.R(12),
  3310  					action: store.SnapAction{
  3311  						Action:       "refresh",
  3312  						InstanceName: "some-snap",
  3313  						SnapID:       "some-snap-id",
  3314  						Flags:        0,
  3315  					},
  3316  					userID: 1,
  3317  				})
  3318  			case "some-snap_instance":
  3319  				c.Check(op, DeepEquals, fakeOp{
  3320  					op:    "storesvc-snap-action:action",
  3321  					revno: snap.R(12),
  3322  					action: store.SnapAction{
  3323  						Action:       "refresh",
  3324  						InstanceName: "some-snap_instance",
  3325  						SnapID:       "some-snap-id",
  3326  						Flags:        0,
  3327  					},
  3328  					userID: 1,
  3329  				})
  3330  			default:
  3331  				c.Fatalf("unexpected instance name %q", op.action.InstanceName)
  3332  			}
  3333  		default:
  3334  			c.Fatalf("unexpected action %q", op.op)
  3335  		}
  3336  	}
  3337  
  3338  }
  3339  
  3340  func (s *snapmgrTestSuite) TestUpdateFromLocal(c *C) {
  3341  	si := snap.SideInfo{
  3342  		RealName: "some-snap",
  3343  		Revision: snap.R("x1"),
  3344  	}
  3345  
  3346  	s.state.Lock()
  3347  	defer s.state.Unlock()
  3348  
  3349  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3350  		Active:          true,
  3351  		Sequence:        []*snap.SideInfo{&si},
  3352  		TrackingChannel: "channel-for-7/stable",
  3353  		Current:         si.Revision,
  3354  	})
  3355  
  3356  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{})
  3357  	c.Assert(err, Equals, store.ErrLocalSnap)
  3358  }
  3359  
  3360  func (s *snapmgrTestSuite) TestUpdateAmend(c *C) {
  3361  	si := snap.SideInfo{
  3362  		RealName: "some-snap",
  3363  		Revision: snap.R("x1"),
  3364  	}
  3365  
  3366  	s.state.Lock()
  3367  	defer s.state.Unlock()
  3368  
  3369  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3370  		Active:          true,
  3371  		Sequence:        []*snap.SideInfo{&si},
  3372  		TrackingChannel: "channel-for-7/stable",
  3373  		Current:         si.Revision,
  3374  	})
  3375  
  3376  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "channel-for-7"}, s.user.ID, snapstate.Flags{Amend: true})
  3377  	c.Assert(err, IsNil)
  3378  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  3379  
  3380  	// ensure we go from local to store revision-7
  3381  	var snapsup snapstate.SnapSetup
  3382  	tasks := ts.Tasks()
  3383  	c.Check(tasks[1].Kind(), Equals, "download-snap")
  3384  	err = tasks[1].Get("snap-setup", &snapsup)
  3385  	c.Assert(err, IsNil)
  3386  	c.Check(snapsup.Revision(), Equals, snap.R(7))
  3387  }
  3388  
  3389  func (s *snapmgrTestSuite) TestUpdateAmendSnapNotFound(c *C) {
  3390  	si := snap.SideInfo{
  3391  		RealName: "snap-unknown",
  3392  		Revision: snap.R("x1"),
  3393  	}
  3394  
  3395  	s.state.Lock()
  3396  	defer s.state.Unlock()
  3397  
  3398  	snapstate.Set(s.state, "snap-unknown", &snapstate.SnapState{
  3399  		Active:          true,
  3400  		Sequence:        []*snap.SideInfo{&si},
  3401  		TrackingChannel: "latest/stable",
  3402  		Current:         si.Revision,
  3403  	})
  3404  
  3405  	_, err := snapstate.Update(s.state, "snap-unknown", &snapstate.RevisionOptions{Channel: "stable"}, s.user.ID, snapstate.Flags{Amend: true})
  3406  	c.Assert(err, Equals, store.ErrSnapNotFound)
  3407  }
  3408  
  3409  func (s *snapmgrTestSuite) TestSingleUpdateBlockedRevision(c *C) {
  3410  	// single updates should *not* set the block list
  3411  	si7 := snap.SideInfo{
  3412  		RealName: "some-snap",
  3413  		SnapID:   "some-snap-id",
  3414  		Revision: snap.R(7),
  3415  	}
  3416  	si11 := snap.SideInfo{
  3417  		RealName: "some-snap",
  3418  		SnapID:   "some-snap-id",
  3419  		Revision: snap.R(11),
  3420  	}
  3421  
  3422  	s.state.Lock()
  3423  	defer s.state.Unlock()
  3424  
  3425  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3426  		Active:   true,
  3427  		Sequence: []*snap.SideInfo{&si7, &si11},
  3428  		Current:  si7.Revision,
  3429  		SnapType: "app",
  3430  	})
  3431  
  3432  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  3433  	c.Assert(err, IsNil)
  3434  
  3435  	c.Assert(s.fakeBackend.ops, HasLen, 2)
  3436  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3437  		op: "storesvc-snap-action",
  3438  		curSnaps: []store.CurrentSnap{{
  3439  			InstanceName:  "some-snap",
  3440  			SnapID:        "some-snap-id",
  3441  			Revision:      snap.R(7),
  3442  			RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  3443  			Epoch:         snap.E("1*"),
  3444  		}},
  3445  		userID: 1,
  3446  	})
  3447  }
  3448  
  3449  func (s *snapmgrTestSuite) TestMultiUpdateBlockedRevision(c *C) {
  3450  	// multi-updates should *not* set the block list
  3451  	si7 := snap.SideInfo{
  3452  		RealName: "some-snap",
  3453  		SnapID:   "some-snap-id",
  3454  		Revision: snap.R(7),
  3455  	}
  3456  	si11 := snap.SideInfo{
  3457  		RealName: "some-snap",
  3458  		SnapID:   "some-snap-id",
  3459  		Revision: snap.R(11),
  3460  	}
  3461  
  3462  	s.state.Lock()
  3463  	defer s.state.Unlock()
  3464  
  3465  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3466  		Active:   true,
  3467  		Sequence: []*snap.SideInfo{&si7, &si11},
  3468  		Current:  si7.Revision,
  3469  		SnapType: "app",
  3470  	})
  3471  
  3472  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  3473  	c.Assert(err, IsNil)
  3474  	c.Check(updates, DeepEquals, []string{"some-snap"})
  3475  
  3476  	c.Assert(s.fakeBackend.ops, HasLen, 2)
  3477  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3478  		op: "storesvc-snap-action",
  3479  		curSnaps: []store.CurrentSnap{{
  3480  			InstanceName:  "some-snap",
  3481  			SnapID:        "some-snap-id",
  3482  			Revision:      snap.R(7),
  3483  			RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  3484  			Epoch:         snap.E("1*"),
  3485  		}},
  3486  		userID: 1,
  3487  	})
  3488  }
  3489  
  3490  func (s *snapmgrTestSuite) TestAllUpdateBlockedRevision(c *C) {
  3491  	//  update-all *should* set the block list
  3492  	si7 := snap.SideInfo{
  3493  		RealName: "some-snap",
  3494  		SnapID:   "some-snap-id",
  3495  		Revision: snap.R(7),
  3496  	}
  3497  	si11 := snap.SideInfo{
  3498  		RealName: "some-snap",
  3499  		SnapID:   "some-snap-id",
  3500  		Revision: snap.R(11),
  3501  	}
  3502  
  3503  	s.state.Lock()
  3504  	defer s.state.Unlock()
  3505  
  3506  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3507  		Active:   true,
  3508  		Sequence: []*snap.SideInfo{&si7, &si11},
  3509  		Current:  si7.Revision,
  3510  	})
  3511  
  3512  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, s.user.ID, nil)
  3513  	c.Check(err, IsNil)
  3514  	c.Check(updates, HasLen, 0)
  3515  
  3516  	c.Assert(s.fakeBackend.ops, HasLen, 2)
  3517  	c.Check(s.fakeBackend.ops[0], DeepEquals, fakeOp{
  3518  		op: "storesvc-snap-action",
  3519  		curSnaps: []store.CurrentSnap{{
  3520  			InstanceName:  "some-snap",
  3521  			SnapID:        "some-snap-id",
  3522  			Revision:      snap.R(7),
  3523  			RefreshedDate: fakeRevDateEpoch.AddDate(0, 0, 7),
  3524  			Block:         []snap.Revision{snap.R(11)},
  3525  			Epoch:         snap.E("1*"),
  3526  		}},
  3527  		userID: 1,
  3528  	})
  3529  }
  3530  
  3531  func (s *snapmgrTestSuite) TestUpdateManyPartialFailureCheckRerefreshDone(c *C) {
  3532  	s.state.Lock()
  3533  	defer s.state.Unlock()
  3534  
  3535  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
  3536  	makeTestRefreshConfig(s.state)
  3537  
  3538  	var someSnapValidation bool
  3539  
  3540  	// override validate-snap handler set by AddForeignTaskHandlers.
  3541  	s.o.TaskRunner().AddHandler("validate-snap", func(t *state.Task, _ *tomb.Tomb) error {
  3542  		t.State().Lock()
  3543  		defer t.State().Unlock()
  3544  		snapsup, err := snapstate.TaskSnapSetup(t)
  3545  		c.Assert(err, IsNil)
  3546  		if snapsup.SnapName() == "some-snap" {
  3547  			someSnapValidation = true
  3548  			return fmt.Errorf("boom")
  3549  		}
  3550  		return nil
  3551  	}, nil)
  3552  
  3553  	snapstate.Set(s.state, "some-other-snap", &snapstate.SnapState{
  3554  		Active: true,
  3555  		Sequence: []*snap.SideInfo{
  3556  			{RealName: "some-other-snap", SnapID: "some-other-snap-id", Revision: snap.R(1)},
  3557  		},
  3558  		Current:  snap.R(1),
  3559  		SnapType: "app",
  3560  	})
  3561  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3562  		Active: true,
  3563  		Sequence: []*snap.SideInfo{
  3564  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  3565  		},
  3566  		Current:  snap.R(1),
  3567  		SnapType: "app",
  3568  	})
  3569  
  3570  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  3571  		c.Check(refreshes, HasLen, 2)
  3572  		c.Check(ignoreValidation, HasLen, 0)
  3573  		return refreshes, nil
  3574  	}
  3575  	// hook it up
  3576  	snapstate.ValidateRefreshes = validateRefreshes
  3577  
  3578  	s.state.Unlock()
  3579  	s.snapmgr.Ensure()
  3580  	s.state.Lock()
  3581  
  3582  	c.Assert(s.state.Changes(), HasLen, 1)
  3583  	chg := s.state.Changes()[0]
  3584  	c.Check(chg.Kind(), Equals, "auto-refresh")
  3585  	c.Check(chg.IsReady(), Equals, false)
  3586  	s.verifyRefreshLast(c)
  3587  
  3588  	checkIsAutoRefresh(c, chg.Tasks(), true)
  3589  
  3590  	s.state.Unlock()
  3591  	defer s.se.Stop()
  3592  	s.settle(c)
  3593  	s.state.Lock()
  3594  
  3595  	// not updated
  3596  	var snapst snapstate.SnapState
  3597  	c.Assert(snapstate.Get(s.state, "some-snap", &snapst), IsNil)
  3598  	c.Check(snapst.Current, Equals, snap.Revision{N: 1})
  3599  
  3600  	// updated
  3601  	c.Assert(snapstate.Get(s.state, "some-other-snap", &snapst), IsNil)
  3602  	c.Check(snapst.Current, Equals, snap.Revision{N: 11})
  3603  
  3604  	c.Assert(chg.Err(), ErrorMatches, "cannot perform the following tasks:\n.*Fetch and check assertions for snap \"some-snap\" \\(11\\) \\(boom\\)")
  3605  	c.Assert(chg.IsReady(), Equals, true)
  3606  
  3607  	// check-rerefresh is last
  3608  	tasks := chg.Tasks()
  3609  	checkRerefresh := tasks[len(tasks)-1]
  3610  	c.Check(checkRerefresh.Kind(), Equals, "check-rerefresh")
  3611  	c.Check(checkRerefresh.Status(), Equals, state.DoneStatus)
  3612  
  3613  	// sanity
  3614  	c.Check(someSnapValidation, Equals, true)
  3615  }
  3616  
  3617  var orthogonalAutoAliasesScenarios = []struct {
  3618  	aliasesBefore map[string][]string
  3619  	names         []string
  3620  	prune         []string
  3621  	update        bool
  3622  	new           bool
  3623  }{
  3624  	{nil, nil, nil, true, true},
  3625  	{nil, []string{"some-snap"}, nil, true, false},
  3626  	{nil, []string{"other-snap"}, nil, false, true},
  3627  	{map[string][]string{"some-snap": {"aliasA", "aliasC"}}, []string{"some-snap"}, nil, true, false},
  3628  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, []string{"other-snap"}, []string{"other-snap"}, false, false},
  3629  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, nil, []string{"other-snap"}, true, false},
  3630  	{map[string][]string{"other-snap": {"aliasB", "aliasC"}}, []string{"some-snap"}, nil, true, false},
  3631  	{map[string][]string{"other-snap": {"aliasC"}}, []string{"other-snap"}, []string{"other-snap"}, false, true},
  3632  	{map[string][]string{"other-snap": {"aliasC"}}, nil, []string{"other-snap"}, true, true},
  3633  	{map[string][]string{"other-snap": {"aliasC"}}, []string{"some-snap"}, nil, true, false},
  3634  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, []string{"some-snap"}, []string{"other-snap"}, true, false},
  3635  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, nil, []string{"other-snap", "some-snap"}, true, true},
  3636  	{map[string][]string{"some-snap": {"aliasB"}, "other-snap": {"aliasA"}}, []string{"other-snap"}, []string{"other-snap", "some-snap"}, false, true},
  3637  	{map[string][]string{"some-snap": {"aliasB"}}, nil, []string{"some-snap"}, true, true},
  3638  	{map[string][]string{"some-snap": {"aliasB"}}, []string{"other-snap"}, []string{"some-snap"}, false, true},
  3639  	{map[string][]string{"some-snap": {"aliasB"}}, []string{"some-snap"}, nil, true, false},
  3640  	{map[string][]string{"other-snap": {"aliasA"}}, nil, []string{"other-snap"}, true, true},
  3641  	{map[string][]string{"other-snap": {"aliasA"}}, []string{"other-snap"}, []string{"other-snap"}, false, true},
  3642  	{map[string][]string{"other-snap": {"aliasA"}}, []string{"some-snap"}, []string{"other-snap"}, true, false},
  3643  }
  3644  
  3645  func (s *snapmgrTestSuite) TestUpdateManyAutoAliasesScenarios(c *C) {
  3646  	s.state.Lock()
  3647  	defer s.state.Unlock()
  3648  
  3649  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
  3650  		Active: true,
  3651  		Sequence: []*snap.SideInfo{
  3652  			{RealName: "other-snap", SnapID: "other-snap-id", Revision: snap.R(2)},
  3653  		},
  3654  		Current:  snap.R(2),
  3655  		SnapType: "app",
  3656  	})
  3657  
  3658  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  3659  		switch info.InstanceName() {
  3660  		case "some-snap":
  3661  			return map[string]string{"aliasA": "cmdA"}, nil
  3662  		case "other-snap":
  3663  			return map[string]string{"aliasB": "cmdB"}, nil
  3664  		}
  3665  		return nil, nil
  3666  	}
  3667  
  3668  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3669  		Active: true,
  3670  		Sequence: []*snap.SideInfo{
  3671  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  3672  		},
  3673  		Current:  snap.R(4),
  3674  		SnapType: "app",
  3675  	})
  3676  
  3677  	expectedSet := func(aliases []string) map[string]bool {
  3678  		res := make(map[string]bool, len(aliases))
  3679  		for _, alias := range aliases {
  3680  			res[alias] = true
  3681  		}
  3682  		return res
  3683  	}
  3684  
  3685  	for _, scenario := range orthogonalAutoAliasesScenarios {
  3686  		for _, instanceName := range []string{"some-snap", "other-snap"} {
  3687  			var snapst snapstate.SnapState
  3688  			err := snapstate.Get(s.state, instanceName, &snapst)
  3689  			c.Assert(err, IsNil)
  3690  			snapst.Aliases = nil
  3691  			snapst.AutoAliasesDisabled = false
  3692  			if autoAliases := scenario.aliasesBefore[instanceName]; autoAliases != nil {
  3693  				targets := make(map[string]*snapstate.AliasTarget)
  3694  				for _, alias := range autoAliases {
  3695  					targets[alias] = &snapstate.AliasTarget{Auto: "cmd" + alias[len(alias)-1:]}
  3696  				}
  3697  
  3698  				snapst.Aliases = targets
  3699  			}
  3700  			snapstate.Set(s.state, instanceName, &snapst)
  3701  		}
  3702  
  3703  		updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, scenario.names, s.user.ID, nil)
  3704  		c.Check(err, IsNil)
  3705  		if scenario.update {
  3706  			verifyLastTasksetIsReRefresh(c, tts)
  3707  		}
  3708  
  3709  		_, dropped, err := snapstate.AutoAliasesDelta(s.state, []string{"some-snap", "other-snap"})
  3710  		c.Assert(err, IsNil)
  3711  
  3712  		j := 0
  3713  		expectedUpdatesSet := make(map[string]bool)
  3714  		var expectedPruned map[string]map[string]bool
  3715  		var pruneTs *state.TaskSet
  3716  		if len(scenario.prune) != 0 {
  3717  			pruneTs = tts[0]
  3718  			j++
  3719  			taskAliases := make(map[string]map[string]bool)
  3720  			for _, aliasTask := range pruneTs.Tasks() {
  3721  				c.Check(aliasTask.Kind(), Equals, "prune-auto-aliases")
  3722  				var aliases []string
  3723  				err := aliasTask.Get("aliases", &aliases)
  3724  				c.Assert(err, IsNil)
  3725  				snapsup, err := snapstate.TaskSnapSetup(aliasTask)
  3726  				c.Assert(err, IsNil)
  3727  				taskAliases[snapsup.InstanceName()] = expectedSet(aliases)
  3728  			}
  3729  			expectedPruned = make(map[string]map[string]bool)
  3730  			for _, instanceName := range scenario.prune {
  3731  				expectedPruned[instanceName] = expectedSet(dropped[instanceName])
  3732  				if instanceName == "other-snap" && !scenario.new && !scenario.update {
  3733  					expectedUpdatesSet["other-snap"] = true
  3734  				}
  3735  			}
  3736  			c.Check(taskAliases, DeepEquals, expectedPruned)
  3737  		}
  3738  		if scenario.update {
  3739  			updateTs := tts[j]
  3740  			j++
  3741  			expectedUpdatesSet["some-snap"] = true
  3742  			first := updateTs.Tasks()[0]
  3743  			c.Check(first.Kind(), Equals, "prerequisites")
  3744  			wait := false
  3745  			if expectedPruned["other-snap"]["aliasA"] {
  3746  				wait = true
  3747  			} else if expectedPruned["some-snap"] != nil {
  3748  				wait = true
  3749  			}
  3750  			if wait {
  3751  				c.Check(first.WaitTasks(), DeepEquals, pruneTs.Tasks())
  3752  			} else {
  3753  				c.Check(first.WaitTasks(), HasLen, 0)
  3754  			}
  3755  		}
  3756  		if scenario.new {
  3757  			newTs := tts[j]
  3758  			j++
  3759  			expectedUpdatesSet["other-snap"] = true
  3760  			tasks := newTs.Tasks()
  3761  			c.Check(tasks, HasLen, 1)
  3762  			aliasTask := tasks[0]
  3763  			c.Check(aliasTask.Kind(), Equals, "refresh-aliases")
  3764  
  3765  			wait := false
  3766  			if expectedPruned["some-snap"]["aliasB"] {
  3767  				wait = true
  3768  			} else if expectedPruned["other-snap"] != nil {
  3769  				wait = true
  3770  			}
  3771  			if wait {
  3772  				c.Check(aliasTask.WaitTasks(), DeepEquals, pruneTs.Tasks())
  3773  			} else {
  3774  				c.Check(aliasTask.WaitTasks(), HasLen, 0)
  3775  			}
  3776  		}
  3777  		l := len(tts)
  3778  		if scenario.update {
  3779  			l--
  3780  		}
  3781  		c.Assert(j, Equals, l, Commentf("%#v", scenario))
  3782  
  3783  		// check reported updated names
  3784  		c.Check(len(updates) > 0, Equals, true)
  3785  		sort.Strings(updates)
  3786  		expectedUpdates := make([]string, 0, len(expectedUpdatesSet))
  3787  		for x := range expectedUpdatesSet {
  3788  			expectedUpdates = append(expectedUpdates, x)
  3789  		}
  3790  		sort.Strings(expectedUpdates)
  3791  		c.Check(updates, DeepEquals, expectedUpdates)
  3792  	}
  3793  }
  3794  
  3795  func (s *snapmgrTestSuite) TestUpdateOneAutoAliasesScenarios(c *C) {
  3796  	s.state.Lock()
  3797  	defer s.state.Unlock()
  3798  
  3799  	snapstate.Set(s.state, "other-snap", &snapstate.SnapState{
  3800  		Active: true,
  3801  		Sequence: []*snap.SideInfo{
  3802  			{RealName: "other-snap", SnapID: "other-snap-id", Revision: snap.R(2)},
  3803  		},
  3804  		Current:  snap.R(2),
  3805  		SnapType: "app",
  3806  	})
  3807  
  3808  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  3809  		switch info.InstanceName() {
  3810  		case "some-snap":
  3811  			return map[string]string{"aliasA": "cmdA"}, nil
  3812  		case "other-snap":
  3813  			return map[string]string{"aliasB": "cmdB"}, nil
  3814  		}
  3815  		return nil, nil
  3816  	}
  3817  
  3818  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3819  		Active: true,
  3820  		Sequence: []*snap.SideInfo{
  3821  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  3822  		},
  3823  		Current:  snap.R(4),
  3824  		SnapType: "app",
  3825  	})
  3826  
  3827  	expectedSet := func(aliases []string) map[string]bool {
  3828  		res := make(map[string]bool, len(aliases))
  3829  		for _, alias := range aliases {
  3830  			res[alias] = true
  3831  		}
  3832  		return res
  3833  	}
  3834  
  3835  	for _, scenario := range orthogonalAutoAliasesScenarios {
  3836  		if len(scenario.names) != 1 {
  3837  			continue
  3838  		}
  3839  
  3840  		for _, instanceName := range []string{"some-snap", "other-snap"} {
  3841  			var snapst snapstate.SnapState
  3842  			err := snapstate.Get(s.state, instanceName, &snapst)
  3843  			c.Assert(err, IsNil)
  3844  			snapst.Aliases = nil
  3845  			snapst.AutoAliasesDisabled = false
  3846  			if autoAliases := scenario.aliasesBefore[instanceName]; autoAliases != nil {
  3847  				targets := make(map[string]*snapstate.AliasTarget)
  3848  				for _, alias := range autoAliases {
  3849  					targets[alias] = &snapstate.AliasTarget{Auto: "cmd" + alias[len(alias)-1:]}
  3850  				}
  3851  
  3852  				snapst.Aliases = targets
  3853  			}
  3854  			snapstate.Set(s.state, instanceName, &snapst)
  3855  		}
  3856  
  3857  		ts, err := snapstate.Update(s.state, scenario.names[0], nil, s.user.ID, snapstate.Flags{})
  3858  		c.Assert(err, IsNil)
  3859  		_, dropped, err := snapstate.AutoAliasesDelta(s.state, []string{"some-snap", "other-snap"})
  3860  		c.Assert(err, IsNil)
  3861  
  3862  		j := 0
  3863  
  3864  		tasks := ts.Tasks()
  3865  		// make sure the last task from Update is the rerefresh
  3866  		if scenario.update {
  3867  			reRefresh := tasks[len(tasks)-1]
  3868  			c.Check(reRefresh.Kind(), Equals, "check-rerefresh")
  3869  			// nothing should wait on it
  3870  			c.Check(reRefresh.NumHaltTasks(), Equals, 0)
  3871  			tasks = tasks[:len(tasks)-1] // and now forget about it
  3872  		}
  3873  
  3874  		var expectedPruned map[string]map[string]bool
  3875  		var pruneTasks []*state.Task
  3876  		if len(scenario.prune) != 0 {
  3877  			nprune := len(scenario.prune)
  3878  			pruneTasks = tasks[:nprune]
  3879  			j += nprune
  3880  			taskAliases := make(map[string]map[string]bool)
  3881  			for _, aliasTask := range pruneTasks {
  3882  				c.Check(aliasTask.Kind(), Equals, "prune-auto-aliases")
  3883  				var aliases []string
  3884  				err := aliasTask.Get("aliases", &aliases)
  3885  				c.Assert(err, IsNil)
  3886  				snapsup, err := snapstate.TaskSnapSetup(aliasTask)
  3887  				c.Assert(err, IsNil)
  3888  				taskAliases[snapsup.InstanceName()] = expectedSet(aliases)
  3889  			}
  3890  			expectedPruned = make(map[string]map[string]bool)
  3891  			for _, instanceName := range scenario.prune {
  3892  				expectedPruned[instanceName] = expectedSet(dropped[instanceName])
  3893  			}
  3894  			c.Check(taskAliases, DeepEquals, expectedPruned)
  3895  		}
  3896  		if scenario.update {
  3897  			first := tasks[j]
  3898  			j += 19
  3899  			c.Check(first.Kind(), Equals, "prerequisites")
  3900  			wait := false
  3901  			if expectedPruned["other-snap"]["aliasA"] {
  3902  				wait = true
  3903  			} else if expectedPruned["some-snap"] != nil {
  3904  				wait = true
  3905  			}
  3906  			if wait {
  3907  				c.Check(first.WaitTasks(), DeepEquals, pruneTasks)
  3908  			} else {
  3909  				c.Check(first.WaitTasks(), HasLen, 0)
  3910  			}
  3911  		}
  3912  		if scenario.new {
  3913  			aliasTask := tasks[j]
  3914  			j++
  3915  			c.Check(aliasTask.Kind(), Equals, "refresh-aliases")
  3916  			wait := false
  3917  			if expectedPruned["some-snap"]["aliasB"] {
  3918  				wait = true
  3919  			} else if expectedPruned["other-snap"] != nil {
  3920  				wait = true
  3921  			}
  3922  			if wait {
  3923  				c.Check(aliasTask.WaitTasks(), DeepEquals, pruneTasks)
  3924  			} else {
  3925  				c.Check(aliasTask.WaitTasks(), HasLen, 0)
  3926  			}
  3927  		}
  3928  		c.Assert(len(tasks), Equals, j, Commentf("%#v", scenario))
  3929  
  3930  		// conflict checks are triggered
  3931  		chg := s.state.NewChange("update", "...")
  3932  		chg.AddAll(ts)
  3933  		err = snapstate.CheckChangeConflict(s.state, scenario.names[0], nil)
  3934  		c.Check(err, ErrorMatches, `.* has "update" change in progress`)
  3935  		chg.SetStatus(state.DoneStatus)
  3936  	}
  3937  }
  3938  
  3939  func (s *snapmgrTestSuite) TestUpdateLocalSnapFails(c *C) {
  3940  	si := snap.SideInfo{
  3941  		RealName: "some-snap",
  3942  		Revision: snap.R(7),
  3943  	}
  3944  
  3945  	s.state.Lock()
  3946  	defer s.state.Unlock()
  3947  
  3948  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3949  		Active:   true,
  3950  		Sequence: []*snap.SideInfo{&si},
  3951  		Current:  si.Revision,
  3952  	})
  3953  
  3954  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  3955  	c.Assert(err, Equals, store.ErrLocalSnap)
  3956  }
  3957  
  3958  func (s *snapmgrTestSuite) TestUpdateDisabledUnsupported(c *C) {
  3959  	si := snap.SideInfo{
  3960  		RealName: "some-snap",
  3961  		SnapID:   "some-snap-id",
  3962  		Revision: snap.R(7),
  3963  	}
  3964  
  3965  	s.state.Lock()
  3966  	defer s.state.Unlock()
  3967  
  3968  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3969  		Active:   false,
  3970  		Sequence: []*snap.SideInfo{&si},
  3971  		Current:  si.Revision,
  3972  	})
  3973  
  3974  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  3975  	c.Assert(err, ErrorMatches, `refreshing disabled snap "some-snap" not supported`)
  3976  }
  3977  
  3978  func (s *snapmgrTestSuite) TestUpdateKernelTrackChecksSwitchingTracks(c *C) {
  3979  	si := snap.SideInfo{
  3980  		RealName: "kernel",
  3981  		SnapID:   "kernel-id",
  3982  		Revision: snap.R(7),
  3983  	}
  3984  
  3985  	s.state.Lock()
  3986  	defer s.state.Unlock()
  3987  
  3988  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  3989  	defer r()
  3990  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  3991  		Active:          true,
  3992  		Sequence:        []*snap.SideInfo{&si},
  3993  		Current:         si.Revision,
  3994  		TrackingChannel: "18/stable",
  3995  	})
  3996  
  3997  	// switching tracks is not ok
  3998  	_, err := snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{})
  3999  	c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "new-channel"`)
  4000  
  4001  	// no change to the channel is ok
  4002  	_, err = snapstate.Update(s.state, "kernel", nil, s.user.ID, snapstate.Flags{})
  4003  	c.Assert(err, IsNil)
  4004  
  4005  	// switching risk level is ok
  4006  	_, err = snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "18/beta"}, s.user.ID, snapstate.Flags{})
  4007  	c.Assert(err, IsNil)
  4008  
  4009  	// switching just risk within the pinned track is ok
  4010  	_, err = snapstate.Update(s.state, "kernel", &snapstate.RevisionOptions{Channel: "beta"}, s.user.ID, snapstate.Flags{})
  4011  	c.Assert(err, IsNil)
  4012  }
  4013  
  4014  func (s *snapmgrTestSuite) TestUpdateGadgetTrackChecksSwitchingTracks(c *C) {
  4015  	si := snap.SideInfo{
  4016  		RealName: "brand-gadget",
  4017  		SnapID:   "brand-gadget-id",
  4018  		Revision: snap.R(7),
  4019  	}
  4020  
  4021  	s.state.Lock()
  4022  	defer s.state.Unlock()
  4023  
  4024  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
  4025  	defer r()
  4026  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
  4027  		Active:          true,
  4028  		Sequence:        []*snap.SideInfo{&si},
  4029  		Current:         si.Revision,
  4030  		TrackingChannel: "18/stable",
  4031  	})
  4032  
  4033  	// switching tracks is not ok
  4034  	_, err := snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "new-channel"}, s.user.ID, snapstate.Flags{})
  4035  	c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "new-channel"`)
  4036  
  4037  	// no change to the channel is ok
  4038  	_, err = snapstate.Update(s.state, "brand-gadget", nil, s.user.ID, snapstate.Flags{})
  4039  	c.Assert(err, IsNil)
  4040  
  4041  	// switching risk level is ok
  4042  	_, err = snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "18/beta"}, s.user.ID, snapstate.Flags{})
  4043  	c.Assert(err, IsNil)
  4044  
  4045  	// switching just risk within the pinned track is ok
  4046  	_, err = snapstate.Update(s.state, "brand-gadget", &snapstate.RevisionOptions{Channel: "beta"}, s.user.ID, snapstate.Flags{})
  4047  	c.Assert(err, IsNil)
  4048  
  4049  }
  4050  
  4051  func (s *snapmgrTestSuite) TestUpdateWithDeviceContext(c *C) {
  4052  	s.state.Lock()
  4053  	defer s.state.Unlock()
  4054  
  4055  	// unset the global store, it will need to come via the device context
  4056  	snapstate.ReplaceStore(s.state, nil)
  4057  
  4058  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  4059  		DeviceModel: DefaultModel(),
  4060  		CtxStore:    s.fakeStore,
  4061  	}
  4062  
  4063  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4064  		Active:          true,
  4065  		TrackingChannel: "latest/edge",
  4066  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4067  		Current:         snap.R(7),
  4068  		SnapType:        "app",
  4069  	})
  4070  
  4071  	validateCalled := false
  4072  	happyValidateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx1 snapstate.DeviceContext) ([]*snap.Info, error) {
  4073  		c.Check(deviceCtx1, Equals, deviceCtx)
  4074  		validateCalled = true
  4075  		return refreshes, nil
  4076  	}
  4077  	// hook it up
  4078  	snapstate.ValidateRefreshes = happyValidateRefreshes
  4079  
  4080  	ts, err := snapstate.UpdateWithDeviceContext(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{}, deviceCtx, "")
  4081  	c.Assert(err, IsNil)
  4082  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  4083  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  4084  
  4085  	c.Check(validateCalled, Equals, true)
  4086  }
  4087  
  4088  func (s *snapmgrTestSuite) TestUpdateWithDeviceContextToRevision(c *C) {
  4089  	s.state.Lock()
  4090  	defer s.state.Unlock()
  4091  
  4092  	// unset the global store, it will need to come via the device context
  4093  	snapstate.ReplaceStore(s.state, nil)
  4094  
  4095  	deviceCtx := &snapstatetest.TrivialDeviceContext{
  4096  		DeviceModel: DefaultModel(),
  4097  		CtxStore:    s.fakeStore,
  4098  	}
  4099  
  4100  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4101  		Active: true,
  4102  		Sequence: []*snap.SideInfo{
  4103  			{RealName: "some-snap", Revision: snap.R(5), SnapID: "some-snap-id"},
  4104  		},
  4105  		Current:  snap.R(5),
  4106  		SnapType: "app",
  4107  		UserID:   1,
  4108  	})
  4109  
  4110  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(11)}
  4111  	ts, err := snapstate.UpdateWithDeviceContext(s.state, "some-snap", opts, 0, snapstate.Flags{}, deviceCtx, "")
  4112  	c.Assert(err, IsNil)
  4113  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 0, ts, s.state)
  4114  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  4115  }
  4116  
  4117  func (s *snapmgrTestSuite) TestUpdateTasksCoreSetsIgnoreOnConfigure(c *C) {
  4118  	s.state.Lock()
  4119  	defer s.state.Unlock()
  4120  
  4121  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4122  		Active:          true,
  4123  		TrackingChannel: "latest/edge",
  4124  		Sequence:        []*snap.SideInfo{{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(7)}},
  4125  		Current:         snap.R(7),
  4126  		SnapType:        "os",
  4127  	})
  4128  
  4129  	oldConfigure := snapstate.Configure
  4130  	defer func() { snapstate.Configure = oldConfigure }()
  4131  
  4132  	var configureFlags int
  4133  	snapstate.Configure = func(st *state.State, snapName string, patch map[string]interface{}, flags int) *state.TaskSet {
  4134  		configureFlags = flags
  4135  		return state.NewTaskSet()
  4136  	}
  4137  
  4138  	_, err := snapstate.Update(s.state, "core", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4139  	c.Assert(err, IsNil)
  4140  
  4141  	// ensure the core snap sets the "ignore-hook-error" flag
  4142  	c.Check(configureFlags&snapstate.IgnoreHookError, Equals, 1)
  4143  }
  4144  
  4145  func (s *snapmgrTestSuite) TestUpdateDevModeConfinementFiltering(c *C) {
  4146  	restore := maybeMockClassicSupport(c)
  4147  	defer restore()
  4148  
  4149  	s.state.Lock()
  4150  	defer s.state.Unlock()
  4151  
  4152  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4153  		Active:          true,
  4154  		TrackingChannel: "channel-for-devmode/stable",
  4155  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4156  		Current:         snap.R(7),
  4157  		SnapType:        "app",
  4158  	})
  4159  
  4160  	// updated snap is devmode, refresh without --devmode, do nothing
  4161  	// TODO: better error message here
  4162  	_, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  4163  	c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`)
  4164  
  4165  	// updated snap is devmode, refresh with --devmode
  4166  	_, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{DevMode: true})
  4167  	c.Assert(err, IsNil)
  4168  }
  4169  
  4170  func (s *snapmgrTestSuite) TestUpdateClassicConfinementFiltering(c *C) {
  4171  	restore := maybeMockClassicSupport(c)
  4172  	defer restore()
  4173  
  4174  	s.state.Lock()
  4175  	defer s.state.Unlock()
  4176  
  4177  	snapstate.Set(s.state, "some-snap-now-classic", &snapstate.SnapState{
  4178  		Active:   true,
  4179  		Sequence: []*snap.SideInfo{{RealName: "some-snap-now-classic", SnapID: "some-snap-now-classic-id", Revision: snap.R(7)}},
  4180  		Current:  snap.R(7),
  4181  		SnapType: "app",
  4182  	})
  4183  
  4184  	// updated snap is classic, refresh without --classic, do nothing
  4185  	// TODO: better error message here
  4186  	_, err := snapstate.Update(s.state, "some-snap-now-classic", nil, s.user.ID, snapstate.Flags{})
  4187  	c.Assert(err, ErrorMatches, `.* requires classic confinement`)
  4188  
  4189  	// updated snap is classic, refresh with --classic
  4190  	ts, err := snapstate.Update(s.state, "some-snap-now-classic", nil, s.user.ID, snapstate.Flags{Classic: true})
  4191  	c.Assert(err, IsNil)
  4192  
  4193  	chg := s.state.NewChange("refresh", "refresh snap")
  4194  	chg.AddAll(ts)
  4195  
  4196  	s.state.Unlock()
  4197  	defer s.se.Stop()
  4198  	s.settle(c)
  4199  	s.state.Lock()
  4200  
  4201  	c.Assert(chg.Err(), IsNil)
  4202  	c.Assert(chg.IsReady(), Equals, true)
  4203  
  4204  	// verify snap is in classic
  4205  	var snapst snapstate.SnapState
  4206  	err = snapstate.Get(s.state, "some-snap-now-classic", &snapst)
  4207  	c.Assert(err, IsNil)
  4208  	c.Check(snapst.Classic, Equals, true)
  4209  }
  4210  
  4211  func (s *snapmgrTestSuite) TestUpdateClassicFromClassic(c *C) {
  4212  	restore := maybeMockClassicSupport(c)
  4213  	defer restore()
  4214  
  4215  	s.state.Lock()
  4216  	defer s.state.Unlock()
  4217  
  4218  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4219  		Active:          true,
  4220  		TrackingChannel: "channel-for-classic/stable",
  4221  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4222  		Current:         snap.R(7),
  4223  		SnapType:        "app",
  4224  		Flags:           snapstate.Flags{Classic: true},
  4225  	})
  4226  
  4227  	// snap installed with --classic, update needs classic, refresh with --classic works
  4228  	ts, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{Classic: true})
  4229  	c.Assert(err, IsNil)
  4230  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4231  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
  4232  	c.Assert(err, IsNil)
  4233  	c.Check(snapsup.Flags.Classic, Equals, true)
  4234  
  4235  	// devmode overrides the snapsetup classic flag
  4236  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{DevMode: true})
  4237  	c.Assert(err, IsNil)
  4238  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4239  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4240  	c.Assert(err, IsNil)
  4241  	c.Check(snapsup.Flags.Classic, Equals, false)
  4242  
  4243  	// jailmode overrides it too (you need to provide both)
  4244  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{JailMode: true})
  4245  	c.Assert(err, IsNil)
  4246  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4247  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4248  	c.Assert(err, IsNil)
  4249  	c.Check(snapsup.Flags.Classic, Equals, false)
  4250  
  4251  	// jailmode and classic together gets you both
  4252  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{JailMode: true, Classic: true})
  4253  	c.Assert(err, IsNil)
  4254  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4255  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4256  	c.Assert(err, IsNil)
  4257  	c.Check(snapsup.Flags.Classic, Equals, true)
  4258  
  4259  	// snap installed with --classic, update needs classic, refresh without --classic works
  4260  	ts, err = snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  4261  	c.Assert(err, IsNil)
  4262  	c.Assert(ts.Tasks(), Not(HasLen), 0)
  4263  	snapsup, err = snapstate.TaskSnapSetup(ts.Tasks()[0])
  4264  	c.Assert(err, IsNil)
  4265  	c.Check(snapsup.Flags.Classic, Equals, true)
  4266  
  4267  	chg := s.state.NewChange("refresh", "refresh snap")
  4268  	chg.AddAll(ts)
  4269  
  4270  	s.state.Unlock()
  4271  	defer s.se.Stop()
  4272  	s.settle(c)
  4273  	s.state.Lock()
  4274  
  4275  	// verify snap is in classic
  4276  	var snapst snapstate.SnapState
  4277  	err = snapstate.Get(s.state, "some-snap", &snapst)
  4278  	c.Assert(err, IsNil)
  4279  	c.Check(snapst.Classic, Equals, true)
  4280  }
  4281  
  4282  func (s *snapmgrTestSuite) TestUpdateStrictFromClassic(c *C) {
  4283  	restore := maybeMockClassicSupport(c)
  4284  	defer restore()
  4285  
  4286  	s.state.Lock()
  4287  	defer s.state.Unlock()
  4288  
  4289  	snapstate.Set(s.state, "some-snap-was-classic", &snapstate.SnapState{
  4290  		Active:          true,
  4291  		TrackingChannel: "channel/stable",
  4292  		Sequence:        []*snap.SideInfo{{RealName: "some-snap-was-classic", SnapID: "some-snap-was-classic-id", Revision: snap.R(7)}},
  4293  		Current:         snap.R(7),
  4294  		SnapType:        "app",
  4295  		Flags:           snapstate.Flags{Classic: true},
  4296  	})
  4297  
  4298  	// snap installed with --classic, update does not need classic, refresh works without --classic
  4299  	_, err := snapstate.Update(s.state, "some-snap-was-classic", nil, s.user.ID, snapstate.Flags{})
  4300  	c.Assert(err, IsNil)
  4301  
  4302  	// snap installed with --classic, update does not need classic, refresh works with --classic
  4303  	_, err = snapstate.Update(s.state, "some-snap-was-classic", nil, s.user.ID, snapstate.Flags{Classic: true})
  4304  	c.Assert(err, IsNil)
  4305  }
  4306  
  4307  func (s *snapmgrTestSuite) TestUpdateChannelFallback(c *C) {
  4308  	s.state.Lock()
  4309  	defer s.state.Unlock()
  4310  
  4311  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4312  		Active:          true,
  4313  		TrackingChannel: "latest/edge",
  4314  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4315  		Current:         snap.R(7),
  4316  		SnapType:        "app",
  4317  	})
  4318  
  4319  	ts, err := snapstate.Update(s.state, "some-snap", nil, s.user.ID, snapstate.Flags{})
  4320  	c.Assert(err, IsNil)
  4321  
  4322  	var snapsup snapstate.SnapSetup
  4323  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
  4324  	c.Assert(err, IsNil)
  4325  
  4326  	c.Check(snapsup.Channel, Equals, "latest/edge")
  4327  }
  4328  
  4329  func (s *snapmgrTestSuite) TestUpdateTooEarly(c *C) {
  4330  	s.state.Lock()
  4331  	defer s.state.Unlock()
  4332  
  4333  	s.state.Set("seeded", nil)
  4334  
  4335  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4336  		Active:   true,
  4337  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4338  		Current:  snap.R(7),
  4339  		SnapType: "app",
  4340  	})
  4341  
  4342  	_, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4343  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  4344  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
  4345  }
  4346  
  4347  func (s *snapmgrTestSuite) TestUpdateConflict(c *C) {
  4348  	s.state.Lock()
  4349  	defer s.state.Unlock()
  4350  
  4351  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4352  		Active:   true,
  4353  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4354  		Current:  snap.R(7),
  4355  		SnapType: "app",
  4356  	})
  4357  
  4358  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4359  	c.Assert(err, IsNil)
  4360  	// need a change to make the tasks visible
  4361  	s.state.NewChange("refresh", "...").AddAll(ts)
  4362  
  4363  	_, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  4364  	c.Assert(err, ErrorMatches, `snap "some-snap" has "refresh" change in progress`)
  4365  }
  4366  
  4367  func (s *snapmgrTestSuite) TestUpdateCreatesGCTasks(c *C) {
  4368  	restore := release.MockOnClassic(false)
  4369  	defer restore()
  4370  
  4371  	s.testUpdateCreatesGCTasks(c, 2)
  4372  }
  4373  
  4374  func (s *snapmgrTestSuite) TestUpdateCreatesGCTasksOnClassic(c *C) {
  4375  	restore := release.MockOnClassic(true)
  4376  	defer restore()
  4377  
  4378  	s.testUpdateCreatesGCTasks(c, 3)
  4379  }
  4380  
  4381  func (s *snapmgrTestSuite) testUpdateCreatesGCTasks(c *C, expectedDiscards int) {
  4382  	s.state.Lock()
  4383  	defer s.state.Unlock()
  4384  
  4385  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4386  		Active: true,
  4387  		Sequence: []*snap.SideInfo{
  4388  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4389  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4390  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4391  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4392  		},
  4393  		Current:  snap.R(4),
  4394  		SnapType: "app",
  4395  	})
  4396  
  4397  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{})
  4398  	c.Assert(err, IsNil)
  4399  
  4400  	// ensure edges information is still there
  4401  	te, err := ts.Edge(snapstate.DownloadAndChecksDoneEdge)
  4402  	c.Assert(te, NotNil)
  4403  	c.Assert(err, IsNil)
  4404  
  4405  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, expectedDiscards, ts, s.state)
  4406  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  4407  }
  4408  
  4409  func (s *snapmgrTestSuite) TestUpdateCreatesDiscardAfterCurrentTasks(c *C) {
  4410  	s.state.Lock()
  4411  	defer s.state.Unlock()
  4412  
  4413  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4414  		Active: true,
  4415  		Sequence: []*snap.SideInfo{
  4416  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4417  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4418  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4419  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4420  		},
  4421  		Current:  snap.R(1),
  4422  		SnapType: "app",
  4423  	})
  4424  
  4425  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{})
  4426  	c.Assert(err, IsNil)
  4427  
  4428  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter|doesReRefresh, 3, ts, s.state)
  4429  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
  4430  }
  4431  
  4432  func (s *snapmgrTestSuite) TestUpdateManyTooEarly(c *C) {
  4433  	s.state.Lock()
  4434  	defer s.state.Unlock()
  4435  
  4436  	s.state.Set("seeded", nil)
  4437  
  4438  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4439  		Active:   true,
  4440  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4441  		Current:  snap.R(7),
  4442  		SnapType: "app",
  4443  	})
  4444  
  4445  	_, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4446  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  4447  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
  4448  }
  4449  
  4450  func (s *snapmgrTestSuite) TestUpdateMany(c *C) {
  4451  	s.state.Lock()
  4452  	defer s.state.Unlock()
  4453  
  4454  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4455  		Active: true,
  4456  		Sequence: []*snap.SideInfo{
  4457  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4458  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4459  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4460  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4461  		},
  4462  		Current:  snap.R(1),
  4463  		SnapType: "app",
  4464  	})
  4465  
  4466  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4467  	c.Assert(err, IsNil)
  4468  	c.Assert(tts, HasLen, 2)
  4469  	verifyLastTasksetIsReRefresh(c, tts)
  4470  	c.Check(updates, DeepEquals, []string{"some-snap"})
  4471  
  4472  	ts := tts[0]
  4473  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 3, ts, s.state)
  4474  
  4475  	// check that the tasks are in non-default lane
  4476  	for _, t := range ts.Tasks() {
  4477  		c.Assert(t.Lanes(), DeepEquals, []int{1})
  4478  	}
  4479  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks())+1) // 1==rerefresh
  4480  
  4481  	// ensure edges information is still there
  4482  	te, err := ts.Edge(snapstate.DownloadAndChecksDoneEdge)
  4483  	c.Assert(te, NotNil)
  4484  	c.Assert(err, IsNil)
  4485  
  4486  	checkIsAutoRefresh(c, ts.Tasks(), false)
  4487  }
  4488  
  4489  func (s *snapmgrTestSuite) TestUpdateManyFailureDoesntUndoSnapdRefresh(c *C) {
  4490  	s.state.Lock()
  4491  	defer s.state.Unlock()
  4492  
  4493  	r := snapstatetest.MockDeviceModel(ModelWithBase("core18"))
  4494  	defer r()
  4495  
  4496  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4497  		Active: true,
  4498  		Sequence: []*snap.SideInfo{
  4499  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4500  		},
  4501  		Current:         snap.R(1),
  4502  		SnapType:        "app",
  4503  		TrackingChannel: "channel-for-base/stable",
  4504  	})
  4505  
  4506  	snapstate.Set(s.state, "core18", &snapstate.SnapState{
  4507  		Active: true,
  4508  		Sequence: []*snap.SideInfo{
  4509  			{RealName: "core18", SnapID: "core18-snap-id", Revision: snap.R(1)},
  4510  		},
  4511  		Current:  snap.R(1),
  4512  		SnapType: "base",
  4513  	})
  4514  
  4515  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  4516  		Active: true,
  4517  		Sequence: []*snap.SideInfo{
  4518  			{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)},
  4519  		},
  4520  		Current:  snap.R(1),
  4521  		SnapType: "base",
  4522  	})
  4523  
  4524  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
  4525  		Active: true,
  4526  		Sequence: []*snap.SideInfo{
  4527  			{RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)},
  4528  		},
  4529  		Current:  snap.R(1),
  4530  		SnapType: "app",
  4531  	})
  4532  
  4533  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "some-base", "snapd"}, 0, nil)
  4534  	c.Assert(err, IsNil)
  4535  	c.Assert(tts, HasLen, 4)
  4536  	c.Assert(updates, HasLen, 3)
  4537  
  4538  	chg := s.state.NewChange("refresh", "...")
  4539  	for _, ts := range tts {
  4540  		chg.AddAll(ts)
  4541  	}
  4542  
  4543  	// refresh of some-snap fails on link-snap
  4544  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "/some-snap/11")
  4545  
  4546  	s.state.Unlock()
  4547  	defer s.se.Stop()
  4548  	s.settle(c)
  4549  	s.state.Lock()
  4550  
  4551  	c.Check(chg.Err(), ErrorMatches, ".*cannot perform the following tasks:\n- Make snap \"some-snap\" \\(11\\) available to the system.*")
  4552  	c.Check(chg.IsReady(), Equals, true)
  4553  
  4554  	var snapst snapstate.SnapState
  4555  
  4556  	// failed snap remains at the old revision, snapd and some-base are refreshed.
  4557  	c.Assert(snapstate.Get(s.state, "some-snap", &snapst), IsNil)
  4558  	c.Check(snapst.Current, Equals, snap.Revision{N: 1})
  4559  
  4560  	c.Assert(snapstate.Get(s.state, "snapd", &snapst), IsNil)
  4561  	c.Check(snapst.Current, Equals, snap.Revision{N: 11})
  4562  
  4563  	c.Assert(snapstate.Get(s.state, "some-base", &snapst), IsNil)
  4564  	c.Check(snapst.Current, Equals, snap.Revision{N: 11})
  4565  
  4566  	var undoneDownloads, doneDownloads int
  4567  	for _, ts := range tts {
  4568  		for _, t := range ts.Tasks() {
  4569  			if t.Kind() == "download-snap" {
  4570  				sup, err := snapstate.TaskSnapSetup(t)
  4571  				c.Assert(err, IsNil)
  4572  				switch sup.SnapName() {
  4573  				case "some-snap":
  4574  					undoneDownloads++
  4575  					c.Check(t.Status(), Equals, state.UndoneStatus)
  4576  				case "snapd", "some-base":
  4577  					doneDownloads++
  4578  					c.Check(t.Status(), Equals, state.DoneStatus)
  4579  				default:
  4580  					c.Errorf("unexpected snap %s", sup.SnapName())
  4581  				}
  4582  			}
  4583  		}
  4584  	}
  4585  	c.Assert(undoneDownloads, Equals, 1)
  4586  	c.Assert(doneDownloads, Equals, 2)
  4587  }
  4588  
  4589  func (s *snapmgrTestSuite) TestUpdateManyDevModeConfinementFiltering(c *C) {
  4590  	s.state.Lock()
  4591  	defer s.state.Unlock()
  4592  
  4593  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4594  		Active:          true,
  4595  		TrackingChannel: "channel-for-devmode/stable",
  4596  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4597  		Current:         snap.R(7),
  4598  		SnapType:        "app",
  4599  	})
  4600  
  4601  	// updated snap is devmode, updatemany doesn't update it
  4602  	_, tts, _ := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  4603  	// FIXME: UpdateMany will not error out in this case (daemon catches this case, with a weird error)
  4604  	c.Assert(tts, HasLen, 0)
  4605  }
  4606  
  4607  func (s *snapmgrTestSuite) TestUpdateManyClassicConfinementFiltering(c *C) {
  4608  	restore := maybeMockClassicSupport(c)
  4609  	defer restore()
  4610  
  4611  	s.state.Lock()
  4612  	defer s.state.Unlock()
  4613  
  4614  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4615  		Active:          true,
  4616  		TrackingChannel: "channel-for-classic/stable",
  4617  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4618  		Current:         snap.R(7),
  4619  		SnapType:        "app",
  4620  	})
  4621  
  4622  	// if a snap installed without --classic gets a classic update it isn't installed
  4623  	_, tts, _ := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  4624  	// FIXME: UpdateMany will not error out in this case (daemon catches this case, with a weird error)
  4625  	c.Assert(tts, HasLen, 0)
  4626  }
  4627  
  4628  func (s *snapmgrTestSuite) TestUpdateManyClassic(c *C) {
  4629  	restore := maybeMockClassicSupport(c)
  4630  	defer restore()
  4631  
  4632  	s.state.Lock()
  4633  	defer s.state.Unlock()
  4634  
  4635  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4636  		Active:          true,
  4637  		TrackingChannel: "channel-for-classic/stable",
  4638  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4639  		Current:         snap.R(7),
  4640  		SnapType:        "app",
  4641  		Flags:           snapstate.Flags{Classic: true},
  4642  	})
  4643  
  4644  	// snap installed with classic: refresh gets classic
  4645  	_, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, nil)
  4646  	c.Assert(err, IsNil)
  4647  	c.Assert(tts, HasLen, 2)
  4648  	verifyLastTasksetIsReRefresh(c, tts)
  4649  }
  4650  
  4651  func (s *snapmgrTestSuite) TestUpdateManyClassicToStrict(c *C) {
  4652  	restore := maybeMockClassicSupport(c)
  4653  	defer restore()
  4654  
  4655  	s.state.Lock()
  4656  	defer s.state.Unlock()
  4657  
  4658  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4659  		Active:          true,
  4660  		TrackingChannel: "stable",
  4661  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(7)}},
  4662  		Current:         snap.R(7),
  4663  		SnapType:        "app",
  4664  		Flags:           snapstate.Flags{Classic: true},
  4665  	})
  4666  
  4667  	// snap installed with classic: refresh gets classic
  4668  	_, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, s.user.ID, &snapstate.Flags{Classic: true})
  4669  	c.Assert(err, IsNil)
  4670  	c.Assert(tts, HasLen, 2)
  4671  	// ensure we clear the classic flag
  4672  	snapsup, err := snapstate.TaskSnapSetup(tts[0].Tasks()[0])
  4673  	c.Assert(err, IsNil)
  4674  	c.Assert(snapsup.Flags.Classic, Equals, false)
  4675  
  4676  	verifyLastTasksetIsReRefresh(c, tts)
  4677  }
  4678  
  4679  func (s *snapmgrTestSuite) TestUpdateManyDevMode(c *C) {
  4680  	s.state.Lock()
  4681  	defer s.state.Unlock()
  4682  
  4683  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4684  		Active: true,
  4685  		Flags:  snapstate.Flags{DevMode: true},
  4686  		Sequence: []*snap.SideInfo{
  4687  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4688  		},
  4689  		Current:  snap.R(1),
  4690  		SnapType: "app",
  4691  	})
  4692  
  4693  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, 0, nil)
  4694  	c.Assert(err, IsNil)
  4695  	c.Check(updates, HasLen, 1)
  4696  }
  4697  
  4698  func (s *snapmgrTestSuite) TestUpdateAllDevMode(c *C) {
  4699  	s.state.Lock()
  4700  	defer s.state.Unlock()
  4701  
  4702  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4703  		Active: true,
  4704  		Flags:  snapstate.Flags{DevMode: true},
  4705  		Sequence: []*snap.SideInfo{
  4706  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4707  		},
  4708  		Current:  snap.R(1),
  4709  		SnapType: "app",
  4710  	})
  4711  
  4712  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4713  	c.Assert(err, IsNil)
  4714  	c.Check(updates, HasLen, 0)
  4715  }
  4716  
  4717  func (s *snapmgrTestSuite) TestUpdateManyWaitForBasesUC16(c *C) {
  4718  	s.state.Lock()
  4719  	defer s.state.Unlock()
  4720  
  4721  	snapstate.Set(s.state, "core", &snapstate.SnapState{
  4722  		Active: true,
  4723  		Sequence: []*snap.SideInfo{
  4724  			{RealName: "core", SnapID: "core-snap-id", Revision: snap.R(1)},
  4725  		},
  4726  		Current:  snap.R(1),
  4727  		SnapType: "os",
  4728  	})
  4729  
  4730  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  4731  		Active: true,
  4732  		Sequence: []*snap.SideInfo{
  4733  			{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)},
  4734  		},
  4735  		Current:  snap.R(1),
  4736  		SnapType: "base",
  4737  	})
  4738  
  4739  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4740  		Active: true,
  4741  		Sequence: []*snap.SideInfo{
  4742  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4743  		},
  4744  		Current:         snap.R(1),
  4745  		SnapType:        "app",
  4746  		TrackingChannel: "channel-for-base/stable",
  4747  	})
  4748  
  4749  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "core", "some-base"}, 0, nil)
  4750  	c.Assert(err, IsNil)
  4751  	c.Assert(tts, HasLen, 4)
  4752  	verifyLastTasksetIsReRefresh(c, tts)
  4753  	c.Check(updates, HasLen, 3)
  4754  
  4755  	// to make TaskSnapSetup work
  4756  	chg := s.state.NewChange("refresh", "...")
  4757  	for _, ts := range tts {
  4758  		chg.AddAll(ts)
  4759  	}
  4760  
  4761  	prereqTotal := len(tts[0].Tasks()) + len(tts[1].Tasks())
  4762  	prereqs := map[string]bool{}
  4763  	for i, task := range tts[2].Tasks() {
  4764  		waitTasks := task.WaitTasks()
  4765  		if i == 0 {
  4766  			c.Check(len(waitTasks), Equals, prereqTotal)
  4767  		} else if task.Kind() == "link-snap" {
  4768  			c.Check(len(waitTasks), Equals, prereqTotal+1)
  4769  			for _, pre := range waitTasks {
  4770  				if pre.Kind() == "link-snap" {
  4771  					snapsup, err := snapstate.TaskSnapSetup(pre)
  4772  					c.Assert(err, IsNil)
  4773  					prereqs[snapsup.InstanceName()] = true
  4774  				}
  4775  			}
  4776  		}
  4777  	}
  4778  
  4779  	c.Check(prereqs, DeepEquals, map[string]bool{
  4780  		"core":      true,
  4781  		"some-base": true,
  4782  	})
  4783  }
  4784  
  4785  func (s *snapmgrTestSuite) TestUpdateManyWaitForBasesUC18(c *C) {
  4786  	r := snapstatetest.MockDeviceModel(ModelWithBase("core18"))
  4787  	defer r()
  4788  
  4789  	s.state.Lock()
  4790  	defer s.state.Unlock()
  4791  
  4792  	snapstate.Set(s.state, "core18", &snapstate.SnapState{
  4793  		Active: true,
  4794  		Sequence: []*snap.SideInfo{
  4795  			{RealName: "core18", SnapID: "core18-snap-id", Revision: snap.R(1)},
  4796  		},
  4797  		Current:  snap.R(1),
  4798  		SnapType: "base",
  4799  	})
  4800  
  4801  	snapstate.Set(s.state, "some-base", &snapstate.SnapState{
  4802  		Active: true,
  4803  		Sequence: []*snap.SideInfo{
  4804  			{RealName: "some-base", SnapID: "some-base-id", Revision: snap.R(1)},
  4805  		},
  4806  		Current:  snap.R(1),
  4807  		SnapType: "base",
  4808  	})
  4809  
  4810  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
  4811  		Active: true,
  4812  		Sequence: []*snap.SideInfo{
  4813  			{RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)},
  4814  		},
  4815  		Current:  snap.R(1),
  4816  		SnapType: "app",
  4817  	})
  4818  
  4819  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4820  		Active: true,
  4821  		Sequence: []*snap.SideInfo{
  4822  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4823  		},
  4824  		Current:         snap.R(1),
  4825  		SnapType:        "app",
  4826  		TrackingChannel: "channel-for-base/stable",
  4827  	})
  4828  
  4829  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap", "core18", "some-base", "snapd"}, 0, nil)
  4830  	c.Assert(err, IsNil)
  4831  	c.Assert(tts, HasLen, 5)
  4832  	verifyLastTasksetIsReRefresh(c, tts)
  4833  	c.Check(updates, HasLen, 4)
  4834  
  4835  	// to make TaskSnapSetup work
  4836  	chg := s.state.NewChange("refresh", "...")
  4837  	for _, ts := range tts {
  4838  		chg.AddAll(ts)
  4839  	}
  4840  
  4841  	// Note that some-app only waits for snapd+some-base. The core18
  4842  	// base is not special to this snap and not waited for
  4843  	prereqTotal := len(tts[0].Tasks()) + len(tts[1].Tasks())
  4844  	prereqs := map[string]bool{}
  4845  	for i, task := range tts[3].Tasks() {
  4846  		waitTasks := task.WaitTasks()
  4847  		if i == 0 {
  4848  			c.Check(len(waitTasks), Equals, prereqTotal)
  4849  		} else if task.Kind() == "link-snap" {
  4850  			c.Check(len(waitTasks), Equals, prereqTotal+1)
  4851  			for _, pre := range waitTasks {
  4852  				if pre.Kind() == "link-snap" {
  4853  					snapsup, err := snapstate.TaskSnapSetup(pre)
  4854  					c.Assert(err, IsNil)
  4855  					prereqs[snapsup.InstanceName()] = true
  4856  				}
  4857  			}
  4858  		}
  4859  	}
  4860  
  4861  	// Note that "core18" is not part of the prereqs for some-app
  4862  	// as it does not use this base.
  4863  	c.Check(prereqs, DeepEquals, map[string]bool{
  4864  		"some-base": true,
  4865  		"snapd":     true,
  4866  	})
  4867  }
  4868  
  4869  func (s *snapmgrTestSuite) TestUpdateManyValidateRefreshes(c *C) {
  4870  	s.state.Lock()
  4871  	defer s.state.Unlock()
  4872  
  4873  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4874  		Active: true,
  4875  		Sequence: []*snap.SideInfo{
  4876  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4877  		},
  4878  		Current:  snap.R(1),
  4879  		SnapType: "app",
  4880  	})
  4881  
  4882  	validateCalled := false
  4883  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  4884  		validateCalled = true
  4885  		c.Check(refreshes, HasLen, 1)
  4886  		c.Check(refreshes[0].InstanceName(), Equals, "some-snap")
  4887  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  4888  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  4889  		c.Check(ignoreValidation, HasLen, 0)
  4890  		return refreshes, nil
  4891  	}
  4892  	// hook it up
  4893  	snapstate.ValidateRefreshes = validateRefreshes
  4894  
  4895  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4896  	c.Assert(err, IsNil)
  4897  	c.Assert(tts, HasLen, 2)
  4898  	verifyLastTasksetIsReRefresh(c, tts)
  4899  	c.Check(updates, DeepEquals, []string{"some-snap"})
  4900  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[0], s.state)
  4901  
  4902  	c.Check(validateCalled, Equals, true)
  4903  }
  4904  
  4905  func (s *snapmgrTestSuite) TestParallelInstanceUpdateMany(c *C) {
  4906  	restore := release.MockOnClassic(false)
  4907  	defer restore()
  4908  
  4909  	s.state.Lock()
  4910  	defer s.state.Unlock()
  4911  
  4912  	tr := config.NewTransaction(s.state)
  4913  	tr.Set("core", "experimental.parallel-instances", true)
  4914  	tr.Commit()
  4915  
  4916  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4917  		Active: true,
  4918  		Sequence: []*snap.SideInfo{
  4919  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4920  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4921  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4922  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  4923  		},
  4924  		Current:  snap.R(1),
  4925  		SnapType: "app",
  4926  	})
  4927  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  4928  		Active: true,
  4929  		Sequence: []*snap.SideInfo{
  4930  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4931  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
  4932  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(3)},
  4933  		},
  4934  		Current:     snap.R(3),
  4935  		SnapType:    "app",
  4936  		InstanceKey: "instance",
  4937  	})
  4938  
  4939  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  4940  	c.Assert(err, IsNil)
  4941  	c.Assert(tts, HasLen, 3)
  4942  	verifyLastTasksetIsReRefresh(c, tts)
  4943  	// ensure stable ordering of updates list
  4944  	if updates[0] != "some-snap" {
  4945  		updates[1], updates[0] = updates[0], updates[1]
  4946  	}
  4947  
  4948  	c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"})
  4949  
  4950  	var snapsup, snapsupInstance *snapstate.SnapSetup
  4951  
  4952  	// ensure stable ordering of task sets list
  4953  	snapsup, err = snapstate.TaskSnapSetup(tts[0].Tasks()[0])
  4954  	c.Assert(err, IsNil)
  4955  	if snapsup.InstanceName() != "some-snap" {
  4956  		tts[0], tts[1] = tts[1], tts[0]
  4957  		snapsup, err = snapstate.TaskSnapSetup(tts[0].Tasks()[0])
  4958  		c.Assert(err, IsNil)
  4959  	}
  4960  	snapsupInstance, err = snapstate.TaskSnapSetup(tts[1].Tasks()[0])
  4961  	c.Assert(err, IsNil)
  4962  
  4963  	c.Assert(snapsup.InstanceName(), Equals, "some-snap")
  4964  	c.Assert(snapsupInstance.InstanceName(), Equals, "some-snap_instance")
  4965  
  4966  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 3, tts[0], s.state)
  4967  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 1, tts[1], s.state)
  4968  }
  4969  
  4970  func (s *snapmgrTestSuite) TestParallelInstanceUpdateManyValidateRefreshes(c *C) {
  4971  	s.state.Lock()
  4972  	defer s.state.Unlock()
  4973  
  4974  	tr := config.NewTransaction(s.state)
  4975  	tr.Set("core", "experimental.parallel-instances", true)
  4976  	tr.Commit()
  4977  
  4978  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  4979  		Active: true,
  4980  		Sequence: []*snap.SideInfo{
  4981  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4982  		},
  4983  		Current:  snap.R(1),
  4984  		SnapType: "app",
  4985  	})
  4986  	snapstate.Set(s.state, "some-snap_instance", &snapstate.SnapState{
  4987  		Active: true,
  4988  		Sequence: []*snap.SideInfo{
  4989  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  4990  		},
  4991  		Current:     snap.R(1),
  4992  		SnapType:    "app",
  4993  		InstanceKey: "instance",
  4994  	})
  4995  
  4996  	validateCalled := false
  4997  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  4998  		validateCalled = true
  4999  		c.Check(refreshes, HasLen, 2)
  5000  		instanceIdx := 0
  5001  		someIdx := 1
  5002  		if refreshes[0].InstanceName() != "some-snap_instance" {
  5003  			instanceIdx = 1
  5004  			someIdx = 0
  5005  		}
  5006  		c.Check(refreshes[someIdx].InstanceName(), Equals, "some-snap")
  5007  		c.Check(refreshes[instanceIdx].InstanceName(), Equals, "some-snap_instance")
  5008  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  5009  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  5010  		c.Check(refreshes[1].SnapID, Equals, "some-snap-id")
  5011  		c.Check(refreshes[1].Revision, Equals, snap.R(11))
  5012  		c.Check(ignoreValidation, HasLen, 0)
  5013  		return refreshes, nil
  5014  	}
  5015  	// hook it up
  5016  	snapstate.ValidateRefreshes = validateRefreshes
  5017  
  5018  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  5019  	c.Assert(err, IsNil)
  5020  	c.Assert(tts, HasLen, 3)
  5021  	verifyLastTasksetIsReRefresh(c, tts)
  5022  	sort.Strings(updates)
  5023  	c.Check(updates, DeepEquals, []string{"some-snap", "some-snap_instance"})
  5024  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[0], s.state)
  5025  	verifyUpdateTasks(c, unlinkBefore|cleanupAfter, 0, tts[1], s.state)
  5026  
  5027  	c.Check(validateCalled, Equals, true)
  5028  }
  5029  
  5030  func (s *snapmgrTestSuite) TestUpdateManyValidateRefreshesUnhappy(c *C) {
  5031  	s.state.Lock()
  5032  	defer s.state.Unlock()
  5033  
  5034  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5035  		Active: true,
  5036  		Sequence: []*snap.SideInfo{
  5037  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  5038  		},
  5039  		Current: snap.R(1),
  5040  	})
  5041  
  5042  	validateErr := errors.New("refresh control error")
  5043  	validateRefreshes := func(st *state.State, refreshes []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) ([]*snap.Info, error) {
  5044  		c.Check(refreshes, HasLen, 1)
  5045  		c.Check(refreshes[0].SnapID, Equals, "some-snap-id")
  5046  		c.Check(refreshes[0].Revision, Equals, snap.R(11))
  5047  		c.Check(ignoreValidation, HasLen, 0)
  5048  		return nil, validateErr
  5049  	}
  5050  	// hook it up
  5051  	snapstate.ValidateRefreshes = validateRefreshes
  5052  
  5053  	// refresh all => no error
  5054  	updates, tts, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  5055  	c.Assert(err, IsNil)
  5056  	c.Check(tts, HasLen, 0)
  5057  	c.Check(updates, HasLen, 0)
  5058  
  5059  	// refresh some-snap => report error
  5060  	updates, tts, err = snapstate.UpdateMany(context.Background(), s.state, []string{"some-snap"}, 0, nil)
  5061  	c.Assert(err, Equals, validateErr)
  5062  	c.Check(tts, HasLen, 0)
  5063  	c.Check(updates, HasLen, 0)
  5064  
  5065  }
  5066  
  5067  func (s *snapmgrTestSuite) testUpdateManyDiskSpaceCheck(c *C, featureFlag, failDiskCheck, failInstallSize bool) error {
  5068  	var diskCheckCalled, installSizeCalled bool
  5069  	restore := snapstate.MockOsutilCheckFreeSpace(func(path string, sz uint64) error {
  5070  		diskCheckCalled = true
  5071  		c.Check(path, Equals, filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd"))
  5072  		c.Check(sz, Equals, snapstate.SafetyMarginDiskSpace(123))
  5073  		if failDiskCheck {
  5074  			return &osutil.NotEnoughDiskSpaceError{}
  5075  		}
  5076  		return nil
  5077  	})
  5078  	defer restore()
  5079  
  5080  	restoreInstallSize := snapstate.MockInstallSize(func(st *state.State, snaps []snapstate.MinimalInstallInfo, userID int) (uint64, error) {
  5081  		installSizeCalled = true
  5082  		if failInstallSize {
  5083  			return 0, fmt.Errorf("boom")
  5084  		}
  5085  		c.Assert(snaps, HasLen, 2)
  5086  		c.Check(snaps[0].InstanceName(), Equals, "snapd")
  5087  		c.Check(snaps[1].InstanceName(), Equals, "some-snap")
  5088  		return 123, nil
  5089  	})
  5090  	defer restoreInstallSize()
  5091  
  5092  	s.state.Lock()
  5093  	defer s.state.Unlock()
  5094  
  5095  	tr := config.NewTransaction(s.state)
  5096  	tr.Set("core", "experimental.check-disk-space-refresh", featureFlag)
  5097  	tr.Commit()
  5098  
  5099  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5100  		Active: true,
  5101  		Sequence: []*snap.SideInfo{
  5102  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  5103  		},
  5104  		Current:  snap.R(1),
  5105  		SnapType: "app",
  5106  	})
  5107  
  5108  	snapstate.Set(s.state, "snapd", &snapstate.SnapState{
  5109  		Active: true,
  5110  		Sequence: []*snap.SideInfo{
  5111  			{RealName: "snapd", SnapID: "snapd-snap-id", Revision: snap.R(1)},
  5112  		},
  5113  		Current:  snap.R(1),
  5114  		SnapType: "app",
  5115  	})
  5116  
  5117  	updates, _, err := snapstate.UpdateMany(context.Background(), s.state, nil, 0, nil)
  5118  	if featureFlag {
  5119  		c.Check(installSizeCalled, Equals, true)
  5120  		if failInstallSize {
  5121  			c.Check(diskCheckCalled, Equals, false)
  5122  		} else {
  5123  			c.Check(diskCheckCalled, Equals, true)
  5124  			if failDiskCheck {
  5125  				c.Check(updates, HasLen, 0)
  5126  			} else {
  5127  				c.Check(updates, HasLen, 2)
  5128  			}
  5129  		}
  5130  	} else {
  5131  		c.Check(installSizeCalled, Equals, false)
  5132  		c.Check(diskCheckCalled, Equals, false)
  5133  	}
  5134  
  5135  	return err
  5136  }
  5137  
  5138  func (s *snapmgrTestSuite) TestUpdateManyDiskSpaceCheckError(c *C) {
  5139  	featureFlag := true
  5140  	failDiskCheck := true
  5141  	failInstallSize := false
  5142  	err := s.testUpdateManyDiskSpaceCheck(c, featureFlag, failDiskCheck, failInstallSize)
  5143  	diskSpaceErr := err.(*snapstate.InsufficientSpaceError)
  5144  	c.Assert(diskSpaceErr, ErrorMatches, `insufficient space in .* to perform "refresh" change for the following snaps: snapd, some-snap`)
  5145  	c.Check(diskSpaceErr.Path, Equals, filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd"))
  5146  	c.Check(diskSpaceErr.Snaps, DeepEquals, []string{"snapd", "some-snap"})
  5147  }
  5148  
  5149  func (s *snapmgrTestSuite) TestUpdateManyDiskSpaceSkippedIfFeatureDisabled(c *C) {
  5150  	featureFlag := false
  5151  	failDiskCheck := true
  5152  	failInstallSize := false
  5153  	err := s.testUpdateManyDiskSpaceCheck(c, featureFlag, failDiskCheck, failInstallSize)
  5154  	c.Assert(err, IsNil)
  5155  }
  5156  
  5157  func (s *snapmgrTestSuite) TestUpdateManyDiskSpaceFailInstallSize(c *C) {
  5158  	featureFlag := true
  5159  	failDiskCheck := false
  5160  	failInstallSize := true
  5161  	err := s.testUpdateManyDiskSpaceCheck(c, featureFlag, failDiskCheck, failInstallSize)
  5162  	c.Assert(err, ErrorMatches, "boom")
  5163  }
  5164  
  5165  func (s *snapmgrTestSuite) TestUnlinkCurrentSnapLastActiveDisabledServicesSet(c *C) {
  5166  	si := snap.SideInfo{
  5167  		RealName: "services-snap",
  5168  		Revision: snap.R(-42),
  5169  	}
  5170  	snaptest.MockSnap(c, `name: services-snap`, &si)
  5171  
  5172  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  5173  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  5174  
  5175  	// reset the services to what they were before after the test is done
  5176  	defer func() {
  5177  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  5178  	}()
  5179  
  5180  	s.state.Lock()
  5181  	defer s.state.Unlock()
  5182  
  5183  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  5184  		Active:                     true,
  5185  		Sequence:                   []*snap.SideInfo{&si},
  5186  		Current:                    si.Revision,
  5187  		SnapType:                   "app",
  5188  		TrackingChannel:            "stable",
  5189  		LastActiveDisabledServices: []string{},
  5190  	})
  5191  
  5192  	chg := s.state.NewChange("refresh", "refresh a snap")
  5193  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
  5194  
  5195  	c.Assert(err, IsNil)
  5196  	// only add up to unlink-current-snap task
  5197  	for _, t := range ts.Tasks() {
  5198  		chg.AddTask(t)
  5199  		if t.Kind() == "unlink-current-snap" {
  5200  			// don't add any more from this point on
  5201  			break
  5202  		}
  5203  	}
  5204  
  5205  	s.state.Unlock()
  5206  	defer s.se.Stop()
  5207  	s.settle(c)
  5208  	s.state.Lock()
  5209  
  5210  	c.Assert(chg.Err(), IsNil)
  5211  	c.Assert(chg.IsReady(), Equals, true)
  5212  
  5213  	// get the snap state
  5214  	var snapst snapstate.SnapState
  5215  	err = snapstate.Get(s.state, "services-snap", &snapst)
  5216  	c.Assert(err, IsNil)
  5217  
  5218  	// make sure that the disabled services in this snap's state is what we
  5219  	// provided
  5220  	sort.Strings(snapst.LastActiveDisabledServices)
  5221  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  5222  }
  5223  
  5224  func (s *snapmgrTestSuite) TestUnlinkCurrentSnapMergedLastActiveDisabledServicesSet(c *C) {
  5225  	si := snap.SideInfo{
  5226  		RealName: "services-snap",
  5227  		Revision: snap.R(-42),
  5228  	}
  5229  	snaptest.MockSnap(c, `name: services-snap`, &si)
  5230  
  5231  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  5232  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  5233  
  5234  	// reset the services to what they were before after the test is done
  5235  	defer func() {
  5236  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  5237  	}()
  5238  
  5239  	s.state.Lock()
  5240  	defer s.state.Unlock()
  5241  
  5242  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  5243  		Active:                     true,
  5244  		Sequence:                   []*snap.SideInfo{&si},
  5245  		Current:                    si.Revision,
  5246  		SnapType:                   "app",
  5247  		TrackingChannel:            "stable",
  5248  		LastActiveDisabledServices: []string{"missing-svc3"},
  5249  	})
  5250  
  5251  	chg := s.state.NewChange("refresh", "refresh a snap")
  5252  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
  5253  
  5254  	c.Assert(err, IsNil)
  5255  	// only add up to unlink-current-snap task
  5256  	for _, t := range ts.Tasks() {
  5257  		chg.AddTask(t)
  5258  		if t.Kind() == "unlink-current-snap" {
  5259  			// don't add any more from this point on
  5260  			break
  5261  		}
  5262  	}
  5263  
  5264  	s.state.Unlock()
  5265  	defer s.se.Stop()
  5266  	s.settle(c)
  5267  	s.state.Lock()
  5268  
  5269  	c.Assert(chg.Err(), IsNil)
  5270  	c.Assert(chg.IsReady(), Equals, true)
  5271  
  5272  	// get the snap state
  5273  	var snapst snapstate.SnapState
  5274  	err = snapstate.Get(s.state, "services-snap", &snapst)
  5275  	c.Assert(err, IsNil)
  5276  
  5277  	// make sure that the disabled services in this snap's state is what we
  5278  	// provided
  5279  	sort.Strings(snapst.LastActiveDisabledServices)
  5280  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3", "svc1", "svc2"})
  5281  }
  5282  
  5283  func (s *snapmgrTestSuite) TestUnlinkCurrentSnapPassthroughLastActiveDisabledServicesSet(c *C) {
  5284  	si := snap.SideInfo{
  5285  		RealName: "services-snap",
  5286  		Revision: snap.R(-42),
  5287  	}
  5288  	snaptest.MockSnap(c, `name: services-snap`, &si)
  5289  
  5290  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  5291  	s.fakeBackend.servicesCurrentlyDisabled = []string{}
  5292  
  5293  	// reset the services to what they were before after the test is done
  5294  	defer func() {
  5295  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  5296  	}()
  5297  
  5298  	s.state.Lock()
  5299  	defer s.state.Unlock()
  5300  
  5301  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  5302  		Active:                     true,
  5303  		Sequence:                   []*snap.SideInfo{&si},
  5304  		Current:                    si.Revision,
  5305  		SnapType:                   "app",
  5306  		TrackingChannel:            "stable",
  5307  		LastActiveDisabledServices: []string{"missing-svc3"},
  5308  	})
  5309  
  5310  	chg := s.state.NewChange("refresh", "refresh a snap")
  5311  	ts, err := snapstate.Update(s.state, "services-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{Amend: true})
  5312  
  5313  	c.Assert(err, IsNil)
  5314  	// only add up to unlink-current-snap task
  5315  	for _, t := range ts.Tasks() {
  5316  		chg.AddTask(t)
  5317  		if t.Kind() == "unlink-current-snap" {
  5318  			// don't add any more from this point on
  5319  			break
  5320  		}
  5321  	}
  5322  
  5323  	s.state.Unlock()
  5324  	defer s.se.Stop()
  5325  	s.settle(c)
  5326  	s.state.Lock()
  5327  
  5328  	c.Assert(chg.Err(), IsNil)
  5329  	c.Assert(chg.IsReady(), Equals, true)
  5330  
  5331  	// get the snap state
  5332  	var snapst snapstate.SnapState
  5333  	err = snapstate.Get(s.state, "services-snap", &snapst)
  5334  	c.Assert(err, IsNil)
  5335  
  5336  	// make sure that the disabled services in this snap's state is what we
  5337  	// provided
  5338  	sort.Strings(snapst.LastActiveDisabledServices)
  5339  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"missing-svc3"})
  5340  }
  5341  
  5342  func (s *snapmgrTestSuite) TestStopSnapServicesSavesSnapSetupLastActiveDisabledServices(c *C) {
  5343  	s.state.Lock()
  5344  	defer s.state.Unlock()
  5345  
  5346  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  5347  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1", "svc2"}
  5348  
  5349  	// reset the services to what they were before after the test is done
  5350  	defer func() {
  5351  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  5352  	}()
  5353  
  5354  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  5355  		Sequence: []*snap.SideInfo{
  5356  			{RealName: "services-snap", Revision: snap.R(11)},
  5357  		},
  5358  		Current: snap.R(11),
  5359  		Active:  true,
  5360  	})
  5361  
  5362  	snapsup := &snapstate.SnapSetup{
  5363  		SideInfo: &snap.SideInfo{
  5364  			RealName: "services-snap",
  5365  			Revision: snap.R(11),
  5366  			SnapID:   "services-snap-id",
  5367  		},
  5368  	}
  5369  
  5370  	chg := s.state.NewChange("stop-services", "stop the services")
  5371  	t1 := s.state.NewTask("prerequisites", "...")
  5372  	t1.Set("snap-setup", snapsup)
  5373  	t2 := s.state.NewTask("stop-snap-services", "...")
  5374  	t2.Set("stop-reason", snap.StopReasonDisable)
  5375  	t2.Set("snap-setup-task", t1.ID())
  5376  	t2.WaitFor(t1)
  5377  	chg.AddTask(t1)
  5378  	chg.AddTask(t2)
  5379  
  5380  	s.state.Unlock()
  5381  	defer s.se.Stop()
  5382  	s.settle(c)
  5383  	s.state.Lock()
  5384  
  5385  	c.Assert(chg.Err(), IsNil)
  5386  	c.Assert(chg.IsReady(), Equals, true)
  5387  
  5388  	// get the snap state
  5389  	var snapst snapstate.SnapState
  5390  	c.Assert(snapstate.Get(s.state, "services-snap", &snapst), IsNil)
  5391  
  5392  	// make sure that the disabled services in this snap's state is what we
  5393  	// provided
  5394  	sort.Strings(snapst.LastActiveDisabledServices)
  5395  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  5396  }
  5397  
  5398  func (s *snapmgrTestSuite) TestStopSnapServicesFirstSavesSnapSetupLastActiveDisabledServices(c *C) {
  5399  	s.state.Lock()
  5400  	defer s.state.Unlock()
  5401  
  5402  	prevCurrentlyDisabled := s.fakeBackend.servicesCurrentlyDisabled
  5403  	s.fakeBackend.servicesCurrentlyDisabled = []string{"svc1"}
  5404  
  5405  	// reset the services to what they were before after the test is done
  5406  	defer func() {
  5407  		s.fakeBackend.servicesCurrentlyDisabled = prevCurrentlyDisabled
  5408  	}()
  5409  
  5410  	snapstate.Set(s.state, "services-snap", &snapstate.SnapState{
  5411  		Sequence: []*snap.SideInfo{
  5412  			{RealName: "services-snap", Revision: snap.R(11)},
  5413  		},
  5414  		Current: snap.R(11),
  5415  		Active:  true,
  5416  		// leave this line to keep gofmt 1.10 happy
  5417  		LastActiveDisabledServices: []string{"svc2"},
  5418  	})
  5419  
  5420  	snapsup := &snapstate.SnapSetup{
  5421  		SideInfo: &snap.SideInfo{
  5422  			RealName: "services-snap",
  5423  			Revision: snap.R(11),
  5424  			SnapID:   "services-snap-id",
  5425  		},
  5426  	}
  5427  
  5428  	chg := s.state.NewChange("stop-services", "stop the services")
  5429  	t := s.state.NewTask("stop-snap-services", "...")
  5430  	t.Set("stop-reason", snap.StopReasonDisable)
  5431  	t.Set("snap-setup", snapsup)
  5432  	chg.AddTask(t)
  5433  
  5434  	s.state.Unlock()
  5435  	defer s.se.Stop()
  5436  	s.settle(c)
  5437  	s.state.Lock()
  5438  
  5439  	c.Assert(chg.Err(), IsNil)
  5440  	c.Assert(chg.IsReady(), Equals, true)
  5441  
  5442  	// get the snap state
  5443  	var snapst snapstate.SnapState
  5444  	c.Assert(snapstate.Get(s.state, "services-snap", &snapst), IsNil)
  5445  
  5446  	// make sure that the disabled services in this snap's state is what we
  5447  	// provided
  5448  	sort.Strings(snapst.LastActiveDisabledServices)
  5449  	c.Assert(snapst.LastActiveDisabledServices, DeepEquals, []string{"svc1", "svc2"})
  5450  }
  5451  
  5452  func (s *snapmgrTestSuite) TestRefreshDoesntRestoreRevisionConfig(c *C) {
  5453  	restore := release.MockOnClassic(false)
  5454  	defer restore()
  5455  
  5456  	s.state.Lock()
  5457  	defer s.state.Unlock()
  5458  
  5459  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5460  		Active: true,
  5461  		Sequence: []*snap.SideInfo{
  5462  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  5463  		},
  5464  		Current:  snap.R(1),
  5465  		SnapType: "app",
  5466  	})
  5467  
  5468  	// set global configuration (affecting current snap)
  5469  	tr := config.NewTransaction(s.state)
  5470  	tr.Set("some-snap", "foo", "100")
  5471  	tr.Commit()
  5472  
  5473  	// set per-revision config for the upcoming rev. 2, we don't expect it restored though
  5474  	// since only revert restores revision configs.
  5475  	s.state.Set("revision-config", map[string]interface{}{
  5476  		"some-snap": map[string]interface{}{
  5477  			"2": map[string]interface{}{"foo": "200"},
  5478  		},
  5479  	})
  5480  
  5481  	// simulate a refresh to rev. 2
  5482  	chg := s.state.NewChange("update", "update some-snap")
  5483  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(2)}, s.user.ID, snapstate.Flags{})
  5484  	c.Assert(err, IsNil)
  5485  	chg.AddAll(ts)
  5486  
  5487  	s.state.Unlock()
  5488  	defer s.se.Stop()
  5489  	s.settle(c)
  5490  
  5491  	s.state.Lock()
  5492  	// config of rev. 1 has been stored in per-revision map
  5493  	var cfgs map[string]interface{}
  5494  	c.Assert(s.state.Get("revision-config", &cfgs), IsNil)
  5495  	c.Assert(cfgs["some-snap"], DeepEquals, map[string]interface{}{
  5496  		"1": map[string]interface{}{"foo": "100"},
  5497  		"2": map[string]interface{}{"foo": "200"},
  5498  	})
  5499  
  5500  	// config of rev. 2 hasn't been restored by refresh, old value returned
  5501  	tr = config.NewTransaction(s.state)
  5502  	var res string
  5503  	c.Assert(tr.Get("some-snap", "foo", &res), IsNil)
  5504  	c.Assert(res, Equals, "100")
  5505  }
  5506  
  5507  func (s *snapmgrTestSuite) TestRefreshFailureCausesErrorReport(c *C) {
  5508  	var errSnap, errMsg, errSig string
  5509  	var errExtra map[string]string
  5510  	var n int
  5511  	restore := snapstate.MockErrtrackerReport(func(aSnap, aErrMsg, aDupSig string, extra map[string]string) (string, error) {
  5512  		errSnap = aSnap
  5513  		errMsg = aErrMsg
  5514  		errSig = aDupSig
  5515  		errExtra = extra
  5516  		n += 1
  5517  		return "oopsid", nil
  5518  	})
  5519  	defer restore()
  5520  
  5521  	si := snap.SideInfo{
  5522  		RealName: "some-snap",
  5523  		SnapID:   "some-snap-id",
  5524  		Revision: snap.R(7),
  5525  	}
  5526  
  5527  	s.state.Lock()
  5528  	defer s.state.Unlock()
  5529  
  5530  	s.state.Set("ubuntu-core-transition-retry", 7)
  5531  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5532  		Active:   true,
  5533  		Sequence: []*snap.SideInfo{&si},
  5534  		Current:  si.Revision,
  5535  		SnapType: "app",
  5536  	})
  5537  
  5538  	chg := s.state.NewChange("install", "install a snap")
  5539  	ts, err := snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  5540  	c.Assert(err, IsNil)
  5541  	chg.AddAll(ts)
  5542  
  5543  	s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11")
  5544  
  5545  	s.state.Unlock()
  5546  	defer s.se.Stop()
  5547  	s.settle(c)
  5548  	s.state.Lock()
  5549  
  5550  	// verify we generated a failure report
  5551  	c.Check(n, Equals, 1)
  5552  	c.Check(errSnap, Equals, "some-snap")
  5553  	c.Check(errExtra, DeepEquals, map[string]string{
  5554  		"UbuntuCoreTransitionCount": "7",
  5555  		"Channel":                   "some-channel",
  5556  		"Revision":                  "11",
  5557  	})
  5558  	c.Check(errMsg, Matches, `(?sm)change "install": "install a snap"
  5559  prerequisites: Undo
  5560   snap-setup: "some-snap" \(11\) "some-channel"
  5561  download-snap: Undoing
  5562  validate-snap: Done
  5563  .*
  5564  link-snap: Error
  5565   INFO unlink
  5566   ERROR fail
  5567  auto-connect: Hold
  5568  set-auto-aliases: Hold
  5569  setup-aliases: Hold
  5570  run-hook: Hold
  5571  start-snap-services: Hold
  5572  cleanup: Hold
  5573  run-hook: Hold`)
  5574  	c.Check(errSig, Matches, `(?sm)snap-install:
  5575  prerequisites: Undo
  5576   snap-setup: "some-snap"
  5577  download-snap: Undoing
  5578  validate-snap: Done
  5579  .*
  5580  link-snap: Error
  5581   INFO unlink
  5582   ERROR fail
  5583  auto-connect: Hold
  5584  set-auto-aliases: Hold
  5585  setup-aliases: Hold
  5586  run-hook: Hold
  5587  start-snap-services: Hold
  5588  cleanup: Hold
  5589  run-hook: Hold`)
  5590  
  5591  	// run again with empty "ubuntu-core-transition-retry"
  5592  	s.state.Set("ubuntu-core-transition-retry", 0)
  5593  	chg = s.state.NewChange("install", "install a snap")
  5594  	ts, err = snapstate.Update(s.state, "some-snap", &snapstate.RevisionOptions{Channel: "some-channel"}, s.user.ID, snapstate.Flags{})
  5595  	c.Assert(err, IsNil)
  5596  	chg.AddAll(ts)
  5597  	s.state.Unlock()
  5598  	defer s.se.Stop()
  5599  	s.settle(c)
  5600  	s.state.Lock()
  5601  	// verify that we excluded this field from the bugreport
  5602  	c.Check(n, Equals, 2)
  5603  	c.Check(errExtra, DeepEquals, map[string]string{
  5604  		"Channel":  "some-channel",
  5605  		"Revision": "11",
  5606  	})
  5607  }
  5608  
  5609  func (s *snapmgrTestSuite) TestNoReRefreshInUpdate(c *C) {
  5610  	s.state.Lock()
  5611  	defer s.state.Unlock()
  5612  
  5613  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5614  		Active: true,
  5615  		Sequence: []*snap.SideInfo{
  5616  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
  5617  		},
  5618  		Current:  snap.R(1),
  5619  		SnapType: "app",
  5620  	})
  5621  
  5622  	ts, err := snapstate.Update(s.state, "some-snap", nil, 0, snapstate.Flags{NoReRefresh: true})
  5623  	c.Assert(err, IsNil)
  5624  
  5625  	// ensure we have no re-refresh task
  5626  	for _, t := range ts.Tasks() {
  5627  		c.Assert(t.Kind(), Not(Equals), "check-rerefresh")
  5628  	}
  5629  
  5630  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
  5631  	c.Assert(err, IsNil)
  5632  	// NoReRefresh is consumed and consulted when creating the taskset
  5633  	// but is not copied into SnapSetup
  5634  	c.Check(snapsup.Flags.NoReRefresh, Equals, false)
  5635  }
  5636  
  5637  func (s *snapmgrTestSuite) TestEmptyUpdateWithChannelChangeAndAutoAlias(c *C) {
  5638  	// this reproduces the cause behind lp:1860324,
  5639  	// namely an empty refresh with a channel change on a snap
  5640  	// with changed aliases
  5641  
  5642  	s.state.Lock()
  5643  	defer s.state.Unlock()
  5644  
  5645  	n := 0
  5646  	snapstate.AutoAliases = func(st *state.State, info *snap.Info) (map[string]string, error) {
  5647  		if info.InstanceName() == "alias-snap" {
  5648  			if n > 0 {
  5649  				return map[string]string{
  5650  					"alias1": "cmd1",
  5651  					"alias2": "cmd2",
  5652  				}, nil
  5653  			}
  5654  			n++
  5655  		}
  5656  		return nil, nil
  5657  	}
  5658  
  5659  	snapstate.Set(s.state, "alias-snap", &snapstate.SnapState{
  5660  		TrackingChannel: "latest/stable",
  5661  		Sequence: []*snap.SideInfo{
  5662  			{RealName: "alias-snap", Revision: snap.R(11), SnapID: "alias-snap-id"},
  5663  		},
  5664  		Current: snap.R(11),
  5665  		Active:  true,
  5666  	})
  5667  
  5668  	s.state.Set("aliases", map[string]map[string]string{
  5669  		"alias-snap": {
  5670  			"alias1": "auto",
  5671  		},
  5672  	})
  5673  
  5674  	s.state.Unlock()
  5675  	err := s.snapmgr.Ensure()
  5676  	s.state.Lock()
  5677  	c.Assert(err, IsNil)
  5678  
  5679  	ts, err := snapstate.Update(s.state, "alias-snap", &snapstate.RevisionOptions{Channel: "latest/candidate"}, s.user.ID, snapstate.Flags{})
  5680  	c.Assert(err, IsNil)
  5681  
  5682  	chg := s.state.NewChange("refresh", "refresh snap")
  5683  	chg.AddAll(ts)
  5684  
  5685  	s.state.Unlock()
  5686  	defer s.se.Stop()
  5687  	s.settle(c)
  5688  	s.state.Lock()
  5689  
  5690  	c.Assert(chg.Err(), IsNil)
  5691  	c.Assert(chg.IsReady(), Equals, true)
  5692  }
  5693  
  5694  func (s *snapmgrTestSuite) testUpdateDiskSpaceCheck(c *C, featureFlag, failInstallSize, failDiskCheck bool) error {
  5695  	restore := snapstate.MockOsutilCheckFreeSpace(func(path string, sz uint64) error {
  5696  		c.Check(sz, Equals, snapstate.SafetyMarginDiskSpace(123))
  5697  		if failDiskCheck {
  5698  			return &osutil.NotEnoughDiskSpaceError{}
  5699  		}
  5700  		return nil
  5701  	})
  5702  	defer restore()
  5703  
  5704  	var installSizeCalled bool
  5705  
  5706  	restoreInstallSize := snapstate.MockInstallSize(func(st *state.State, snaps []snapstate.MinimalInstallInfo, userID int) (uint64, error) {
  5707  		installSizeCalled = true
  5708  		if failInstallSize {
  5709  			return 0, fmt.Errorf("boom")
  5710  		}
  5711  		c.Assert(snaps, HasLen, 1)
  5712  		c.Check(snaps[0].InstanceName(), Equals, "some-snap")
  5713  		return 123, nil
  5714  	})
  5715  	defer restoreInstallSize()
  5716  
  5717  	s.state.Lock()
  5718  	defer s.state.Unlock()
  5719  
  5720  	tr := config.NewTransaction(s.state)
  5721  	tr.Set("core", "experimental.check-disk-space-refresh", featureFlag)
  5722  	tr.Commit()
  5723  
  5724  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  5725  		Active: true,
  5726  		Sequence: []*snap.SideInfo{
  5727  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(4)},
  5728  		},
  5729  		Current:  snap.R(4),
  5730  		SnapType: "app",
  5731  	})
  5732  
  5733  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  5734  	_, err := snapstate.Update(s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  5735  
  5736  	if featureFlag {
  5737  		c.Check(installSizeCalled, Equals, true)
  5738  	} else {
  5739  		c.Check(installSizeCalled, Equals, false)
  5740  	}
  5741  
  5742  	return err
  5743  }
  5744  
  5745  func (s *snapmgrTestSuite) TestUpdateDiskSpaceError(c *C) {
  5746  	featureFlag := true
  5747  	failInstallSize := false
  5748  	failDiskCheck := true
  5749  	err := s.testUpdateDiskSpaceCheck(c, featureFlag, failInstallSize, failDiskCheck)
  5750  	diskSpaceErr := err.(*snapstate.InsufficientSpaceError)
  5751  	c.Assert(diskSpaceErr, ErrorMatches, `insufficient space in .* to perform "refresh" change for the following snaps: some-snap`)
  5752  	c.Check(diskSpaceErr.Path, Equals, filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd"))
  5753  	c.Check(diskSpaceErr.Snaps, DeepEquals, []string{"some-snap"})
  5754  }
  5755  
  5756  func (s *snapmgrTestSuite) TestUpdateDiskCheckSkippedIfDisabled(c *C) {
  5757  	featureFlag := false
  5758  	failInstallSize := false
  5759  	failDiskCheck := true
  5760  	err := s.testUpdateDiskSpaceCheck(c, featureFlag, failInstallSize, failDiskCheck)
  5761  	c.Check(err, IsNil)
  5762  }
  5763  
  5764  func (s *snapmgrTestSuite) TestUpdateDiskCheckInstallSizeError(c *C) {
  5765  	featureFlag := true
  5766  	failInstallSize := true
  5767  	failDiskCheck := false
  5768  	err := s.testUpdateDiskSpaceCheck(c, featureFlag, failInstallSize, failDiskCheck)
  5769  	c.Check(err, ErrorMatches, "boom")
  5770  }
  5771  
  5772  func (s *snapmgrTestSuite) TestUpdateDiskCheckHappy(c *C) {
  5773  	featureFlag := true
  5774  	failInstallSize := false
  5775  	failDiskCheck := false
  5776  	err := s.testUpdateDiskSpaceCheck(c, featureFlag, failInstallSize, failDiskCheck)
  5777  	c.Check(err, IsNil)
  5778  }