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