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