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