github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/snapstate/snapstate_install_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  	"fmt"
    25  	"io/ioutil"
    26  	"path/filepath"
    27  	"time"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"github.com/snapcore/snapd/boot"
    32  	"github.com/snapcore/snapd/dirs"
    33  	"github.com/snapcore/snapd/gadget"
    34  	"github.com/snapcore/snapd/interfaces"
    35  	"github.com/snapcore/snapd/osutil"
    36  	"github.com/snapcore/snapd/overlord/auth"
    37  	"github.com/snapcore/snapd/overlord/configstate/config"
    38  	"github.com/snapcore/snapd/overlord/hookstate"
    39  	"github.com/snapcore/snapd/overlord/ifacestate/ifacerepo"
    40  	"github.com/snapcore/snapd/overlord/snapstate"
    41  	"github.com/snapcore/snapd/overlord/snapstate/backend"
    42  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    43  	"github.com/snapcore/snapd/overlord/state"
    44  	"github.com/snapcore/snapd/release"
    45  	"github.com/snapcore/snapd/snap"
    46  	"github.com/snapcore/snapd/snap/snaptest"
    47  	"github.com/snapcore/snapd/store"
    48  	"github.com/snapcore/snapd/testutil"
    49  
    50  	// So it registers Configure.
    51  	_ "github.com/snapcore/snapd/overlord/configstate"
    52  )
    53  
    54  func verifyInstallTasks(c *C, opts, discards int, ts *state.TaskSet, st *state.State) {
    55  	kinds := taskKinds(ts.Tasks())
    56  
    57  	expected := []string{
    58  		"prerequisites",
    59  		"download-snap",
    60  		"validate-snap",
    61  		"mount-snap",
    62  	}
    63  	if opts&unlinkBefore != 0 {
    64  		expected = append(expected,
    65  			"stop-snap-services",
    66  			"remove-aliases",
    67  			"unlink-current-snap",
    68  		)
    69  	}
    70  	if opts&updatesGadget != 0 {
    71  		expected = append(expected, "update-gadget-assets")
    72  	}
    73  	expected = append(expected,
    74  		"copy-snap-data",
    75  		"setup-profiles",
    76  		"link-snap",
    77  	)
    78  	expected = append(expected,
    79  		"auto-connect",
    80  		"set-auto-aliases",
    81  		"setup-aliases",
    82  		"run-hook[install]",
    83  		"start-snap-services")
    84  	for i := 0; i < discards; i++ {
    85  		expected = append(expected,
    86  			"clear-snap",
    87  			"discard-snap",
    88  		)
    89  	}
    90  	if opts&cleanupAfter != 0 {
    91  		expected = append(expected,
    92  			"cleanup",
    93  		)
    94  	}
    95  	if opts&noConfigure == 0 {
    96  		expected = append(expected,
    97  			"run-hook[configure]",
    98  		)
    99  	}
   100  	expected = append(expected,
   101  		"run-hook[check-health]",
   102  	)
   103  
   104  	c.Assert(kinds, DeepEquals, expected)
   105  }
   106  
   107  func (s *snapmgrTestSuite) TestInstallDevModeConfinementFiltering(c *C) {
   108  	s.state.Lock()
   109  	defer s.state.Unlock()
   110  
   111  	// if a snap is devmode, you can't install it without --devmode
   112  	opts := &snapstate.RevisionOptions{Channel: "channel-for-devmode"}
   113  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
   114  	c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`)
   115  
   116  	// if a snap is devmode, you *can* install it with --devmode
   117  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{DevMode: true})
   118  	c.Assert(err, IsNil)
   119  
   120  	// if a snap is *not* devmode, you can still install it with --devmode
   121  	opts.Channel = "channel-for-strict"
   122  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{DevMode: true})
   123  	c.Assert(err, IsNil)
   124  }
   125  
   126  func (s *snapmgrTestSuite) TestInstallClassicConfinementFiltering(c *C) {
   127  	restore := maybeMockClassicSupport(c)
   128  	defer restore()
   129  
   130  	s.state.Lock()
   131  	defer s.state.Unlock()
   132  
   133  	// if a snap is classic, you can't install it without --classic
   134  	opts := &snapstate.RevisionOptions{Channel: "channel-for-classic"}
   135  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
   136  	c.Assert(err, ErrorMatches, `.* requires classic confinement`)
   137  
   138  	// if a snap is classic, you *can* install it with --classic
   139  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true})
   140  	c.Assert(err, IsNil)
   141  
   142  	// if a snap is *not* classic, but can install it with --classic which gets ignored
   143  	opts.Channel = "channel-for-strict"
   144  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true})
   145  	c.Assert(err, IsNil)
   146  }
   147  
   148  func (s *snapmgrTestSuite) TestInstallTasks(c *C) {
   149  	s.state.Lock()
   150  	defer s.state.Unlock()
   151  
   152  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
   153  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{})
   154  	c.Assert(err, IsNil)
   155  
   156  	verifyInstallTasks(c, 0, 0, ts, s.state)
   157  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   158  }
   159  
   160  func (s *snapmgrTestSuite) TestInstallTaskEdgesForPreseeding(c *C) {
   161  	s.state.Lock()
   162  	defer s.state.Unlock()
   163  
   164  	mockSnap := makeTestSnap(c, `name: some-snap
   165  version: 1.0
   166  `)
   167  
   168  	for _, skipConfig := range []bool{false, true} {
   169  		ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{SkipConfigure: skipConfig})
   170  		c.Assert(err, IsNil)
   171  
   172  		te, err := ts.Edge(snapstate.BeginEdge)
   173  		c.Assert(err, IsNil)
   174  		c.Check(te.Kind(), Equals, "prerequisites")
   175  
   176  		te, err = ts.Edge(snapstate.BeforeHooksEdge)
   177  		c.Assert(err, IsNil)
   178  		c.Check(te.Kind(), Equals, "setup-aliases")
   179  
   180  		te, err = ts.Edge(snapstate.HooksEdge)
   181  		c.Assert(err, IsNil)
   182  		c.Assert(te.Kind(), Equals, "run-hook")
   183  
   184  		var hsup *hookstate.HookSetup
   185  		c.Assert(te.Get("hook-setup", &hsup), IsNil)
   186  		c.Check(hsup.Hook, Equals, "install")
   187  		c.Check(hsup.Snap, Equals, "some-snap")
   188  	}
   189  }
   190  
   191  func (s *snapmgrTestSuite) TestInstallSnapdSnapType(c *C) {
   192  	s.state.Lock()
   193  	defer s.state.Unlock()
   194  
   195  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
   196  	ts, err := snapstate.Install(context.Background(), s.state, "snapd", opts, 0, snapstate.Flags{})
   197  	c.Assert(err, IsNil)
   198  
   199  	verifyInstallTasks(c, noConfigure, 0, ts, s.state)
   200  
   201  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
   202  	c.Assert(err, IsNil)
   203  	c.Check(snapsup.Type, Equals, snap.TypeSnapd)
   204  }
   205  
   206  func (s *snapmgrTestSuite) TestInstallCohortTasks(c *C) {
   207  	s.state.Lock()
   208  	defer s.state.Unlock()
   209  
   210  	opts := &snapstate.RevisionOptions{Channel: "some-channel", CohortKey: "what"}
   211  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{})
   212  	c.Assert(err, IsNil)
   213  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
   214  	c.Assert(err, IsNil)
   215  	c.Check(snapsup.CohortKey, Equals, "what")
   216  
   217  	verifyInstallTasks(c, 0, 0, ts, s.state)
   218  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   219  }
   220  
   221  func (s *snapmgrTestSuite) TestInstallWithDeviceContext(c *C) {
   222  	s.state.Lock()
   223  	defer s.state.Unlock()
   224  
   225  	// unset the global store, it will need to come via the device context
   226  	snapstate.ReplaceStore(s.state, nil)
   227  
   228  	deviceCtx := &snapstatetest.TrivialDeviceContext{CtxStore: s.fakeStore}
   229  
   230  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
   231  	ts, err := snapstate.InstallWithDeviceContext(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{}, deviceCtx, "")
   232  	c.Assert(err, IsNil)
   233  
   234  	verifyInstallTasks(c, 0, 0, ts, s.state)
   235  	c.Assert(s.state.TaskCount(), Equals, len(ts.Tasks()))
   236  }
   237  
   238  func (s *snapmgrTestSuite) TestInstallHookNotRunForInstalledSnap(c *C) {
   239  	s.state.Lock()
   240  	defer s.state.Unlock()
   241  
   242  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   243  		Active: true,
   244  		Sequence: []*snap.SideInfo{
   245  			{RealName: "some-snap", Revision: snap.R(7)},
   246  		},
   247  		Current:  snap.R(7),
   248  		SnapType: "app",
   249  	})
   250  
   251  	mockSnap := makeTestSnap(c, `name: some-snap
   252  version: 1.0
   253  epoch: 1*
   254  `)
   255  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{})
   256  	c.Assert(err, IsNil)
   257  
   258  	runHooks := tasksWithKind(ts, "run-hook")
   259  	// no install hook task
   260  	c.Assert(taskKinds(runHooks), DeepEquals, []string{
   261  		"run-hook[pre-refresh]",
   262  		"run-hook[post-refresh]",
   263  		"run-hook[configure]",
   264  		"run-hook[check-health]",
   265  	})
   266  }
   267  
   268  func (s *snapmgrTestSuite) TestInstallFailsOnDisabledSnap(c *C) {
   269  	s.state.Lock()
   270  	defer s.state.Unlock()
   271  
   272  	snapst := &snapstate.SnapState{
   273  		Active:          false,
   274  		TrackingChannel: "channel/stable",
   275  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}},
   276  		Current:         snap.R(2),
   277  		SnapType:        "app",
   278  	}
   279  	snapsup := &snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}}
   280  	_, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "", nil)
   281  	c.Assert(err, NotNil)
   282  	c.Assert(err, ErrorMatches, `cannot update disabled snap "some-snap"`)
   283  }
   284  
   285  func dummyInUseCheck(snap.Type) (boot.InUseFunc, error) {
   286  	return func(string, snap.Revision) bool {
   287  		return false
   288  	}, nil
   289  }
   290  
   291  func (s *snapmgrTestSuite) TestInstallFailsOnBusySnap(c *C) {
   292  	s.state.Lock()
   293  	defer s.state.Unlock()
   294  
   295  	// With the refresh-app-awareness feature enabled.
   296  	tr := config.NewTransaction(s.state)
   297  	tr.Set("core", "experimental.refresh-app-awareness", true)
   298  	tr.Commit()
   299  
   300  	// With a snap state indicating a snap is already installed.
   301  	snapst := &snapstate.SnapState{
   302  		Active: true,
   303  		Sequence: []*snap.SideInfo{
   304  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   305  		},
   306  		Current:  snap.R(1),
   307  		SnapType: "app",
   308  	}
   309  	snapstate.Set(s.state, "some-snap", snapst)
   310  
   311  	// With a snap info indicating it has an application called "app"
   312  	snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
   313  		if name != "some-snap" {
   314  			return s.fakeBackend.ReadInfo(name, si)
   315  		}
   316  		info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp}
   317  		info.Apps = map[string]*snap.AppInfo{
   318  			"app": {Snap: info, Name: "app"},
   319  		}
   320  		return info, nil
   321  	})
   322  
   323  	// mock that "some-snap" has an app and that this app has pids running
   324  	restore := snapstate.MockPidsOfSnap(func(instanceName string) (map[string][]int, error) {
   325  		c.Assert(instanceName, Equals, "some-snap")
   326  		return map[string][]int{
   327  			"snap.some-snap.app": {1234},
   328  		}, nil
   329  	})
   330  	defer restore()
   331  
   332  	// Attempt to install revision 2 of the snap.
   333  	snapsup := &snapstate.SnapSetup{
   334  		SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
   335  	}
   336  
   337  	// And observe that we cannot refresh because the snap is busy.
   338  	_, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "", dummyInUseCheck)
   339  	c.Assert(err, ErrorMatches, `snap "some-snap" has running apps \(app\)`)
   340  
   341  	// The state records the time of the failed refresh operation.
   342  	err = snapstate.Get(s.state, "some-snap", snapst)
   343  	c.Assert(err, IsNil)
   344  	c.Check(snapst.RefreshInhibitedTime, NotNil)
   345  }
   346  
   347  func (s *snapmgrTestSuite) TestInstallDespiteBusySnap(c *C) {
   348  	s.state.Lock()
   349  	defer s.state.Unlock()
   350  
   351  	// With the refresh-app-awareness feature enabled.
   352  	tr := config.NewTransaction(s.state)
   353  	tr.Set("core", "experimental.refresh-app-awareness", true)
   354  	tr.Commit()
   355  
   356  	// With a snap state indicating a snap is already installed and it failed
   357  	// to refresh over a week ago. Use UTC and Round to have predictable
   358  	// behaviour across time-zones and with enough precision loss to be
   359  	// compatible with the serialization format.
   360  	var longAgo = time.Now().UTC().Round(time.Second).Add(-time.Hour * 24 * 8)
   361  	snapst := &snapstate.SnapState{
   362  		Active: true,
   363  		Sequence: []*snap.SideInfo{
   364  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   365  		},
   366  		Current:              snap.R(1),
   367  		SnapType:             "app",
   368  		RefreshInhibitedTime: &longAgo,
   369  	}
   370  	snapstate.Set(s.state, "some-snap", snapst)
   371  
   372  	// With a snap info indicating it has an application called "app"
   373  	snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) {
   374  		if name != "some-snap" {
   375  			return s.fakeBackend.ReadInfo(name, si)
   376  		}
   377  		info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp}
   378  		info.Apps = map[string]*snap.AppInfo{
   379  			"app": {Snap: info, Name: "app"},
   380  		}
   381  		return info, nil
   382  	})
   383  	// And with cgroup information indicating the app has a process with pid 1234.
   384  	restore := snapstate.MockPidsOfSnap(func(instanceName string) (map[string][]int, error) {
   385  		c.Assert(instanceName, Equals, "some-snap")
   386  		return map[string][]int{
   387  			"snap.some-snap.some-app": {1234},
   388  		}, nil
   389  	})
   390  	defer restore()
   391  
   392  	// Attempt to install revision 2 of the snap.
   393  	snapsup := &snapstate.SnapSetup{
   394  		SideInfo: &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)},
   395  	}
   396  
   397  	// And observe that refresh occurred regardless of the running process.
   398  	_, err := snapstate.DoInstall(s.state, snapst, snapsup, 0, "", dummyInUseCheck)
   399  	c.Assert(err, IsNil)
   400  }
   401  
   402  func (s *snapmgrTestSuite) TestInstallFailsOnSystem(c *C) {
   403  	s.state.Lock()
   404  	defer s.state.Unlock()
   405  
   406  	snapsup := &snapstate.SnapSetup{SideInfo: &snap.SideInfo{RealName: "system", SnapID: "some-snap-id", Revision: snap.R(1)}}
   407  	_, err := snapstate.DoInstall(s.state, nil, snapsup, 0, "", nil)
   408  	c.Assert(err, NotNil)
   409  	c.Assert(err, ErrorMatches, `cannot install reserved snap name 'system'`)
   410  }
   411  
   412  func (s *snapmgrTestSuite) TestDoInstallChannelDefault(c *C) {
   413  	s.state.Lock()
   414  	defer s.state.Unlock()
   415  
   416  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
   417  	c.Assert(err, IsNil)
   418  
   419  	var snapsup snapstate.SnapSetup
   420  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
   421  	c.Assert(err, IsNil)
   422  
   423  	c.Check(snapsup.Channel, Equals, "stable")
   424  }
   425  
   426  func (s *snapmgrTestSuite) TestInstallRevision(c *C) {
   427  	s.state.Lock()
   428  	defer s.state.Unlock()
   429  
   430  	opts := &snapstate.RevisionOptions{Revision: snap.R(7)}
   431  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{})
   432  	c.Assert(err, IsNil)
   433  
   434  	var snapsup snapstate.SnapSetup
   435  	err = ts.Tasks()[0].Get("snap-setup", &snapsup)
   436  	c.Assert(err, IsNil)
   437  
   438  	c.Check(snapsup.Revision(), Equals, snap.R(7))
   439  }
   440  
   441  func (s *snapmgrTestSuite) TestInstallTooEarly(c *C) {
   442  	s.state.Lock()
   443  	defer s.state.Unlock()
   444  
   445  	s.state.Set("seeded", nil)
   446  
   447  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
   448  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
   449  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
   450  }
   451  
   452  func (s *snapmgrTestSuite) TestInstallConflict(c *C) {
   453  	s.state.Lock()
   454  	defer s.state.Unlock()
   455  
   456  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
   457  	c.Assert(err, IsNil)
   458  	// need a change to make the tasks visible
   459  	s.state.NewChange("install", "...").AddAll(ts)
   460  
   461  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
   462  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
   463  	c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`)
   464  }
   465  
   466  func (s *snapmgrTestSuite) TestInstallAliasConflict(c *C) {
   467  	s.state.Lock()
   468  	defer s.state.Unlock()
   469  
   470  	snapstate.Set(s.state, "otherfoosnap", &snapstate.SnapState{
   471  		Sequence: []*snap.SideInfo{
   472  			{RealName: "otherfoosnap", Revision: snap.R(30)},
   473  		},
   474  		Current: snap.R(30),
   475  		Active:  true,
   476  		Aliases: map[string]*snapstate.AliasTarget{
   477  			"foo.bar": {Manual: "bar"},
   478  		},
   479  		SnapType: "app",
   480  	})
   481  
   482  	_, err := snapstate.Install(context.Background(), s.state, "foo", nil, 0, snapstate.Flags{})
   483  	c.Assert(err, ErrorMatches, `snap "foo" command namespace conflicts with alias "foo\.bar" for "otherfoosnap" snap`)
   484  }
   485  
   486  func (s *snapmgrTestSuite) TestInstallStrictIgnoresClassic(c *C) {
   487  	restore := maybeMockClassicSupport(c)
   488  	defer restore()
   489  
   490  	s.state.Lock()
   491  	defer s.state.Unlock()
   492  
   493  	opts := &snapstate.RevisionOptions{Channel: "channel-for-strict"}
   494  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true})
   495  	c.Assert(err, IsNil)
   496  
   497  	c.Assert(err, IsNil)
   498  
   499  	chg := s.state.NewChange("install", "install snap")
   500  	chg.AddAll(ts)
   501  
   502  	s.state.Unlock()
   503  	defer s.se.Stop()
   504  	s.settle(c)
   505  	s.state.Lock()
   506  
   507  	c.Assert(chg.Err(), IsNil)
   508  	c.Assert(chg.IsReady(), Equals, true)
   509  
   510  	// verify snap is *not* classic
   511  	var snapst snapstate.SnapState
   512  	err = snapstate.Get(s.state, "some-snap", &snapst)
   513  	c.Assert(err, IsNil)
   514  	c.Check(snapst.TrackingChannel, Equals, "channel-for-strict/stable")
   515  	c.Check(snapst.Classic, Equals, false)
   516  }
   517  
   518  func (s *snapmgrTestSuite) TestInstallSnapWithDefaultTrack(c *C) {
   519  	restore := maybeMockClassicSupport(c)
   520  	defer restore()
   521  
   522  	s.state.Lock()
   523  	defer s.state.Unlock()
   524  
   525  	opts := &snapstate.RevisionOptions{Channel: "candidate"}
   526  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap-with-default-track", opts, s.user.ID, snapstate.Flags{})
   527  	c.Assert(err, IsNil)
   528  
   529  	chg := s.state.NewChange("install", "install snap")
   530  	chg.AddAll(ts)
   531  
   532  	s.state.Unlock()
   533  	defer s.se.Stop()
   534  	s.settle(c)
   535  	s.state.Lock()
   536  
   537  	c.Assert(chg.Err(), IsNil)
   538  	c.Assert(chg.IsReady(), Equals, true)
   539  
   540  	// verify snap is in the 2.0 track
   541  	var snapst snapstate.SnapState
   542  	err = snapstate.Get(s.state, "some-snap-with-default-track", &snapst)
   543  	c.Assert(err, IsNil)
   544  	c.Check(snapst.TrackingChannel, Equals, "2.0/candidate")
   545  }
   546  
   547  func (s *snapmgrTestSuite) TestInstallManySnapOneWithDefaultTrack(c *C) {
   548  	restore := maybeMockClassicSupport(c)
   549  	defer restore()
   550  
   551  	s.state.Lock()
   552  	defer s.state.Unlock()
   553  
   554  	snapNames := []string{"some-snap", "some-snap-with-default-track"}
   555  	installed, tss, err := snapstate.InstallMany(s.state, snapNames, s.user.ID)
   556  	c.Assert(err, IsNil)
   557  	c.Assert(installed, DeepEquals, snapNames)
   558  
   559  	chg := s.state.NewChange("install", "install two snaps")
   560  	for _, ts := range tss {
   561  		chg.AddAll(ts)
   562  	}
   563  
   564  	s.state.Unlock()
   565  	defer s.se.Stop()
   566  	s.settle(c)
   567  	s.state.Lock()
   568  
   569  	c.Assert(chg.Err(), IsNil)
   570  	c.Assert(chg.IsReady(), Equals, true)
   571  
   572  	// verify snap is in the 2.0 track
   573  	var snapst snapstate.SnapState
   574  	err = snapstate.Get(s.state, "some-snap-with-default-track", &snapst)
   575  	c.Assert(err, IsNil)
   576  	c.Check(snapst.TrackingChannel, Equals, "2.0/stable")
   577  
   578  	err = snapstate.Get(s.state, "some-snap", &snapst)
   579  	c.Assert(err, IsNil)
   580  	c.Check(snapst.TrackingChannel, Equals, "latest/stable")
   581  }
   582  
   583  // A sneakyStore changes the state when called
   584  type sneakyStore struct {
   585  	*fakeStore
   586  	state *state.State
   587  }
   588  
   589  func (s sneakyStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) {
   590  	s.state.Lock()
   591  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   592  		Active:          true,
   593  		TrackingChannel: "latest/edge",
   594  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}},
   595  		Current:         snap.R(1),
   596  		SnapType:        "app",
   597  	})
   598  	s.state.Unlock()
   599  	return s.fakeStore.SnapAction(ctx, currentSnaps, actions, assertQuery, user, opts)
   600  }
   601  
   602  func (s *snapmgrTestSuite) TestInstallStateConflict(c *C) {
   603  	s.state.Lock()
   604  	defer s.state.Unlock()
   605  
   606  	snapstate.ReplaceStore(s.state, sneakyStore{fakeStore: s.fakeStore, state: s.state})
   607  
   608  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
   609  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
   610  	c.Assert(err, ErrorMatches, `snap "some-snap" has changes in progress`)
   611  }
   612  
   613  func (s *snapmgrTestSuite) TestInstallPathTooEarly(c *C) {
   614  	s.state.Lock()
   615  	defer s.state.Unlock()
   616  
   617  	r := snapstatetest.MockDeviceModel(nil)
   618  	defer r()
   619  
   620  	mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0")
   621  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{})
   622  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
   623  	c.Assert(err, ErrorMatches, `too early for operation, device model not yet acknowledged`)
   624  
   625  }
   626  
   627  func (s *snapmgrTestSuite) TestInstallPathConflict(c *C) {
   628  	s.state.Lock()
   629  	defer s.state.Unlock()
   630  
   631  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", nil, 0, snapstate.Flags{})
   632  	c.Assert(err, IsNil)
   633  	// need a change to make the tasks visible
   634  	s.state.NewChange("install", "...").AddAll(ts)
   635  
   636  	mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0")
   637  	_, _, err = snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{})
   638  	c.Assert(err, ErrorMatches, `snap "some-snap" has "install" change in progress`)
   639  }
   640  
   641  func (s *snapmgrTestSuite) TestInstallPathMissingName(c *C) {
   642  	s.state.Lock()
   643  	defer s.state.Unlock()
   644  
   645  	mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0")
   646  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{}, mockSnap, "", "", snapstate.Flags{})
   647  	c.Assert(err, ErrorMatches, fmt.Sprintf(`internal error: snap name to install %q not provided`, mockSnap))
   648  }
   649  
   650  func (s *snapmgrTestSuite) TestInstallPathSnapIDRevisionUnset(c *C) {
   651  	s.state.Lock()
   652  	defer s.state.Unlock()
   653  
   654  	mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0")
   655  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "snapididid"}, mockSnap, "", "", snapstate.Flags{})
   656  	c.Assert(err, ErrorMatches, fmt.Sprintf(`internal error: snap id set to install %q but revision is unset`, mockSnap))
   657  }
   658  
   659  func (s *snapmgrTestSuite) TestInstallPathValidateFlags(c *C) {
   660  	s.state.Lock()
   661  	defer s.state.Unlock()
   662  
   663  	mockSnap := makeTestSnap(c, `name: some-snap
   664  version: 1.0
   665  confinement: devmode
   666  `)
   667  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{})
   668  	c.Assert(err, ErrorMatches, `.* requires devmode or confinement override`)
   669  }
   670  
   671  func (s *snapmgrTestSuite) TestInstallPathStrictIgnoresClassic(c *C) {
   672  	restore := maybeMockClassicSupport(c)
   673  	defer restore()
   674  
   675  	s.state.Lock()
   676  	defer s.state.Unlock()
   677  
   678  	mockSnap := makeTestSnap(c, `name: some-snap
   679  version: 1.0
   680  confinement: strict
   681  `)
   682  
   683  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{Classic: true})
   684  	c.Assert(err, IsNil)
   685  
   686  	c.Assert(err, IsNil)
   687  
   688  	chg := s.state.NewChange("install", "install snap")
   689  	chg.AddAll(ts)
   690  
   691  	s.state.Unlock()
   692  	defer s.se.Stop()
   693  	s.settle(c)
   694  	s.state.Lock()
   695  
   696  	c.Assert(chg.Err(), IsNil)
   697  	c.Assert(chg.IsReady(), Equals, true)
   698  
   699  	// verify snap is *not* classic
   700  	var snapst snapstate.SnapState
   701  	err = snapstate.Get(s.state, "some-snap", &snapst)
   702  	c.Assert(err, IsNil)
   703  	c.Check(snapst.Classic, Equals, false)
   704  }
   705  
   706  func (s *snapmgrTestSuite) TestInstallPathAsRefresh(c *C) {
   707  	s.state.Lock()
   708  	defer s.state.Unlock()
   709  
   710  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   711  		Active: true,
   712  		Flags:  snapstate.Flags{DevMode: true},
   713  		Sequence: []*snap.SideInfo{
   714  			{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)},
   715  		},
   716  		Current:         snap.R(1),
   717  		SnapType:        "app",
   718  		TrackingChannel: "wibbly/stable",
   719  	})
   720  
   721  	mockSnap := makeTestSnap(c, `name: some-snap
   722  version: 1.0
   723  epoch: 1
   724  `)
   725  
   726  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "edge", snapstate.Flags{})
   727  	c.Assert(err, IsNil)
   728  
   729  	c.Assert(err, IsNil)
   730  
   731  	chg := s.state.NewChange("install", "install snap")
   732  	chg.AddAll(ts)
   733  
   734  	s.state.Unlock()
   735  	defer s.se.Stop()
   736  	s.settle(c)
   737  	s.state.Lock()
   738  
   739  	c.Assert(chg.Err(), IsNil)
   740  	c.Assert(chg.IsReady(), Equals, true)
   741  
   742  	// verify snap is *not* classic
   743  	var snapst snapstate.SnapState
   744  	err = snapstate.Get(s.state, "some-snap", &snapst)
   745  	c.Assert(err, IsNil)
   746  	c.Check(snapst.TrackingChannel, Equals, "wibbly/edge")
   747  }
   748  
   749  func (s *snapmgrTestSuite) TestParallelInstanceInstallNotAllowed(c *C) {
   750  	s.state.Lock()
   751  	defer s.state.Unlock()
   752  
   753  	snapstate.ReplaceStore(s.state, sneakyStore{fakeStore: s.fakeStore, state: s.state})
   754  
   755  	tr := config.NewTransaction(s.state)
   756  	tr.Set("core", "experimental.parallel-instances", true)
   757  	tr.Commit()
   758  
   759  	_, err := snapstate.Install(context.Background(), s.state, "core_foo", nil, 0, snapstate.Flags{})
   760  	c.Check(err, ErrorMatches, `cannot install snap of type os as "core_foo"`)
   761  
   762  	_, err = snapstate.Install(context.Background(), s.state, "some-base_foo", nil, 0, snapstate.Flags{})
   763  	c.Check(err, ErrorMatches, `cannot install snap of type base as "some-base_foo"`)
   764  
   765  	_, err = snapstate.Install(context.Background(), s.state, "some-gadget_foo", nil, 0, snapstate.Flags{})
   766  	c.Check(err, ErrorMatches, `cannot install snap of type gadget as "some-gadget_foo"`)
   767  
   768  	_, err = snapstate.Install(context.Background(), s.state, "some-kernel_foo", nil, 0, snapstate.Flags{})
   769  	c.Check(err, ErrorMatches, `cannot install snap of type kernel as "some-kernel_foo"`)
   770  
   771  	_, err = snapstate.Install(context.Background(), s.state, "some-snapd_foo", nil, 0, snapstate.Flags{})
   772  	c.Check(err, ErrorMatches, `cannot install snap of type snapd as "some-snapd_foo"`)
   773  }
   774  
   775  func (s *snapmgrTestSuite) TestInstallPathFailsEarlyOnEpochMismatch(c *C) {
   776  	s.state.Lock()
   777  	defer s.state.Unlock()
   778  
   779  	// have epoch 1* installed
   780  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
   781  		Active:          true,
   782  		TrackingChannel: "latest/edge",
   783  		Sequence:        []*snap.SideInfo{{RealName: "some-snap", Revision: snap.R(7)}},
   784  		Current:         snap.R(7),
   785  	})
   786  
   787  	// try to install epoch 42
   788  	mockSnap := makeTestSnap(c, "name: some-snap\nversion: 1.0\nepoch: 42\n")
   789  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap"}, mockSnap, "", "", snapstate.Flags{})
   790  	c.Assert(err, ErrorMatches, `cannot refresh "some-snap" to local snap with epoch 42, because it can't read the current epoch of 1\*`)
   791  }
   792  
   793  func (s *snapmgrTestSuite) TestInstallRunThrough(c *C) {
   794  	s.state.Lock()
   795  	defer s.state.Unlock()
   796  
   797  	// we start without the auxiliary store info
   798  	c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FileAbsent)
   799  
   800  	chg := s.state.NewChange("install", "install a snap")
   801  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
   802  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
   803  	c.Assert(err, IsNil)
   804  	chg.AddAll(ts)
   805  
   806  	s.state.Unlock()
   807  	defer s.se.Stop()
   808  	s.settle(c)
   809  	s.state.Lock()
   810  
   811  	// ensure all our tasks ran
   812  	c.Assert(chg.Err(), IsNil)
   813  	c.Assert(chg.IsReady(), Equals, true)
   814  	c.Check(snapstate.Installing(s.state), Equals, false)
   815  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
   816  		macaroon: s.user.StoreMacaroon,
   817  		name:     "some-snap",
   818  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
   819  	}})
   820  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
   821  	expected := fakeOps{
   822  		{
   823  			op:     "storesvc-snap-action",
   824  			userID: 1,
   825  		},
   826  		{
   827  			op: "storesvc-snap-action:action",
   828  			action: store.SnapAction{
   829  				Action:       "install",
   830  				InstanceName: "some-snap",
   831  				Channel:      "some-channel",
   832  			},
   833  			revno:  snap.R(11),
   834  			userID: 1,
   835  		},
   836  		{
   837  			op:   "storesvc-download",
   838  			name: "some-snap",
   839  		},
   840  		{
   841  			op:    "validate-snap:Doing",
   842  			name:  "some-snap",
   843  			revno: snap.R(11),
   844  		},
   845  		{
   846  			op:  "current",
   847  			old: "<no-current>",
   848  		},
   849  		{
   850  			op:   "open-snap-file",
   851  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
   852  			sinfo: snap.SideInfo{
   853  				RealName: "some-snap",
   854  				SnapID:   "some-snap-id",
   855  				Channel:  "some-channel",
   856  				Revision: snap.R(11),
   857  			},
   858  		},
   859  		{
   860  			op:    "setup-snap",
   861  			name:  "some-snap",
   862  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
   863  			revno: snap.R(11),
   864  		},
   865  		{
   866  			op:   "copy-data",
   867  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
   868  			old:  "<no-old>",
   869  		},
   870  		{
   871  			op:    "setup-profiles:Doing",
   872  			name:  "some-snap",
   873  			revno: snap.R(11),
   874  		},
   875  		{
   876  			op: "candidate",
   877  			sinfo: snap.SideInfo{
   878  				RealName: "some-snap",
   879  				SnapID:   "some-snap-id",
   880  				Channel:  "some-channel",
   881  				Revision: snap.R(11),
   882  			},
   883  		},
   884  		{
   885  			op:   "link-snap",
   886  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
   887  		},
   888  		{
   889  			op:    "auto-connect:Doing",
   890  			name:  "some-snap",
   891  			revno: snap.R(11),
   892  		},
   893  		{
   894  			op: "update-aliases",
   895  		},
   896  		{
   897  			op:    "cleanup-trash",
   898  			name:  "some-snap",
   899  			revno: snap.R(11),
   900  		},
   901  	}
   902  	// start with an easier-to-read error if this fails:
   903  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
   904  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
   905  
   906  	// check progress
   907  	ta := ts.Tasks()
   908  	task := ta[1]
   909  	_, cur, total := task.Progress()
   910  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
   911  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
   912  	c.Check(task.Summary(), Equals, `Download snap "some-snap" (11) from channel "some-channel"`)
   913  
   914  	// check install-record present
   915  	mountTask := ta[len(ta)-11]
   916  	c.Check(mountTask.Kind(), Equals, "mount-snap")
   917  	var installRecord backend.InstallRecord
   918  	c.Assert(mountTask.Get("install-record", &installRecord), IsNil)
   919  	c.Check(installRecord.TargetSnapExisted, Equals, false)
   920  
   921  	// check link/start snap summary
   922  	linkTask := ta[len(ta)-8]
   923  	c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (11) available to the system`)
   924  	startTask := ta[len(ta)-3]
   925  	c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (11) services`)
   926  
   927  	// verify snap-setup in the task state
   928  	var snapsup snapstate.SnapSetup
   929  	err = task.Get("snap-setup", &snapsup)
   930  	c.Assert(err, IsNil)
   931  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
   932  		Channel:  "some-channel",
   933  		UserID:   s.user.ID,
   934  		SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
   935  		DownloadInfo: &snap.DownloadInfo{
   936  			DownloadURL: "https://some-server.com/some/path.snap",
   937  			Size:        5,
   938  		},
   939  		SideInfo:  snapsup.SideInfo,
   940  		Type:      snap.TypeApp,
   941  		PlugsOnly: true,
   942  	})
   943  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
   944  		RealName: "some-snap",
   945  		Channel:  "some-channel",
   946  		Revision: snap.R(11),
   947  		SnapID:   "some-snap-id",
   948  	})
   949  
   950  	// verify snaps in the system state
   951  	var snaps map[string]*snapstate.SnapState
   952  	err = s.state.Get("snaps", &snaps)
   953  	c.Assert(err, IsNil)
   954  
   955  	snapst := snaps["some-snap"]
   956  	c.Assert(snapst, NotNil)
   957  	c.Assert(snapst.Active, Equals, true)
   958  	c.Assert(snapst.TrackingChannel, Equals, "some-channel/stable")
   959  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
   960  		RealName: "some-snap",
   961  		SnapID:   "some-snap-id",
   962  		Channel:  "some-channel",
   963  		Revision: snap.R(11),
   964  	})
   965  	c.Assert(snapst.Required, Equals, false)
   966  
   967  	// we end with the auxiliary store info
   968  	c.Check(snapstate.AuxStoreInfoFilename("some-snap-id"), testutil.FilePresent)
   969  }
   970  
   971  func (s *snapmgrTestSuite) TestParallelInstanceInstallRunThrough(c *C) {
   972  	s.state.Lock()
   973  	defer s.state.Unlock()
   974  
   975  	tr := config.NewTransaction(s.state)
   976  	tr.Set("core", "experimental.parallel-instances", true)
   977  	tr.Commit()
   978  
   979  	chg := s.state.NewChange("install", "install a snap")
   980  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
   981  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap_instance", opts, s.user.ID, snapstate.Flags{})
   982  	c.Assert(err, IsNil)
   983  	chg.AddAll(ts)
   984  
   985  	s.state.Unlock()
   986  	s.settle(c)
   987  	s.state.Lock()
   988  
   989  	// ensure all our tasks ran
   990  	c.Assert(chg.Err(), IsNil)
   991  	c.Assert(chg.IsReady(), Equals, true)
   992  	c.Check(snapstate.Installing(s.state), Equals, false)
   993  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
   994  		macaroon: s.user.StoreMacaroon,
   995  		name:     "some-snap",
   996  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"),
   997  	}})
   998  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true, Commentf("salts seen: %v", s.fakeStore.seenPrivacyKeys))
   999  	expected := fakeOps{
  1000  		{
  1001  			op:     "storesvc-snap-action",
  1002  			userID: 1,
  1003  		},
  1004  		{
  1005  			op: "storesvc-snap-action:action",
  1006  			action: store.SnapAction{
  1007  				Action:       "install",
  1008  				InstanceName: "some-snap_instance",
  1009  				Channel:      "some-channel",
  1010  			},
  1011  			revno:  snap.R(11),
  1012  			userID: 1,
  1013  		},
  1014  		{
  1015  			op:   "storesvc-download",
  1016  			name: "some-snap",
  1017  		},
  1018  		{
  1019  			op:    "validate-snap:Doing",
  1020  			name:  "some-snap_instance",
  1021  			revno: snap.R(11),
  1022  		},
  1023  		{
  1024  			op:  "current",
  1025  			old: "<no-current>",
  1026  		},
  1027  		{
  1028  			op:   "open-snap-file",
  1029  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"),
  1030  			sinfo: snap.SideInfo{
  1031  				RealName: "some-snap",
  1032  				SnapID:   "some-snap-id",
  1033  				Channel:  "some-channel",
  1034  				Revision: snap.R(11),
  1035  			},
  1036  		},
  1037  		{
  1038  			op:    "setup-snap",
  1039  			name:  "some-snap_instance",
  1040  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"),
  1041  			revno: snap.R(11),
  1042  		},
  1043  		{
  1044  			op:   "copy-data",
  1045  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/11"),
  1046  			old:  "<no-old>",
  1047  		},
  1048  		{
  1049  			op:    "setup-profiles:Doing",
  1050  			name:  "some-snap_instance",
  1051  			revno: snap.R(11),
  1052  		},
  1053  		{
  1054  			op: "candidate",
  1055  			sinfo: snap.SideInfo{
  1056  				RealName: "some-snap",
  1057  				SnapID:   "some-snap-id",
  1058  				Channel:  "some-channel",
  1059  				Revision: snap.R(11),
  1060  			},
  1061  		},
  1062  		{
  1063  			op:   "link-snap",
  1064  			path: filepath.Join(dirs.SnapMountDir, "some-snap_instance/11"),
  1065  		},
  1066  		{
  1067  			op:    "auto-connect:Doing",
  1068  			name:  "some-snap_instance",
  1069  			revno: snap.R(11),
  1070  		},
  1071  		{
  1072  			op: "update-aliases",
  1073  		},
  1074  		{
  1075  			op:    "cleanup-trash",
  1076  			name:  "some-snap_instance",
  1077  			revno: snap.R(11),
  1078  		},
  1079  	}
  1080  	// start with an easier-to-read error if this fails:
  1081  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1082  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  1083  
  1084  	// check progress
  1085  	ta := ts.Tasks()
  1086  	task := ta[1]
  1087  	_, cur, total := task.Progress()
  1088  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  1089  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  1090  	c.Check(task.Summary(), Equals, `Download snap "some-snap_instance" (11) from channel "some-channel"`)
  1091  
  1092  	// check link/start snap summary
  1093  	linkTask := ta[len(ta)-8]
  1094  	c.Check(linkTask.Summary(), Equals, `Make snap "some-snap_instance" (11) available to the system`)
  1095  	startTask := ta[len(ta)-3]
  1096  	c.Check(startTask.Summary(), Equals, `Start snap "some-snap_instance" (11) services`)
  1097  
  1098  	// verify snap-setup in the task state
  1099  	var snapsup snapstate.SnapSetup
  1100  	err = task.Get("snap-setup", &snapsup)
  1101  	c.Assert(err, IsNil)
  1102  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1103  		Channel:  "some-channel",
  1104  		UserID:   s.user.ID,
  1105  		SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_instance_11.snap"),
  1106  		DownloadInfo: &snap.DownloadInfo{
  1107  			DownloadURL: "https://some-server.com/some/path.snap",
  1108  			Size:        5,
  1109  		},
  1110  		SideInfo:    snapsup.SideInfo,
  1111  		Type:        snap.TypeApp,
  1112  		PlugsOnly:   true,
  1113  		InstanceKey: "instance",
  1114  	})
  1115  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1116  		RealName: "some-snap",
  1117  		Channel:  "some-channel",
  1118  		Revision: snap.R(11),
  1119  		SnapID:   "some-snap-id",
  1120  	})
  1121  
  1122  	// verify snaps in the system state
  1123  	var snaps map[string]*snapstate.SnapState
  1124  	err = s.state.Get("snaps", &snaps)
  1125  	c.Assert(err, IsNil)
  1126  
  1127  	snapst := snaps["some-snap_instance"]
  1128  	c.Assert(snapst, NotNil)
  1129  	c.Assert(snapst.Active, Equals, true)
  1130  	c.Assert(snapst.TrackingChannel, Equals, "some-channel/stable")
  1131  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1132  		RealName: "some-snap",
  1133  		SnapID:   "some-snap-id",
  1134  		Channel:  "some-channel",
  1135  		Revision: snap.R(11),
  1136  	})
  1137  	c.Assert(snapst.Required, Equals, false)
  1138  	c.Assert(snapst.InstanceKey, Equals, "instance")
  1139  
  1140  	runHooks := tasksWithKind(ts, "run-hook")
  1141  	c.Assert(taskKinds(runHooks), DeepEquals, []string{"run-hook[install]", "run-hook[configure]", "run-hook[check-health]"})
  1142  	for _, hookTask := range runHooks {
  1143  		c.Assert(hookTask.Kind(), Equals, "run-hook")
  1144  		var hooksup hookstate.HookSetup
  1145  		err = hookTask.Get("hook-setup", &hooksup)
  1146  		c.Assert(err, IsNil)
  1147  		c.Assert(hooksup.Snap, Equals, "some-snap_instance")
  1148  	}
  1149  }
  1150  
  1151  func (s *snapmgrTestSuite) TestInstallUndoRunThroughJustOneSnap(c *C) {
  1152  	s.state.Lock()
  1153  	defer s.state.Unlock()
  1154  
  1155  	chg := s.state.NewChange("install", "install a snap")
  1156  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  1157  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  1158  	c.Assert(err, IsNil)
  1159  	chg.AddAll(ts)
  1160  
  1161  	tasks := ts.Tasks()
  1162  	last := tasks[len(tasks)-1]
  1163  	// sanity
  1164  	c.Assert(last.Lanes(), HasLen, 1)
  1165  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  1166  	terr.WaitFor(last)
  1167  	terr.JoinLane(last.Lanes()[0])
  1168  	chg.AddTask(terr)
  1169  
  1170  	s.state.Unlock()
  1171  	defer s.se.Stop()
  1172  	s.settle(c)
  1173  	s.state.Lock()
  1174  
  1175  	mountTask := tasks[len(tasks)-11]
  1176  	c.Assert(mountTask.Kind(), Equals, "mount-snap")
  1177  	var installRecord backend.InstallRecord
  1178  	c.Assert(mountTask.Get("install-record", &installRecord), IsNil)
  1179  	c.Check(installRecord.TargetSnapExisted, Equals, false)
  1180  
  1181  	// ensure all our tasks ran
  1182  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  1183  		macaroon: s.user.StoreMacaroon,
  1184  		name:     "some-snap",
  1185  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  1186  	}})
  1187  	expected := fakeOps{
  1188  		{
  1189  			op:     "storesvc-snap-action",
  1190  			userID: 1,
  1191  		},
  1192  		{
  1193  			op: "storesvc-snap-action:action",
  1194  			action: store.SnapAction{
  1195  				Action:       "install",
  1196  				InstanceName: "some-snap",
  1197  				Channel:      "some-channel",
  1198  			},
  1199  			revno:  snap.R(11),
  1200  			userID: 1,
  1201  		},
  1202  		{
  1203  			op:   "storesvc-download",
  1204  			name: "some-snap",
  1205  		},
  1206  		{
  1207  			op:    "validate-snap:Doing",
  1208  			name:  "some-snap",
  1209  			revno: snap.R(11),
  1210  		},
  1211  		{
  1212  			op:  "current",
  1213  			old: "<no-current>",
  1214  		},
  1215  		{
  1216  			op:   "open-snap-file",
  1217  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  1218  			sinfo: snap.SideInfo{
  1219  				RealName: "some-snap",
  1220  				SnapID:   "some-snap-id",
  1221  				Channel:  "some-channel",
  1222  				Revision: snap.R(11),
  1223  			},
  1224  		},
  1225  		{
  1226  			op:    "setup-snap",
  1227  			name:  "some-snap",
  1228  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  1229  			revno: snap.R(11),
  1230  		},
  1231  		{
  1232  			op:   "copy-data",
  1233  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  1234  			old:  "<no-old>",
  1235  		},
  1236  		{
  1237  			op:    "setup-profiles:Doing",
  1238  			name:  "some-snap",
  1239  			revno: snap.R(11),
  1240  		},
  1241  		{
  1242  			op: "candidate",
  1243  			sinfo: snap.SideInfo{
  1244  				RealName: "some-snap",
  1245  				SnapID:   "some-snap-id",
  1246  				Channel:  "some-channel",
  1247  				Revision: snap.R(11),
  1248  			},
  1249  		},
  1250  		{
  1251  			op:   "link-snap",
  1252  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  1253  		},
  1254  		{
  1255  			op:    "auto-connect:Doing",
  1256  			name:  "some-snap",
  1257  			revno: snap.R(11),
  1258  		},
  1259  		{
  1260  			op: "update-aliases",
  1261  		},
  1262  		{
  1263  			op:   "remove-snap-aliases",
  1264  			name: "some-snap",
  1265  		},
  1266  		{
  1267  			op:   "discard-namespace",
  1268  			name: "some-snap",
  1269  		},
  1270  		{
  1271  			op:   "unlink-snap",
  1272  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  1273  
  1274  			unlinkFirstInstallUndo: true,
  1275  		},
  1276  		{
  1277  			op:    "setup-profiles:Undoing",
  1278  			name:  "some-snap",
  1279  			revno: snap.R(11),
  1280  		},
  1281  		{
  1282  			op:   "undo-copy-snap-data",
  1283  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  1284  			old:  "<no-old>",
  1285  		},
  1286  		{
  1287  			op:   "remove-snap-data-dir",
  1288  			name: "some-snap",
  1289  			path: filepath.Join(dirs.SnapDataDir, "some-snap"),
  1290  		},
  1291  		{
  1292  			op:    "undo-setup-snap",
  1293  			name:  "some-snap",
  1294  			stype: "app",
  1295  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  1296  		},
  1297  		{
  1298  			op:   "remove-snap-dir",
  1299  			name: "some-snap",
  1300  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  1301  		},
  1302  	}
  1303  	// start with an easier-to-read error if this fails:
  1304  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1305  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  1306  }
  1307  
  1308  func (s *snapmgrTestSuite) TestInstallWithCohortRunThrough(c *C) {
  1309  	s.state.Lock()
  1310  	defer s.state.Unlock()
  1311  
  1312  	chg := s.state.NewChange("install", "install a snap")
  1313  	opts := &snapstate.RevisionOptions{Channel: "some-channel", CohortKey: "scurries"}
  1314  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  1315  	c.Assert(err, IsNil)
  1316  	chg.AddAll(ts)
  1317  
  1318  	s.state.Unlock()
  1319  	defer s.se.Stop()
  1320  	s.settle(c)
  1321  	s.state.Lock()
  1322  
  1323  	// ensure all our tasks ran
  1324  	c.Assert(chg.Err(), IsNil)
  1325  	c.Assert(chg.IsReady(), Equals, true)
  1326  	c.Check(snapstate.Installing(s.state), Equals, false)
  1327  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  1328  		macaroon: s.user.StoreMacaroon,
  1329  		name:     "some-snap",
  1330  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"),
  1331  	}})
  1332  	expected := fakeOps{
  1333  		{
  1334  			op:     "storesvc-snap-action",
  1335  			userID: 1,
  1336  		},
  1337  		{
  1338  			op: "storesvc-snap-action:action",
  1339  			action: store.SnapAction{
  1340  				Action:       "install",
  1341  				InstanceName: "some-snap",
  1342  				CohortKey:    "scurries",
  1343  				Channel:      "some-channel",
  1344  			},
  1345  			revno:  snap.R(666),
  1346  			userID: 1,
  1347  		},
  1348  		{
  1349  			op:   "storesvc-download",
  1350  			name: "some-snap",
  1351  		},
  1352  		{
  1353  			op:    "validate-snap:Doing",
  1354  			name:  "some-snap",
  1355  			revno: snap.R(666),
  1356  		},
  1357  		{
  1358  			op:  "current",
  1359  			old: "<no-current>",
  1360  		},
  1361  		{
  1362  			op:   "open-snap-file",
  1363  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"),
  1364  			sinfo: snap.SideInfo{
  1365  				RealName: "some-snap",
  1366  				SnapID:   "some-snap-id",
  1367  				Revision: snap.R(666),
  1368  				Channel:  "some-channel",
  1369  			},
  1370  		},
  1371  		{
  1372  			op:    "setup-snap",
  1373  			name:  "some-snap",
  1374  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"),
  1375  			revno: snap.R(666),
  1376  		},
  1377  		{
  1378  			op:   "copy-data",
  1379  			path: filepath.Join(dirs.SnapMountDir, "some-snap/666"),
  1380  			old:  "<no-old>",
  1381  		},
  1382  		{
  1383  			op:    "setup-profiles:Doing",
  1384  			name:  "some-snap",
  1385  			revno: snap.R(666),
  1386  		},
  1387  		{
  1388  			op: "candidate",
  1389  			sinfo: snap.SideInfo{
  1390  				RealName: "some-snap",
  1391  				SnapID:   "some-snap-id",
  1392  				Revision: snap.R(666),
  1393  				Channel:  "some-channel",
  1394  			},
  1395  		},
  1396  		{
  1397  			op:   "link-snap",
  1398  			path: filepath.Join(dirs.SnapMountDir, "some-snap/666"),
  1399  		},
  1400  		{
  1401  			op:    "auto-connect:Doing",
  1402  			name:  "some-snap",
  1403  			revno: snap.R(666),
  1404  		},
  1405  		{
  1406  			op: "update-aliases",
  1407  		},
  1408  		{
  1409  			op:    "cleanup-trash",
  1410  			name:  "some-snap",
  1411  			revno: snap.R(666),
  1412  		},
  1413  	}
  1414  	// start with an easier-to-read error if this fails:
  1415  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1416  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  1417  
  1418  	// check progress
  1419  	ta := ts.Tasks()
  1420  	task := ta[1]
  1421  	_, cur, total := task.Progress()
  1422  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  1423  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  1424  	c.Check(task.Summary(), Equals, `Download snap "some-snap" (666) from channel "some-channel"`)
  1425  
  1426  	// check link/start snap summary
  1427  	linkTask := ta[len(ta)-8]
  1428  	c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (666) available to the system`)
  1429  	startTask := ta[len(ta)-3]
  1430  	c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (666) services`)
  1431  
  1432  	// verify snap-setup in the task state
  1433  	var snapsup snapstate.SnapSetup
  1434  	err = task.Get("snap-setup", &snapsup)
  1435  	c.Assert(err, IsNil)
  1436  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1437  		Channel:  "some-channel",
  1438  		UserID:   s.user.ID,
  1439  		SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_666.snap"),
  1440  		DownloadInfo: &snap.DownloadInfo{
  1441  			DownloadURL: "https://some-server.com/some/path.snap",
  1442  			Size:        5,
  1443  		},
  1444  		SideInfo:  snapsup.SideInfo,
  1445  		Type:      snap.TypeApp,
  1446  		PlugsOnly: true,
  1447  		CohortKey: "scurries",
  1448  	})
  1449  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1450  		RealName: "some-snap",
  1451  		Revision: snap.R(666),
  1452  		SnapID:   "some-snap-id",
  1453  		Channel:  "some-channel",
  1454  	})
  1455  
  1456  	// verify snaps in the system state
  1457  	var snaps map[string]*snapstate.SnapState
  1458  	err = s.state.Get("snaps", &snaps)
  1459  	c.Assert(err, IsNil)
  1460  
  1461  	snapst := snaps["some-snap"]
  1462  	c.Assert(snapst, NotNil)
  1463  	c.Assert(snapst.Active, Equals, true)
  1464  	c.Assert(snapst.TrackingChannel, Equals, "some-channel/stable")
  1465  	c.Assert(snapst.CohortKey, Equals, "scurries")
  1466  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1467  		RealName: "some-snap",
  1468  		SnapID:   "some-snap-id",
  1469  		Revision: snap.R(666),
  1470  		Channel:  "some-channel",
  1471  	})
  1472  	c.Assert(snapst.Required, Equals, false)
  1473  }
  1474  
  1475  func (s *snapmgrTestSuite) TestInstallWithRevisionRunThrough(c *C) {
  1476  	s.state.Lock()
  1477  	defer s.state.Unlock()
  1478  
  1479  	chg := s.state.NewChange("install", "install a snap")
  1480  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
  1481  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  1482  	c.Assert(err, IsNil)
  1483  	chg.AddAll(ts)
  1484  
  1485  	s.state.Unlock()
  1486  	defer s.se.Stop()
  1487  	s.settle(c)
  1488  	s.state.Lock()
  1489  
  1490  	// ensure all our tasks ran
  1491  	c.Assert(chg.Err(), IsNil)
  1492  	c.Assert(chg.IsReady(), Equals, true)
  1493  	c.Check(snapstate.Installing(s.state), Equals, false)
  1494  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{{
  1495  		macaroon: s.user.StoreMacaroon,
  1496  		name:     "some-snap",
  1497  		target:   filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
  1498  	}})
  1499  	expected := fakeOps{
  1500  		{
  1501  			op:     "storesvc-snap-action",
  1502  			userID: 1,
  1503  		},
  1504  		{
  1505  			op: "storesvc-snap-action:action",
  1506  			action: store.SnapAction{
  1507  				Action:       "install",
  1508  				InstanceName: "some-snap",
  1509  				Revision:     snap.R(42),
  1510  			},
  1511  			revno:  snap.R(42),
  1512  			userID: 1,
  1513  		},
  1514  		{
  1515  			op:   "storesvc-download",
  1516  			name: "some-snap",
  1517  		},
  1518  		{
  1519  			op:    "validate-snap:Doing",
  1520  			name:  "some-snap",
  1521  			revno: snap.R(42),
  1522  		},
  1523  		{
  1524  			op:  "current",
  1525  			old: "<no-current>",
  1526  		},
  1527  		{
  1528  			op:   "open-snap-file",
  1529  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
  1530  			sinfo: snap.SideInfo{
  1531  				RealName: "some-snap",
  1532  				SnapID:   "some-snap-id",
  1533  				Revision: snap.R(42),
  1534  			},
  1535  		},
  1536  		{
  1537  			op:    "setup-snap",
  1538  			name:  "some-snap",
  1539  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
  1540  			revno: snap.R(42),
  1541  		},
  1542  		{
  1543  			op:   "copy-data",
  1544  			path: filepath.Join(dirs.SnapMountDir, "some-snap/42"),
  1545  			old:  "<no-old>",
  1546  		},
  1547  		{
  1548  			op:    "setup-profiles:Doing",
  1549  			name:  "some-snap",
  1550  			revno: snap.R(42),
  1551  		},
  1552  		{
  1553  			op: "candidate",
  1554  			sinfo: snap.SideInfo{
  1555  				RealName: "some-snap",
  1556  				SnapID:   "some-snap-id",
  1557  				Revision: snap.R(42),
  1558  			},
  1559  		},
  1560  		{
  1561  			op:   "link-snap",
  1562  			path: filepath.Join(dirs.SnapMountDir, "some-snap/42"),
  1563  		},
  1564  		{
  1565  			op:    "auto-connect:Doing",
  1566  			name:  "some-snap",
  1567  			revno: snap.R(42),
  1568  		},
  1569  		{
  1570  			op: "update-aliases",
  1571  		},
  1572  		{
  1573  			op:    "cleanup-trash",
  1574  			name:  "some-snap",
  1575  			revno: snap.R(42),
  1576  		},
  1577  	}
  1578  	// start with an easier-to-read error if this fails:
  1579  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1580  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  1581  
  1582  	// check progress
  1583  	ta := ts.Tasks()
  1584  	task := ta[1]
  1585  	_, cur, total := task.Progress()
  1586  	c.Assert(cur, Equals, s.fakeStore.fakeCurrentProgress)
  1587  	c.Assert(total, Equals, s.fakeStore.fakeTotalProgress)
  1588  	c.Check(task.Summary(), Equals, `Download snap "some-snap" (42) from channel "some-channel"`)
  1589  
  1590  	// check link/start snap summary
  1591  	linkTask := ta[len(ta)-8]
  1592  	c.Check(linkTask.Summary(), Equals, `Make snap "some-snap" (42) available to the system`)
  1593  	startTask := ta[len(ta)-3]
  1594  	c.Check(startTask.Summary(), Equals, `Start snap "some-snap" (42) services`)
  1595  
  1596  	// verify snap-setup in the task state
  1597  	var snapsup snapstate.SnapSetup
  1598  	err = task.Get("snap-setup", &snapsup)
  1599  	c.Assert(err, IsNil)
  1600  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1601  		Channel:  "some-channel",
  1602  		UserID:   s.user.ID,
  1603  		SnapPath: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
  1604  		DownloadInfo: &snap.DownloadInfo{
  1605  			DownloadURL: "https://some-server.com/some/path.snap",
  1606  			Size:        5,
  1607  		},
  1608  		SideInfo:  snapsup.SideInfo,
  1609  		Type:      snap.TypeApp,
  1610  		PlugsOnly: true,
  1611  	})
  1612  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1613  		RealName: "some-snap",
  1614  		Revision: snap.R(42),
  1615  		SnapID:   "some-snap-id",
  1616  	})
  1617  
  1618  	// verify snaps in the system state
  1619  	var snaps map[string]*snapstate.SnapState
  1620  	err = s.state.Get("snaps", &snaps)
  1621  	c.Assert(err, IsNil)
  1622  
  1623  	snapst := snaps["some-snap"]
  1624  	c.Assert(snapst, NotNil)
  1625  	c.Assert(snapst.Active, Equals, true)
  1626  	c.Assert(snapst.TrackingChannel, Equals, "some-channel/stable")
  1627  	c.Assert(snapst.CohortKey, Equals, "")
  1628  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1629  		RealName: "some-snap",
  1630  		SnapID:   "some-snap-id",
  1631  		Revision: snap.R(42),
  1632  	})
  1633  	c.Assert(snapst.Required, Equals, false)
  1634  }
  1635  
  1636  func (s *snapmgrTestSuite) TestInstallStartOrder(c *C) {
  1637  	s.state.Lock()
  1638  	defer s.state.Unlock()
  1639  
  1640  	chg := s.state.NewChange("install", "install a snap")
  1641  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  1642  	ts, err := snapstate.Install(context.Background(), s.state, "services-snap", opts, s.user.ID, snapstate.Flags{})
  1643  	c.Assert(err, IsNil)
  1644  	chg.AddAll(ts)
  1645  
  1646  	s.state.Unlock()
  1647  	defer s.se.Stop()
  1648  	s.settle(c)
  1649  	s.state.Lock()
  1650  
  1651  	// ensure all our tasks ran
  1652  	c.Assert(chg.Err(), IsNil)
  1653  	c.Assert(chg.IsReady(), Equals, true)
  1654  	c.Check(snapstate.Installing(s.state), Equals, false)
  1655  	op := s.fakeBackend.ops.First("start-snap-services")
  1656  	c.Assert(op, NotNil)
  1657  	c.Assert(op, DeepEquals, &fakeOp{
  1658  		op:   "start-snap-services",
  1659  		path: filepath.Join(dirs.SnapMountDir, "services-snap/11"),
  1660  		// ordered to preserve after/before relation
  1661  		services: []string{"svc1", "svc3", "svc2"},
  1662  	})
  1663  }
  1664  
  1665  func (s *snapmgrTestSuite) TestInstalling(c *C) {
  1666  	s.state.Lock()
  1667  	defer s.state.Unlock()
  1668  
  1669  	c.Check(snapstate.Installing(s.state), Equals, false)
  1670  
  1671  	chg := s.state.NewChange("install", "install a snap")
  1672  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
  1673  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, 0, snapstate.Flags{})
  1674  	c.Assert(err, IsNil)
  1675  	chg.AddAll(ts)
  1676  
  1677  	c.Check(snapstate.Installing(s.state), Equals, true)
  1678  }
  1679  
  1680  func (s *snapmgrTestSuite) TestInstallFirstLocalRunThrough(c *C) {
  1681  	// use the real thing for this one
  1682  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
  1683  
  1684  	restoreInstallSize := snapstate.MockInstallSize(func(st *state.State, snaps []*snap.Info, userID int) (uint64, error) {
  1685  		c.Fatalf("installSize shouldn't be hit with local install")
  1686  		return 0, nil
  1687  	})
  1688  	defer restoreInstallSize()
  1689  
  1690  	s.state.Lock()
  1691  	defer s.state.Unlock()
  1692  
  1693  	mockSnap := makeTestSnap(c, `name: mock
  1694  version: 1.0`)
  1695  	chg := s.state.NewChange("install", "install a local snap")
  1696  	ts, info, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{})
  1697  	c.Assert(err, IsNil)
  1698  	chg.AddAll(ts)
  1699  
  1700  	// ensure the returned info is correct
  1701  	c.Check(info.SideInfo.RealName, Equals, "mock")
  1702  	c.Check(info.Version, Equals, "1.0")
  1703  
  1704  	s.state.Unlock()
  1705  	defer s.se.Stop()
  1706  	s.settle(c)
  1707  	s.state.Lock()
  1708  
  1709  	expected := fakeOps{
  1710  		{
  1711  			// only local install was run, i.e. first actions are pseudo-action current
  1712  			op:  "current",
  1713  			old: "<no-current>",
  1714  		},
  1715  		{
  1716  			// and setup-snap
  1717  			op:    "setup-snap",
  1718  			name:  "mock",
  1719  			path:  mockSnap,
  1720  			revno: snap.R("x1"),
  1721  		},
  1722  		{
  1723  			op:   "copy-data",
  1724  			path: filepath.Join(dirs.SnapMountDir, "mock/x1"),
  1725  			old:  "<no-old>",
  1726  		},
  1727  		{
  1728  			op:    "setup-profiles:Doing",
  1729  			name:  "mock",
  1730  			revno: snap.R("x1"),
  1731  		},
  1732  		{
  1733  			op: "candidate",
  1734  			sinfo: snap.SideInfo{
  1735  				RealName: "mock",
  1736  				Revision: snap.R("x1"),
  1737  			},
  1738  		},
  1739  		{
  1740  			op:   "link-snap",
  1741  			path: filepath.Join(dirs.SnapMountDir, "mock/x1"),
  1742  		},
  1743  		{
  1744  			op:    "auto-connect:Doing",
  1745  			name:  "mock",
  1746  			revno: snap.R("x1"),
  1747  		},
  1748  		{
  1749  			op: "update-aliases",
  1750  		},
  1751  		{
  1752  			op:    "cleanup-trash",
  1753  			name:  "mock",
  1754  			revno: snap.R("x1"),
  1755  		},
  1756  	}
  1757  
  1758  	// start with an easier-to-read error if this fails:
  1759  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1760  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  1761  
  1762  	// verify snapSetup info
  1763  	var snapsup snapstate.SnapSetup
  1764  	task := ts.Tasks()[1]
  1765  	err = task.Get("snap-setup", &snapsup)
  1766  	c.Assert(err, IsNil)
  1767  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1768  		SnapPath:  mockSnap,
  1769  		SideInfo:  snapsup.SideInfo,
  1770  		Type:      snap.TypeApp,
  1771  		PlugsOnly: true,
  1772  	})
  1773  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1774  		RealName: "mock",
  1775  		Revision: snap.R(-1),
  1776  	})
  1777  
  1778  	// verify snaps in the system state
  1779  	var snapst snapstate.SnapState
  1780  	err = snapstate.Get(s.state, "mock", &snapst)
  1781  	c.Assert(err, IsNil)
  1782  
  1783  	c.Assert(snapst.Active, Equals, true)
  1784  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  1785  		RealName: "mock",
  1786  		Channel:  "",
  1787  		Revision: snap.R(-1),
  1788  	})
  1789  	c.Assert(snapst.LocalRevision(), Equals, snap.R(-1))
  1790  }
  1791  
  1792  func (s *snapmgrTestSuite) TestInstallSubsequentLocalRunThrough(c *C) {
  1793  	// use the real thing for this one
  1794  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
  1795  
  1796  	s.state.Lock()
  1797  	defer s.state.Unlock()
  1798  
  1799  	snapstate.Set(s.state, "mock", &snapstate.SnapState{
  1800  		Active: true,
  1801  		Sequence: []*snap.SideInfo{
  1802  			{RealName: "mock", Revision: snap.R(-2)},
  1803  		},
  1804  		Current:  snap.R(-2),
  1805  		SnapType: "app",
  1806  	})
  1807  
  1808  	mockSnap := makeTestSnap(c, `name: mock
  1809  version: 1.0
  1810  epoch: 1*
  1811  `)
  1812  	chg := s.state.NewChange("install", "install a local snap")
  1813  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{})
  1814  	c.Assert(err, IsNil)
  1815  	chg.AddAll(ts)
  1816  
  1817  	s.state.Unlock()
  1818  	defer s.se.Stop()
  1819  	s.settle(c)
  1820  	s.state.Lock()
  1821  
  1822  	expected := fakeOps{
  1823  		{
  1824  			op:  "current",
  1825  			old: filepath.Join(dirs.SnapMountDir, "mock/x2"),
  1826  		},
  1827  		{
  1828  			op:    "setup-snap",
  1829  			name:  "mock",
  1830  			path:  mockSnap,
  1831  			revno: snap.R("x3"),
  1832  		},
  1833  		{
  1834  			op:   "remove-snap-aliases",
  1835  			name: "mock",
  1836  		},
  1837  		{
  1838  			op:   "unlink-snap",
  1839  			path: filepath.Join(dirs.SnapMountDir, "mock/x2"),
  1840  		},
  1841  		{
  1842  			op:   "copy-data",
  1843  			path: filepath.Join(dirs.SnapMountDir, "mock/x3"),
  1844  			old:  filepath.Join(dirs.SnapMountDir, "mock/x2"),
  1845  		},
  1846  		{
  1847  			op:    "setup-profiles:Doing",
  1848  			name:  "mock",
  1849  			revno: snap.R(-3),
  1850  		},
  1851  		{
  1852  			op: "candidate",
  1853  			sinfo: snap.SideInfo{
  1854  				RealName: "mock",
  1855  				Revision: snap.R(-3),
  1856  			},
  1857  		},
  1858  		{
  1859  			op:   "link-snap",
  1860  			path: filepath.Join(dirs.SnapMountDir, "mock/x3"),
  1861  		},
  1862  		{
  1863  			op:    "auto-connect:Doing",
  1864  			name:  "mock",
  1865  			revno: snap.R("x3"),
  1866  		},
  1867  		{
  1868  			op: "update-aliases",
  1869  		},
  1870  		{
  1871  			op:    "cleanup-trash",
  1872  			name:  "mock",
  1873  			revno: snap.R("x3"),
  1874  		},
  1875  	}
  1876  
  1877  	// start with an easier-to-read error if this fails:
  1878  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  1879  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  1880  
  1881  	// verify snapSetup info
  1882  	var snapsup snapstate.SnapSetup
  1883  	task := ts.Tasks()[1]
  1884  	err = task.Get("snap-setup", &snapsup)
  1885  	c.Assert(err, IsNil)
  1886  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  1887  		SnapPath:  mockSnap,
  1888  		SideInfo:  snapsup.SideInfo,
  1889  		Type:      snap.TypeApp,
  1890  		PlugsOnly: true,
  1891  	})
  1892  	c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{
  1893  		RealName: "mock",
  1894  		Revision: snap.R(-3),
  1895  	})
  1896  
  1897  	// verify snaps in the system state
  1898  	var snapst snapstate.SnapState
  1899  	err = snapstate.Get(s.state, "mock", &snapst)
  1900  	c.Assert(err, IsNil)
  1901  
  1902  	c.Assert(snapst.Active, Equals, true)
  1903  	c.Assert(snapst.Sequence, HasLen, 2)
  1904  	c.Assert(snapst.CurrentSideInfo(), DeepEquals, &snap.SideInfo{
  1905  		RealName: "mock",
  1906  		Channel:  "",
  1907  		Revision: snap.R(-3),
  1908  	})
  1909  	c.Assert(snapst.LocalRevision(), Equals, snap.R(-3))
  1910  }
  1911  
  1912  func (s *snapmgrTestSuite) TestInstallOldSubsequentLocalRunThrough(c *C) {
  1913  	// use the real thing for this one
  1914  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
  1915  
  1916  	s.state.Lock()
  1917  	defer s.state.Unlock()
  1918  
  1919  	snapstate.Set(s.state, "mock", &snapstate.SnapState{
  1920  		Active: true,
  1921  		Sequence: []*snap.SideInfo{
  1922  			{RealName: "mock", Revision: snap.R(100001)},
  1923  		},
  1924  		Current:  snap.R(100001),
  1925  		SnapType: "app",
  1926  	})
  1927  
  1928  	mockSnap := makeTestSnap(c, `name: mock
  1929  version: 1.0
  1930  epoch: 1*
  1931  `)
  1932  	chg := s.state.NewChange("install", "install a local snap")
  1933  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "mock"}, mockSnap, "", "", snapstate.Flags{})
  1934  	c.Assert(err, IsNil)
  1935  	chg.AddAll(ts)
  1936  
  1937  	s.state.Unlock()
  1938  	defer s.se.Stop()
  1939  	s.settle(c)
  1940  	s.state.Lock()
  1941  
  1942  	expected := fakeOps{
  1943  		{
  1944  			// ensure only local install was run, i.e. first action is pseudo-action current
  1945  			op:  "current",
  1946  			old: filepath.Join(dirs.SnapMountDir, "mock/100001"),
  1947  		},
  1948  		{
  1949  			// and setup-snap
  1950  			op:    "setup-snap",
  1951  			name:  "mock",
  1952  			path:  mockSnap,
  1953  			revno: snap.R("x1"),
  1954  		},
  1955  		{
  1956  			op:   "remove-snap-aliases",
  1957  			name: "mock",
  1958  		},
  1959  		{
  1960  			op:   "unlink-snap",
  1961  			path: filepath.Join(dirs.SnapMountDir, "mock/100001"),
  1962  		},
  1963  		{
  1964  			op:   "copy-data",
  1965  			path: filepath.Join(dirs.SnapMountDir, "mock/x1"),
  1966  			old:  filepath.Join(dirs.SnapMountDir, "mock/100001"),
  1967  		},
  1968  		{
  1969  			op:    "setup-profiles:Doing",
  1970  			name:  "mock",
  1971  			revno: snap.R("x1"),
  1972  		},
  1973  		{
  1974  			op: "candidate",
  1975  			sinfo: snap.SideInfo{
  1976  				RealName: "mock",
  1977  				Revision: snap.R("x1"),
  1978  			},
  1979  		},
  1980  		{
  1981  			op:   "link-snap",
  1982  			path: filepath.Join(dirs.SnapMountDir, "mock/x1"),
  1983  		},
  1984  		{
  1985  			op:    "auto-connect:Doing",
  1986  			name:  "mock",
  1987  			revno: snap.R("x1"),
  1988  		},
  1989  		{
  1990  			op: "update-aliases",
  1991  		},
  1992  		{
  1993  			// and cleanup
  1994  			op:    "cleanup-trash",
  1995  			name:  "mock",
  1996  			revno: snap.R("x1"),
  1997  		},
  1998  	}
  1999  	// start with an easier-to-read error if this fails:
  2000  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2001  	c.Check(s.fakeBackend.ops, DeepEquals, expected)
  2002  
  2003  	var snapst snapstate.SnapState
  2004  	err = snapstate.Get(s.state, "mock", &snapst)
  2005  	c.Assert(err, IsNil)
  2006  
  2007  	c.Assert(snapst.Active, Equals, true)
  2008  	c.Assert(snapst.Sequence, HasLen, 2)
  2009  	c.Assert(snapst.CurrentSideInfo(), DeepEquals, &snap.SideInfo{
  2010  		RealName: "mock",
  2011  		Channel:  "",
  2012  		Revision: snap.R(-1),
  2013  	})
  2014  	c.Assert(snapst.LocalRevision(), Equals, snap.R(-1))
  2015  }
  2016  
  2017  func (s *snapmgrTestSuite) TestInstallPathWithMetadataRunThrough(c *C) {
  2018  	// use the real thing for this one
  2019  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
  2020  
  2021  	s.state.Lock()
  2022  	defer s.state.Unlock()
  2023  
  2024  	someSnap := makeTestSnap(c, `name: orig-name
  2025  version: 1.0`)
  2026  	chg := s.state.NewChange("install", "install a local snap")
  2027  
  2028  	si := &snap.SideInfo{
  2029  		RealName: "some-snap",
  2030  		SnapID:   "some-snap-id",
  2031  		Revision: snap.R(42),
  2032  	}
  2033  	ts, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "", snapstate.Flags{Required: true})
  2034  	c.Assert(err, IsNil)
  2035  	chg.AddAll(ts)
  2036  
  2037  	s.state.Unlock()
  2038  	defer s.se.Stop()
  2039  	s.settle(c)
  2040  	s.state.Lock()
  2041  
  2042  	// ensure only local install was run, i.e. first actions are pseudo-action current
  2043  	c.Assert(s.fakeBackend.ops.Ops(), HasLen, 9)
  2044  	c.Check(s.fakeBackend.ops[0].op, Equals, "current")
  2045  	c.Check(s.fakeBackend.ops[0].old, Equals, "<no-current>")
  2046  	// and setup-snap
  2047  	c.Check(s.fakeBackend.ops[1].op, Equals, "setup-snap")
  2048  	c.Check(s.fakeBackend.ops[1].name, Equals, "some-snap")
  2049  	c.Check(s.fakeBackend.ops[1].path, Matches, `.*/orig-name_1.0_all.snap`)
  2050  	c.Check(s.fakeBackend.ops[1].revno, Equals, snap.R(42))
  2051  
  2052  	c.Check(s.fakeBackend.ops[4].op, Equals, "candidate")
  2053  	c.Check(s.fakeBackend.ops[4].sinfo, DeepEquals, *si)
  2054  	c.Check(s.fakeBackend.ops[5].op, Equals, "link-snap")
  2055  	c.Check(s.fakeBackend.ops[5].path, Equals, filepath.Join(dirs.SnapMountDir, "some-snap/42"))
  2056  
  2057  	// verify snapSetup info
  2058  	var snapsup snapstate.SnapSetup
  2059  	task := ts.Tasks()[0]
  2060  	err = task.Get("snap-setup", &snapsup)
  2061  	c.Assert(err, IsNil)
  2062  	c.Assert(snapsup, DeepEquals, snapstate.SnapSetup{
  2063  		SnapPath: someSnap,
  2064  		SideInfo: snapsup.SideInfo,
  2065  		Flags: snapstate.Flags{
  2066  			Required: true,
  2067  		},
  2068  		Type:      snap.TypeApp,
  2069  		PlugsOnly: true,
  2070  	})
  2071  	c.Assert(snapsup.SideInfo, DeepEquals, si)
  2072  
  2073  	// verify snaps in the system state
  2074  	var snapst snapstate.SnapState
  2075  	err = snapstate.Get(s.state, "some-snap", &snapst)
  2076  	c.Assert(err, IsNil)
  2077  
  2078  	c.Assert(snapst.Active, Equals, true)
  2079  	c.Assert(snapst.TrackingChannel, Equals, "")
  2080  	c.Assert(snapst.Sequence[0], DeepEquals, si)
  2081  	c.Assert(snapst.LocalRevision().Unset(), Equals, true)
  2082  	c.Assert(snapst.Required, Equals, true)
  2083  }
  2084  
  2085  func (s *snapmgrTestSuite) TestInstallPathSkipConfigure(c *C) {
  2086  	r := release.MockOnClassic(false)
  2087  	defer r()
  2088  
  2089  	makeInstalledMockCoreSnap(c)
  2090  
  2091  	// using MockSnap, we want to read the bits on disk
  2092  	snapstate.MockSnapReadInfo(snap.ReadInfo)
  2093  
  2094  	s.state.Lock()
  2095  	defer s.state.Unlock()
  2096  
  2097  	s.prepareGadget(c)
  2098  
  2099  	snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0")
  2100  
  2101  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{SkipConfigure: true})
  2102  	c.Assert(err, IsNil)
  2103  
  2104  	snapsup, err := snapstate.TaskSnapSetup(ts.Tasks()[0])
  2105  	c.Assert(err, IsNil)
  2106  	// SkipConfigure is consumed and consulted when creating the taskset
  2107  	// but is not copied into SnapSetup
  2108  	c.Check(snapsup.Flags.SkipConfigure, Equals, false)
  2109  }
  2110  
  2111  func (s *snapmgrTestSuite) TestInstallWithoutCoreRunThrough1(c *C) {
  2112  	s.state.Lock()
  2113  	defer s.state.Unlock()
  2114  
  2115  	// pretend we don't have core
  2116  	snapstate.Set(s.state, "core", nil)
  2117  
  2118  	chg := s.state.NewChange("install", "install a snap on a system without core")
  2119  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
  2120  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2121  	c.Assert(err, IsNil)
  2122  	chg.AddAll(ts)
  2123  
  2124  	s.state.Unlock()
  2125  	defer s.se.Stop()
  2126  	s.settle(c)
  2127  	s.state.Lock()
  2128  
  2129  	// ensure all our tasks ran
  2130  	c.Assert(chg.Err(), IsNil)
  2131  	c.Assert(chg.IsReady(), Equals, true)
  2132  	c.Check(s.fakeStore.downloads, DeepEquals, []fakeDownload{
  2133  		{
  2134  			macaroon: s.user.StoreMacaroon,
  2135  			name:     "core",
  2136  			target:   filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
  2137  		},
  2138  		{
  2139  			macaroon: s.user.StoreMacaroon,
  2140  			name:     "some-snap",
  2141  			target:   filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
  2142  		}})
  2143  	expected := fakeOps{
  2144  		// we check the snap
  2145  		{
  2146  			op:     "storesvc-snap-action",
  2147  			userID: 1,
  2148  		},
  2149  		{
  2150  			op: "storesvc-snap-action:action",
  2151  			action: store.SnapAction{
  2152  				Action:       "install",
  2153  				InstanceName: "some-snap",
  2154  				Revision:     snap.R(42),
  2155  			},
  2156  			revno:  snap.R(42),
  2157  			userID: 1,
  2158  		},
  2159  		// then we check core because its not installed already
  2160  		// and continue with that
  2161  		{
  2162  			op:     "storesvc-snap-action",
  2163  			userID: 1,
  2164  		},
  2165  		{
  2166  			op: "storesvc-snap-action:action",
  2167  			action: store.SnapAction{
  2168  				Action:       "install",
  2169  				InstanceName: "core",
  2170  				Channel:      "stable",
  2171  			},
  2172  			revno:  snap.R(11),
  2173  			userID: 1,
  2174  		},
  2175  		{
  2176  			op:   "storesvc-download",
  2177  			name: "core",
  2178  		},
  2179  		{
  2180  			op:    "validate-snap:Doing",
  2181  			name:  "core",
  2182  			revno: snap.R(11),
  2183  		},
  2184  		{
  2185  			op:  "current",
  2186  			old: "<no-current>",
  2187  		},
  2188  		{
  2189  			op:   "open-snap-file",
  2190  			path: filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
  2191  			sinfo: snap.SideInfo{
  2192  				RealName: "core",
  2193  				Channel:  "stable",
  2194  				SnapID:   "core-id",
  2195  				Revision: snap.R(11),
  2196  			},
  2197  		},
  2198  		{
  2199  			op:    "setup-snap",
  2200  			name:  "core",
  2201  			path:  filepath.Join(dirs.SnapBlobDir, "core_11.snap"),
  2202  			revno: snap.R(11),
  2203  		},
  2204  		{
  2205  			op:   "copy-data",
  2206  			path: filepath.Join(dirs.SnapMountDir, "core/11"),
  2207  			old:  "<no-old>",
  2208  		},
  2209  		{
  2210  			op:    "setup-profiles:Doing",
  2211  			name:  "core",
  2212  			revno: snap.R(11),
  2213  		},
  2214  		{
  2215  			op: "candidate",
  2216  			sinfo: snap.SideInfo{
  2217  				RealName: "core",
  2218  				Channel:  "stable",
  2219  				SnapID:   "core-id",
  2220  				Revision: snap.R(11),
  2221  			},
  2222  		},
  2223  		{
  2224  			op:   "link-snap",
  2225  			path: filepath.Join(dirs.SnapMountDir, "core/11"),
  2226  		},
  2227  		{
  2228  			op:    "auto-connect:Doing",
  2229  			name:  "core",
  2230  			revno: snap.R(11),
  2231  		},
  2232  		{
  2233  			op: "update-aliases",
  2234  		},
  2235  		// after core is in place continue with the snap
  2236  		{
  2237  			op:   "storesvc-download",
  2238  			name: "some-snap",
  2239  		},
  2240  		{
  2241  			op:    "validate-snap:Doing",
  2242  			name:  "some-snap",
  2243  			revno: snap.R(42),
  2244  		},
  2245  		{
  2246  			op:  "current",
  2247  			old: "<no-current>",
  2248  		},
  2249  		{
  2250  			op:   "open-snap-file",
  2251  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
  2252  			sinfo: snap.SideInfo{
  2253  				RealName: "some-snap",
  2254  				SnapID:   "some-snap-id",
  2255  				Revision: snap.R(42),
  2256  			},
  2257  		},
  2258  		{
  2259  			op:    "setup-snap",
  2260  			name:  "some-snap",
  2261  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_42.snap"),
  2262  			revno: snap.R(42),
  2263  		},
  2264  		{
  2265  			op:   "copy-data",
  2266  			path: filepath.Join(dirs.SnapMountDir, "some-snap/42"),
  2267  			old:  "<no-old>",
  2268  		},
  2269  		{
  2270  			op:    "setup-profiles:Doing",
  2271  			name:  "some-snap",
  2272  			revno: snap.R(42),
  2273  		},
  2274  		{
  2275  			op: "candidate",
  2276  			sinfo: snap.SideInfo{
  2277  				RealName: "some-snap",
  2278  				SnapID:   "some-snap-id",
  2279  				Revision: snap.R(42),
  2280  			},
  2281  		},
  2282  		{
  2283  			op:   "link-snap",
  2284  			path: filepath.Join(dirs.SnapMountDir, "some-snap/42"),
  2285  		},
  2286  		{
  2287  			op:    "auto-connect:Doing",
  2288  			name:  "some-snap",
  2289  			revno: snap.R(42),
  2290  		},
  2291  		{
  2292  			op: "update-aliases",
  2293  		},
  2294  		// cleanups order is random
  2295  		{
  2296  			op:    "cleanup-trash",
  2297  			name:  "core",
  2298  			revno: snap.R(42),
  2299  		},
  2300  		{
  2301  			op:    "cleanup-trash",
  2302  			name:  "some-snap",
  2303  			revno: snap.R(42),
  2304  		},
  2305  	}
  2306  	// start with an easier-to-read error if this fails:
  2307  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  2308  	// compare the details without the cleanup tasks, the order is random
  2309  	// as they run in parallel
  2310  	opsLenWithoutCleanups := len(s.fakeBackend.ops) - 2
  2311  	c.Assert(s.fakeBackend.ops[:opsLenWithoutCleanups], DeepEquals, expected[:opsLenWithoutCleanups])
  2312  
  2313  	// verify core in the system state
  2314  	var snaps map[string]*snapstate.SnapState
  2315  	err = s.state.Get("snaps", &snaps)
  2316  	c.Assert(err, IsNil)
  2317  
  2318  	snapst := snaps["core"]
  2319  	c.Assert(snapst, NotNil)
  2320  	c.Assert(snapst.Active, Equals, true)
  2321  	c.Assert(snapst.TrackingChannel, Equals, "latest/stable")
  2322  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2323  		RealName: "core",
  2324  		Channel:  "stable",
  2325  		SnapID:   "core-id",
  2326  		Revision: snap.R(11),
  2327  	})
  2328  }
  2329  
  2330  func (s *snapmgrTestSuite) TestInstallWithoutCoreTwoSnapsRunThrough(c *C) {
  2331  	s.state.Lock()
  2332  	defer s.state.Unlock()
  2333  
  2334  	restore := snapstate.MockPrerequisitesRetryTimeout(10 * time.Millisecond)
  2335  	defer restore()
  2336  
  2337  	// pretend we don't have core
  2338  	snapstate.Set(s.state, "core", nil)
  2339  
  2340  	chg1 := s.state.NewChange("install", "install snap 1")
  2341  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
  2342  	ts1, err := snapstate.Install(context.Background(), s.state, "snap1", opts, s.user.ID, snapstate.Flags{})
  2343  	c.Assert(err, IsNil)
  2344  	chg1.AddAll(ts1)
  2345  
  2346  	chg2 := s.state.NewChange("install", "install snap 2")
  2347  	opts = &snapstate.RevisionOptions{Channel: "some-other-channel", Revision: snap.R(21)}
  2348  	ts2, err := snapstate.Install(context.Background(), s.state, "snap2", opts, s.user.ID, snapstate.Flags{})
  2349  	c.Assert(err, IsNil)
  2350  	chg2.AddAll(ts2)
  2351  
  2352  	s.state.Unlock()
  2353  	defer s.se.Stop()
  2354  	s.settle(c)
  2355  	s.state.Lock()
  2356  
  2357  	// ensure all our tasks ran and core was only installed once
  2358  	c.Assert(chg1.Err(), IsNil)
  2359  	c.Assert(chg2.Err(), IsNil)
  2360  
  2361  	c.Assert(chg1.IsReady(), Equals, true)
  2362  	c.Assert(chg2.IsReady(), Equals, true)
  2363  
  2364  	// order in which the changes run is random
  2365  	if len(chg1.Tasks()) < len(chg2.Tasks()) {
  2366  		chg1, chg2 = chg2, chg1
  2367  	}
  2368  	c.Assert(taskKinds(chg1.Tasks()), HasLen, 28)
  2369  	c.Assert(taskKinds(chg2.Tasks()), HasLen, 14)
  2370  
  2371  	// FIXME: add helpers and do a DeepEquals here for the operations
  2372  }
  2373  
  2374  func (s *snapmgrTestSuite) TestInstallWithoutCoreTwoSnapsWithFailureRunThrough(c *C) {
  2375  	s.state.Lock()
  2376  	defer s.state.Unlock()
  2377  
  2378  	// slightly longer retry timeout to avoid deadlock when we
  2379  	// trigger a retry quickly that the link snap for core does
  2380  	// not have a chance to run
  2381  	restore := snapstate.MockPrerequisitesRetryTimeout(40 * time.Millisecond)
  2382  	defer restore()
  2383  
  2384  	defer s.se.Stop()
  2385  	// Two changes are created, the first will fails, the second will
  2386  	// be fine. The order of what change runs first is random, the
  2387  	// first change will also install core in its own lane. This test
  2388  	// ensures that core gets installed and there are no conflicts
  2389  	// even if core already got installed from the first change.
  2390  	//
  2391  	// It runs multiple times so that both possible cases get a chance
  2392  	// to run
  2393  	for i := 0; i < 5; i++ {
  2394  		// start clean
  2395  		snapstate.Set(s.state, "core", nil)
  2396  		snapstate.Set(s.state, "snap2", nil)
  2397  
  2398  		// chg1 has an error
  2399  		chg1 := s.state.NewChange("install", "install snap 1")
  2400  		opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
  2401  		ts1, err := snapstate.Install(context.Background(), s.state, "snap1", opts, s.user.ID, snapstate.Flags{})
  2402  		c.Assert(err, IsNil)
  2403  		chg1.AddAll(ts1)
  2404  
  2405  		tasks := ts1.Tasks()
  2406  		last := tasks[len(tasks)-1]
  2407  		terr := s.state.NewTask("error-trigger", "provoking total undo")
  2408  		terr.WaitFor(last)
  2409  		chg1.AddTask(terr)
  2410  
  2411  		// chg2 is good
  2412  		chg2 := s.state.NewChange("install", "install snap 2")
  2413  		opts = &snapstate.RevisionOptions{Channel: "some-other-channel", Revision: snap.R(21)}
  2414  		ts2, err := snapstate.Install(context.Background(), s.state, "snap2", opts, s.user.ID, snapstate.Flags{})
  2415  		c.Assert(err, IsNil)
  2416  		chg2.AddAll(ts2)
  2417  
  2418  		// we use our own settle as we need a bigger timeout
  2419  		s.state.Unlock()
  2420  		err = s.o.Settle(testutil.HostScaledTimeout(15 * time.Second))
  2421  		s.state.Lock()
  2422  		c.Assert(err, IsNil)
  2423  
  2424  		// ensure expected change states
  2425  		c.Check(chg1.Status(), Equals, state.ErrorStatus)
  2426  		c.Check(chg2.Status(), Equals, state.DoneStatus)
  2427  
  2428  		// ensure we have both core and snap2
  2429  		var snapst snapstate.SnapState
  2430  		err = snapstate.Get(s.state, "core", &snapst)
  2431  		c.Assert(err, IsNil)
  2432  		c.Assert(snapst.Active, Equals, true)
  2433  		c.Assert(snapst.Sequence, HasLen, 1)
  2434  		c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2435  			RealName: "core",
  2436  			SnapID:   "core-id",
  2437  			Channel:  "stable",
  2438  			Revision: snap.R(11),
  2439  		})
  2440  
  2441  		var snapst2 snapstate.SnapState
  2442  		err = snapstate.Get(s.state, "snap2", &snapst2)
  2443  		c.Assert(err, IsNil)
  2444  		c.Assert(snapst2.Active, Equals, true)
  2445  		c.Assert(snapst2.Sequence, HasLen, 1)
  2446  		c.Assert(snapst2.Sequence[0], DeepEquals, &snap.SideInfo{
  2447  			RealName: "snap2",
  2448  			SnapID:   "snap2-id",
  2449  			Channel:  "",
  2450  			Revision: snap.R(21),
  2451  		})
  2452  
  2453  	}
  2454  }
  2455  
  2456  type behindYourBackStore struct {
  2457  	*fakeStore
  2458  	state *state.State
  2459  
  2460  	coreInstallRequested bool
  2461  	coreInstalled        bool
  2462  	chg                  *state.Change
  2463  }
  2464  
  2465  func (s behindYourBackStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) {
  2466  	if assertQuery != nil {
  2467  		panic("no assertion query support")
  2468  	}
  2469  
  2470  	if len(actions) == 1 && actions[0].Action == "install" && actions[0].InstanceName == "core" {
  2471  		s.state.Lock()
  2472  		if !s.coreInstallRequested {
  2473  			s.coreInstallRequested = true
  2474  			snapsup := &snapstate.SnapSetup{
  2475  				SideInfo: &snap.SideInfo{
  2476  					RealName: "core",
  2477  				},
  2478  			}
  2479  			t := s.state.NewTask("prepare", "prepare core")
  2480  			t.Set("snap-setup", snapsup)
  2481  			s.chg = s.state.NewChange("install", "install core")
  2482  			s.chg.AddAll(state.NewTaskSet(t))
  2483  		}
  2484  		if s.chg != nil && !s.coreInstalled {
  2485  			// marks change ready but also
  2486  			// tasks need to also be marked cleaned
  2487  			for _, t := range s.chg.Tasks() {
  2488  				t.SetStatus(state.DoneStatus)
  2489  				t.SetClean()
  2490  			}
  2491  			snapstate.Set(s.state, "core", &snapstate.SnapState{
  2492  				Active: true,
  2493  				Sequence: []*snap.SideInfo{
  2494  					{RealName: "core", Revision: snap.R(1)},
  2495  				},
  2496  				Current:  snap.R(1),
  2497  				SnapType: "os",
  2498  			})
  2499  			s.coreInstalled = true
  2500  		}
  2501  		s.state.Unlock()
  2502  	}
  2503  
  2504  	return s.fakeStore.SnapAction(ctx, currentSnaps, actions, nil, user, opts)
  2505  }
  2506  
  2507  // this test the scenario that some-snap gets installed and during the
  2508  // install (when unlocking for the store info call for core) an
  2509  // explicit "snap install core" happens. In this case the snapstate
  2510  // will return a change conflict. we handle this via a retry, ensure
  2511  // this is actually what happens.
  2512  func (s *snapmgrTestSuite) TestInstallWithoutCoreConflictingInstall(c *C) {
  2513  	s.state.Lock()
  2514  	defer s.state.Unlock()
  2515  
  2516  	restore := snapstate.MockPrerequisitesRetryTimeout(10 * time.Millisecond)
  2517  	defer restore()
  2518  
  2519  	snapstate.ReplaceStore(s.state, behindYourBackStore{fakeStore: s.fakeStore, state: s.state})
  2520  
  2521  	// pretend we don't have core
  2522  	snapstate.Set(s.state, "core", nil)
  2523  
  2524  	// now install a snap that will pull in core
  2525  	chg := s.state.NewChange("install", "install a snap on a system without core")
  2526  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  2527  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2528  	c.Assert(err, IsNil)
  2529  	chg.AddAll(ts)
  2530  
  2531  	prereq := ts.Tasks()[0]
  2532  	c.Assert(prereq.Kind(), Equals, "prerequisites")
  2533  	c.Check(prereq.AtTime().IsZero(), Equals, true)
  2534  
  2535  	s.state.Unlock()
  2536  	defer s.se.Stop()
  2537  
  2538  	// start running the change, this will trigger the
  2539  	// prerequisites task, which will trigger the install of core
  2540  	// and also call our mock store which will generate a parallel
  2541  	// change
  2542  	s.se.Ensure()
  2543  	s.se.Wait()
  2544  
  2545  	// change is not ready yet, because the prerequists triggered
  2546  	// a state.Retry{} because of the conflicting change
  2547  	c.Assert(chg.IsReady(), Equals, false)
  2548  	s.state.Lock()
  2549  	// marked for retry
  2550  	c.Check(prereq.AtTime().IsZero(), Equals, false)
  2551  	c.Check(prereq.Status().Ready(), Equals, false)
  2552  	s.state.Unlock()
  2553  
  2554  	// retry interval is 10ms so 20ms should be plenty of time
  2555  	time.Sleep(20 * time.Millisecond)
  2556  	s.settle(c)
  2557  	// chg got retried, core is now installed, things are good
  2558  	c.Assert(chg.IsReady(), Equals, true)
  2559  
  2560  	s.state.Lock()
  2561  
  2562  	// ensure all our tasks ran
  2563  	c.Assert(chg.Err(), IsNil)
  2564  	c.Assert(chg.IsReady(), Equals, true)
  2565  
  2566  	// verify core in the system state
  2567  	var snaps map[string]*snapstate.SnapState
  2568  	err = s.state.Get("snaps", &snaps)
  2569  	c.Assert(err, IsNil)
  2570  
  2571  	snapst := snaps["core"]
  2572  	c.Assert(snapst, NotNil)
  2573  	c.Assert(snapst.Active, Equals, true)
  2574  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2575  		RealName: "core",
  2576  		Revision: snap.R(1),
  2577  	})
  2578  
  2579  	snapst = snaps["some-snap"]
  2580  	c.Assert(snapst, NotNil)
  2581  	c.Assert(snapst.Active, Equals, true)
  2582  	c.Assert(snapst.Sequence[0], DeepEquals, &snap.SideInfo{
  2583  		RealName: "some-snap",
  2584  		SnapID:   "some-snap-id",
  2585  		Channel:  "some-channel",
  2586  		Revision: snap.R(11),
  2587  	})
  2588  }
  2589  
  2590  func (s *snapmgrTestSuite) TestInstallDefaultProviderRunThrough(c *C) {
  2591  	s.state.Lock()
  2592  	defer s.state.Unlock()
  2593  
  2594  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
  2595  
  2596  	repo := interfaces.NewRepository()
  2597  	ifacerepo.Replace(s.state, repo)
  2598  
  2599  	chg := s.state.NewChange("install", "install a snap")
  2600  	opts := &snapstate.RevisionOptions{Channel: "stable", Revision: snap.R(42)}
  2601  	ts, err := snapstate.Install(context.Background(), s.state, "snap-content-plug", opts, s.user.ID, snapstate.Flags{})
  2602  	c.Assert(err, IsNil)
  2603  	chg.AddAll(ts)
  2604  
  2605  	s.state.Unlock()
  2606  	defer s.se.Stop()
  2607  	s.settle(c)
  2608  	s.state.Lock()
  2609  
  2610  	// ensure all our tasks ran
  2611  	c.Assert(chg.Err(), IsNil)
  2612  	c.Assert(chg.IsReady(), Equals, true)
  2613  	expected := fakeOps{{
  2614  		op:     "storesvc-snap-action",
  2615  		userID: 1,
  2616  	}, {
  2617  		op: "storesvc-snap-action:action",
  2618  		action: store.SnapAction{
  2619  			Action:       "install",
  2620  			InstanceName: "snap-content-plug",
  2621  			Revision:     snap.R(42),
  2622  		},
  2623  		revno:  snap.R(42),
  2624  		userID: 1,
  2625  	}, {
  2626  		op:     "storesvc-snap-action",
  2627  		userID: 1,
  2628  	}, {
  2629  		op: "storesvc-snap-action:action",
  2630  		action: store.SnapAction{
  2631  			Action:       "install",
  2632  			InstanceName: "snap-content-slot",
  2633  			Channel:      "stable",
  2634  		},
  2635  		revno:  snap.R(11),
  2636  		userID: 1,
  2637  	}, {
  2638  		op:   "storesvc-download",
  2639  		name: "snap-content-slot",
  2640  	}, {
  2641  		op:    "validate-snap:Doing",
  2642  		name:  "snap-content-slot",
  2643  		revno: snap.R(11),
  2644  	}, {
  2645  		op:  "current",
  2646  		old: "<no-current>",
  2647  	}, {
  2648  		op:   "open-snap-file",
  2649  		path: filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap"),
  2650  		sinfo: snap.SideInfo{
  2651  			RealName: "snap-content-slot",
  2652  			Channel:  "stable",
  2653  			SnapID:   "snap-content-slot-id",
  2654  			Revision: snap.R(11),
  2655  		},
  2656  	}, {
  2657  		op:    "setup-snap",
  2658  		name:  "snap-content-slot",
  2659  		path:  filepath.Join(dirs.SnapBlobDir, "snap-content-slot_11.snap"),
  2660  		revno: snap.R(11),
  2661  	}, {
  2662  		op:   "copy-data",
  2663  		path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"),
  2664  		old:  "<no-old>",
  2665  	}, {
  2666  		op:    "setup-profiles:Doing",
  2667  		name:  "snap-content-slot",
  2668  		revno: snap.R(11),
  2669  	}, {
  2670  		op: "candidate",
  2671  		sinfo: snap.SideInfo{
  2672  			RealName: "snap-content-slot",
  2673  			Channel:  "stable",
  2674  			SnapID:   "snap-content-slot-id",
  2675  			Revision: snap.R(11),
  2676  		},
  2677  	}, {
  2678  		op:   "link-snap",
  2679  		path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"),
  2680  	}, {
  2681  		op:    "auto-connect:Doing",
  2682  		name:  "snap-content-slot",
  2683  		revno: snap.R(11),
  2684  	}, {
  2685  		op: "update-aliases",
  2686  	}, {
  2687  		op:   "storesvc-download",
  2688  		name: "snap-content-plug",
  2689  	}, {
  2690  		op:    "validate-snap:Doing",
  2691  		name:  "snap-content-plug",
  2692  		revno: snap.R(42),
  2693  	}, {
  2694  		op:  "current",
  2695  		old: "<no-current>",
  2696  	}, {
  2697  		op:   "open-snap-file",
  2698  		path: filepath.Join(dirs.SnapBlobDir, "snap-content-plug_42.snap"),
  2699  		sinfo: snap.SideInfo{
  2700  			RealName: "snap-content-plug",
  2701  			SnapID:   "snap-content-plug-id",
  2702  			Revision: snap.R(42),
  2703  		},
  2704  	}, {
  2705  		op:    "setup-snap",
  2706  		name:  "snap-content-plug",
  2707  		path:  filepath.Join(dirs.SnapBlobDir, "snap-content-plug_42.snap"),
  2708  		revno: snap.R(42),
  2709  	}, {
  2710  		op:   "copy-data",
  2711  		path: filepath.Join(dirs.SnapMountDir, "snap-content-plug/42"),
  2712  		old:  "<no-old>",
  2713  	}, {
  2714  		op:    "setup-profiles:Doing",
  2715  		name:  "snap-content-plug",
  2716  		revno: snap.R(42),
  2717  	}, {
  2718  		op: "candidate",
  2719  		sinfo: snap.SideInfo{
  2720  			RealName: "snap-content-plug",
  2721  			SnapID:   "snap-content-plug-id",
  2722  			Revision: snap.R(42),
  2723  		},
  2724  	}, {
  2725  		op:   "link-snap",
  2726  		path: filepath.Join(dirs.SnapMountDir, "snap-content-plug/42"),
  2727  	}, {
  2728  		op:    "auto-connect:Doing",
  2729  		name:  "snap-content-plug",
  2730  		revno: snap.R(42),
  2731  	}, {
  2732  		op: "update-aliases",
  2733  	}, {
  2734  		op:    "cleanup-trash",
  2735  		name:  "snap-content-plug",
  2736  		revno: snap.R(42),
  2737  	}, {
  2738  		op:    "cleanup-trash",
  2739  		name:  "snap-content-slot",
  2740  		revno: snap.R(11),
  2741  	},
  2742  	}
  2743  	// snap and default provider are installed in parallel so we can't
  2744  	// do a simple c.Check(ops, DeepEquals, fakeOps{...})
  2745  	c.Check(len(s.fakeBackend.ops), Equals, len(expected))
  2746  	for _, op := range expected {
  2747  		c.Assert(s.fakeBackend.ops, testutil.DeepContains, op)
  2748  	}
  2749  	for _, op := range s.fakeBackend.ops {
  2750  		c.Assert(expected, testutil.DeepContains, op)
  2751  	}
  2752  }
  2753  
  2754  func (s *snapmgrTestSuite) TestInstallDefaultProviderCompat(c *C) {
  2755  	s.state.Lock()
  2756  	defer s.state.Unlock()
  2757  
  2758  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
  2759  
  2760  	repo := interfaces.NewRepository()
  2761  	ifacerepo.Replace(s.state, repo)
  2762  
  2763  	chg := s.state.NewChange("install", "install a snap")
  2764  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
  2765  	ts, err := snapstate.Install(context.Background(), s.state, "snap-content-plug-compat", opts, s.user.ID, snapstate.Flags{})
  2766  	c.Assert(err, IsNil)
  2767  	chg.AddAll(ts)
  2768  
  2769  	s.state.Unlock()
  2770  	defer s.se.Stop()
  2771  	s.settle(c)
  2772  	s.state.Lock()
  2773  
  2774  	// ensure all our tasks ran
  2775  	c.Assert(chg.Err(), IsNil)
  2776  	c.Assert(chg.IsReady(), Equals, true)
  2777  	// and both circular snaps got linked
  2778  	c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{
  2779  		op:   "link-snap",
  2780  		path: filepath.Join(dirs.SnapMountDir, "snap-content-plug-compat/42"),
  2781  	})
  2782  	c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{
  2783  		op:   "link-snap",
  2784  		path: filepath.Join(dirs.SnapMountDir, "snap-content-slot/11"),
  2785  	})
  2786  }
  2787  
  2788  func (s *snapmgrTestSuite) TestInstallDiskSpaceError(c *C) {
  2789  	restore := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return &osutil.NotEnoughDiskSpaceError{} })
  2790  	defer restore()
  2791  
  2792  	s.state.Lock()
  2793  	defer s.state.Unlock()
  2794  
  2795  	tr := config.NewTransaction(s.state)
  2796  	tr.Set("core", "experimental.check-disk-space-install", true)
  2797  	tr.Commit()
  2798  
  2799  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  2800  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2801  	diskSpaceErr := err.(*snapstate.InsufficientSpaceError)
  2802  	c.Assert(diskSpaceErr, ErrorMatches, `insufficient space in .* to perform "install" change for the following snaps: some-snap`)
  2803  	c.Check(diskSpaceErr.Path, Equals, filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd"))
  2804  	c.Check(diskSpaceErr.Snaps, DeepEquals, []string{"some-snap"})
  2805  }
  2806  
  2807  func (s *snapmgrTestSuite) TestInstallSizeError(c *C) {
  2808  	restore := snapstate.MockInstallSize(func(st *state.State, snaps []*snap.Info, userID int) (uint64, error) {
  2809  		return 0, fmt.Errorf("boom")
  2810  	})
  2811  	defer restore()
  2812  
  2813  	s.state.Lock()
  2814  	defer s.state.Unlock()
  2815  
  2816  	tr := config.NewTransaction(s.state)
  2817  	tr.Set("core", "experimental.check-disk-space-install", true)
  2818  	tr.Commit()
  2819  
  2820  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  2821  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2822  	c.Check(err, ErrorMatches, `boom`)
  2823  }
  2824  
  2825  func (s *snapmgrTestSuite) TestInstallPathWithLayoutsChecksFeatureFlag(c *C) {
  2826  	s.state.Lock()
  2827  	defer s.state.Unlock()
  2828  
  2829  	// When layouts are disabled we cannot install a local snap depending on the feature.
  2830  	tr := config.NewTransaction(s.state)
  2831  	tr.Set("core", "experimental.layouts", false)
  2832  	tr.Commit()
  2833  
  2834  	mockSnap := makeTestSnap(c, `name: some-snap
  2835  version: 1.0
  2836  layout:
  2837   /usr:
  2838    bind: $SNAP/usr
  2839  `)
  2840  	_, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{})
  2841  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true")
  2842  
  2843  	// When layouts are enabled we can install a local snap depending on the feature.
  2844  	tr = config.NewTransaction(s.state)
  2845  	tr.Set("core", "experimental.layouts", true)
  2846  	tr.Commit()
  2847  
  2848  	_, _, err = snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}, mockSnap, "", "", snapstate.Flags{})
  2849  	c.Assert(err, IsNil)
  2850  }
  2851  
  2852  func (s *snapmgrTestSuite) TestInstallPathWithMetadataChannelSwitchKernel(c *C) {
  2853  	// use the real thing for this one
  2854  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
  2855  
  2856  	s.state.Lock()
  2857  	defer s.state.Unlock()
  2858  
  2859  	// snapd cannot be installed unless the model uses a base snap
  2860  	r := snapstatetest.MockDeviceModel(ModelWithKernelTrack("18"))
  2861  	defer r()
  2862  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
  2863  		Sequence: []*snap.SideInfo{
  2864  			{RealName: "kernel", Revision: snap.R(11)},
  2865  		},
  2866  		TrackingChannel: "18/stable",
  2867  		Current:         snap.R(11),
  2868  		Active:          true,
  2869  	})
  2870  
  2871  	someSnap := makeTestSnap(c, `name: kernel
  2872  version: 1.0`)
  2873  	si := &snap.SideInfo{
  2874  		RealName: "kernel",
  2875  		SnapID:   "kernel-id",
  2876  		Revision: snap.R(42),
  2877  		Channel:  "some-channel",
  2878  	}
  2879  	_, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true})
  2880  	c.Assert(err, ErrorMatches, `cannot switch from kernel track "18" as specified for the \(device\) model to "some-channel"`)
  2881  }
  2882  
  2883  func (s *snapmgrTestSuite) TestInstallPathWithMetadataChannelSwitchGadget(c *C) {
  2884  	// use the real thing for this one
  2885  	snapstate.MockOpenSnapFile(backend.OpenSnapFile)
  2886  
  2887  	s.state.Lock()
  2888  	defer s.state.Unlock()
  2889  
  2890  	// snapd cannot be installed unless the model uses a base snap
  2891  	r := snapstatetest.MockDeviceModel(ModelWithGadgetTrack("18"))
  2892  	defer r()
  2893  	snapstate.Set(s.state, "brand-gadget", &snapstate.SnapState{
  2894  		Sequence: []*snap.SideInfo{
  2895  			{RealName: "brand-gadget", Revision: snap.R(11)},
  2896  		},
  2897  		TrackingChannel: "18/stable",
  2898  		Current:         snap.R(11),
  2899  		Active:          true,
  2900  	})
  2901  
  2902  	someSnap := makeTestSnap(c, `name: brand-gadget
  2903  version: 1.0`)
  2904  	si := &snap.SideInfo{
  2905  		RealName: "brand-gadget",
  2906  		SnapID:   "brand-gadget-id",
  2907  		Revision: snap.R(42),
  2908  		Channel:  "some-channel",
  2909  	}
  2910  	_, _, err := snapstate.InstallPath(s.state, si, someSnap, "", "some-channel", snapstate.Flags{Required: true})
  2911  	c.Assert(err, ErrorMatches, `cannot switch from gadget track "18" as specified for the \(device\) model to "some-channel"`)
  2912  }
  2913  
  2914  func (s *snapmgrTestSuite) TestInstallLayoutsChecksFeatureFlag(c *C) {
  2915  	s.state.Lock()
  2916  	defer s.state.Unlock()
  2917  
  2918  	// Layouts are now enabled by default.
  2919  	opts := &snapstate.RevisionOptions{Channel: "channel-for-layout"}
  2920  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2921  	c.Assert(err, IsNil)
  2922  
  2923  	// Layouts can be explicitly disabled.
  2924  	tr := config.NewTransaction(s.state)
  2925  	tr.Set("core", "experimental.layouts", false)
  2926  	tr.Commit()
  2927  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2928  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.layouts' to true")
  2929  
  2930  	// Layouts can be explicitly enabled.
  2931  	tr = config.NewTransaction(s.state)
  2932  	tr.Set("core", "experimental.layouts", true)
  2933  	tr.Commit()
  2934  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2935  	c.Assert(err, IsNil)
  2936  
  2937  	// The default empty value now means "enabled".
  2938  	tr = config.NewTransaction(s.state)
  2939  	tr.Set("core", "experimental.layouts", "")
  2940  	tr.Commit()
  2941  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2942  	c.Assert(err, IsNil)
  2943  
  2944  	// Layouts are enabled when the controlling flag is reset to nil.
  2945  	tr = config.NewTransaction(s.state)
  2946  	tr.Set("core", "experimental.layouts", nil)
  2947  	tr.Commit()
  2948  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2949  	c.Assert(err, IsNil)
  2950  }
  2951  
  2952  func (s *snapmgrTestSuite) TestInstallUserDaemonsChecksFeatureFlag(c *C) {
  2953  	if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" {
  2954  		c.Skip("Ubuntu 14.04 does not support user daemons")
  2955  	}
  2956  
  2957  	s.state.Lock()
  2958  	defer s.state.Unlock()
  2959  
  2960  	// User daemons are disabled by default.
  2961  	opts := &snapstate.RevisionOptions{Channel: "channel-for-user-daemon"}
  2962  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2963  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.user-daemons' to true")
  2964  
  2965  	// User daemons can be explicitly enabled.
  2966  	tr := config.NewTransaction(s.state)
  2967  	tr.Set("core", "experimental.user-daemons", true)
  2968  	tr.Commit()
  2969  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2970  	c.Assert(err, IsNil)
  2971  
  2972  	// User daemons can be explicitly disabled.
  2973  	tr = config.NewTransaction(s.state)
  2974  	tr.Set("core", "experimental.user-daemons", false)
  2975  	tr.Commit()
  2976  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2977  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.user-daemons' to true")
  2978  
  2979  	// The default empty value means "disabled"".
  2980  	tr = config.NewTransaction(s.state)
  2981  	tr.Set("core", "experimental.user-daemons", "")
  2982  	tr.Commit()
  2983  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2984  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.user-daemons' to true")
  2985  
  2986  	// User daemons are disabled when the controlling flag is reset to nil.
  2987  	tr = config.NewTransaction(s.state)
  2988  	tr.Set("core", "experimental.user-daemons", nil)
  2989  	tr.Commit()
  2990  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  2991  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.user-daemons' to true")
  2992  }
  2993  
  2994  func (s *snapmgrTestSuite) TestInstallUserDaemonsUsupportedOnTrusty(c *C) {
  2995  	restore := release.MockReleaseInfo(&release.OS{ID: "ubuntu", VersionID: "14.04"})
  2996  	defer restore()
  2997  	s.state.Lock()
  2998  	defer s.state.Unlock()
  2999  
  3000  	tr := config.NewTransaction(s.state)
  3001  	tr.Set("core", "experimental.user-daemons", true)
  3002  	tr.Commit()
  3003  
  3004  	// Even with the experimental.user-daemons flag set, user
  3005  	// daemons are not supported on Trusty
  3006  	opts := &snapstate.RevisionOptions{Channel: "channel-for-user-daemon"}
  3007  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3008  	c.Assert(err, ErrorMatches, "user session daemons are not supported on this release")
  3009  }
  3010  
  3011  func (s *snapmgrTestSuite) TestInstallDbusActivationChecksFeatureFlag(c *C) {
  3012  	s.state.Lock()
  3013  	defer s.state.Unlock()
  3014  
  3015  	// D-Bus activation is disabled by default.
  3016  	opts := &snapstate.RevisionOptions{Channel: "channel-for-dbus-activation"}
  3017  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3018  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.dbus-activation' to true")
  3019  
  3020  	// D-Bus activation can be explicitly enabled.
  3021  	tr := config.NewTransaction(s.state)
  3022  	tr.Set("core", "experimental.dbus-activation", true)
  3023  	tr.Commit()
  3024  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3025  	c.Assert(err, IsNil)
  3026  
  3027  	// D-Bus activation can be explicitly disabled.
  3028  	tr = config.NewTransaction(s.state)
  3029  	tr.Set("core", "experimental.dbus-activation", false)
  3030  	tr.Commit()
  3031  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3032  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.dbus-activation' to true")
  3033  
  3034  	// The default empty value means "disabled"
  3035  	tr = config.NewTransaction(s.state)
  3036  	tr.Set("core", "experimental.dbus-activation", "")
  3037  	tr.Commit()
  3038  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3039  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.dbus-activation' to true")
  3040  
  3041  	// D-Bus activation is disabled when the controlling flag is reset to nil.
  3042  	tr = config.NewTransaction(s.state)
  3043  	tr.Set("core", "experimental.dbus-activation", nil)
  3044  	tr.Commit()
  3045  	_, err = snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3046  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.dbus-activation' to true")
  3047  }
  3048  
  3049  func (s *snapmgrTestSuite) TestInstallValidatesInstanceNames(c *C) {
  3050  	s.state.Lock()
  3051  	defer s.state.Unlock()
  3052  
  3053  	_, err := snapstate.Install(context.Background(), s.state, "foo--invalid", nil, 0, snapstate.Flags{})
  3054  	c.Assert(err, ErrorMatches, `invalid instance name: invalid snap name: "foo--invalid"`)
  3055  
  3056  	_, err = snapstate.Install(context.Background(), s.state, "foo_123_456", nil, 0, snapstate.Flags{})
  3057  	c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`)
  3058  
  3059  	_, _, err = snapstate.InstallMany(s.state, []string{"foo--invalid"}, 0)
  3060  	c.Assert(err, ErrorMatches, `invalid instance name: invalid snap name: "foo--invalid"`)
  3061  
  3062  	_, _, err = snapstate.InstallMany(s.state, []string{"foo_123_456"}, 0)
  3063  	c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`)
  3064  
  3065  	mockSnap := makeTestSnap(c, `name: some-snap
  3066  version: 1.0
  3067  epoch: 1*
  3068  `)
  3069  	si := snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}
  3070  	_, _, err = snapstate.InstallPath(s.state, &si, mockSnap, "some-snap_123_456", "", snapstate.Flags{})
  3071  	c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "123_456"`)
  3072  }
  3073  
  3074  func (s *snapmgrTestSuite) TestInstallFailsWhenClassicSnapsAreNotSupported(c *C) {
  3075  	s.state.Lock()
  3076  	defer s.state.Unlock()
  3077  
  3078  	reset := release.MockReleaseInfo(&release.OS{
  3079  		ID: "fedora",
  3080  	})
  3081  	defer reset()
  3082  
  3083  	// this needs doing because dirs depends on the release info
  3084  	dirs.SetRootDir(dirs.GlobalRootDir)
  3085  
  3086  	opts := &snapstate.RevisionOptions{Channel: "channel-for-classic"}
  3087  	_, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{Classic: true})
  3088  	c.Assert(err, ErrorMatches, "classic confinement requires snaps under /snap or symlink from /snap to "+dirs.SnapMountDir)
  3089  }
  3090  
  3091  func (s *snapmgrTestSuite) TestInstallUndoRunThroughUndoContextOptional(c *C) {
  3092  	s.state.Lock()
  3093  	defer s.state.Unlock()
  3094  
  3095  	chg := s.state.NewChange("install", "install a snap")
  3096  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  3097  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap-no-install-record", opts, s.user.ID, snapstate.Flags{})
  3098  	c.Assert(err, IsNil)
  3099  	chg.AddAll(ts)
  3100  
  3101  	tasks := ts.Tasks()
  3102  	last := tasks[len(tasks)-1]
  3103  	// sanity
  3104  	c.Assert(last.Lanes(), HasLen, 1)
  3105  	terr := s.state.NewTask("error-trigger", "provoking total undo")
  3106  	terr.WaitFor(last)
  3107  	terr.JoinLane(last.Lanes()[0])
  3108  	chg.AddTask(terr)
  3109  
  3110  	s.state.Unlock()
  3111  	defer s.se.Stop()
  3112  	s.settle(c)
  3113  	s.state.Lock()
  3114  
  3115  	mountTask := tasks[len(tasks)-11]
  3116  	c.Assert(mountTask.Kind(), Equals, "mount-snap")
  3117  	var installRecord backend.InstallRecord
  3118  	c.Assert(mountTask.Get("install-record", &installRecord), Equals, state.ErrNoState)
  3119  }
  3120  
  3121  func (s *snapmgrTestSuite) TestInstallDefaultProviderCircular(c *C) {
  3122  	s.state.Lock()
  3123  	defer s.state.Unlock()
  3124  
  3125  	snapstate.ReplaceStore(s.state, contentStore{fakeStore: s.fakeStore, state: s.state})
  3126  
  3127  	repo := interfaces.NewRepository()
  3128  	ifacerepo.Replace(s.state, repo)
  3129  
  3130  	chg := s.state.NewChange("install", "install a snap")
  3131  	opts := &snapstate.RevisionOptions{Channel: "some-channel", Revision: snap.R(42)}
  3132  	ts, err := snapstate.Install(context.Background(), s.state, "snap-content-circular1", opts, s.user.ID, snapstate.Flags{})
  3133  	c.Assert(err, IsNil)
  3134  	chg.AddAll(ts)
  3135  
  3136  	s.state.Unlock()
  3137  	defer s.se.Stop()
  3138  	s.settle(c)
  3139  	s.state.Lock()
  3140  
  3141  	// ensure all our tasks ran
  3142  	c.Assert(chg.Err(), IsNil)
  3143  	c.Assert(chg.IsReady(), Equals, true)
  3144  	// and both circular snaps got linked
  3145  	c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{
  3146  		op:   "link-snap",
  3147  		path: filepath.Join(dirs.SnapMountDir, "snap-content-circular1/42"),
  3148  	})
  3149  	c.Check(s.fakeBackend.ops, testutil.DeepContains, fakeOp{
  3150  		op:   "link-snap",
  3151  		path: filepath.Join(dirs.SnapMountDir, "snap-content-circular2/11"),
  3152  	})
  3153  }
  3154  
  3155  func (s *snapmgrTestSuite) TestParallelInstallInstallPathExperimentalSwitch(c *C) {
  3156  	s.state.Lock()
  3157  	defer s.state.Unlock()
  3158  
  3159  	mockSnap := makeTestSnap(c, `name: some-snap
  3160  version: 1.0
  3161  `)
  3162  	si := &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(8)}
  3163  	_, _, err := snapstate.InstallPath(s.state, si, mockSnap, "some-snap_foo", "", snapstate.Flags{})
  3164  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.parallel-instances' to true")
  3165  
  3166  	// enable parallel instances
  3167  	tr := config.NewTransaction(s.state)
  3168  	tr.Set("core", "experimental.parallel-instances", true)
  3169  	tr.Commit()
  3170  
  3171  	_, _, err = snapstate.InstallPath(s.state, si, mockSnap, "some-snap_foo", "", snapstate.Flags{})
  3172  	c.Assert(err, IsNil)
  3173  }
  3174  
  3175  func (s *snapmgrTestSuite) TestInstallMany(c *C) {
  3176  	s.state.Lock()
  3177  	defer s.state.Unlock()
  3178  
  3179  	installed, tts, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0)
  3180  	c.Assert(err, IsNil)
  3181  	c.Assert(tts, HasLen, 2)
  3182  	c.Check(installed, DeepEquals, []string{"one", "two"})
  3183  
  3184  	c.Check(s.fakeStore.seenPrivacyKeys["privacy-key"], Equals, true)
  3185  
  3186  	for i, ts := range tts {
  3187  		verifyInstallTasks(c, 0, 0, ts, s.state)
  3188  		// check that tasksets are in separate lanes
  3189  		for _, t := range ts.Tasks() {
  3190  			c.Assert(t.Lanes(), DeepEquals, []int{i + 1})
  3191  		}
  3192  	}
  3193  }
  3194  
  3195  func (s *snapmgrTestSuite) TestInstallManyDiskSpaceError(c *C) {
  3196  	restore := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return &osutil.NotEnoughDiskSpaceError{} })
  3197  	defer restore()
  3198  
  3199  	s.state.Lock()
  3200  	defer s.state.Unlock()
  3201  
  3202  	tr := config.NewTransaction(s.state)
  3203  	tr.Set("core", "experimental.check-disk-space-install", true)
  3204  	tr.Commit()
  3205  
  3206  	_, _, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0)
  3207  	diskSpaceErr := err.(*snapstate.InsufficientSpaceError)
  3208  	c.Assert(diskSpaceErr, ErrorMatches, `insufficient space in .* to perform "install" change for the following snaps: one, two`)
  3209  	c.Check(diskSpaceErr.Path, Equals, filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd"))
  3210  	c.Check(diskSpaceErr.Snaps, DeepEquals, []string{"one", "two"})
  3211  	c.Check(diskSpaceErr.ChangeKind, Equals, "install")
  3212  }
  3213  
  3214  func (s *snapmgrTestSuite) TestInstallManyDiskCheckDisabled(c *C) {
  3215  	restore := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return &osutil.NotEnoughDiskSpaceError{} })
  3216  	defer restore()
  3217  
  3218  	s.state.Lock()
  3219  	defer s.state.Unlock()
  3220  
  3221  	tr := config.NewTransaction(s.state)
  3222  	tr.Set("core", "experimental.check-disk-space-install", false)
  3223  	tr.Commit()
  3224  
  3225  	_, _, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0)
  3226  	c.Check(err, IsNil)
  3227  }
  3228  
  3229  func (s *snapmgrTestSuite) TestInstallManyTooEarly(c *C) {
  3230  	s.state.Lock()
  3231  	defer s.state.Unlock()
  3232  
  3233  	s.state.Set("seeded", nil)
  3234  
  3235  	_, _, err := snapstate.InstallMany(s.state, []string{"one", "two"}, 0)
  3236  	c.Check(err, FitsTypeOf, &snapstate.ChangeConflictError{})
  3237  	c.Assert(err, ErrorMatches, `too early for operation, device not yet seeded or device model not acknowledged`)
  3238  }
  3239  
  3240  func (s *snapmgrTestSuite) TestInstallManyChecksPreconditions(c *C) {
  3241  	s.state.Lock()
  3242  	defer s.state.Unlock()
  3243  
  3244  	_, _, err := snapstate.InstallMany(s.state, []string{"some-snap-now-classic"}, 0)
  3245  	c.Assert(err, NotNil)
  3246  	c.Check(err, DeepEquals, &snapstate.SnapNeedsClassicError{Snap: "some-snap-now-classic"})
  3247  
  3248  	_, _, err = snapstate.InstallMany(s.state, []string{"some-snap_foo"}, 0)
  3249  	c.Assert(err, ErrorMatches, "experimental feature disabled - test it by setting 'experimental.parallel-instances' to true")
  3250  }
  3251  
  3252  func verifyStopReason(c *C, ts *state.TaskSet, reason string) {
  3253  	tl := tasksWithKind(ts, "stop-snap-services")
  3254  	c.Check(tl, HasLen, 1)
  3255  
  3256  	var stopReason string
  3257  	err := tl[0].Get("stop-reason", &stopReason)
  3258  	c.Assert(err, IsNil)
  3259  	c.Check(stopReason, Equals, reason)
  3260  
  3261  }
  3262  
  3263  func (s *snapmgrTestSuite) TestUndoMountSnapFailsInCopyData(c *C) {
  3264  	s.state.Lock()
  3265  	defer s.state.Unlock()
  3266  
  3267  	chg := s.state.NewChange("install", "install a snap")
  3268  	opts := &snapstate.RevisionOptions{Channel: "some-channel"}
  3269  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3270  	c.Assert(err, IsNil)
  3271  	chg.AddAll(ts)
  3272  
  3273  	s.fakeBackend.copySnapDataFailTrigger = filepath.Join(dirs.SnapMountDir, "some-snap/11")
  3274  
  3275  	s.state.Unlock()
  3276  	defer s.se.Stop()
  3277  	s.settle(c)
  3278  	s.state.Lock()
  3279  
  3280  	expected := fakeOps{
  3281  		{
  3282  			op:     "storesvc-snap-action",
  3283  			userID: 1,
  3284  		},
  3285  		{
  3286  			op: "storesvc-snap-action:action",
  3287  			action: store.SnapAction{
  3288  				Action:       "install",
  3289  				InstanceName: "some-snap",
  3290  				Channel:      "some-channel",
  3291  			},
  3292  			revno:  snap.R(11),
  3293  			userID: 1,
  3294  		},
  3295  		{
  3296  			op:   "storesvc-download",
  3297  			name: "some-snap",
  3298  		},
  3299  		{
  3300  			op:    "validate-snap:Doing",
  3301  			name:  "some-snap",
  3302  			revno: snap.R(11),
  3303  		},
  3304  		{
  3305  			op:  "current",
  3306  			old: "<no-current>",
  3307  		},
  3308  		{
  3309  			op:   "open-snap-file",
  3310  			path: filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  3311  			sinfo: snap.SideInfo{
  3312  				RealName: "some-snap",
  3313  				SnapID:   "some-snap-id",
  3314  				Channel:  "some-channel",
  3315  				Revision: snap.R(11),
  3316  			},
  3317  		},
  3318  		{
  3319  			op:    "setup-snap",
  3320  			name:  "some-snap",
  3321  			path:  filepath.Join(dirs.SnapBlobDir, "some-snap_11.snap"),
  3322  			revno: snap.R(11),
  3323  		},
  3324  		{
  3325  			op:   "copy-data.failed",
  3326  			path: filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  3327  			old:  "<no-old>",
  3328  		},
  3329  		{
  3330  			op:   "remove-snap-data-dir",
  3331  			name: "some-snap",
  3332  			path: filepath.Join(dirs.SnapDataDir, "some-snap"),
  3333  		},
  3334  		{
  3335  			op:    "undo-setup-snap",
  3336  			name:  "some-snap",
  3337  			path:  filepath.Join(dirs.SnapMountDir, "some-snap/11"),
  3338  			stype: "app",
  3339  		},
  3340  		{
  3341  			op:   "remove-snap-dir",
  3342  			name: "some-snap",
  3343  			path: filepath.Join(dirs.SnapMountDir, "some-snap"),
  3344  		},
  3345  	}
  3346  	// start with an easier-to-read error if this fails:
  3347  	c.Assert(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops())
  3348  	c.Assert(s.fakeBackend.ops, DeepEquals, expected)
  3349  }
  3350  
  3351  func (s *snapmgrTestSuite) TestSideInfoPaid(c *C) {
  3352  	s.state.Lock()
  3353  	defer s.state.Unlock()
  3354  	opts := &snapstate.RevisionOptions{Channel: "channel-for-paid"}
  3355  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3356  	c.Assert(err, IsNil)
  3357  
  3358  	chg := s.state.NewChange("install", "install paid snap")
  3359  	chg.AddAll(ts)
  3360  
  3361  	s.state.Unlock()
  3362  	defer s.se.Stop()
  3363  	s.settle(c)
  3364  	s.state.Lock()
  3365  
  3366  	// verify snap has paid sideinfo
  3367  	var snapst snapstate.SnapState
  3368  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3369  	c.Assert(err, IsNil)
  3370  	c.Check(snapst.CurrentSideInfo().Paid, Equals, true)
  3371  	c.Check(snapst.CurrentSideInfo().Private, Equals, false)
  3372  }
  3373  
  3374  func (s *snapmgrTestSuite) TestSideInfoPrivate(c *C) {
  3375  	s.state.Lock()
  3376  	defer s.state.Unlock()
  3377  	opts := &snapstate.RevisionOptions{Channel: "channel-for-private"}
  3378  	ts, err := snapstate.Install(context.Background(), s.state, "some-snap", opts, s.user.ID, snapstate.Flags{})
  3379  	c.Assert(err, IsNil)
  3380  
  3381  	chg := s.state.NewChange("install", "install private snap")
  3382  	chg.AddAll(ts)
  3383  
  3384  	s.state.Unlock()
  3385  	defer s.se.Stop()
  3386  	s.settle(c)
  3387  	s.state.Lock()
  3388  
  3389  	// verify snap has private sideinfo
  3390  	var snapst snapstate.SnapState
  3391  	err = snapstate.Get(s.state, "some-snap", &snapst)
  3392  	c.Assert(err, IsNil)
  3393  	c.Check(snapst.CurrentSideInfo().Private, Equals, true)
  3394  	c.Check(snapst.CurrentSideInfo().Paid, Equals, false)
  3395  }
  3396  
  3397  func (s *snapmgrTestSuite) TestGadgetDefaultsInstalled(c *C) {
  3398  	makeInstalledMockCoreSnap(c)
  3399  
  3400  	// using MockSnap, we want to read the bits on disk
  3401  	snapstate.MockSnapReadInfo(snap.ReadInfo)
  3402  
  3403  	s.state.Lock()
  3404  	defer s.state.Unlock()
  3405  
  3406  	s.prepareGadget(c)
  3407  
  3408  	snapstate.Set(s.state, "some-snap", &snapstate.SnapState{
  3409  		Active:   true,
  3410  		Sequence: []*snap.SideInfo{{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}},
  3411  		Current:  snap.R(1),
  3412  		SnapType: "app",
  3413  	})
  3414  
  3415  	snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0")
  3416  
  3417  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(2)}, snapPath, "", "edge", snapstate.Flags{})
  3418  	c.Assert(err, IsNil)
  3419  
  3420  	var m map[string]interface{}
  3421  	runHooks := tasksWithKind(ts, "run-hook")
  3422  
  3423  	c.Assert(runHooks[0].Kind(), Equals, "run-hook")
  3424  	err = runHooks[0].Get("hook-context", &m)
  3425  	c.Assert(err, Equals, state.ErrNoState)
  3426  }
  3427  
  3428  func makeInstalledMockCoreSnap(c *C) {
  3429  	coreSnapYaml := `name: core
  3430  version: 1.0
  3431  type: os
  3432  `
  3433  	snaptest.MockSnap(c, coreSnapYaml, &snap.SideInfo{
  3434  		RealName: "core",
  3435  		Revision: snap.R(1),
  3436  	})
  3437  }
  3438  
  3439  func (s *snapmgrTestSuite) TestGadgetDefaults(c *C) {
  3440  	r := release.MockOnClassic(false)
  3441  	defer r()
  3442  
  3443  	makeInstalledMockCoreSnap(c)
  3444  
  3445  	// using MockSnap, we want to read the bits on disk
  3446  	snapstate.MockSnapReadInfo(snap.ReadInfo)
  3447  
  3448  	s.state.Lock()
  3449  	defer s.state.Unlock()
  3450  
  3451  	s.prepareGadget(c)
  3452  
  3453  	snapPath := makeTestSnap(c, "name: some-snap\nversion: 1.0")
  3454  
  3455  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "some-snap", SnapID: "some-snap-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{})
  3456  	c.Assert(err, IsNil)
  3457  
  3458  	var m map[string]interface{}
  3459  	runHooks := tasksWithKind(ts, "run-hook")
  3460  
  3461  	c.Assert(taskKinds(runHooks), DeepEquals, []string{
  3462  		"run-hook[install]",
  3463  		"run-hook[configure]",
  3464  		"run-hook[check-health]",
  3465  	})
  3466  	err = runHooks[1].Get("hook-context", &m)
  3467  	c.Assert(err, IsNil)
  3468  	c.Assert(m, DeepEquals, map[string]interface{}{"use-defaults": true})
  3469  }
  3470  
  3471  func (s *snapmgrTestSuite) TestGadgetDefaultsNotForOS(c *C) {
  3472  	r := release.MockOnClassic(false)
  3473  	defer r()
  3474  
  3475  	// using MockSnap, we want to read the bits on disk
  3476  	snapstate.MockSnapReadInfo(snap.ReadInfo)
  3477  
  3478  	s.state.Lock()
  3479  	defer s.state.Unlock()
  3480  
  3481  	snapstate.Set(s.state, "core", nil)
  3482  
  3483  	s.prepareGadget(c)
  3484  
  3485  	const coreSnapYaml = `
  3486  name: core
  3487  type: os
  3488  version: 1.0
  3489  `
  3490  	snapPath := makeTestSnap(c, coreSnapYaml)
  3491  
  3492  	ts, _, err := snapstate.InstallPath(s.state, &snap.SideInfo{RealName: "core", SnapID: "core-id", Revision: snap.R(1)}, snapPath, "", "edge", snapstate.Flags{})
  3493  	c.Assert(err, IsNil)
  3494  
  3495  	var m map[string]interface{}
  3496  	runHooks := tasksWithKind(ts, "run-hook")
  3497  
  3498  	c.Assert(taskKinds(runHooks), DeepEquals, []string{
  3499  		"run-hook[install]",
  3500  		"run-hook[configure]",
  3501  		"run-hook[check-health]",
  3502  	})
  3503  	// use-defaults flag is part of hook-context which isn't set
  3504  	err = runHooks[1].Get("hook-context", &m)
  3505  	c.Assert(err, Equals, state.ErrNoState)
  3506  }
  3507  
  3508  func (s *snapmgrTestSuite) TestGadgetDefaultsAreNormalizedForConfigHook(c *C) {
  3509  	var mockGadgetSnapYaml = `
  3510  name: canonical-pc
  3511  type: gadget
  3512  `
  3513  	var mockGadgetYaml = []byte(`
  3514  defaults:
  3515    otheridididididididididididididi:
  3516      foo:
  3517        bar: baz
  3518        num: 1.305
  3519  
  3520  volumes:
  3521      volume-id:
  3522          bootloader: grub
  3523  `)
  3524  
  3525  	info := snaptest.MockSnap(c, mockGadgetSnapYaml, &snap.SideInfo{Revision: snap.R(2)})
  3526  	err := ioutil.WriteFile(filepath.Join(info.MountDir(), "meta", "gadget.yaml"), mockGadgetYaml, 0644)
  3527  	c.Assert(err, IsNil)
  3528  
  3529  	gi, err := gadget.ReadInfo(info.MountDir(), nil)
  3530  	c.Assert(err, IsNil)
  3531  	c.Assert(gi, NotNil)
  3532  
  3533  	snapName := "some-snap"
  3534  	hooksup := &hookstate.HookSetup{
  3535  		Snap:        snapName,
  3536  		Hook:        "configure",
  3537  		Optional:    true,
  3538  		IgnoreError: false,
  3539  		TrackError:  false,
  3540  	}
  3541  
  3542  	var contextData map[string]interface{}
  3543  	contextData = map[string]interface{}{"patch": gi.Defaults}
  3544  
  3545  	s.state.Lock()
  3546  	defer s.state.Unlock()
  3547  	c.Assert(hookstate.HookTask(s.state, "", hooksup, contextData), NotNil)
  3548  }