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