github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/overlord/managers_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2019 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 overlord_test
    21  
    22  // test the various managers and their operation together through overlord
    23  
    24  import (
    25  	"bytes"
    26  	"context"
    27  	"encoding/json"
    28  	"errors"
    29  	"fmt"
    30  	"io"
    31  	"io/ioutil"
    32  	"net/http"
    33  	"net/http/httptest"
    34  	"net/url"
    35  	"os"
    36  	"path/filepath"
    37  	"sort"
    38  	"strings"
    39  	"time"
    40  
    41  	. "gopkg.in/check.v1"
    42  	"gopkg.in/tomb.v2"
    43  	"gopkg.in/yaml.v2"
    44  
    45  	"github.com/snapcore/snapd/asserts"
    46  	"github.com/snapcore/snapd/asserts/assertstest"
    47  	"github.com/snapcore/snapd/asserts/sysdb"
    48  	"github.com/snapcore/snapd/boot"
    49  	"github.com/snapcore/snapd/boot/boottest"
    50  	"github.com/snapcore/snapd/bootloader"
    51  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    52  	"github.com/snapcore/snapd/client"
    53  	"github.com/snapcore/snapd/dirs"
    54  	"github.com/snapcore/snapd/gadget"
    55  	"github.com/snapcore/snapd/interfaces"
    56  	"github.com/snapcore/snapd/osutil"
    57  	"github.com/snapcore/snapd/overlord"
    58  	"github.com/snapcore/snapd/overlord/assertstate"
    59  	"github.com/snapcore/snapd/overlord/assertstate/assertstatetest"
    60  	"github.com/snapcore/snapd/overlord/auth"
    61  	"github.com/snapcore/snapd/overlord/configstate/config"
    62  	"github.com/snapcore/snapd/overlord/devicestate"
    63  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    64  	"github.com/snapcore/snapd/overlord/hookstate"
    65  	"github.com/snapcore/snapd/overlord/hookstate/ctlcmd"
    66  	"github.com/snapcore/snapd/overlord/ifacestate"
    67  	"github.com/snapcore/snapd/overlord/snapshotstate"
    68  	snapshotbackend "github.com/snapcore/snapd/overlord/snapshotstate/backend"
    69  	"github.com/snapcore/snapd/overlord/snapstate"
    70  	"github.com/snapcore/snapd/overlord/state"
    71  	"github.com/snapcore/snapd/release"
    72  	"github.com/snapcore/snapd/snap"
    73  	"github.com/snapcore/snapd/snap/snapfile"
    74  	"github.com/snapcore/snapd/snap/snaptest"
    75  	"github.com/snapcore/snapd/store"
    76  	"github.com/snapcore/snapd/systemd"
    77  	"github.com/snapcore/snapd/testutil"
    78  )
    79  
    80  var (
    81  	settleTimeout           = testutil.HostScaledTimeout(45 * time.Second)
    82  	aggressiveSettleTimeout = testutil.HostScaledTimeout(50 * time.Millisecond)
    83  	connectRetryTimeout     = testutil.HostScaledTimeout(70 * time.Millisecond)
    84  )
    85  
    86  type automaticSnapshotCall struct {
    87  	InstanceName string
    88  	SnapConfig   map[string]interface{}
    89  	Usernames    []string
    90  	Flags        *snapshotbackend.Flags
    91  }
    92  
    93  type baseMgrsSuite struct {
    94  	testutil.BaseTest
    95  
    96  	tempdir string
    97  
    98  	storeSigning *assertstest.StoreStack
    99  	brands       *assertstest.SigningAccounts
   100  
   101  	devAcct *asserts.Account
   102  
   103  	serveIDtoName map[string]string
   104  	serveSnapPath map[string]string
   105  	serveRevision map[string]string
   106  	serveOldPaths map[string][]string
   107  	serveOldRevs  map[string][]string
   108  
   109  	hijackServeSnap func(http.ResponseWriter)
   110  
   111  	checkDeviceAndAuthContext func(store.DeviceAndAuthContext)
   112  	expectedSerial            string
   113  	expectedStore             string
   114  	sessionMacaroon           string
   115  
   116  	o *overlord.Overlord
   117  
   118  	failNextDownload string
   119  
   120  	automaticSnapshots []automaticSnapshotCall
   121  }
   122  
   123  var (
   124  	_ = Suite(&mgrsSuite{})
   125  	_ = Suite(&storeCtxSetupSuite{})
   126  )
   127  
   128  var (
   129  	brandPrivKey, _ = assertstest.GenerateKey(752)
   130  
   131  	develPrivKey, _ = assertstest.GenerateKey(752)
   132  
   133  	deviceKey, _ = assertstest.GenerateKey(752)
   134  )
   135  
   136  func verifyLastTasksetIsRerefresh(c *C, tts []*state.TaskSet) {
   137  	ts := tts[len(tts)-1]
   138  	c.Assert(ts.Tasks(), HasLen, 1)
   139  	c.Check(ts.Tasks()[0].Kind(), Equals, "check-rerefresh")
   140  }
   141  
   142  func (s *baseMgrsSuite) SetUpTest(c *C) {
   143  	s.BaseTest.SetUpTest(c)
   144  
   145  	s.tempdir = c.MkDir()
   146  	dirs.SetRootDir(s.tempdir)
   147  	s.AddCleanup(func() { dirs.SetRootDir("") })
   148  
   149  	// needed for system key generation
   150  	s.AddCleanup(osutil.MockMountInfo(""))
   151  
   152  	err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755)
   153  	c.Assert(err, IsNil)
   154  
   155  	// needed by hooks
   156  	s.AddCleanup(testutil.MockCommand(c, "snap", "").Restore)
   157  
   158  	restoreCheckFreeSpace := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return nil })
   159  	s.AddCleanup(restoreCheckFreeSpace)
   160  
   161  	oldSetupInstallHook := snapstate.SetupInstallHook
   162  	oldSetupRemoveHook := snapstate.SetupRemoveHook
   163  	snapstate.SetupRemoveHook = hookstate.SetupRemoveHook
   164  	snapstate.SetupInstallHook = hookstate.SetupInstallHook
   165  	s.AddCleanup(func() {
   166  		snapstate.SetupRemoveHook = oldSetupRemoveHook
   167  		snapstate.SetupInstallHook = oldSetupInstallHook
   168  	})
   169  
   170  	s.automaticSnapshots = nil
   171  	r := snapshotstate.MockBackendSave(func(_ context.Context, id uint64, si *snap.Info, cfg map[string]interface{}, usernames []string, flags *snapshotbackend.Flags) (*client.Snapshot, error) {
   172  		s.automaticSnapshots = append(s.automaticSnapshots, automaticSnapshotCall{InstanceName: si.InstanceName(), SnapConfig: cfg, Usernames: usernames, Flags: flags})
   173  		return nil, nil
   174  	})
   175  	s.AddCleanup(r)
   176  
   177  	s.AddCleanup(ifacestate.MockConnectRetryTimeout(connectRetryTimeout))
   178  
   179  	os.Setenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS", "1")
   180  	s.AddCleanup(func() { os.Unsetenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") })
   181  
   182  	// create a fake systemd environment
   183  	os.MkdirAll(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants"), 0755)
   184  
   185  	r = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   186  		return []byte("ActiveState=inactive\n"), nil
   187  	})
   188  	s.AddCleanup(r)
   189  
   190  	s.storeSigning = assertstest.NewStoreStack("can0nical", nil)
   191  	s.brands = assertstest.NewSigningAccounts(s.storeSigning)
   192  	s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{
   193  		"validation": "verified",
   194  	})
   195  	s.AddCleanup(sysdb.InjectTrusted(s.storeSigning.Trusted))
   196  
   197  	s.devAcct = assertstest.NewAccount(s.storeSigning, "devdevdev", map[string]interface{}{
   198  		"account-id": "devdevdev",
   199  	}, "")
   200  	err = s.storeSigning.Add(s.devAcct)
   201  	c.Assert(err, IsNil)
   202  
   203  	s.serveIDtoName = make(map[string]string)
   204  	s.serveSnapPath = make(map[string]string)
   205  	s.serveRevision = make(map[string]string)
   206  	s.serveOldPaths = make(map[string][]string)
   207  	s.serveOldRevs = make(map[string][]string)
   208  	s.hijackServeSnap = nil
   209  
   210  	s.checkDeviceAndAuthContext = nil
   211  	s.expectedSerial = ""
   212  	s.expectedStore = ""
   213  	s.sessionMacaroon = ""
   214  
   215  	s.AddCleanup(ifacestate.MockSecurityBackends(nil))
   216  
   217  	o, err := overlord.New(nil)
   218  	c.Assert(err, IsNil)
   219  	err = o.StartUp()
   220  	c.Assert(err, IsNil)
   221  	o.InterfaceManager().DisableUDevMonitor()
   222  	s.o = o
   223  	st := s.o.State()
   224  	st.Lock()
   225  	defer st.Unlock()
   226  	st.Set("seeded", true)
   227  	// registered
   228  	err = assertstate.Add(st, sysdb.GenericClassicModel())
   229  	c.Assert(err, IsNil)
   230  	devicestatetest.SetDevice(st, &auth.DeviceState{
   231  		Brand:  "generic",
   232  		Model:  "generic-classic",
   233  		Serial: "serialserial",
   234  	})
   235  
   236  	// add "core" snap declaration
   237  	headers := map[string]interface{}{
   238  		"series":       "16",
   239  		"snap-name":    "core",
   240  		"publisher-id": "can0nical",
   241  		"timestamp":    time.Now().Format(time.RFC3339),
   242  	}
   243  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   244  	err = assertstate.Add(st, s.storeSigning.StoreAccountKey(""))
   245  	c.Assert(err, IsNil)
   246  	a, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   247  	c.Assert(err, IsNil)
   248  	err = assertstate.Add(st, a)
   249  	c.Assert(err, IsNil)
   250  	s.serveRevision["core"] = "1"
   251  	s.serveIDtoName[fakeSnapID("core")] = "core"
   252  	err = s.storeSigning.Add(a)
   253  	c.Assert(err, IsNil)
   254  
   255  	// add "snap1" snap declaration
   256  	headers = map[string]interface{}{
   257  		"series":       "16",
   258  		"snap-name":    "snap1",
   259  		"publisher-id": "can0nical",
   260  		"timestamp":    time.Now().Format(time.RFC3339),
   261  	}
   262  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   263  	a2, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   264  	c.Assert(err, IsNil)
   265  	c.Assert(assertstate.Add(st, a2), IsNil)
   266  	c.Assert(s.storeSigning.Add(a2), IsNil)
   267  
   268  	// add "snap2" snap declaration
   269  	headers = map[string]interface{}{
   270  		"series":       "16",
   271  		"snap-name":    "snap2",
   272  		"publisher-id": "can0nical",
   273  		"timestamp":    time.Now().Format(time.RFC3339),
   274  	}
   275  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   276  	a3, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   277  	c.Assert(err, IsNil)
   278  	c.Assert(assertstate.Add(st, a3), IsNil)
   279  	c.Assert(s.storeSigning.Add(a3), IsNil)
   280  
   281  	// add "some-snap" snap declaration
   282  	headers = map[string]interface{}{
   283  		"series":       "16",
   284  		"snap-name":    "some-snap",
   285  		"publisher-id": "can0nical",
   286  		"timestamp":    time.Now().Format(time.RFC3339),
   287  	}
   288  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   289  	a4, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   290  	c.Assert(err, IsNil)
   291  	c.Assert(assertstate.Add(st, a4), IsNil)
   292  	c.Assert(s.storeSigning.Add(a4), IsNil)
   293  
   294  	// add "other-snap" snap declaration
   295  	headers = map[string]interface{}{
   296  		"series":       "16",
   297  		"snap-name":    "other-snap",
   298  		"publisher-id": "can0nical",
   299  		"timestamp":    time.Now().Format(time.RFC3339),
   300  	}
   301  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   302  	a5, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   303  	c.Assert(err, IsNil)
   304  	c.Assert(assertstate.Add(st, a5), IsNil)
   305  	c.Assert(s.storeSigning.Add(a5), IsNil)
   306  
   307  	// add pc-kernel snap declaration
   308  	headers = map[string]interface{}{
   309  		"series":       "16",
   310  		"snap-name":    "pc-kernel",
   311  		"publisher-id": "can0nical",
   312  		"timestamp":    time.Now().Format(time.RFC3339),
   313  	}
   314  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   315  	a6, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   316  	c.Assert(err, IsNil)
   317  	c.Assert(assertstate.Add(st, a6), IsNil)
   318  	c.Assert(s.storeSigning.Add(a6), IsNil)
   319  
   320  	// add pc snap declaration
   321  	headers = map[string]interface{}{
   322  		"series":       "16",
   323  		"snap-name":    "pc",
   324  		"publisher-id": "can0nical",
   325  		"timestamp":    time.Now().Format(time.RFC3339),
   326  	}
   327  	headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   328  	a7, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   329  	c.Assert(err, IsNil)
   330  	c.Assert(assertstate.Add(st, a7), IsNil)
   331  	c.Assert(s.storeSigning.Add(a7), IsNil)
   332  
   333  	// add core itself
   334  	snapstate.Set(st, "core", &snapstate.SnapState{
   335  		Active: true,
   336  		Sequence: []*snap.SideInfo{
   337  			{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)},
   338  		},
   339  		Current:  snap.R(1),
   340  		SnapType: "os",
   341  		Flags: snapstate.Flags{
   342  			Required: true,
   343  		},
   344  	})
   345  
   346  	// don't actually try to talk to the store on snapstate.Ensure
   347  	// needs doing after the call to devicestate.Manager (which happens in overlord.New)
   348  	snapstate.CanAutoRefresh = nil
   349  
   350  	st.Set("refresh-privacy-key", "privacy-key")
   351  
   352  	// For triggering errors
   353  	erroringHandler := func(task *state.Task, _ *tomb.Tomb) error {
   354  		return errors.New("error out")
   355  	}
   356  	s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil)
   357  
   358  	// setup cloud-init as restricted so that tests by default don't run the
   359  	// full EnsureCloudInitRestricted logic in the devicestate mgr
   360  	snapdCloudInitRestrictedFile := filepath.Join(dirs.GlobalRootDir, "etc/cloud/cloud.cfg.d/zzzz_snapd.cfg")
   361  	err = os.MkdirAll(filepath.Dir(snapdCloudInitRestrictedFile), 0755)
   362  	c.Assert(err, IsNil)
   363  	err = ioutil.WriteFile(snapdCloudInitRestrictedFile, nil, 0644)
   364  	c.Assert(err, IsNil)
   365  }
   366  
   367  type mgrsSuite struct {
   368  	baseMgrsSuite
   369  }
   370  
   371  func makeTestSnapWithFiles(c *C, snapYamlContent string, files [][]string) string {
   372  	info, err := snap.InfoFromSnapYaml([]byte(snapYamlContent))
   373  	c.Assert(err, IsNil)
   374  
   375  	for _, app := range info.Apps {
   376  		// files is a list of (filename, content)
   377  		files = append(files, []string{app.Command, ""})
   378  	}
   379  
   380  	return snaptest.MakeTestSnapWithFiles(c, snapYamlContent, files)
   381  }
   382  
   383  func makeTestSnap(c *C, snapYamlContent string) string {
   384  	return makeTestSnapWithFiles(c, snapYamlContent, nil)
   385  }
   386  
   387  func (s *mgrsSuite) TestHappyLocalInstall(c *C) {
   388  	snapYamlContent := `name: foo
   389  apps:
   390   bar:
   391    command: bin/bar
   392  `
   393  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.0")
   394  
   395  	st := s.o.State()
   396  	st.Lock()
   397  	defer st.Unlock()
   398  
   399  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "foo"}, snapPath, "", "", snapstate.Flags{DevMode: true})
   400  	c.Assert(err, IsNil)
   401  	chg := st.NewChange("install-snap", "...")
   402  	chg.AddAll(ts)
   403  
   404  	st.Unlock()
   405  	err = s.o.Settle(settleTimeout)
   406  	st.Lock()
   407  	c.Assert(err, IsNil)
   408  
   409  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
   410  
   411  	snap, err := snapstate.CurrentInfo(st, "foo")
   412  	c.Assert(err, IsNil)
   413  
   414  	// ensure that the binary wrapper file got generated with the right
   415  	// name
   416  	binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar")
   417  	c.Assert(osutil.IsSymlink(binaryWrapper), Equals, true)
   418  
   419  	// data dirs
   420  	c.Assert(osutil.IsDirectory(snap.DataDir()), Equals, true)
   421  	c.Assert(osutil.IsDirectory(snap.CommonDataDir()), Equals, true)
   422  
   423  	// snap file and its mounting
   424  
   425  	// after install the snap file is in the right dir
   426  	c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_x1.snap")), Equals, true)
   427  
   428  	// ensure the right unit is created
   429  	mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/x1"))
   430  	c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s/foo/x1", dirs.StripRootDir(dirs.SnapMountDir)))
   431  	c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/foo_x1.snap")
   432  }
   433  
   434  func (s *mgrsSuite) TestLocalInstallUndo(c *C) {
   435  	snapYamlContent := `name: foo
   436  apps:
   437   bar:
   438    command: bin/bar
   439  hooks:
   440    install:
   441    configure:
   442  `
   443  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.0")
   444  
   445  	installHook := false
   446  	defer hookstate.MockRunHook(func(ctx *hookstate.Context, _ *tomb.Tomb) ([]byte, error) {
   447  		switch ctx.HookName() {
   448  		case "install":
   449  			installHook = true
   450  			_, _, err := ctlcmd.Run(ctx, []string{"set", "installed=true"}, 0)
   451  			c.Assert(err, IsNil)
   452  			return nil, nil
   453  		case "configure":
   454  			return nil, errors.New("configure failed")
   455  		}
   456  		return nil, nil
   457  	})()
   458  
   459  	st := s.o.State()
   460  	st.Lock()
   461  	defer st.Unlock()
   462  
   463  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "foo"}, snapPath, "", "", snapstate.Flags{DevMode: true})
   464  	c.Assert(err, IsNil)
   465  	chg := st.NewChange("install-snap", "...")
   466  	chg.AddAll(ts)
   467  
   468  	st.Unlock()
   469  	err = s.o.Settle(settleTimeout)
   470  	st.Lock()
   471  	c.Assert(err, IsNil)
   472  
   473  	c.Assert(chg.Status(), Equals, state.ErrorStatus, Commentf("install-snap unexpectedly succeeded"))
   474  
   475  	// check undo statutes
   476  	for _, t := range chg.Tasks() {
   477  		which := t.Kind()
   478  		expectedStatus := state.UndoneStatus
   479  		switch t.Kind() {
   480  		case "prerequisites":
   481  			expectedStatus = state.DoneStatus
   482  		case "run-hook":
   483  			var hs hookstate.HookSetup
   484  			err := t.Get("hook-setup", &hs)
   485  			c.Assert(err, IsNil)
   486  			switch hs.Hook {
   487  			case "install":
   488  				expectedStatus = state.UndoneStatus
   489  			case "configure":
   490  				expectedStatus = state.ErrorStatus
   491  			case "check-health":
   492  				expectedStatus = state.HoldStatus
   493  			}
   494  			which += fmt.Sprintf("[%s]", hs.Hook)
   495  		}
   496  		c.Assert(t.Status(), Equals, expectedStatus, Commentf("%s", which))
   497  	}
   498  
   499  	// install hooks was called
   500  	c.Check(installHook, Equals, true)
   501  
   502  	// nothing in snaps
   503  	all, err := snapstate.All(st)
   504  	c.Assert(err, IsNil)
   505  	c.Check(all, HasLen, 1)
   506  	_, ok := all["core"]
   507  	c.Check(ok, Equals, true)
   508  
   509  	// nothing in config
   510  	var config map[string]*json.RawMessage
   511  	err = st.Get("config", &config)
   512  	c.Assert(err, IsNil)
   513  	c.Check(config, HasLen, 1)
   514  	_, ok = config["core"]
   515  	c.Check(ok, Equals, true)
   516  
   517  	snapdirs, err := filepath.Glob(filepath.Join(dirs.SnapMountDir, "*"))
   518  	c.Assert(err, IsNil)
   519  	// just README and bin
   520  	c.Check(snapdirs, HasLen, 2)
   521  	for _, d := range snapdirs {
   522  		c.Check(filepath.Base(d), Not(Equals), "foo")
   523  	}
   524  }
   525  
   526  func (s *mgrsSuite) TestHappyRemove(c *C) {
   527  	oldEstimateSnapshotSize := snapstate.EstimateSnapshotSize
   528  	snapstate.EstimateSnapshotSize = func(st *state.State, instanceName string, users []string) (uint64, error) {
   529  		return 0, nil
   530  	}
   531  	defer func() {
   532  		snapstate.EstimateSnapshotSize = oldEstimateSnapshotSize
   533  	}()
   534  
   535  	st := s.o.State()
   536  	st.Lock()
   537  	defer st.Unlock()
   538  
   539  	snapYamlContent := `name: foo
   540  apps:
   541   bar:
   542    command: bin/bar
   543  `
   544  	snapInfo := s.installLocalTestSnap(c, snapYamlContent+"version: 1.0")
   545  
   546  	// set config
   547  	tr := config.NewTransaction(st)
   548  	c.Assert(tr.Set("foo", "key", "value"), IsNil)
   549  	tr.Commit()
   550  
   551  	ts, err := snapstate.Remove(st, "foo", snap.R(0), nil)
   552  	c.Assert(err, IsNil)
   553  	chg := st.NewChange("remove-snap", "...")
   554  	chg.AddAll(ts)
   555  
   556  	st.Unlock()
   557  	err = s.o.Settle(settleTimeout)
   558  	st.Lock()
   559  	c.Assert(err, IsNil)
   560  
   561  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err()))
   562  
   563  	// ensure that the binary wrapper file got removed
   564  	binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar")
   565  	c.Assert(osutil.FileExists(binaryWrapper), Equals, false)
   566  
   567  	// data dirs
   568  	c.Assert(osutil.FileExists(snapInfo.DataDir()), Equals, false)
   569  	c.Assert(osutil.FileExists(snapInfo.CommonDataDir()), Equals, false)
   570  
   571  	// snap file and its mount
   572  	c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_x1.snap")), Equals, false)
   573  	mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/x1"))
   574  	c.Assert(osutil.FileExists(mup), Equals, false)
   575  
   576  	// automatic snapshot was created
   577  	c.Assert(s.automaticSnapshots, DeepEquals, []automaticSnapshotCall{{"foo", map[string]interface{}{"key": "value"}, nil, &snapshotbackend.Flags{Auto: true}}})
   578  }
   579  
   580  func fakeSnapID(name string) string {
   581  	const suffix = "idididididididididididididididid"
   582  	return name + suffix[len(name)+1:]
   583  }
   584  
   585  const (
   586  	snapV2 = `{
   587  	"architectures": [
   588  	    "all"
   589  	],
   590          "download": {
   591  			"url": "@URL@",
   592  			"size": 123
   593          },
   594          "epoch": @EPOCH@,
   595          "type": "@TYPE@",
   596  	"name": "@NAME@",
   597  	"revision": @REVISION@,
   598  	"snap-id": "@SNAPID@",
   599  	"summary": "Foo",
   600  	"description": "this is a description",
   601  	"version": "@VERSION@",
   602          "publisher": {
   603             "id": "devdevdev",
   604             "name": "bar"
   605           },
   606           "media": [
   607              {"type": "icon", "url": "@ICON@"}
   608           ]
   609  }`
   610  )
   611  
   612  var fooSnapID = fakeSnapID("foo")
   613  
   614  func (s *baseMgrsSuite) prereqSnapAssertions(c *C, extraHeaders ...map[string]interface{}) *asserts.SnapDeclaration {
   615  	if len(extraHeaders) == 0 {
   616  		extraHeaders = []map[string]interface{}{{}}
   617  	}
   618  	var snapDecl *asserts.SnapDeclaration
   619  	for _, extraHeaders := range extraHeaders {
   620  		headers := map[string]interface{}{
   621  			"series":       "16",
   622  			"snap-name":    "foo",
   623  			"publisher-id": "devdevdev",
   624  			"timestamp":    time.Now().Format(time.RFC3339),
   625  		}
   626  		for h, v := range extraHeaders {
   627  			headers[h] = v
   628  		}
   629  		headers["snap-id"] = fakeSnapID(headers["snap-name"].(string))
   630  		a, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
   631  		c.Assert(err, IsNil)
   632  		err = s.storeSigning.Add(a)
   633  		c.Assert(err, IsNil)
   634  		snapDecl = a.(*asserts.SnapDeclaration)
   635  	}
   636  	return snapDecl
   637  }
   638  
   639  func (s *baseMgrsSuite) makeStoreTestSnapWithFiles(c *C, snapYaml string, revno string, files [][]string) (path, digest string) {
   640  	info, err := snap.InfoFromSnapYaml([]byte(snapYaml))
   641  	c.Assert(err, IsNil)
   642  
   643  	snapPath := makeTestSnapWithFiles(c, snapYaml, files)
   644  
   645  	snapDigest, size, err := asserts.SnapFileSHA3_384(snapPath)
   646  	c.Assert(err, IsNil)
   647  
   648  	headers := map[string]interface{}{
   649  		"snap-id":       fakeSnapID(info.SnapName()),
   650  		"snap-sha3-384": snapDigest,
   651  		"snap-size":     fmt.Sprintf("%d", size),
   652  		"snap-revision": revno,
   653  		"developer-id":  "devdevdev",
   654  		"timestamp":     time.Now().Format(time.RFC3339),
   655  	}
   656  	snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "")
   657  	c.Assert(err, IsNil)
   658  	err = s.storeSigning.Add(snapRev)
   659  	c.Assert(err, IsNil)
   660  
   661  	return snapPath, snapDigest
   662  }
   663  
   664  func (s *baseMgrsSuite) makeStoreTestSnap(c *C, snapYaml string, revno string) (path, digest string) {
   665  	return s.makeStoreTestSnapWithFiles(c, snapYaml, revno, nil)
   666  }
   667  
   668  func (s *baseMgrsSuite) pathFor(name, revno string) string {
   669  	if revno == s.serveRevision[name] {
   670  		return s.serveSnapPath[name]
   671  	}
   672  	for i, r := range s.serveOldRevs[name] {
   673  		if r == revno {
   674  			return s.serveOldPaths[name][i]
   675  		}
   676  	}
   677  	return "/not/found"
   678  }
   679  
   680  func (s *baseMgrsSuite) newestThatCanRead(name string, epoch snap.Epoch) (info *snap.Info, rev string) {
   681  	if s.serveSnapPath[name] == "" {
   682  		return nil, ""
   683  	}
   684  	idx := len(s.serveOldPaths[name])
   685  	rev = s.serveRevision[name]
   686  	path := s.serveSnapPath[name]
   687  	for {
   688  		snapf, err := snapfile.Open(path)
   689  		if err != nil {
   690  			panic(err)
   691  		}
   692  		info, err := snap.ReadInfoFromSnapFile(snapf, nil)
   693  		if err != nil {
   694  			panic(err)
   695  		}
   696  		if info.Epoch.CanRead(epoch) {
   697  			return info, rev
   698  		}
   699  		idx--
   700  		if idx < 0 {
   701  			return nil, ""
   702  		}
   703  		path = s.serveOldPaths[name][idx]
   704  		rev = s.serveOldRevs[name][idx]
   705  	}
   706  }
   707  
   708  func (s *baseMgrsSuite) mockStore(c *C) *httptest.Server {
   709  	var baseURL *url.URL
   710  	fillHit := func(hitTemplate, revno string, info *snap.Info) string {
   711  		epochBuf, err := json.Marshal(info.Epoch)
   712  		if err != nil {
   713  			panic(err)
   714  		}
   715  		name := info.SnapName()
   716  
   717  		hit := strings.Replace(hitTemplate, "@URL@", baseURL.String()+"/api/v1/snaps/download/"+name+"/"+revno, -1)
   718  		hit = strings.Replace(hit, "@NAME@", name, -1)
   719  		hit = strings.Replace(hit, "@SNAPID@", fakeSnapID(name), -1)
   720  		hit = strings.Replace(hit, "@ICON@", baseURL.String()+"/icon", -1)
   721  		hit = strings.Replace(hit, "@VERSION@", info.Version, -1)
   722  		hit = strings.Replace(hit, "@REVISION@", revno, -1)
   723  		hit = strings.Replace(hit, `@TYPE@`, string(info.Type()), -1)
   724  		hit = strings.Replace(hit, `@EPOCH@`, string(epochBuf), -1)
   725  		return hit
   726  	}
   727  
   728  	mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   729  		// all URLS are /api/v1/snaps/... or /v2/snaps/... so
   730  		// check the url is sane and discard the common prefix
   731  		// to simplify indexing into the comps slice.
   732  		comps := strings.Split(r.URL.Path, "/")
   733  		if len(comps) < 2 {
   734  			panic("unexpected url path: " + r.URL.Path)
   735  		}
   736  		if comps[1] == "api" { //v1
   737  			if len(comps) <= 4 {
   738  				panic("unexpected url path: " + r.URL.Path)
   739  			}
   740  			comps = comps[4:]
   741  			if comps[0] == "auth" {
   742  				comps[0] = "auth:" + comps[1]
   743  			}
   744  		} else { // v2
   745  			if len(comps) <= 3 {
   746  				panic("unexpected url path: " + r.URL.Path)
   747  			}
   748  			comps = comps[3:]
   749  			comps[0] = "v2:" + comps[0]
   750  		}
   751  
   752  		switch comps[0] {
   753  		case "auth:nonces":
   754  			w.Write([]byte(`{"nonce": "NONCE"}`))
   755  			return
   756  		case "auth:sessions":
   757  			// quick sanity check
   758  			reqBody, err := ioutil.ReadAll(r.Body)
   759  			c.Check(err, IsNil)
   760  			c.Check(bytes.Contains(reqBody, []byte("nonce: NONCE")), Equals, true)
   761  			c.Check(bytes.Contains(reqBody, []byte(fmt.Sprintf("serial: %s", s.expectedSerial))), Equals, true)
   762  			c.Check(bytes.Contains(reqBody, []byte(fmt.Sprintf("store: %s", s.expectedStore))), Equals, true)
   763  
   764  			c.Check(s.sessionMacaroon, Not(Equals), "")
   765  			w.WriteHeader(200)
   766  			w.Write([]byte(fmt.Sprintf(`{"macaroon": "%s"}`, s.sessionMacaroon)))
   767  			return
   768  		case "assertions":
   769  			ref := &asserts.Ref{
   770  				Type:       asserts.Type(comps[1]),
   771  				PrimaryKey: comps[2:],
   772  			}
   773  			a, err := ref.Resolve(s.storeSigning.Find)
   774  			if asserts.IsNotFound(err) {
   775  				w.Header().Set("Content-Type", "application/json")
   776  				w.WriteHeader(404)
   777  				w.Write([]byte(`{"status": 404}`))
   778  				return
   779  			}
   780  			if err != nil {
   781  				panic(err)
   782  			}
   783  			w.Header().Set("Content-Type", asserts.MediaType)
   784  			w.WriteHeader(200)
   785  			w.Write(asserts.Encode(a))
   786  			return
   787  		case "download":
   788  			if s.sessionMacaroon != "" {
   789  				// FIXME: download is still using the old headers!
   790  				c.Check(r.Header.Get("X-Device-Authorization"), Equals, fmt.Sprintf(`Macaroon root="%s"`, s.sessionMacaroon))
   791  			}
   792  			if s.failNextDownload == comps[1] {
   793  				s.failNextDownload = ""
   794  				w.WriteHeader(418)
   795  				return
   796  			}
   797  			if s.hijackServeSnap != nil {
   798  				s.hijackServeSnap(w)
   799  				return
   800  			}
   801  			snapR, err := os.Open(s.pathFor(comps[1], comps[2]))
   802  			if err != nil {
   803  				panic(err)
   804  			}
   805  			io.Copy(w, snapR)
   806  		case "v2:refresh":
   807  			if s.sessionMacaroon != "" {
   808  				c.Check(r.Header.Get("Snap-Device-Authorization"), Equals, fmt.Sprintf(`Macaroon root="%s"`, s.sessionMacaroon))
   809  			}
   810  			dec := json.NewDecoder(r.Body)
   811  			var input struct {
   812  				Actions []struct {
   813  					Action      string     `json:"action"`
   814  					SnapID      string     `json:"snap-id"`
   815  					Name        string     `json:"name"`
   816  					InstanceKey string     `json:"instance-key"`
   817  					Epoch       snap.Epoch `json:"epoch"`
   818  					// assertions
   819  					Key        string `json:"key"`
   820  					Assertions []struct {
   821  						Type        string   `json:"type"`
   822  						PrimaryKey  []string `json:"primary-key"`
   823  						IfNewerThan *int     `json:"if-newer-than"`
   824  					}
   825  				} `json:"actions"`
   826  				Context []struct {
   827  					SnapID string     `json:"snap-id"`
   828  					Epoch  snap.Epoch `json:"epoch"`
   829  				} `json:"context"`
   830  			}
   831  			if err := dec.Decode(&input); err != nil {
   832  				panic(err)
   833  			}
   834  			id2epoch := make(map[string]snap.Epoch, len(input.Context))
   835  			for _, s := range input.Context {
   836  				id2epoch[s.SnapID] = s.Epoch
   837  			}
   838  			type resultJSON struct {
   839  				Result      string          `json:"result"`
   840  				SnapID      string          `json:"snap-id"`
   841  				Name        string          `json:"name"`
   842  				Snap        json.RawMessage `json:"snap"`
   843  				InstanceKey string          `json:"instance-key"`
   844  				// For assertions
   845  				Key           string   `json:"key"`
   846  				AssertionURLs []string `json:"assertion-stream-urls"`
   847  			}
   848  			var results []resultJSON
   849  			for _, a := range input.Actions {
   850  				if a.Action == "fetch-assertions" {
   851  					urls := []string{}
   852  					for _, ar := range a.Assertions {
   853  						ref := &asserts.Ref{
   854  							Type:       asserts.Type(ar.Type),
   855  							PrimaryKey: ar.PrimaryKey,
   856  						}
   857  						_, err := ref.Resolve(s.storeSigning.Find)
   858  						if err != nil {
   859  							panic("missing assertions not supported")
   860  						}
   861  						urls = append(urls, fmt.Sprintf("%s/api/v1/snaps/assertions/%s", baseURL.String(), ref.Unique()))
   862  
   863  					}
   864  					results = append(results, resultJSON{
   865  						Result:        "fetch-assertions",
   866  						Key:           a.Key,
   867  						AssertionURLs: urls,
   868  					})
   869  					continue
   870  				}
   871  				name := s.serveIDtoName[a.SnapID]
   872  				epoch := id2epoch[a.SnapID]
   873  				if a.Action == "install" {
   874  					name = a.Name
   875  					epoch = a.Epoch
   876  				}
   877  
   878  				info, revno := s.newestThatCanRead(name, epoch)
   879  				if info == nil {
   880  					// no match
   881  					continue
   882  				}
   883  				results = append(results, resultJSON{
   884  					Result:      a.Action,
   885  					SnapID:      a.SnapID,
   886  					InstanceKey: a.InstanceKey,
   887  					Name:        name,
   888  					Snap:        json.RawMessage(fillHit(snapV2, revno, info)),
   889  				})
   890  			}
   891  			w.WriteHeader(200)
   892  			output, err := json.Marshal(map[string]interface{}{
   893  				"results": results,
   894  			})
   895  			if err != nil {
   896  				panic(err)
   897  			}
   898  			w.Write(output)
   899  
   900  		default:
   901  			panic("unexpected url path: " + r.URL.Path)
   902  		}
   903  	}))
   904  	c.Assert(mockServer, NotNil)
   905  
   906  	baseURL, _ = url.Parse(mockServer.URL)
   907  	storeCfg := store.Config{
   908  		StoreBaseURL: baseURL,
   909  	}
   910  
   911  	mStore := store.New(&storeCfg, nil)
   912  	st := s.o.State()
   913  	st.Lock()
   914  	snapstate.ReplaceStore(s.o.State(), mStore)
   915  	st.Unlock()
   916  
   917  	// this will be used by remodeling cases
   918  	storeNew := func(cfg *store.Config, dac store.DeviceAndAuthContext) *store.Store {
   919  		cfg.StoreBaseURL = baseURL
   920  		if s.checkDeviceAndAuthContext != nil {
   921  			s.checkDeviceAndAuthContext(dac)
   922  		}
   923  		return store.New(cfg, dac)
   924  	}
   925  
   926  	s.AddCleanup(overlord.MockStoreNew(storeNew))
   927  
   928  	return mockServer
   929  }
   930  
   931  // serveSnap starts serving the snap at snapPath, moving the current
   932  // one onto the list of previous ones if already set.
   933  func (s *baseMgrsSuite) serveSnap(snapPath, revno string) {
   934  	snapf, err := snapfile.Open(snapPath)
   935  	if err != nil {
   936  		panic(err)
   937  	}
   938  	info, err := snap.ReadInfoFromSnapFile(snapf, nil)
   939  	if err != nil {
   940  		panic(err)
   941  	}
   942  	name := info.SnapName()
   943  	s.serveIDtoName[fakeSnapID(name)] = name
   944  
   945  	if oldPath := s.serveSnapPath[name]; oldPath != "" {
   946  		oldRev := s.serveRevision[name]
   947  		if oldRev == "" {
   948  			panic("old path set but not old revision")
   949  		}
   950  		s.serveOldPaths[name] = append(s.serveOldPaths[name], oldPath)
   951  		s.serveOldRevs[name] = append(s.serveOldRevs[name], oldRev)
   952  	}
   953  	s.serveSnapPath[name] = snapPath
   954  	s.serveRevision[name] = revno
   955  }
   956  
   957  func (s *mgrsSuite) TestHappyRemoteInstallAndUpgradeSvc(c *C) {
   958  	// test install through store and update, plus some mechanics
   959  	// of update
   960  	// TODO: ok to split if it gets too messy to maintain
   961  
   962  	s.prereqSnapAssertions(c)
   963  
   964  	snapYamlContent := `name: foo
   965  version: @VERSION@
   966  apps:
   967   bar:
   968    command: bin/bar
   969   svc:
   970    command: svc
   971    daemon: forking
   972  `
   973  
   974  	ver := "1.0"
   975  	revno := "42"
   976  	snapPath, digest := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
   977  	s.serveSnap(snapPath, revno)
   978  
   979  	mockServer := s.mockStore(c)
   980  	defer mockServer.Close()
   981  
   982  	st := s.o.State()
   983  	st.Lock()
   984  	defer st.Unlock()
   985  
   986  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
   987  	c.Assert(err, IsNil)
   988  	chg := st.NewChange("install-snap", "...")
   989  	chg.AddAll(ts)
   990  
   991  	st.Unlock()
   992  	err = s.o.Settle(settleTimeout)
   993  	st.Lock()
   994  	c.Assert(err, IsNil)
   995  
   996  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
   997  
   998  	info, err := snapstate.CurrentInfo(st, "foo")
   999  	c.Assert(err, IsNil)
  1000  
  1001  	c.Check(info.Revision, Equals, snap.R(42))
  1002  	c.Check(info.SnapID, Equals, fooSnapID)
  1003  	c.Check(info.Version, Equals, "1.0")
  1004  	c.Check(info.Summary(), Equals, "Foo")
  1005  	c.Check(info.Description(), Equals, "this is a description")
  1006  	c.Assert(osutil.FileExists(info.MountFile()), Equals, true)
  1007  
  1008  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
  1009  	c.Assert(err, IsNil)
  1010  	c.Check(pubAcct.AccountID(), Equals, "devdevdev")
  1011  	c.Check(pubAcct.Username(), Equals, "devdevdev")
  1012  
  1013  	snapRev42, err := assertstate.DB(st).Find(asserts.SnapRevisionType, map[string]string{
  1014  		"snap-sha3-384": digest,
  1015  	})
  1016  	c.Assert(err, IsNil)
  1017  	c.Check(snapRev42.(*asserts.SnapRevision).SnapID(), Equals, fooSnapID)
  1018  	c.Check(snapRev42.(*asserts.SnapRevision).SnapRevision(), Equals, 42)
  1019  
  1020  	// check service was setup properly
  1021  	svcFile := filepath.Join(dirs.SnapServicesDir, "snap.foo.svc.service")
  1022  	c.Assert(osutil.FileExists(svcFile), Equals, true)
  1023  	stat, err := os.Stat(svcFile)
  1024  	c.Assert(err, IsNil)
  1025  	// should _not_ be executable
  1026  	c.Assert(stat.Mode().String(), Equals, "-rw-r--r--")
  1027  
  1028  	// Refresh
  1029  
  1030  	ver = "2.0"
  1031  	revno = "50"
  1032  	snapPath, digest = s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  1033  	s.serveSnap(snapPath, revno)
  1034  
  1035  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
  1036  	c.Assert(err, IsNil)
  1037  	chg = st.NewChange("upgrade-snap", "...")
  1038  	chg.AddAll(ts)
  1039  
  1040  	st.Unlock()
  1041  	err = s.o.Settle(settleTimeout)
  1042  	st.Lock()
  1043  	c.Assert(err, IsNil)
  1044  
  1045  	c.Assert(chg.Err(), IsNil)
  1046  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1047  
  1048  	info, err = snapstate.CurrentInfo(st, "foo")
  1049  	c.Assert(err, IsNil)
  1050  
  1051  	c.Check(info.Revision, Equals, snap.R(50))
  1052  	c.Check(info.SnapID, Equals, fooSnapID)
  1053  	c.Check(info.Version, Equals, "2.0")
  1054  
  1055  	snapRev50, err := assertstate.DB(st).Find(asserts.SnapRevisionType, map[string]string{
  1056  		"snap-sha3-384": digest,
  1057  	})
  1058  	c.Assert(err, IsNil)
  1059  	c.Check(snapRev50.(*asserts.SnapRevision).SnapID(), Equals, fooSnapID)
  1060  	c.Check(snapRev50.(*asserts.SnapRevision).SnapRevision(), Equals, 50)
  1061  
  1062  	// check updated wrapper
  1063  	symlinkTarget, err := os.Readlink(info.Apps["bar"].WrapperPath())
  1064  	c.Assert(err, IsNil)
  1065  	c.Assert(symlinkTarget, Equals, "/usr/bin/snap")
  1066  
  1067  	// check updated service file
  1068  	c.Assert(svcFile, testutil.FileContains, "/var/snap/foo/"+revno)
  1069  }
  1070  
  1071  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithEpochBump(c *C) {
  1072  	// test install through store and update, where there's an epoch bump in the upgrade
  1073  	// this does less checks on the details of install/update than TestHappyRemoteInstallAndUpgradeSvc
  1074  
  1075  	s.prereqSnapAssertions(c)
  1076  
  1077  	snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 0}", "1")
  1078  	s.serveSnap(snapPath, "1")
  1079  
  1080  	mockServer := s.mockStore(c)
  1081  	defer mockServer.Close()
  1082  
  1083  	st := s.o.State()
  1084  	st.Lock()
  1085  	defer st.Unlock()
  1086  
  1087  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  1088  	c.Assert(err, IsNil)
  1089  	chg := st.NewChange("install-snap", "...")
  1090  	chg.AddAll(ts)
  1091  
  1092  	st.Unlock()
  1093  	err = s.o.Settle(settleTimeout)
  1094  	st.Lock()
  1095  	c.Assert(err, IsNil)
  1096  
  1097  	// confirm it worked
  1098  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1099  
  1100  	// sanity checks
  1101  	info, err := snapstate.CurrentInfo(st, "foo")
  1102  	c.Assert(err, IsNil)
  1103  	c.Assert(info.Revision, Equals, snap.R(1))
  1104  	c.Assert(info.SnapID, Equals, fooSnapID)
  1105  	c.Assert(info.Epoch.String(), Equals, "0")
  1106  
  1107  	// now add some more snaps
  1108  	for i, epoch := range []string{"1*", "2*", "3*"} {
  1109  		revno := fmt.Sprint(i + 2)
  1110  		snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 0, epoch: "+epoch+"}", revno)
  1111  		s.serveSnap(snapPath, revno)
  1112  	}
  1113  
  1114  	// refresh
  1115  
  1116  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
  1117  	c.Assert(err, IsNil)
  1118  	chg = st.NewChange("upgrade-snap", "...")
  1119  	chg.AddAll(ts)
  1120  
  1121  	st.Unlock()
  1122  	err = s.o.Settle(settleTimeout)
  1123  	st.Lock()
  1124  	c.Assert(err, IsNil)
  1125  
  1126  	c.Assert(chg.Err(), IsNil)
  1127  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1128  
  1129  	info, err = snapstate.CurrentInfo(st, "foo")
  1130  	c.Assert(err, IsNil)
  1131  
  1132  	c.Check(info.Revision, Equals, snap.R(4))
  1133  	c.Check(info.SnapID, Equals, fooSnapID)
  1134  	c.Check(info.Epoch.String(), Equals, "3*")
  1135  }
  1136  
  1137  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithPostHocEpochBump(c *C) {
  1138  	// test install through store and update, where there is an epoch
  1139  	// bump in the upgrade that comes in after the initial update is
  1140  	// computed.
  1141  
  1142  	// this is mostly checking the same as TestHappyRemoteInstallAndUpdateWithEpochBump
  1143  	// but serves as a sanity check for the Without case that follows
  1144  	// (these two together serve as a test for the refresh filtering)
  1145  	s.testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c, true)
  1146  }
  1147  
  1148  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithoutEpochBump(c *C) {
  1149  	// test install through store and update, where there _isn't_ an epoch bump in the upgrade
  1150  	// note that there _are_ refreshes available after the refresh,
  1151  	// but they're not an epoch bump so they're ignored
  1152  	s.testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c, false)
  1153  }
  1154  
  1155  func (s *mgrsSuite) testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c *C, doBump bool) {
  1156  	s.prereqSnapAssertions(c)
  1157  
  1158  	snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 1}", "1")
  1159  	s.serveSnap(snapPath, "1")
  1160  
  1161  	mockServer := s.mockStore(c)
  1162  	defer mockServer.Close()
  1163  
  1164  	st := s.o.State()
  1165  	st.Lock()
  1166  	defer st.Unlock()
  1167  
  1168  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  1169  	c.Assert(err, IsNil)
  1170  	chg := st.NewChange("install-snap", "...")
  1171  	chg.AddAll(ts)
  1172  
  1173  	st.Unlock()
  1174  	err = s.o.Settle(settleTimeout)
  1175  	st.Lock()
  1176  	c.Assert(err, IsNil)
  1177  
  1178  	// confirm it worked
  1179  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1180  
  1181  	// sanity checks
  1182  	info, err := snapstate.CurrentInfo(st, "foo")
  1183  	c.Assert(err, IsNil)
  1184  	c.Assert(info.Revision, Equals, snap.R(1))
  1185  	c.Assert(info.SnapID, Equals, fooSnapID)
  1186  	c.Assert(info.Epoch.String(), Equals, "0")
  1187  
  1188  	// add a new revision
  1189  	snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 2}", "2")
  1190  	s.serveSnap(snapPath, "2")
  1191  
  1192  	// refresh
  1193  
  1194  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
  1195  	c.Assert(err, IsNil)
  1196  	chg = st.NewChange("upgrade-snap", "...")
  1197  	chg.AddAll(ts)
  1198  
  1199  	// add another new revision, after the update was computed (maybe with an epoch bump)
  1200  	if doBump {
  1201  		snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 3, epoch: 1*}", "3")
  1202  	} else {
  1203  		snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 3}", "3")
  1204  	}
  1205  	s.serveSnap(snapPath, "3")
  1206  
  1207  	st.Unlock()
  1208  	err = s.o.Settle(settleTimeout)
  1209  	st.Lock()
  1210  	c.Assert(err, IsNil)
  1211  
  1212  	c.Assert(chg.Err(), IsNil)
  1213  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1214  
  1215  	info, err = snapstate.CurrentInfo(st, "foo")
  1216  	c.Assert(err, IsNil)
  1217  
  1218  	if doBump {
  1219  		// if the epoch bumped, then we should've re-refreshed
  1220  		c.Check(info.Revision, Equals, snap.R(3))
  1221  		c.Check(info.SnapID, Equals, fooSnapID)
  1222  		c.Check(info.Epoch.String(), Equals, "1*")
  1223  	} else {
  1224  		// if the epoch did not bump, then we should _not_ have re-refreshed
  1225  		c.Check(info.Revision, Equals, snap.R(2))
  1226  		c.Check(info.SnapID, Equals, fooSnapID)
  1227  		c.Check(info.Epoch.String(), Equals, "0")
  1228  	}
  1229  }
  1230  
  1231  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateManyWithEpochBump(c *C) {
  1232  	// test install through store and update many, where there's an epoch bump in the upgrade
  1233  	// this does less checks on the details of install/update than TestHappyRemoteInstallAndUpgradeSvc
  1234  
  1235  	snapNames := []string{"aaaa", "bbbb", "cccc"}
  1236  	for _, name := range snapNames {
  1237  		s.prereqSnapAssertions(c, map[string]interface{}{"snap-name": name})
  1238  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0}", name), "1")
  1239  		s.serveSnap(snapPath, "1")
  1240  	}
  1241  
  1242  	mockServer := s.mockStore(c)
  1243  	defer mockServer.Close()
  1244  
  1245  	st := s.o.State()
  1246  	st.Lock()
  1247  	defer st.Unlock()
  1248  
  1249  	affected, tasksets, err := snapstate.InstallMany(st, snapNames, 0)
  1250  	c.Assert(err, IsNil)
  1251  	sort.Strings(affected)
  1252  	c.Check(affected, DeepEquals, snapNames)
  1253  	chg := st.NewChange("install-snaps", "...")
  1254  	for _, taskset := range tasksets {
  1255  		chg.AddAll(taskset)
  1256  	}
  1257  
  1258  	st.Unlock()
  1259  	err = s.o.Settle(settleTimeout)
  1260  	st.Lock()
  1261  	c.Assert(err, IsNil)
  1262  
  1263  	// confirm it worked
  1264  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1265  
  1266  	// sanity checks
  1267  	for _, name := range snapNames {
  1268  		info, err := snapstate.CurrentInfo(st, name)
  1269  		c.Assert(err, IsNil)
  1270  		c.Assert(info.Revision, Equals, snap.R(1))
  1271  		c.Assert(info.SnapID, Equals, fakeSnapID(name))
  1272  		c.Assert(info.Epoch.String(), Equals, "0")
  1273  	}
  1274  
  1275  	// now add some more snap revisions with increasing epochs
  1276  	for _, name := range snapNames {
  1277  		for i, epoch := range []string{"1*", "2*", "3*"} {
  1278  			revno := fmt.Sprint(i + 2)
  1279  			snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0, epoch: %s}", name, epoch), revno)
  1280  			s.serveSnap(snapPath, revno)
  1281  		}
  1282  	}
  1283  
  1284  	// refresh
  1285  
  1286  	affected, tasksets, err = snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{})
  1287  	c.Assert(err, IsNil)
  1288  	sort.Strings(affected)
  1289  	c.Check(affected, DeepEquals, snapNames)
  1290  	chg = st.NewChange("upgrade-snaps", "...")
  1291  	for _, taskset := range tasksets {
  1292  		chg.AddAll(taskset)
  1293  	}
  1294  
  1295  	st.Unlock()
  1296  	err = s.o.Settle(settleTimeout)
  1297  	st.Lock()
  1298  	c.Assert(err, IsNil)
  1299  
  1300  	c.Assert(chg.Err(), IsNil)
  1301  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1302  
  1303  	for _, name := range snapNames {
  1304  		info, err := snapstate.CurrentInfo(st, name)
  1305  		c.Assert(err, IsNil)
  1306  
  1307  		c.Check(info.Revision, Equals, snap.R(4))
  1308  		c.Check(info.SnapID, Equals, fakeSnapID(name))
  1309  		c.Check(info.Epoch.String(), Equals, "3*")
  1310  	}
  1311  }
  1312  
  1313  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateManyWithEpochBumpAndOneFailing(c *C) {
  1314  	// test install through store and update, where there's an epoch bump in the upgrade and one of them fails
  1315  
  1316  	snapNames := []string{"aaaa", "bbbb", "cccc"}
  1317  	for _, name := range snapNames {
  1318  		s.prereqSnapAssertions(c, map[string]interface{}{"snap-name": name})
  1319  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0}", name), "1")
  1320  		s.serveSnap(snapPath, "1")
  1321  	}
  1322  
  1323  	mockServer := s.mockStore(c)
  1324  	defer mockServer.Close()
  1325  
  1326  	st := s.o.State()
  1327  	st.Lock()
  1328  	defer st.Unlock()
  1329  
  1330  	affected, tasksets, err := snapstate.InstallMany(st, snapNames, 0)
  1331  	c.Assert(err, IsNil)
  1332  	sort.Strings(affected)
  1333  	c.Check(affected, DeepEquals, snapNames)
  1334  	chg := st.NewChange("install-snaps", "...")
  1335  	for _, taskset := range tasksets {
  1336  		chg.AddAll(taskset)
  1337  	}
  1338  
  1339  	st.Unlock()
  1340  	err = s.o.Settle(settleTimeout)
  1341  	st.Lock()
  1342  	c.Assert(err, IsNil)
  1343  
  1344  	// confirm it worked
  1345  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1346  
  1347  	// sanity checks
  1348  	for _, name := range snapNames {
  1349  		info, err := snapstate.CurrentInfo(st, name)
  1350  		c.Assert(err, IsNil)
  1351  		c.Assert(info.Revision, Equals, snap.R(1))
  1352  		c.Assert(info.SnapID, Equals, fakeSnapID(name))
  1353  		c.Assert(info.Epoch.String(), Equals, "0")
  1354  	}
  1355  
  1356  	// now add some more snap revisions with increasing epochs
  1357  	for _, name := range snapNames {
  1358  		for i, epoch := range []string{"1*", "2*", "3*"} {
  1359  			revno := fmt.Sprint(i + 2)
  1360  			snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0, epoch: %s}", name, epoch), revno)
  1361  			s.serveSnap(snapPath, revno)
  1362  		}
  1363  	}
  1364  
  1365  	// refresh
  1366  	affected, tasksets, err = snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{})
  1367  	c.Assert(err, IsNil)
  1368  	sort.Strings(affected)
  1369  	c.Check(affected, DeepEquals, snapNames)
  1370  	chg = st.NewChange("upgrade-snaps", "...")
  1371  	for _, taskset := range tasksets {
  1372  		chg.AddAll(taskset)
  1373  	}
  1374  
  1375  	st.Unlock()
  1376  	// the download for the refresh above will be performed below, during 'settle'.
  1377  	// fail the refresh of cccc by failing its download
  1378  	s.failNextDownload = "cccc"
  1379  	err = s.o.Settle(settleTimeout)
  1380  	st.Lock()
  1381  	c.Assert(err, IsNil)
  1382  
  1383  	c.Assert(chg.Err(), NotNil)
  1384  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  1385  
  1386  	for _, name := range snapNames {
  1387  		comment := Commentf("%q", name)
  1388  		info, err := snapstate.CurrentInfo(st, name)
  1389  		c.Assert(err, IsNil, comment)
  1390  
  1391  		if name == "cccc" {
  1392  			// the failed one: still on rev 1 (epoch 0)
  1393  			c.Assert(info.Revision, Equals, snap.R(1))
  1394  			c.Assert(info.SnapID, Equals, fakeSnapID(name))
  1395  			c.Assert(info.Epoch.String(), Equals, "0")
  1396  		} else {
  1397  			// the non-failed ones: refreshed to rev 4 (epoch 3*)
  1398  			c.Check(info.Revision, Equals, snap.R(4), comment)
  1399  			c.Check(info.SnapID, Equals, fakeSnapID(name), comment)
  1400  			c.Check(info.Epoch.String(), Equals, "3*", comment)
  1401  		}
  1402  	}
  1403  }
  1404  
  1405  func (s *mgrsSuite) TestHappyLocalInstallWithStoreMetadata(c *C) {
  1406  	snapDecl := s.prereqSnapAssertions(c)
  1407  
  1408  	snapYamlContent := `name: foo
  1409  apps:
  1410   bar:
  1411    command: bin/bar
  1412  `
  1413  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1414  
  1415  	si := &snap.SideInfo{
  1416  		RealName: "foo",
  1417  		SnapID:   fooSnapID,
  1418  		Revision: snap.R(55),
  1419  	}
  1420  
  1421  	st := s.o.State()
  1422  	st.Lock()
  1423  	defer st.Unlock()
  1424  
  1425  	// have the snap-declaration in the system db
  1426  	err := assertstate.Add(st, s.devAcct)
  1427  	c.Assert(err, IsNil)
  1428  	err = assertstate.Add(st, snapDecl)
  1429  	c.Assert(err, IsNil)
  1430  
  1431  	ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{DevMode: true})
  1432  	c.Assert(err, IsNil)
  1433  	chg := st.NewChange("install-snap", "...")
  1434  	chg.AddAll(ts)
  1435  
  1436  	st.Unlock()
  1437  	err = s.o.Settle(settleTimeout)
  1438  	st.Lock()
  1439  	c.Assert(err, IsNil)
  1440  
  1441  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1442  
  1443  	info, err := snapstate.CurrentInfo(st, "foo")
  1444  	c.Assert(err, IsNil)
  1445  	c.Check(info.Revision, Equals, snap.R(55))
  1446  	c.Check(info.SnapID, Equals, fooSnapID)
  1447  	c.Check(info.Version, Equals, "1.5")
  1448  
  1449  	// ensure that the binary wrapper file got generated with the right
  1450  	// name
  1451  	binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar")
  1452  	c.Assert(osutil.IsSymlink(binaryWrapper), Equals, true)
  1453  
  1454  	// data dirs
  1455  	c.Assert(osutil.IsDirectory(info.DataDir()), Equals, true)
  1456  	c.Assert(osutil.IsDirectory(info.CommonDataDir()), Equals, true)
  1457  
  1458  	// snap file and its mounting
  1459  
  1460  	// after install the snap file is in the right dir
  1461  	c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_55.snap")), Equals, true)
  1462  
  1463  	// ensure the right unit is created
  1464  	mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/55"))
  1465  	c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s/foo/55", dirs.StripRootDir(dirs.SnapMountDir)))
  1466  	c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/foo_55.snap")
  1467  }
  1468  
  1469  func (s *mgrsSuite) TestParallelInstanceLocalInstallSnapNameMismatch(c *C) {
  1470  	snapDecl := s.prereqSnapAssertions(c)
  1471  
  1472  	snapYamlContent := `name: foo
  1473  apps:
  1474   bar:
  1475    command: bin/bar
  1476  `
  1477  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1478  
  1479  	si := &snap.SideInfo{
  1480  		RealName: "foo",
  1481  		SnapID:   fooSnapID,
  1482  		Revision: snap.R(55),
  1483  	}
  1484  
  1485  	st := s.o.State()
  1486  	st.Lock()
  1487  	defer st.Unlock()
  1488  
  1489  	// have the snap-declaration in the system db
  1490  	err := assertstate.Add(st, s.devAcct)
  1491  	c.Assert(err, IsNil)
  1492  	err = assertstate.Add(st, snapDecl)
  1493  	c.Assert(err, IsNil)
  1494  
  1495  	_, _, err = snapstate.InstallPath(st, si, snapPath, "bar_instance", "", snapstate.Flags{DevMode: true})
  1496  	c.Assert(err, ErrorMatches, `cannot install snap "bar_instance", the name does not match the metadata "foo"`)
  1497  }
  1498  
  1499  func (s *mgrsSuite) TestParallelInstanceLocalInstallInvalidInstanceName(c *C) {
  1500  	snapDecl := s.prereqSnapAssertions(c)
  1501  
  1502  	snapYamlContent := `name: foo
  1503  apps:
  1504   bar:
  1505    command: bin/bar
  1506  `
  1507  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1508  
  1509  	si := &snap.SideInfo{
  1510  		RealName: "foo",
  1511  		SnapID:   fooSnapID,
  1512  		Revision: snap.R(55),
  1513  	}
  1514  
  1515  	st := s.o.State()
  1516  	st.Lock()
  1517  	defer st.Unlock()
  1518  
  1519  	// have the snap-declaration in the system db
  1520  	err := assertstate.Add(st, s.devAcct)
  1521  	c.Assert(err, IsNil)
  1522  	err = assertstate.Add(st, snapDecl)
  1523  	c.Assert(err, IsNil)
  1524  
  1525  	_, _, err = snapstate.InstallPath(st, si, snapPath, "bar_invalid_instance_name", "", snapstate.Flags{DevMode: true})
  1526  	c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "invalid_instance_name"`)
  1527  }
  1528  
  1529  func (s *mgrsSuite) TestCheckInterfaces(c *C) {
  1530  	snapDecl := s.prereqSnapAssertions(c)
  1531  
  1532  	snapYamlContent := `name: foo
  1533  apps:
  1534   bar:
  1535    command: bin/bar
  1536  slots:
  1537   network:
  1538  `
  1539  	snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5")
  1540  
  1541  	si := &snap.SideInfo{
  1542  		RealName: "foo",
  1543  		SnapID:   fooSnapID,
  1544  		Revision: snap.R(55),
  1545  	}
  1546  
  1547  	st := s.o.State()
  1548  	st.Lock()
  1549  	defer st.Unlock()
  1550  
  1551  	// have the snap-declaration in the system db
  1552  	err := assertstate.Add(st, s.devAcct)
  1553  	c.Assert(err, IsNil)
  1554  	err = assertstate.Add(st, snapDecl)
  1555  	c.Assert(err, IsNil)
  1556  
  1557  	// mock SanitizePlugsSlots so that unknown interfaces are not rejected
  1558  	restoreSanitize := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
  1559  	defer restoreSanitize()
  1560  
  1561  	ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{DevMode: true})
  1562  	c.Assert(err, IsNil)
  1563  	chg := st.NewChange("install-snap", "...")
  1564  	chg.AddAll(ts)
  1565  
  1566  	st.Unlock()
  1567  	err = s.o.Settle(settleTimeout)
  1568  	st.Lock()
  1569  	c.Assert(err, IsNil)
  1570  
  1571  	c.Assert(chg.Err(), ErrorMatches, `(?s).*installation not allowed by "network" slot rule of interface "network".*`)
  1572  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  1573  }
  1574  
  1575  func (s *mgrsSuite) TestHappyRefreshControl(c *C) {
  1576  	// test install through store and update, plus some mechanics
  1577  	// of update
  1578  	// TODO: ok to split if it gets too messy to maintain
  1579  
  1580  	s.prereqSnapAssertions(c)
  1581  
  1582  	snapYamlContent := `name: foo
  1583  version: @VERSION@
  1584  `
  1585  
  1586  	ver := "1.0"
  1587  	revno := "42"
  1588  	snapPath, _ := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  1589  	s.serveSnap(snapPath, revno)
  1590  
  1591  	mockServer := s.mockStore(c)
  1592  	defer mockServer.Close()
  1593  
  1594  	st := s.o.State()
  1595  	st.Lock()
  1596  	defer st.Unlock()
  1597  
  1598  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  1599  	c.Assert(err, IsNil)
  1600  	chg := st.NewChange("install-snap", "...")
  1601  	chg.AddAll(ts)
  1602  
  1603  	st.Unlock()
  1604  	err = s.o.Settle(settleTimeout)
  1605  	st.Lock()
  1606  	c.Assert(err, IsNil)
  1607  
  1608  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1609  
  1610  	info, err := snapstate.CurrentInfo(st, "foo")
  1611  	c.Assert(err, IsNil)
  1612  
  1613  	c.Check(info.Revision, Equals, snap.R(42))
  1614  
  1615  	// Refresh
  1616  
  1617  	// Setup refresh control
  1618  
  1619  	headers := map[string]interface{}{
  1620  		"series":          "16",
  1621  		"snap-id":         "bar-id",
  1622  		"snap-name":       "bar",
  1623  		"publisher-id":    "devdevdev",
  1624  		"refresh-control": []interface{}{fooSnapID},
  1625  		"timestamp":       time.Now().Format(time.RFC3339),
  1626  	}
  1627  	snapDeclBar, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "")
  1628  	c.Assert(err, IsNil)
  1629  	err = s.storeSigning.Add(snapDeclBar)
  1630  	c.Assert(err, IsNil)
  1631  	err = assertstate.Add(st, snapDeclBar)
  1632  	c.Assert(err, IsNil)
  1633  
  1634  	snapstate.Set(st, "bar", &snapstate.SnapState{
  1635  		Active: true,
  1636  		Sequence: []*snap.SideInfo{
  1637  			{RealName: "bar", SnapID: "bar-id", Revision: snap.R(1)},
  1638  		},
  1639  		Current:  snap.R(1),
  1640  		SnapType: "app",
  1641  	})
  1642  
  1643  	develSigning := assertstest.NewSigningDB("devdevdev", develPrivKey)
  1644  
  1645  	develAccKey := assertstest.NewAccountKey(s.storeSigning, s.devAcct, nil, develPrivKey.PublicKey(), "")
  1646  	err = s.storeSigning.Add(develAccKey)
  1647  	c.Assert(err, IsNil)
  1648  
  1649  	ver = "2.0"
  1650  	revno = "50"
  1651  	snapPath, _ = s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  1652  	s.serveSnap(snapPath, revno)
  1653  
  1654  	updated, tss, err := snapstate.UpdateMany(context.TODO(), st, []string{"foo"}, 0, nil)
  1655  	c.Check(updated, IsNil)
  1656  	c.Check(tss, IsNil)
  1657  	// no validation we, get an error
  1658  	c.Check(err, ErrorMatches, `cannot refresh "foo" to revision 50: no validation by "bar"`)
  1659  
  1660  	// setup validation
  1661  	headers = map[string]interface{}{
  1662  		"series":                 "16",
  1663  		"snap-id":                "bar-id",
  1664  		"approved-snap-id":       fooSnapID,
  1665  		"approved-snap-revision": "50",
  1666  		"timestamp":              time.Now().Format(time.RFC3339),
  1667  	}
  1668  	barValidation, err := develSigning.Sign(asserts.ValidationType, headers, nil, "")
  1669  	c.Assert(err, IsNil)
  1670  	err = s.storeSigning.Add(barValidation)
  1671  	c.Assert(err, IsNil)
  1672  
  1673  	// ... and try again
  1674  	updated, tss, err = snapstate.UpdateMany(context.TODO(), st, []string{"foo"}, 0, nil)
  1675  	c.Assert(err, IsNil)
  1676  	c.Assert(updated, DeepEquals, []string{"foo"})
  1677  	c.Assert(tss, HasLen, 2)
  1678  	verifyLastTasksetIsRerefresh(c, tss)
  1679  	chg = st.NewChange("upgrade-snaps", "...")
  1680  	chg.AddAll(tss[0])
  1681  
  1682  	st.Unlock()
  1683  	err = s.o.Settle(settleTimeout)
  1684  	st.Lock()
  1685  	c.Assert(err, IsNil)
  1686  
  1687  	c.Assert(chg.Err(), IsNil)
  1688  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  1689  
  1690  	info, err = snapstate.CurrentInfo(st, "foo")
  1691  	c.Assert(err, IsNil)
  1692  
  1693  	c.Check(info.Revision, Equals, snap.R(50))
  1694  }
  1695  
  1696  // core & kernel
  1697  
  1698  var modelDefaults = map[string]interface{}{
  1699  	"architecture": "amd64",
  1700  	"store":        "my-brand-store-id",
  1701  	"gadget":       "pc",
  1702  	"kernel":       "pc-kernel",
  1703  }
  1704  
  1705  func findKind(chg *state.Change, kind string) *state.Task {
  1706  	for _, t := range chg.Tasks() {
  1707  		if t.Kind() == kind {
  1708  			return t
  1709  		}
  1710  	}
  1711  	return nil
  1712  }
  1713  
  1714  func (s *mgrsSuite) TestInstallCoreSnapUpdatesBootloaderEnvAndSplitsAcrossRestart(c *C) {
  1715  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  1716  	bootloader.Force(bloader)
  1717  	defer bootloader.Force(nil)
  1718  	bloader.SetBootBase("core_99.snap")
  1719  
  1720  	restore := release.MockOnClassic(false)
  1721  	defer restore()
  1722  
  1723  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  1724  
  1725  	const packageOS = `
  1726  name: core
  1727  version: 16.04-1
  1728  type: os
  1729  `
  1730  	snapPath := makeTestSnap(c, packageOS)
  1731  
  1732  	st := s.o.State()
  1733  	st.Lock()
  1734  	defer st.Unlock()
  1735  
  1736  	// setup model assertion
  1737  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  1738  	devicestatetest.SetDevice(st, &auth.DeviceState{
  1739  		Brand:  "my-brand",
  1740  		Model:  "my-model",
  1741  		Serial: "serialserialserial",
  1742  	})
  1743  	err := assertstate.Add(st, model)
  1744  	c.Assert(err, IsNil)
  1745  
  1746  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "core"}, snapPath, "", "", snapstate.Flags{})
  1747  	c.Assert(err, IsNil)
  1748  	chg := st.NewChange("install-snap", "...")
  1749  	chg.AddAll(ts)
  1750  
  1751  	st.Unlock()
  1752  	err = s.o.Settle(settleTimeout)
  1753  	st.Lock()
  1754  	c.Assert(err, IsNil)
  1755  
  1756  	// final steps will are post poned until we are in the restarted snapd
  1757  	ok, rst := st.Restarting()
  1758  	c.Assert(ok, Equals, true)
  1759  	c.Assert(rst, Equals, state.RestartSystem)
  1760  
  1761  	t := findKind(chg, "auto-connect")
  1762  	c.Assert(t, NotNil)
  1763  	c.Assert(t.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1764  
  1765  	// this is already set
  1766  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  1767  		"snap_core":       "core_99.snap",
  1768  		"snap_try_core":   "core_x1.snap",
  1769  		"snap_try_kernel": "",
  1770  		"snap_mode":       boot.TryStatus,
  1771  	})
  1772  
  1773  	// simulate successful restart happened
  1774  	state.MockRestarting(st, state.RestartUnset)
  1775  	bloader.BootVars["snap_mode"] = boot.DefaultStatus
  1776  	bloader.SetBootBase("core_x1.snap")
  1777  
  1778  	st.Unlock()
  1779  	err = s.o.Settle(settleTimeout)
  1780  	st.Lock()
  1781  	c.Assert(err, IsNil)
  1782  
  1783  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1784  }
  1785  
  1786  type rebootEnv interface {
  1787  	SetTryingDuringReboot(which []snap.Type) error
  1788  	SetRollbackAcrossReboot(which []snap.Type) error
  1789  }
  1790  
  1791  func (s *baseMgrsSuite) mockSuccessfulReboot(c *C, be rebootEnv, which []snap.Type) {
  1792  	st := s.o.State()
  1793  	restarting, restartType := st.Restarting()
  1794  	c.Assert(restarting, Equals, true, Commentf("mockSuccessfulReboot called when there was no pending restart"))
  1795  	c.Assert(restartType, Equals, state.RestartSystem, Commentf("mockSuccessfulReboot called but restartType is not SystemRestart but %v", restartType))
  1796  	state.MockRestarting(st, state.RestartUnset)
  1797  	err := be.SetTryingDuringReboot(which)
  1798  	c.Assert(err, IsNil)
  1799  	s.o.DeviceManager().ResetBootOk()
  1800  	st.Unlock()
  1801  	defer st.Lock()
  1802  	err = s.o.DeviceManager().Ensure()
  1803  	c.Assert(err, IsNil)
  1804  }
  1805  
  1806  func (s *baseMgrsSuite) mockRollbackAcrossReboot(c *C, be rebootEnv, which []snap.Type) {
  1807  	st := s.o.State()
  1808  	restarting, restartType := st.Restarting()
  1809  	c.Assert(restarting, Equals, true, Commentf("mockRollbackAcrossReboot called when there was no pending restart"))
  1810  	c.Assert(restartType, Equals, state.RestartSystem, Commentf("mockRollbackAcrossReboot called but restartType is not SystemRestart but %v", restartType))
  1811  	state.MockRestarting(st, state.RestartUnset)
  1812  	err := be.SetRollbackAcrossReboot(which)
  1813  	c.Assert(err, IsNil)
  1814  	s.o.DeviceManager().ResetBootOk()
  1815  	st.Unlock()
  1816  	s.o.Settle(settleTimeout)
  1817  	st.Lock()
  1818  }
  1819  
  1820  func (s *mgrsSuite) TestInstallKernelSnapUpdatesBootloaderEnv(c *C) {
  1821  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  1822  	bootloader.Force(bloader)
  1823  	defer bootloader.Force(nil)
  1824  
  1825  	restore := release.MockOnClassic(false)
  1826  	defer restore()
  1827  
  1828  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  1829  
  1830  	const packageKernel = `
  1831  name: pc-kernel
  1832  version: 4.0-1
  1833  type: kernel`
  1834  
  1835  	files := [][]string{
  1836  		{"kernel.img", "I'm a kernel"},
  1837  		{"initrd.img", "...and I'm an initrd"},
  1838  		{"meta/kernel.yaml", "version: 4.2"},
  1839  	}
  1840  	snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files)
  1841  
  1842  	st := s.o.State()
  1843  	st.Lock()
  1844  	defer st.Unlock()
  1845  
  1846  	// pretend we have core18/pc-kernel
  1847  	bloader.BootVars = map[string]string{
  1848  		"snap_core":   "core18_2.snap",
  1849  		"snap_kernel": "pc-kernel_123.snap",
  1850  		"snap_mode":   boot.DefaultStatus,
  1851  	}
  1852  	si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(123)}
  1853  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  1854  		SnapType: "kernel",
  1855  		Active:   true,
  1856  		Sequence: []*snap.SideInfo{si1},
  1857  		Current:  si1.Revision,
  1858  	})
  1859  	snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{
  1860  		{"meta/kernel.yaml", ""},
  1861  	})
  1862  	si2 := &snap.SideInfo{RealName: "core18", Revision: snap.R(2)}
  1863  	snapstate.Set(st, "core18", &snapstate.SnapState{
  1864  		SnapType: "base",
  1865  		Active:   true,
  1866  		Sequence: []*snap.SideInfo{si2},
  1867  		Current:  si2.Revision,
  1868  	})
  1869  
  1870  	// setup model assertion
  1871  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  1872  	devicestatetest.SetDevice(st, &auth.DeviceState{
  1873  		Brand:  "my-brand",
  1874  		Model:  "my-model",
  1875  		Serial: "serialserialserial",
  1876  	})
  1877  	err := assertstate.Add(st, model)
  1878  	c.Assert(err, IsNil)
  1879  
  1880  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, snapPath, "", "", snapstate.Flags{})
  1881  	c.Assert(err, IsNil)
  1882  	chg := st.NewChange("install-snap", "...")
  1883  	chg.AddAll(ts)
  1884  
  1885  	// run, this will trigger a wait for the restart
  1886  	st.Unlock()
  1887  	err = s.o.Settle(settleTimeout)
  1888  	st.Lock()
  1889  	c.Assert(err, IsNil)
  1890  	// we are in restarting state and the change is not done yet
  1891  	restarting, _ := st.Restarting()
  1892  	c.Check(restarting, Equals, true)
  1893  	c.Check(chg.Status(), Equals, state.DoingStatus)
  1894  
  1895  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  1896  		"snap_core":       "core18_2.snap",
  1897  		"snap_try_core":   "",
  1898  		"snap_kernel":     "pc-kernel_123.snap",
  1899  		"snap_try_kernel": "pc-kernel_x1.snap",
  1900  		"snap_mode":       boot.TryStatus,
  1901  	})
  1902  	// pretend we restarted
  1903  	s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel})
  1904  
  1905  	st.Unlock()
  1906  	err = s.o.Settle(settleTimeout)
  1907  	st.Lock()
  1908  	c.Assert(err, IsNil)
  1909  
  1910  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  1911  }
  1912  
  1913  func (s *mgrsSuite) TestInstallKernelSnapUndoUpdatesBootloaderEnv(c *C) {
  1914  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  1915  	bootloader.Force(bloader)
  1916  	defer bootloader.Force(nil)
  1917  
  1918  	restore := release.MockOnClassic(false)
  1919  	defer restore()
  1920  
  1921  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  1922  
  1923  	const packageKernel = `
  1924  name: pc-kernel
  1925  version: 4.0-1
  1926  type: kernel`
  1927  
  1928  	files := [][]string{
  1929  		{"kernel.img", "I'm a kernel"},
  1930  		{"initrd.img", "...and I'm an initrd"},
  1931  		{"meta/kernel.yaml", "version: 4.2"},
  1932  	}
  1933  	snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files)
  1934  
  1935  	st := s.o.State()
  1936  	st.Lock()
  1937  	defer st.Unlock()
  1938  
  1939  	// pretend we have core18/pc-kernel
  1940  	bloader.BootVars = map[string]string{
  1941  		"snap_core":   "core18_2.snap",
  1942  		"snap_kernel": "pc-kernel_123.snap",
  1943  		"snap_mode":   boot.DefaultStatus,
  1944  	}
  1945  	si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(123)}
  1946  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  1947  		SnapType: "kernel",
  1948  		Active:   true,
  1949  		Sequence: []*snap.SideInfo{si1},
  1950  		Current:  si1.Revision,
  1951  	})
  1952  	snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{
  1953  		{"meta/kernel.yaml", ""},
  1954  	})
  1955  	si2 := &snap.SideInfo{RealName: "core18", Revision: snap.R(2)}
  1956  	snapstate.Set(st, "core18", &snapstate.SnapState{
  1957  		SnapType: "base",
  1958  		Active:   true,
  1959  		Sequence: []*snap.SideInfo{si2},
  1960  		Current:  si2.Revision,
  1961  	})
  1962  
  1963  	// setup model assertion
  1964  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  1965  	devicestatetest.SetDevice(st, &auth.DeviceState{
  1966  		Brand:  "my-brand",
  1967  		Model:  "my-model",
  1968  		Serial: "serialserialserial",
  1969  	})
  1970  	err := assertstate.Add(st, model)
  1971  	c.Assert(err, IsNil)
  1972  
  1973  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, snapPath, "", "", snapstate.Flags{})
  1974  	c.Assert(err, IsNil)
  1975  
  1976  	terr := st.NewTask("error-trigger", "provoking total undo")
  1977  	terr.WaitFor(ts.Tasks()[len(ts.Tasks())-1])
  1978  	ts.AddTask(terr)
  1979  	chg := st.NewChange("install-snap", "...")
  1980  	chg.AddAll(ts)
  1981  
  1982  	// run, this will trigger a wait for the restart
  1983  	st.Unlock()
  1984  	err = s.o.Settle(settleTimeout)
  1985  	st.Lock()
  1986  	c.Assert(err, IsNil)
  1987  
  1988  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  1989  		"snap_core":       "core18_2.snap",
  1990  		"snap_kernel":     "pc-kernel_123.snap",
  1991  		"snap_try_kernel": "pc-kernel_x1.snap",
  1992  		"snap_mode":       boot.TryStatus,
  1993  		"snap_try_core":   "",
  1994  	})
  1995  
  1996  	// we are in restarting state and the change is not done yet
  1997  	restarting, _ := st.Restarting()
  1998  	c.Check(restarting, Equals, true)
  1999  	c.Check(chg.Status(), Equals, state.DoingStatus)
  2000  	// pretend we restarted
  2001  	s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel})
  2002  
  2003  	st.Unlock()
  2004  	err = s.o.Settle(settleTimeout)
  2005  	st.Lock()
  2006  	c.Assert(err, IsNil)
  2007  
  2008  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  2009  
  2010  	// and we undo the bootvars and trigger a reboot
  2011  	c.Check(bloader.BootVars, DeepEquals, map[string]string{
  2012  		"snap_core":       "core18_2.snap",
  2013  		"snap_try_core":   "",
  2014  		"snap_try_kernel": "pc-kernel_123.snap",
  2015  		"snap_kernel":     "pc-kernel_x1.snap",
  2016  		"snap_mode":       boot.TryStatus,
  2017  	})
  2018  	restarting, _ = st.Restarting()
  2019  	c.Check(restarting, Equals, true)
  2020  }
  2021  
  2022  func (s *mgrsSuite) TestInstallKernelSnap20UpdatesBootloaderEnv(c *C) {
  2023  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  2024  	bootloader.Force(bloader)
  2025  	defer bootloader.Force(nil)
  2026  
  2027  	// we have revision 1 installed
  2028  	kernel, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap")
  2029  	c.Assert(err, IsNil)
  2030  	restore := bloader.SetEnabledKernel(kernel)
  2031  	defer restore()
  2032  
  2033  	restore = release.MockOnClassic(false)
  2034  	defer restore()
  2035  
  2036  	uc20ModelDefaults := map[string]interface{}{
  2037  		"architecture": "amd64",
  2038  		"base":         "core20",
  2039  		"store":        "my-brand-store-id",
  2040  		"snaps": []interface{}{
  2041  			map[string]interface{}{
  2042  				"name":            "pc-kernel",
  2043  				"id":              snaptest.AssertedSnapID("pc-kernel"),
  2044  				"type":            "kernel",
  2045  				"default-channel": "20",
  2046  			},
  2047  			map[string]interface{}{
  2048  				"name":            "pc",
  2049  				"id":              snaptest.AssertedSnapID("pc"),
  2050  				"type":            "gadget",
  2051  				"default-channel": "20",
  2052  			}},
  2053  	}
  2054  
  2055  	model := s.brands.Model("my-brand", "my-model", uc20ModelDefaults)
  2056  
  2057  	const packageKernel = `
  2058  name: pc-kernel
  2059  version: 4.0-1
  2060  type: kernel`
  2061  
  2062  	files := [][]string{
  2063  		{"kernel.efi", "I'm a kernel.efi"},
  2064  		{"meta/kernel.yaml", "version: 4.2"},
  2065  	}
  2066  	kernelSnapSideInfo := &snap.SideInfo{RealName: "pc-kernel"}
  2067  	kernelSnapPath, kernelSnapInfo := snaptest.MakeTestSnapInfoWithFiles(c, packageKernel, files, kernelSnapSideInfo)
  2068  
  2069  	// mock the modeenv file
  2070  	m := boot.Modeenv{
  2071  		Mode:           "run",
  2072  		RecoverySystem: "20191127",
  2073  		Base:           "core20_1.snap",
  2074  	}
  2075  	err = m.WriteTo("")
  2076  	c.Assert(err, IsNil)
  2077  
  2078  	st := s.o.State()
  2079  	st.Lock()
  2080  	defer st.Unlock()
  2081  
  2082  	si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(1)}
  2083  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  2084  		SnapType: "kernel",
  2085  		Active:   true,
  2086  		Sequence: []*snap.SideInfo{si1},
  2087  		Current:  si1.Revision,
  2088  	})
  2089  	snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{
  2090  		{"meta/kernel.yaml", ""},
  2091  	})
  2092  	si2 := &snap.SideInfo{RealName: "core20", Revision: snap.R(1)}
  2093  	snapstate.Set(st, "core20", &snapstate.SnapState{
  2094  		SnapType: "base",
  2095  		Active:   true,
  2096  		Sequence: []*snap.SideInfo{si2},
  2097  		Current:  si2.Revision,
  2098  	})
  2099  
  2100  	// setup model assertion
  2101  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  2102  	devicestatetest.SetDevice(st, &auth.DeviceState{
  2103  		Brand:  "my-brand",
  2104  		Model:  "my-model",
  2105  		Serial: "serialserialserial",
  2106  	})
  2107  	err = assertstate.Add(st, model)
  2108  	c.Assert(err, IsNil)
  2109  
  2110  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, kernelSnapPath, "", "", snapstate.Flags{})
  2111  	c.Assert(err, IsNil)
  2112  	chg := st.NewChange("install-snap", "...")
  2113  	chg.AddAll(ts)
  2114  
  2115  	// run, this will trigger a wait for the restart
  2116  	st.Unlock()
  2117  	err = s.o.Settle(settleTimeout)
  2118  	st.Lock()
  2119  	c.Assert(err, IsNil)
  2120  
  2121  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  2122  		"kernel_status": boot.TryStatus,
  2123  	})
  2124  
  2125  	// we are in restarting state and the change is not done yet
  2126  	restarting, _ := st.Restarting()
  2127  	c.Check(restarting, Equals, true)
  2128  	c.Check(chg.Status(), Equals, state.DoingStatus)
  2129  
  2130  	// the kernelSnapInfo we mocked earlier will not have a revision set for the
  2131  	// SideInfo, but since the previous revision was "1", the next revision will
  2132  	// be x1 since it's unasserted, so we can set the Revision on the SideInfo
  2133  	// here to make comparison easier
  2134  	kernelSnapInfo.SideInfo.Revision = snap.R(-1)
  2135  
  2136  	// the current kernel in the bootloader is still the same
  2137  	currentKernel, err := bloader.Kernel()
  2138  	c.Assert(err, IsNil)
  2139  	firstKernel := snap.Info{SideInfo: *si1}
  2140  	c.Assert(currentKernel.Filename(), Equals, firstKernel.Filename())
  2141  
  2142  	// the current try kernel in the bootloader is our new kernel
  2143  	currentTryKernel, err := bloader.TryKernel()
  2144  	c.Assert(err, IsNil)
  2145  	c.Assert(currentTryKernel.Filename(), Equals, kernelSnapInfo.Filename())
  2146  
  2147  	// check that we extracted the kernel snap assets
  2148  	extractedKernels := bloader.ExtractKernelAssetsCalls
  2149  	c.Assert(extractedKernels, HasLen, 1)
  2150  	c.Assert(extractedKernels[0].Filename(), Equals, kernelSnapInfo.Filename())
  2151  
  2152  	// pretend we restarted
  2153  	s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel})
  2154  
  2155  	st.Unlock()
  2156  	err = s.o.Settle(settleTimeout)
  2157  	st.Lock()
  2158  	c.Assert(err, IsNil)
  2159  
  2160  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2161  
  2162  	// also check that we are active on the second revision
  2163  	var snapst snapstate.SnapState
  2164  	err = snapstate.Get(st, "pc-kernel", &snapst)
  2165  	c.Assert(err, IsNil)
  2166  	c.Check(snapst.Sequence, HasLen, 2)
  2167  	c.Check(snapst.Sequence, DeepEquals, []*snap.SideInfo{si1, &kernelSnapInfo.SideInfo})
  2168  	c.Check(snapst.Active, Equals, true)
  2169  	c.Check(snapst.Current, DeepEquals, snap.R(-1))
  2170  
  2171  	// since we need to do a reboot to go back to the old kernel, we should now
  2172  	// have kernel on the bootloader as the new one, and no try kernel on the
  2173  	// bootloader
  2174  	finalCurrentKernel, err := bloader.Kernel()
  2175  	c.Assert(err, IsNil)
  2176  	c.Assert(finalCurrentKernel.Filename(), Equals, kernelSnapInfo.Filename())
  2177  
  2178  	_, err = bloader.TryKernel()
  2179  	c.Assert(err, Equals, bootloader.ErrNoTryKernelRef)
  2180  
  2181  	// finally check that GetCurrentBoot gives us the new kernel
  2182  	dev, err := devicestate.DeviceCtx(st, nil, nil)
  2183  	c.Assert(err, IsNil)
  2184  	sn, err := boot.GetCurrentBoot(snap.TypeKernel, dev)
  2185  	c.Assert(err, IsNil)
  2186  	c.Assert(sn.Filename(), Equals, kernelSnapInfo.Filename())
  2187  }
  2188  
  2189  func (s *mgrsSuite) TestInstallKernelSnap20UndoUpdatesBootloaderEnv(c *C) {
  2190  	bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir()))
  2191  	bootloader.Force(bloader)
  2192  	defer bootloader.Force(nil)
  2193  
  2194  	// we have revision 1 installed
  2195  	kernel, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap")
  2196  	c.Assert(err, IsNil)
  2197  	restore := bloader.SetEnabledKernel(kernel)
  2198  	defer restore()
  2199  
  2200  	restore = release.MockOnClassic(false)
  2201  	defer restore()
  2202  
  2203  	uc20ModelDefaults := map[string]interface{}{
  2204  		"architecture": "amd64",
  2205  		"base":         "core20",
  2206  		"store":        "my-brand-store-id",
  2207  		"snaps": []interface{}{
  2208  			map[string]interface{}{
  2209  				"name":            "pc-kernel",
  2210  				"id":              snaptest.AssertedSnapID("pc-kernel"),
  2211  				"type":            "kernel",
  2212  				"default-channel": "20",
  2213  			},
  2214  			map[string]interface{}{
  2215  				"name":            "pc",
  2216  				"id":              snaptest.AssertedSnapID("pc"),
  2217  				"type":            "gadget",
  2218  				"default-channel": "20",
  2219  			}},
  2220  	}
  2221  
  2222  	model := s.brands.Model("my-brand", "my-model", uc20ModelDefaults)
  2223  
  2224  	const packageKernel = `
  2225  name: pc-kernel
  2226  version: 4.0-1
  2227  type: kernel`
  2228  
  2229  	files := [][]string{
  2230  		{"kernel.efi", "I'm a kernel.efi"},
  2231  		{"meta/kernel.yaml", "version: 4.2"},
  2232  	}
  2233  	kernelSnapSideInfo := &snap.SideInfo{RealName: "pc-kernel"}
  2234  	kernelSnapPath, kernelSnapInfo := snaptest.MakeTestSnapInfoWithFiles(c, packageKernel, files, kernelSnapSideInfo)
  2235  
  2236  	// mock the modeenv file
  2237  	m := boot.Modeenv{
  2238  		Mode:           "run",
  2239  		RecoverySystem: "20191127",
  2240  		Base:           "core20_1.snap",
  2241  	}
  2242  	err = m.WriteTo("")
  2243  	c.Assert(err, IsNil)
  2244  
  2245  	st := s.o.State()
  2246  	st.Lock()
  2247  	defer st.Unlock()
  2248  
  2249  	si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(1)}
  2250  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  2251  		SnapType: "kernel",
  2252  		Active:   true,
  2253  		Sequence: []*snap.SideInfo{si1},
  2254  		Current:  si1.Revision,
  2255  	})
  2256  	snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{
  2257  		{"meta/kernel.yaml", ""},
  2258  	})
  2259  	si2 := &snap.SideInfo{RealName: "core20", Revision: snap.R(1)}
  2260  	snapstate.Set(st, "core20", &snapstate.SnapState{
  2261  		SnapType: "base",
  2262  		Active:   true,
  2263  		Sequence: []*snap.SideInfo{si2},
  2264  		Current:  si2.Revision,
  2265  	})
  2266  
  2267  	// setup model assertion
  2268  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  2269  	devicestatetest.SetDevice(st, &auth.DeviceState{
  2270  		Brand:  "my-brand",
  2271  		Model:  "my-model",
  2272  		Serial: "serialserialserial",
  2273  	})
  2274  	err = assertstate.Add(st, model)
  2275  	c.Assert(err, IsNil)
  2276  
  2277  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, kernelSnapPath, "", "", snapstate.Flags{})
  2278  	c.Assert(err, IsNil)
  2279  
  2280  	terr := st.NewTask("error-trigger", "provoking total undo")
  2281  	terr.WaitFor(ts.Tasks()[len(ts.Tasks())-1])
  2282  	ts.AddTask(terr)
  2283  	chg := st.NewChange("install-snap", "...")
  2284  	chg.AddAll(ts)
  2285  
  2286  	// run, this will trigger a wait for the restart
  2287  	st.Unlock()
  2288  	err = s.o.Settle(settleTimeout)
  2289  	st.Lock()
  2290  	c.Assert(err, IsNil)
  2291  
  2292  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  2293  		"kernel_status": boot.TryStatus,
  2294  	})
  2295  
  2296  	// the kernelSnapInfo we mocked earlier will not have a revision set for the
  2297  	// SideInfo, but since the previous revision was "1", the next revision will
  2298  	// be x1 since it's unasserted, so we can set the Revision on the SideInfo
  2299  	// here to make comparison easier
  2300  	kernelSnapInfo.SideInfo.Revision = snap.R(-1)
  2301  
  2302  	// check that we extracted the kernel snap assets
  2303  	extractedKernels := bloader.ExtractKernelAssetsCalls
  2304  	c.Assert(extractedKernels, HasLen, 1)
  2305  	c.Assert(extractedKernels[0].Filename(), Equals, kernelSnapInfo.Filename())
  2306  
  2307  	// the current kernel in the bootloader is still the same
  2308  	currentKernel, err := bloader.Kernel()
  2309  	c.Assert(err, IsNil)
  2310  	firstKernel := snap.Info{SideInfo: *si1}
  2311  	c.Assert(currentKernel.Filename(), Equals, firstKernel.Filename())
  2312  
  2313  	// the current try kernel in the bootloader is our new kernel
  2314  	currentTryKernel, err := bloader.TryKernel()
  2315  	c.Assert(err, IsNil)
  2316  	c.Assert(currentTryKernel.Filename(), Equals, kernelSnapInfo.Filename())
  2317  
  2318  	// we are in restarting state and the change is not done yet
  2319  	restarting, _ := st.Restarting()
  2320  	c.Check(restarting, Equals, true)
  2321  	c.Check(chg.Status(), Equals, state.DoingStatus)
  2322  	// pretend we restarted
  2323  	s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel})
  2324  
  2325  	st.Unlock()
  2326  	err = s.o.Settle(settleTimeout)
  2327  	st.Lock()
  2328  	c.Assert(err, IsNil)
  2329  
  2330  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  2331  
  2332  	// we should have triggered a reboot to undo the boot changes
  2333  	restarting, _ = st.Restarting()
  2334  	c.Check(restarting, Equals, true)
  2335  
  2336  	// we need to reboot with a "new" try kernel, so kernel_status was set again
  2337  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  2338  		"kernel_status": boot.TryStatus,
  2339  	})
  2340  
  2341  	// we should not have extracted any more kernel assets than before, since
  2342  	// the fallback kernel was already extracted
  2343  	extractedKernels = bloader.ExtractKernelAssetsCalls
  2344  	c.Assert(extractedKernels, HasLen, 1) // same as above check
  2345  
  2346  	// also check that we are active on the first revision again
  2347  	var snapst snapstate.SnapState
  2348  	err = snapstate.Get(st, "pc-kernel", &snapst)
  2349  	c.Assert(err, IsNil)
  2350  	c.Check(snapst.Sequence, HasLen, 1)
  2351  	c.Check(snapst.Sequence, DeepEquals, []*snap.SideInfo{si1})
  2352  	c.Check(snapst.Active, Equals, true)
  2353  	c.Check(snapst.Current, DeepEquals, snap.R(1))
  2354  
  2355  	// since we need to do a reboot to go back to the old kernel, we should now
  2356  	// have kernel on the bootloader as the new one, and the try kernel on the
  2357  	// booloader as the old one
  2358  	finalCurrentKernel, err := bloader.Kernel()
  2359  	c.Assert(err, IsNil)
  2360  	c.Assert(finalCurrentKernel.Filename(), Equals, kernelSnapInfo.Filename())
  2361  
  2362  	finalTryKernel, err := bloader.TryKernel()
  2363  	c.Assert(err, IsNil)
  2364  	c.Assert(finalTryKernel.Filename(), Equals, firstKernel.Filename())
  2365  
  2366  	// TODO:UC20: this test should probably simulate another reboot and confirm
  2367  	// that at the end of everything we have GetCurrentBoot() return the old
  2368  	// kernel we reverted back to again
  2369  }
  2370  
  2371  func (s *mgrsSuite) installLocalTestSnap(c *C, snapYamlContent string) *snap.Info {
  2372  	st := s.o.State()
  2373  
  2374  	snapPath := makeTestSnap(c, snapYamlContent)
  2375  	snapf, err := snapfile.Open(snapPath)
  2376  	c.Assert(err, IsNil)
  2377  	info, err := snap.ReadInfoFromSnapFile(snapf, nil)
  2378  	c.Assert(err, IsNil)
  2379  
  2380  	// store current state
  2381  	snapName := info.InstanceName()
  2382  	var snapst snapstate.SnapState
  2383  	snapstate.Get(st, snapName, &snapst)
  2384  
  2385  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName}, snapPath, "", "", snapstate.Flags{DevMode: true})
  2386  	c.Assert(err, IsNil)
  2387  	chg := st.NewChange("install-snap", "...")
  2388  	chg.AddAll(ts)
  2389  
  2390  	st.Unlock()
  2391  	err = s.o.Settle(settleTimeout)
  2392  	st.Lock()
  2393  	c.Assert(err, IsNil)
  2394  
  2395  	c.Assert(chg.Err(), IsNil)
  2396  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2397  
  2398  	return info
  2399  }
  2400  
  2401  func (s *mgrsSuite) removeSnap(c *C, name string) {
  2402  	st := s.o.State()
  2403  
  2404  	ts, err := snapstate.Remove(st, name, snap.R(0), &snapstate.RemoveFlags{Purge: true})
  2405  	c.Assert(err, IsNil)
  2406  	chg := st.NewChange("remove-snap", "...")
  2407  	chg.AddAll(ts)
  2408  
  2409  	st.Unlock()
  2410  	err = s.o.Settle(settleTimeout)
  2411  	st.Lock()
  2412  	c.Assert(err, IsNil)
  2413  
  2414  	c.Assert(chg.Err(), IsNil)
  2415  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err()))
  2416  }
  2417  
  2418  func (s *mgrsSuite) TestHappyRevert(c *C) {
  2419  	st := s.o.State()
  2420  	st.Lock()
  2421  	defer st.Unlock()
  2422  
  2423  	x1Yaml := `name: foo
  2424  version: 1.0
  2425  apps:
  2426   x1:
  2427    command: bin/bar
  2428  `
  2429  	x1binary := filepath.Join(dirs.SnapBinariesDir, "foo.x1")
  2430  
  2431  	x2Yaml := `name: foo
  2432  version: 2.0
  2433  apps:
  2434   x2:
  2435    command: bin/bar
  2436  `
  2437  	x2binary := filepath.Join(dirs.SnapBinariesDir, "foo.x2")
  2438  
  2439  	s.installLocalTestSnap(c, x1Yaml)
  2440  	s.installLocalTestSnap(c, x2Yaml)
  2441  
  2442  	// ensure we are on x2
  2443  	_, err := os.Lstat(x2binary)
  2444  	c.Assert(err, IsNil)
  2445  	_, err = os.Lstat(x1binary)
  2446  	c.Assert(err, ErrorMatches, ".*no such file.*")
  2447  
  2448  	// now do the revert
  2449  	ts, err := snapstate.Revert(st, "foo", snapstate.Flags{})
  2450  	c.Assert(err, IsNil)
  2451  	chg := st.NewChange("revert-snap", "...")
  2452  	chg.AddAll(ts)
  2453  
  2454  	st.Unlock()
  2455  	err = s.o.Settle(settleTimeout)
  2456  	st.Lock()
  2457  	c.Assert(err, IsNil)
  2458  
  2459  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("revert-snap change failed with: %v", chg.Err()))
  2460  
  2461  	// ensure that we use x1 now
  2462  	_, err = os.Lstat(x1binary)
  2463  	c.Assert(err, IsNil)
  2464  	_, err = os.Lstat(x2binary)
  2465  	c.Assert(err, ErrorMatches, ".*no such file.*")
  2466  
  2467  	// ensure that x1,x2 is still there, revert just moves the "current"
  2468  	// pointer
  2469  	for _, fn := range []string{"foo_x2.snap", "foo_x1.snap"} {
  2470  		p := filepath.Join(dirs.SnapBlobDir, fn)
  2471  		c.Assert(osutil.FileExists(p), Equals, true)
  2472  	}
  2473  }
  2474  
  2475  func (s *mgrsSuite) TestHappyAlias(c *C) {
  2476  	st := s.o.State()
  2477  	st.Lock()
  2478  	defer st.Unlock()
  2479  
  2480  	fooYaml := `name: foo
  2481  version: 1.0
  2482  apps:
  2483      foo:
  2484          command: bin/foo
  2485  `
  2486  	s.installLocalTestSnap(c, fooYaml)
  2487  
  2488  	ts, err := snapstate.Alias(st, "foo", "foo", "foo_")
  2489  	c.Assert(err, IsNil)
  2490  	chg := st.NewChange("alias", "...")
  2491  	chg.AddAll(ts)
  2492  
  2493  	st.Unlock()
  2494  	err = s.o.Settle(settleTimeout)
  2495  	st.Lock()
  2496  	c.Assert(err, IsNil)
  2497  
  2498  	c.Assert(chg.Err(), IsNil)
  2499  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("alias change failed with: %v", chg.Err()))
  2500  
  2501  	foo_Alias := filepath.Join(dirs.SnapBinariesDir, "foo_")
  2502  	dest, err := os.Readlink(foo_Alias)
  2503  	c.Assert(err, IsNil)
  2504  
  2505  	c.Check(dest, Equals, "foo")
  2506  
  2507  	var snapst snapstate.SnapState
  2508  	err = snapstate.Get(st, "foo", &snapst)
  2509  	c.Assert(err, IsNil)
  2510  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  2511  	c.Check(snapst.AliasesPending, Equals, false)
  2512  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2513  		"foo_": {Manual: "foo"},
  2514  	})
  2515  
  2516  	s.removeSnap(c, "foo")
  2517  
  2518  	c.Check(osutil.IsSymlink(foo_Alias), Equals, false)
  2519  }
  2520  
  2521  func (s *mgrsSuite) TestHappyUnalias(c *C) {
  2522  	st := s.o.State()
  2523  	st.Lock()
  2524  	defer st.Unlock()
  2525  
  2526  	fooYaml := `name: foo
  2527  version: 1.0
  2528  apps:
  2529      foo:
  2530          command: bin/foo
  2531  `
  2532  	s.installLocalTestSnap(c, fooYaml)
  2533  
  2534  	ts, err := snapstate.Alias(st, "foo", "foo", "foo_")
  2535  	c.Assert(err, IsNil)
  2536  	chg := st.NewChange("alias", "...")
  2537  	chg.AddAll(ts)
  2538  
  2539  	st.Unlock()
  2540  	err = s.o.Settle(settleTimeout)
  2541  	st.Lock()
  2542  	c.Assert(err, IsNil)
  2543  
  2544  	c.Assert(chg.Err(), IsNil)
  2545  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("alias change failed with: %v", chg.Err()))
  2546  
  2547  	foo_Alias := filepath.Join(dirs.SnapBinariesDir, "foo_")
  2548  	dest, err := os.Readlink(foo_Alias)
  2549  	c.Assert(err, IsNil)
  2550  
  2551  	c.Check(dest, Equals, "foo")
  2552  
  2553  	ts, snapName, err := snapstate.RemoveManualAlias(st, "foo_")
  2554  	c.Assert(err, IsNil)
  2555  	c.Check(snapName, Equals, "foo")
  2556  	chg = st.NewChange("unalias", "...")
  2557  	chg.AddAll(ts)
  2558  
  2559  	st.Unlock()
  2560  	err = s.o.Settle(settleTimeout)
  2561  	st.Lock()
  2562  	c.Assert(err, IsNil)
  2563  
  2564  	c.Assert(chg.Err(), IsNil)
  2565  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("unalias change failed with: %v", chg.Err()))
  2566  
  2567  	c.Check(osutil.IsSymlink(foo_Alias), Equals, false)
  2568  
  2569  	var snapst snapstate.SnapState
  2570  	err = snapstate.Get(st, "foo", &snapst)
  2571  	c.Assert(err, IsNil)
  2572  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  2573  	c.Check(snapst.AliasesPending, Equals, false)
  2574  	c.Check(snapst.Aliases, HasLen, 0)
  2575  }
  2576  
  2577  func (s *mgrsSuite) TestHappyRemoteInstallAutoAliases(c *C) {
  2578  	s.prereqSnapAssertions(c, map[string]interface{}{
  2579  		"snap-name": "foo",
  2580  		"aliases": []interface{}{
  2581  			map[string]interface{}{"name": "app1", "target": "app1"},
  2582  			map[string]interface{}{"name": "app2", "target": "app2"},
  2583  		},
  2584  	})
  2585  
  2586  	snapYamlContent := `name: foo
  2587  version: @VERSION@
  2588  apps:
  2589   app1:
  2590    command: bin/app1
  2591   app2:
  2592    command: bin/app2
  2593  `
  2594  
  2595  	ver := "1.0"
  2596  	revno := "42"
  2597  	snapPath, _ := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno)
  2598  	s.serveSnap(snapPath, revno)
  2599  
  2600  	mockServer := s.mockStore(c)
  2601  	defer mockServer.Close()
  2602  
  2603  	st := s.o.State()
  2604  	st.Lock()
  2605  	defer st.Unlock()
  2606  
  2607  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  2608  	c.Assert(err, IsNil)
  2609  	chg := st.NewChange("install-snap", "...")
  2610  	chg.AddAll(ts)
  2611  
  2612  	st.Unlock()
  2613  	err = s.o.Settle(settleTimeout)
  2614  	st.Lock()
  2615  	c.Assert(err, IsNil)
  2616  
  2617  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2618  
  2619  	var snapst snapstate.SnapState
  2620  	err = snapstate.Get(st, "foo", &snapst)
  2621  	c.Assert(err, IsNil)
  2622  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  2623  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2624  		"app1": {Auto: "app1"},
  2625  		"app2": {Auto: "app2"},
  2626  	})
  2627  
  2628  	// check disk
  2629  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  2630  	dest, err := os.Readlink(app1Alias)
  2631  	c.Assert(err, IsNil)
  2632  	c.Check(dest, Equals, "foo.app1")
  2633  
  2634  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  2635  	dest, err = os.Readlink(app2Alias)
  2636  	c.Assert(err, IsNil)
  2637  	c.Check(dest, Equals, "foo.app2")
  2638  }
  2639  
  2640  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateAutoAliases(c *C) {
  2641  	s.prereqSnapAssertions(c, map[string]interface{}{
  2642  		"snap-name": "foo",
  2643  		"aliases": []interface{}{
  2644  			map[string]interface{}{"name": "app1", "target": "app1"},
  2645  		},
  2646  	})
  2647  
  2648  	fooYaml := `name: foo
  2649  version: @VERSION@
  2650  apps:
  2651   app1:
  2652    command: bin/app1
  2653   app2:
  2654    command: bin/app2
  2655  `
  2656  
  2657  	fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10")
  2658  	s.serveSnap(fooPath, "10")
  2659  
  2660  	mockServer := s.mockStore(c)
  2661  	defer mockServer.Close()
  2662  
  2663  	st := s.o.State()
  2664  	st.Lock()
  2665  	defer st.Unlock()
  2666  
  2667  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  2668  	c.Assert(err, IsNil)
  2669  	chg := st.NewChange("install-snap", "...")
  2670  	chg.AddAll(ts)
  2671  
  2672  	st.Unlock()
  2673  	err = s.o.Settle(settleTimeout)
  2674  	st.Lock()
  2675  	c.Assert(err, IsNil)
  2676  
  2677  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2678  
  2679  	info, err := snapstate.CurrentInfo(st, "foo")
  2680  	c.Assert(err, IsNil)
  2681  	c.Check(info.Revision, Equals, snap.R(10))
  2682  	c.Check(info.Version, Equals, "1.0")
  2683  
  2684  	var snapst snapstate.SnapState
  2685  	err = snapstate.Get(st, "foo", &snapst)
  2686  	c.Assert(err, IsNil)
  2687  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  2688  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2689  		"app1": {Auto: "app1"},
  2690  	})
  2691  
  2692  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  2693  	dest, err := os.Readlink(app1Alias)
  2694  	c.Assert(err, IsNil)
  2695  	c.Check(dest, Equals, "foo.app1")
  2696  
  2697  	s.prereqSnapAssertions(c, map[string]interface{}{
  2698  		"snap-name": "foo",
  2699  		"aliases": []interface{}{
  2700  			map[string]interface{}{"name": "app2", "target": "app2"},
  2701  		},
  2702  		"revision": "1",
  2703  	})
  2704  
  2705  	// new foo version/revision
  2706  	fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15")
  2707  	s.serveSnap(fooPath, "15")
  2708  
  2709  	// refresh all
  2710  	updated, tss, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, nil)
  2711  	c.Assert(err, IsNil)
  2712  	c.Assert(updated, DeepEquals, []string{"foo"})
  2713  	c.Assert(tss, HasLen, 2)
  2714  	verifyLastTasksetIsRerefresh(c, tss)
  2715  	chg = st.NewChange("upgrade-snaps", "...")
  2716  	chg.AddAll(tss[0])
  2717  
  2718  	st.Unlock()
  2719  	err = s.o.Settle(settleTimeout)
  2720  	st.Lock()
  2721  	c.Assert(err, IsNil)
  2722  
  2723  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  2724  
  2725  	info, err = snapstate.CurrentInfo(st, "foo")
  2726  	c.Assert(err, IsNil)
  2727  	c.Check(info.Revision, Equals, snap.R(15))
  2728  	c.Check(info.Version, Equals, "1.5")
  2729  
  2730  	var snapst2 snapstate.SnapState
  2731  	err = snapstate.Get(st, "foo", &snapst2)
  2732  	c.Assert(err, IsNil)
  2733  	c.Check(snapst2.AutoAliasesDisabled, Equals, false)
  2734  	c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2735  		"app2": {Auto: "app2"},
  2736  	})
  2737  
  2738  	c.Check(osutil.IsSymlink(app1Alias), Equals, false)
  2739  
  2740  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  2741  	dest, err = os.Readlink(app2Alias)
  2742  	c.Assert(err, IsNil)
  2743  	c.Check(dest, Equals, "foo.app2")
  2744  }
  2745  
  2746  func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateAutoAliasesUnaliased(c *C) {
  2747  	s.prereqSnapAssertions(c, map[string]interface{}{
  2748  		"snap-name": "foo",
  2749  		"aliases": []interface{}{
  2750  			map[string]interface{}{"name": "app1", "target": "app1"},
  2751  		},
  2752  	})
  2753  
  2754  	fooYaml := `name: foo
  2755  version: @VERSION@
  2756  apps:
  2757   app1:
  2758    command: bin/app1
  2759   app2:
  2760    command: bin/app2
  2761  `
  2762  
  2763  	fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10")
  2764  	s.serveSnap(fooPath, "10")
  2765  
  2766  	mockServer := s.mockStore(c)
  2767  	defer mockServer.Close()
  2768  
  2769  	st := s.o.State()
  2770  	st.Lock()
  2771  	defer st.Unlock()
  2772  
  2773  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{Unaliased: true})
  2774  	c.Assert(err, IsNil)
  2775  	chg := st.NewChange("install-snap", "...")
  2776  	chg.AddAll(ts)
  2777  
  2778  	st.Unlock()
  2779  	err = s.o.Settle(settleTimeout)
  2780  	st.Lock()
  2781  	c.Assert(err, IsNil)
  2782  
  2783  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2784  
  2785  	info, err := snapstate.CurrentInfo(st, "foo")
  2786  	c.Assert(err, IsNil)
  2787  	c.Check(info.Revision, Equals, snap.R(10))
  2788  	c.Check(info.Version, Equals, "1.0")
  2789  
  2790  	var snapst snapstate.SnapState
  2791  	err = snapstate.Get(st, "foo", &snapst)
  2792  	c.Assert(err, IsNil)
  2793  	c.Check(snapst.AutoAliasesDisabled, Equals, true)
  2794  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2795  		"app1": {Auto: "app1"},
  2796  	})
  2797  
  2798  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  2799  	c.Check(osutil.IsSymlink(app1Alias), Equals, false)
  2800  
  2801  	s.prereqSnapAssertions(c, map[string]interface{}{
  2802  		"snap-name": "foo",
  2803  		"aliases": []interface{}{
  2804  			map[string]interface{}{"name": "app2", "target": "app2"},
  2805  		},
  2806  		"revision": "1",
  2807  	})
  2808  
  2809  	// new foo version/revision
  2810  	fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15")
  2811  	s.serveSnap(fooPath, "15")
  2812  
  2813  	// refresh foo
  2814  	ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{})
  2815  	c.Assert(err, IsNil)
  2816  	chg = st.NewChange("upgrade-snap", "...")
  2817  	chg.AddAll(ts)
  2818  
  2819  	st.Unlock()
  2820  	err = s.o.Settle(settleTimeout)
  2821  	st.Lock()
  2822  	c.Assert(err, IsNil)
  2823  
  2824  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  2825  
  2826  	info, err = snapstate.CurrentInfo(st, "foo")
  2827  	c.Assert(err, IsNil)
  2828  	c.Check(info.Revision, Equals, snap.R(15))
  2829  	c.Check(info.Version, Equals, "1.5")
  2830  
  2831  	var snapst2 snapstate.SnapState
  2832  	err = snapstate.Get(st, "foo", &snapst2)
  2833  	c.Assert(err, IsNil)
  2834  	c.Check(snapst2.AutoAliasesDisabled, Equals, true)
  2835  	c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2836  		"app2": {Auto: "app2"},
  2837  	})
  2838  
  2839  	c.Check(osutil.IsSymlink(app1Alias), Equals, false)
  2840  
  2841  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  2842  	c.Check(osutil.IsSymlink(app2Alias), Equals, false)
  2843  }
  2844  
  2845  func (s *mgrsSuite) TestHappyOrthogonalRefreshAutoAliases(c *C) {
  2846  	s.prereqSnapAssertions(c, map[string]interface{}{
  2847  		"snap-name": "foo",
  2848  		"aliases": []interface{}{
  2849  			map[string]interface{}{"name": "app1", "target": "app1"},
  2850  		},
  2851  	}, map[string]interface{}{
  2852  		"snap-name": "bar",
  2853  	})
  2854  
  2855  	fooYaml := `name: foo
  2856  version: @VERSION@
  2857  apps:
  2858   app1:
  2859    command: bin/app1
  2860   app2:
  2861    command: bin/app2
  2862  `
  2863  
  2864  	barYaml := `name: bar
  2865  version: @VERSION@
  2866  apps:
  2867   app1:
  2868    command: bin/app1
  2869   app3:
  2870    command: bin/app3
  2871  `
  2872  
  2873  	fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10")
  2874  	s.serveSnap(fooPath, "10")
  2875  
  2876  	barPath, _ := s.makeStoreTestSnap(c, strings.Replace(barYaml, "@VERSION@", "2.0", -1), "20")
  2877  	s.serveSnap(barPath, "20")
  2878  
  2879  	mockServer := s.mockStore(c)
  2880  	defer mockServer.Close()
  2881  
  2882  	st := s.o.State()
  2883  	st.Lock()
  2884  	defer st.Unlock()
  2885  
  2886  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  2887  	c.Assert(err, IsNil)
  2888  	chg := st.NewChange("install-snap", "...")
  2889  	chg.AddAll(ts)
  2890  
  2891  	st.Unlock()
  2892  	err = s.o.Settle(settleTimeout)
  2893  	st.Lock()
  2894  	c.Assert(err, IsNil)
  2895  
  2896  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2897  
  2898  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2899  
  2900  	ts, err = snapstate.Install(context.TODO(), st, "bar", nil, 0, snapstate.Flags{})
  2901  	c.Assert(err, IsNil)
  2902  	chg = st.NewChange("install-snap", "...")
  2903  	chg.AddAll(ts)
  2904  
  2905  	st.Unlock()
  2906  	err = s.o.Settle(settleTimeout)
  2907  	st.Lock()
  2908  	c.Assert(err, IsNil)
  2909  
  2910  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  2911  
  2912  	info, err := snapstate.CurrentInfo(st, "foo")
  2913  	c.Assert(err, IsNil)
  2914  	c.Check(info.Revision, Equals, snap.R(10))
  2915  	c.Check(info.Version, Equals, "1.0")
  2916  
  2917  	info, err = snapstate.CurrentInfo(st, "bar")
  2918  	c.Assert(err, IsNil)
  2919  	c.Check(info.Revision, Equals, snap.R(20))
  2920  	c.Check(info.Version, Equals, "2.0")
  2921  
  2922  	var snapst snapstate.SnapState
  2923  	err = snapstate.Get(st, "foo", &snapst)
  2924  	c.Assert(err, IsNil)
  2925  	c.Check(snapst.AutoAliasesDisabled, Equals, false)
  2926  	c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2927  		"app1": {Auto: "app1"},
  2928  	})
  2929  
  2930  	// foo gets a new version/revision and a change of automatic aliases
  2931  	// bar gets only the latter
  2932  	// app1 is transferred from foo to bar
  2933  	// UpdateMany after a snap-declaration refresh handles all of this
  2934  	s.prereqSnapAssertions(c, map[string]interface{}{
  2935  		"snap-name": "foo",
  2936  		"aliases": []interface{}{
  2937  			map[string]interface{}{"name": "app2", "target": "app2"},
  2938  		},
  2939  		"revision": "1",
  2940  	}, map[string]interface{}{
  2941  		"snap-name": "bar",
  2942  		"aliases": []interface{}{
  2943  			map[string]interface{}{"name": "app1", "target": "app1"},
  2944  			map[string]interface{}{"name": "app3", "target": "app3"},
  2945  		},
  2946  		"revision": "1",
  2947  	})
  2948  
  2949  	// new foo version/revision
  2950  	fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15")
  2951  	s.serveSnap(fooPath, "15")
  2952  
  2953  	// refresh all
  2954  	err = assertstate.RefreshSnapDeclarations(st, 0)
  2955  	c.Assert(err, IsNil)
  2956  
  2957  	updated, tss, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, nil)
  2958  	c.Assert(err, IsNil)
  2959  	sort.Strings(updated)
  2960  	c.Assert(updated, DeepEquals, []string{"bar", "foo"})
  2961  	c.Assert(tss, HasLen, 4)
  2962  	verifyLastTasksetIsRerefresh(c, tss)
  2963  	chg = st.NewChange("upgrade-snaps", "...")
  2964  	chg.AddAll(tss[0])
  2965  	chg.AddAll(tss[1])
  2966  	chg.AddAll(tss[2])
  2967  
  2968  	st.Unlock()
  2969  	err = s.o.Settle(settleTimeout)
  2970  	st.Lock()
  2971  	c.Assert(err, IsNil)
  2972  
  2973  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  2974  
  2975  	info, err = snapstate.CurrentInfo(st, "foo")
  2976  	c.Assert(err, IsNil)
  2977  	c.Check(info.Revision, Equals, snap.R(15))
  2978  	c.Check(info.Version, Equals, "1.5")
  2979  
  2980  	var snapst2 snapstate.SnapState
  2981  	err = snapstate.Get(st, "foo", &snapst2)
  2982  	c.Assert(err, IsNil)
  2983  	c.Check(snapst2.AutoAliasesDisabled, Equals, false)
  2984  	c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2985  		"app2": {Auto: "app2"},
  2986  	})
  2987  	var snapst3 snapstate.SnapState
  2988  	err = snapstate.Get(st, "bar", &snapst3)
  2989  	c.Assert(err, IsNil)
  2990  	c.Check(snapst3.AutoAliasesDisabled, Equals, false)
  2991  	c.Check(snapst3.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{
  2992  		"app1": {Auto: "app1"},
  2993  		"app3": {Auto: "app3"},
  2994  	})
  2995  
  2996  	app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2")
  2997  	dest, err := os.Readlink(app2Alias)
  2998  	c.Assert(err, IsNil)
  2999  	c.Check(dest, Equals, "foo.app2")
  3000  
  3001  	app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1")
  3002  	dest, err = os.Readlink(app1Alias)
  3003  	c.Assert(err, IsNil)
  3004  	c.Check(dest, Equals, "bar.app1")
  3005  	app3Alias := filepath.Join(dirs.SnapBinariesDir, "app3")
  3006  	dest, err = os.Readlink(app3Alias)
  3007  	c.Assert(err, IsNil)
  3008  	c.Check(dest, Equals, "bar.app3")
  3009  }
  3010  
  3011  func (s *mgrsSuite) TestHappyStopWhileDownloadingHeader(c *C) {
  3012  	s.prereqSnapAssertions(c)
  3013  
  3014  	snapYamlContent := `name: foo
  3015  version: 1.0
  3016  `
  3017  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "42")
  3018  	s.serveSnap(snapPath, "42")
  3019  
  3020  	stopped := make(chan struct{})
  3021  	s.hijackServeSnap = func(_ http.ResponseWriter) {
  3022  		s.o.Stop()
  3023  		close(stopped)
  3024  	}
  3025  
  3026  	mockServer := s.mockStore(c)
  3027  	defer mockServer.Close()
  3028  
  3029  	st := s.o.State()
  3030  	st.Lock()
  3031  	defer st.Unlock()
  3032  
  3033  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  3034  	c.Assert(err, IsNil)
  3035  	chg := st.NewChange("install-snap", "...")
  3036  	chg.AddAll(ts)
  3037  
  3038  	st.Unlock()
  3039  	s.o.Loop()
  3040  
  3041  	<-stopped
  3042  
  3043  	st.Lock()
  3044  	c.Assert(chg.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  3045  }
  3046  
  3047  func (s *mgrsSuite) TestHappyStopWhileDownloadingBody(c *C) {
  3048  	s.prereqSnapAssertions(c)
  3049  
  3050  	snapYamlContent := `name: foo
  3051  version: 1.0
  3052  `
  3053  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "42")
  3054  	s.serveSnap(snapPath, "42")
  3055  
  3056  	stopped := make(chan struct{})
  3057  	s.hijackServeSnap = func(w http.ResponseWriter) {
  3058  		w.WriteHeader(200)
  3059  		// best effort to reach the body reading part in the client
  3060  		w.Write(make([]byte, 10000))
  3061  		time.Sleep(100 * time.Millisecond)
  3062  		w.Write(make([]byte, 10000))
  3063  		s.o.Stop()
  3064  		close(stopped)
  3065  	}
  3066  
  3067  	mockServer := s.mockStore(c)
  3068  	defer mockServer.Close()
  3069  
  3070  	st := s.o.State()
  3071  	st.Lock()
  3072  	defer st.Unlock()
  3073  
  3074  	ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{})
  3075  	c.Assert(err, IsNil)
  3076  	chg := st.NewChange("install-snap", "...")
  3077  	chg.AddAll(ts)
  3078  
  3079  	st.Unlock()
  3080  	s.o.Loop()
  3081  
  3082  	<-stopped
  3083  
  3084  	st.Lock()
  3085  	c.Assert(chg.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  3086  }
  3087  
  3088  type storeCtxSetupSuite struct {
  3089  	o  *overlord.Overlord
  3090  	sc store.DeviceAndAuthContext
  3091  
  3092  	storeSigning   *assertstest.StoreStack
  3093  	restoreTrusted func()
  3094  
  3095  	brands *assertstest.SigningAccounts
  3096  
  3097  	deviceKey asserts.PrivateKey
  3098  
  3099  	model  *asserts.Model
  3100  	serial *asserts.Serial
  3101  
  3102  	restoreBackends func()
  3103  }
  3104  
  3105  func (s *storeCtxSetupSuite) SetUpTest(c *C) {
  3106  	tempdir := c.MkDir()
  3107  	dirs.SetRootDir(tempdir)
  3108  	err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755)
  3109  	c.Assert(err, IsNil)
  3110  
  3111  	captureStoreCtx := func(_ *store.Config, dac store.DeviceAndAuthContext) *store.Store {
  3112  		s.sc = dac
  3113  		return store.New(nil, nil)
  3114  	}
  3115  	r := overlord.MockStoreNew(captureStoreCtx)
  3116  	defer r()
  3117  
  3118  	s.storeSigning = assertstest.NewStoreStack("can0nical", nil)
  3119  	s.restoreTrusted = sysdb.InjectTrusted(s.storeSigning.Trusted)
  3120  
  3121  	s.brands = assertstest.NewSigningAccounts(s.storeSigning)
  3122  	s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{
  3123  		"verification": "verified",
  3124  	})
  3125  	assertstest.AddMany(s.storeSigning, s.brands.AccountsAndKeys("my-brand")...)
  3126  
  3127  	s.model = s.brands.Model("my-brand", "my-model", modelDefaults)
  3128  
  3129  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
  3130  	c.Assert(err, IsNil)
  3131  	serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{
  3132  		"authority-id":        "my-brand",
  3133  		"brand-id":            "my-brand",
  3134  		"model":               "my-model",
  3135  		"serial":              "7878",
  3136  		"device-key":          string(encDevKey),
  3137  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
  3138  		"timestamp":           time.Now().Format(time.RFC3339),
  3139  	}, nil, "")
  3140  	c.Assert(err, IsNil)
  3141  	s.serial = serial.(*asserts.Serial)
  3142  
  3143  	s.restoreBackends = ifacestate.MockSecurityBackends(nil)
  3144  
  3145  	o, err := overlord.New(nil)
  3146  	c.Assert(err, IsNil)
  3147  	o.InterfaceManager().DisableUDevMonitor()
  3148  	s.o = o
  3149  
  3150  	st := o.State()
  3151  	st.Lock()
  3152  	defer st.Unlock()
  3153  
  3154  	assertstatetest.AddMany(st, s.storeSigning.StoreAccountKey(""))
  3155  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  3156  }
  3157  
  3158  func (s *storeCtxSetupSuite) TearDownTest(c *C) {
  3159  	dirs.SetRootDir("")
  3160  	s.restoreBackends()
  3161  	s.restoreTrusted()
  3162  }
  3163  
  3164  func (s *storeCtxSetupSuite) TestStoreID(c *C) {
  3165  	st := s.o.State()
  3166  	st.Lock()
  3167  	defer st.Unlock()
  3168  
  3169  	st.Unlock()
  3170  	storeID, err := s.sc.StoreID("fallback")
  3171  	st.Lock()
  3172  	c.Assert(err, IsNil)
  3173  	c.Check(storeID, Equals, "fallback")
  3174  
  3175  	// setup model in system statey
  3176  	devicestatetest.SetDevice(st, &auth.DeviceState{
  3177  		Brand:  s.serial.BrandID(),
  3178  		Model:  s.serial.Model(),
  3179  		Serial: s.serial.Serial(),
  3180  	})
  3181  	err = assertstate.Add(st, s.model)
  3182  	c.Assert(err, IsNil)
  3183  
  3184  	st.Unlock()
  3185  	storeID, err = s.sc.StoreID("fallback")
  3186  	st.Lock()
  3187  	c.Assert(err, IsNil)
  3188  	c.Check(storeID, Equals, "my-brand-store-id")
  3189  }
  3190  
  3191  func (s *storeCtxSetupSuite) TestDeviceSessionRequestParams(c *C) {
  3192  	st := s.o.State()
  3193  	st.Lock()
  3194  	defer st.Unlock()
  3195  
  3196  	st.Unlock()
  3197  	_, err := s.sc.DeviceSessionRequestParams("NONCE")
  3198  	st.Lock()
  3199  	c.Check(err, Equals, store.ErrNoSerial)
  3200  
  3201  	// setup model, serial and key in system state
  3202  	err = assertstate.Add(st, s.model)
  3203  	c.Assert(err, IsNil)
  3204  	err = assertstate.Add(st, s.serial)
  3205  	c.Assert(err, IsNil)
  3206  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  3207  	c.Assert(err, IsNil)
  3208  	err = kpMgr.Put(deviceKey)
  3209  	c.Assert(err, IsNil)
  3210  	devicestatetest.SetDevice(st, &auth.DeviceState{
  3211  		Brand:  s.serial.BrandID(),
  3212  		Model:  s.serial.Model(),
  3213  		Serial: s.serial.Serial(),
  3214  		KeyID:  deviceKey.PublicKey().ID(),
  3215  	})
  3216  
  3217  	st.Unlock()
  3218  	params, err := s.sc.DeviceSessionRequestParams("NONCE")
  3219  	st.Lock()
  3220  	c.Assert(err, IsNil)
  3221  	c.Check(strings.HasPrefix(params.EncodedRequest(), "type: device-session-request\n"), Equals, true)
  3222  	c.Check(params.EncodedSerial(), DeepEquals, string(asserts.Encode(s.serial)))
  3223  	c.Check(params.EncodedModel(), DeepEquals, string(asserts.Encode(s.model)))
  3224  
  3225  }
  3226  
  3227  func (s *storeCtxSetupSuite) TestProxyStoreParams(c *C) {
  3228  	st := s.o.State()
  3229  	st.Lock()
  3230  	defer st.Unlock()
  3231  
  3232  	defURL, err := url.Parse("http://store")
  3233  	c.Assert(err, IsNil)
  3234  
  3235  	st.Unlock()
  3236  	proxyStoreID, proxyStoreURL, err := s.sc.ProxyStoreParams(defURL)
  3237  	st.Lock()
  3238  	c.Assert(err, IsNil)
  3239  	c.Check(proxyStoreID, Equals, "")
  3240  	c.Check(proxyStoreURL, Equals, defURL)
  3241  
  3242  	// setup proxy store reference and assertion
  3243  	operatorAcct := assertstest.NewAccount(s.storeSigning, "foo-operator", nil, "")
  3244  	err = assertstate.Add(st, operatorAcct)
  3245  	c.Assert(err, IsNil)
  3246  	stoAs, err := s.storeSigning.Sign(asserts.StoreType, map[string]interface{}{
  3247  		"store":       "foo",
  3248  		"operator-id": operatorAcct.AccountID(),
  3249  		"url":         "http://foo.internal",
  3250  		"timestamp":   time.Now().Format(time.RFC3339),
  3251  	}, nil, "")
  3252  	c.Assert(err, IsNil)
  3253  	err = assertstate.Add(st, stoAs)
  3254  	c.Assert(err, IsNil)
  3255  	tr := config.NewTransaction(st)
  3256  	err = tr.Set("core", "proxy.store", "foo")
  3257  	c.Assert(err, IsNil)
  3258  	tr.Commit()
  3259  
  3260  	fooURL, err := url.Parse("http://foo.internal")
  3261  	c.Assert(err, IsNil)
  3262  
  3263  	st.Unlock()
  3264  	proxyStoreID, proxyStoreURL, err = s.sc.ProxyStoreParams(defURL)
  3265  	st.Lock()
  3266  	c.Assert(err, IsNil)
  3267  	c.Check(proxyStoreID, Equals, "foo")
  3268  	c.Check(proxyStoreURL, DeepEquals, fooURL)
  3269  }
  3270  
  3271  const snapYamlContent1 = `name: snap1
  3272  plugs:
  3273   shared-data-plug:
  3274    interface: content
  3275    target: import
  3276    content: mylib
  3277  apps:
  3278   bar:
  3279    command: bin/bar
  3280  `
  3281  const snapYamlContent2 = `name: snap2
  3282  slots:
  3283   shared-data-slot:
  3284    interface: content
  3285    content: mylib
  3286    read:
  3287     - /
  3288  apps:
  3289   bar:
  3290    command: bin/bar
  3291  `
  3292  
  3293  func (s *mgrsSuite) testTwoInstalls(c *C, snapName1, snapYaml1, snapName2, snapYaml2 string) {
  3294  	snapPath1 := makeTestSnap(c, snapYaml1+"version: 1.0")
  3295  	snapPath2 := makeTestSnap(c, snapYaml2+"version: 1.0")
  3296  
  3297  	st := s.o.State()
  3298  	st.Lock()
  3299  	defer st.Unlock()
  3300  
  3301  	ts1, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName1, SnapID: fakeSnapID(snapName1), Revision: snap.R(3)}, snapPath1, "", "", snapstate.Flags{DevMode: true})
  3302  	c.Assert(err, IsNil)
  3303  	chg := st.NewChange("install-snap", "...")
  3304  	chg.AddAll(ts1)
  3305  
  3306  	ts2, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName2, SnapID: fakeSnapID(snapName2), Revision: snap.R(3)}, snapPath2, "", "", snapstate.Flags{DevMode: true})
  3307  	c.Assert(err, IsNil)
  3308  
  3309  	ts2.WaitAll(ts1)
  3310  	chg.AddAll(ts2)
  3311  
  3312  	st.Unlock()
  3313  	err = s.o.Settle(settleTimeout)
  3314  	st.Lock()
  3315  	c.Assert(err, IsNil)
  3316  
  3317  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  3318  
  3319  	tasks := chg.Tasks()
  3320  	connectTask := tasks[len(tasks)-2]
  3321  	c.Assert(connectTask.Kind(), Equals, "connect")
  3322  
  3323  	setupProfilesTask := tasks[len(tasks)-1]
  3324  	c.Assert(setupProfilesTask.Kind(), Equals, "setup-profiles")
  3325  
  3326  	// verify connect task data
  3327  	var plugRef interfaces.PlugRef
  3328  	var slotRef interfaces.SlotRef
  3329  	c.Assert(connectTask.Get("plug", &plugRef), IsNil)
  3330  	c.Assert(connectTask.Get("slot", &slotRef), IsNil)
  3331  	c.Assert(plugRef.Snap, Equals, "snap1")
  3332  	c.Assert(plugRef.Name, Equals, "shared-data-plug")
  3333  	c.Assert(slotRef.Snap, Equals, "snap2")
  3334  	c.Assert(slotRef.Name, Equals, "shared-data-slot")
  3335  
  3336  	// verify that connection was made
  3337  	var conns map[string]interface{}
  3338  	c.Assert(st.Get("conns", &conns), IsNil)
  3339  	c.Assert(conns, HasLen, 1)
  3340  
  3341  	repo := s.o.InterfaceManager().Repository()
  3342  	cn, err := repo.Connected("snap1", "shared-data-plug")
  3343  	c.Assert(err, IsNil)
  3344  	c.Assert(cn, HasLen, 1)
  3345  	c.Assert(cn, DeepEquals, []*interfaces.ConnRef{{
  3346  		PlugRef: interfaces.PlugRef{Snap: "snap1", Name: "shared-data-plug"},
  3347  		SlotRef: interfaces.SlotRef{Snap: "snap2", Name: "shared-data-slot"},
  3348  	}})
  3349  }
  3350  
  3351  func (s *mgrsSuite) TestTwoInstallsWithAutoconnectPlugSnapFirst(c *C) {
  3352  	s.testTwoInstalls(c, "snap1", snapYamlContent1, "snap2", snapYamlContent2)
  3353  }
  3354  
  3355  func (s *mgrsSuite) TestTwoInstallsWithAutoconnectSlotSnapFirst(c *C) {
  3356  	s.testTwoInstalls(c, "snap2", snapYamlContent2, "snap1", snapYamlContent1)
  3357  }
  3358  
  3359  func (s *mgrsSuite) TestRemoveAndInstallWithAutoconnectHappy(c *C) {
  3360  	st := s.o.State()
  3361  	st.Lock()
  3362  	defer st.Unlock()
  3363  
  3364  	_ = s.installLocalTestSnap(c, snapYamlContent1+"version: 1.0")
  3365  
  3366  	ts, err := snapstate.Remove(st, "snap1", snap.R(0), &snapstate.RemoveFlags{Purge: true})
  3367  	c.Assert(err, IsNil)
  3368  	chg := st.NewChange("remove-snap", "...")
  3369  	chg.AddAll(ts)
  3370  
  3371  	snapPath := makeTestSnap(c, snapYamlContent2+"version: 1.0")
  3372  	chg2 := st.NewChange("install-snap", "...")
  3373  	ts2, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "snap2", SnapID: fakeSnapID("snap2"), Revision: snap.R(3)}, snapPath, "", "", snapstate.Flags{DevMode: true})
  3374  	chg2.AddAll(ts2)
  3375  	c.Assert(err, IsNil)
  3376  
  3377  	st.Unlock()
  3378  	err = s.o.Settle(settleTimeout)
  3379  	st.Lock()
  3380  	c.Assert(err, IsNil)
  3381  
  3382  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err()))
  3383  	c.Assert(chg2.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err()))
  3384  }
  3385  
  3386  const otherSnapYaml = `name: other-snap
  3387  version: 1.0
  3388  apps:
  3389     baz:
  3390          command: bin/bar
  3391          plugs: [media-hub]
  3392  `
  3393  
  3394  func (s *mgrsSuite) TestUpdateManyWithAutoconnect(c *C) {
  3395  	const someSnapYaml = `name: some-snap
  3396  version: 1.0
  3397  apps:
  3398     foo:
  3399          command: bin/bar
  3400          plugs: [network,home]
  3401          slots: [media-hub]
  3402  `
  3403  
  3404  	const coreSnapYaml = `name: core
  3405  type: os
  3406  version: @VERSION@`
  3407  
  3408  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  3409  	s.serveSnap(snapPath, "40")
  3410  
  3411  	snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50")
  3412  	s.serveSnap(snapPath, "50")
  3413  
  3414  	corePath, _ := s.makeStoreTestSnap(c, strings.Replace(coreSnapYaml, "@VERSION@", "30", -1), "30")
  3415  	s.serveSnap(corePath, "30")
  3416  
  3417  	mockServer := s.mockStore(c)
  3418  	defer mockServer.Close()
  3419  
  3420  	st := s.o.State()
  3421  	st.Lock()
  3422  	defer st.Unlock()
  3423  
  3424  	st.Set("conns", map[string]interface{}{})
  3425  
  3426  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3427  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  3428  	c.Assert(snapInfo.Plugs, HasLen, 2)
  3429  
  3430  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  3431  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  3432  	c.Assert(otherInfo.Plugs, HasLen, 1)
  3433  
  3434  	csi := &snap.SideInfo{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)}
  3435  	coreInfo := snaptest.MockSnap(c, strings.Replace(coreSnapYaml, "@VERSION@", "1", -1), csi)
  3436  
  3437  	// add implicit slots
  3438  	coreInfo.Slots["network"] = &snap.SlotInfo{
  3439  		Name:      "network",
  3440  		Snap:      coreInfo,
  3441  		Interface: "network",
  3442  	}
  3443  	coreInfo.Slots["home"] = &snap.SlotInfo{
  3444  		Name:      "home",
  3445  		Snap:      coreInfo,
  3446  		Interface: "home",
  3447  	}
  3448  
  3449  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3450  		Active:   true,
  3451  		Sequence: []*snap.SideInfo{si},
  3452  		Current:  snap.R(1),
  3453  		SnapType: "app",
  3454  	})
  3455  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  3456  		Active:   true,
  3457  		Sequence: []*snap.SideInfo{oi},
  3458  		Current:  snap.R(1),
  3459  		SnapType: "app",
  3460  	})
  3461  
  3462  	repo := s.o.InterfaceManager().Repository()
  3463  
  3464  	// add snaps to the repo to have plugs/slots
  3465  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  3466  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  3467  	c.Assert(repo.AddSnap(coreInfo), IsNil)
  3468  
  3469  	// refresh all
  3470  	err := assertstate.RefreshSnapDeclarations(st, 0)
  3471  	c.Assert(err, IsNil)
  3472  
  3473  	updates, tts, err := snapstate.UpdateMany(context.TODO(), st, []string{"core", "some-snap", "other-snap"}, 0, nil)
  3474  	c.Assert(err, IsNil)
  3475  	c.Check(updates, HasLen, 3)
  3476  	c.Assert(tts, HasLen, 4)
  3477  	verifyLastTasksetIsRerefresh(c, tts)
  3478  
  3479  	// to make TaskSnapSetup work
  3480  	chg := st.NewChange("refresh", "...")
  3481  	for _, ts := range tts[:len(tts)-1] {
  3482  		chg.AddAll(ts)
  3483  	}
  3484  
  3485  	// force hold state to hit ignore status of findSymmetricAutoconnect
  3486  	tts[2].Tasks()[0].SetStatus(state.HoldStatus)
  3487  
  3488  	st.Unlock()
  3489  	err = s.o.Settle(3 * time.Second)
  3490  	st.Lock()
  3491  	c.Assert(err, IsNil)
  3492  
  3493  	// simulate successful restart happened
  3494  	state.MockRestarting(st, state.RestartUnset)
  3495  	tts[2].Tasks()[0].SetStatus(state.DefaultStatus)
  3496  	st.Unlock()
  3497  
  3498  	err = s.o.Settle(settleTimeout)
  3499  	st.Lock()
  3500  
  3501  	c.Assert(err, IsNil)
  3502  
  3503  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  3504  
  3505  	// check connections
  3506  	var conns map[string]interface{}
  3507  	st.Get("conns", &conns)
  3508  	c.Assert(conns, DeepEquals, map[string]interface{}{
  3509  		"some-snap:home core:home":                 map[string]interface{}{"interface": "home", "auto": true},
  3510  		"some-snap:network core:network":           map[string]interface{}{"interface": "network", "auto": true},
  3511  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": true},
  3512  	})
  3513  
  3514  	connections, err := repo.Connections("some-snap")
  3515  	c.Assert(err, IsNil)
  3516  	c.Assert(connections, HasLen, 3)
  3517  }
  3518  
  3519  func (s *mgrsSuite) TestUpdateWithAutoconnectAndInactiveRevisions(c *C) {
  3520  	const someSnapYaml = `name: some-snap
  3521  version: 1.0
  3522  apps:
  3523     foo:
  3524          command: bin/bar
  3525          plugs: [network]
  3526  `
  3527  	const coreSnapYaml = `name: core
  3528  type: os
  3529  version: 1`
  3530  
  3531  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  3532  	s.serveSnap(snapPath, "40")
  3533  
  3534  	mockServer := s.mockStore(c)
  3535  	defer mockServer.Close()
  3536  
  3537  	st := s.o.State()
  3538  	st.Lock()
  3539  	defer st.Unlock()
  3540  
  3541  	si1 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3542  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si1)
  3543  	c.Assert(snapInfo.Plugs, HasLen, 1)
  3544  
  3545  	csi := &snap.SideInfo{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)}
  3546  	coreInfo := snaptest.MockSnap(c, coreSnapYaml, csi)
  3547  
  3548  	// add implicit slots
  3549  	coreInfo.Slots["network"] = &snap.SlotInfo{
  3550  		Name:      "network",
  3551  		Snap:      coreInfo,
  3552  		Interface: "network",
  3553  	}
  3554  
  3555  	// some-snap has inactive revisions
  3556  	si0 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(0)}
  3557  	si2 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(2)}
  3558  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3559  		Active:   true,
  3560  		Sequence: []*snap.SideInfo{si0, si1, si2},
  3561  		Current:  snap.R(1),
  3562  		SnapType: "app",
  3563  	})
  3564  
  3565  	repo := s.o.InterfaceManager().Repository()
  3566  
  3567  	// add snaps to the repo to have plugs/slots
  3568  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  3569  	c.Assert(repo.AddSnap(coreInfo), IsNil)
  3570  
  3571  	// refresh all
  3572  	err := assertstate.RefreshSnapDeclarations(st, 0)
  3573  	c.Assert(err, IsNil)
  3574  
  3575  	updates, tts, err := snapstate.UpdateMany(context.TODO(), st, []string{"some-snap"}, 0, nil)
  3576  	c.Assert(err, IsNil)
  3577  	c.Check(updates, HasLen, 1)
  3578  	c.Assert(tts, HasLen, 2)
  3579  	verifyLastTasksetIsRerefresh(c, tts)
  3580  
  3581  	// to make TaskSnapSetup work
  3582  	chg := st.NewChange("refresh", "...")
  3583  	chg.AddAll(tts[0])
  3584  
  3585  	st.Unlock()
  3586  	err = s.o.Settle(settleTimeout)
  3587  	st.Lock()
  3588  
  3589  	c.Assert(err, IsNil)
  3590  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  3591  
  3592  	// check connections
  3593  	var conns map[string]interface{}
  3594  	st.Get("conns", &conns)
  3595  	c.Assert(conns, DeepEquals, map[string]interface{}{
  3596  		"some-snap:network core:network": map[string]interface{}{"interface": "network", "auto": true},
  3597  	})
  3598  }
  3599  
  3600  const someSnapYaml = `name: some-snap
  3601  version: 1.0
  3602  apps:
  3603     foo:
  3604          command: bin/bar
  3605          slots: [media-hub]
  3606  `
  3607  
  3608  func (s *mgrsSuite) testUpdateWithAutoconnectRetry(c *C, updateSnapName, removeSnapName string) {
  3609  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  3610  	s.serveSnap(snapPath, "40")
  3611  
  3612  	snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50")
  3613  	s.serveSnap(snapPath, "50")
  3614  
  3615  	mockServer := s.mockStore(c)
  3616  	defer mockServer.Close()
  3617  
  3618  	st := s.o.State()
  3619  	st.Lock()
  3620  	defer st.Unlock()
  3621  
  3622  	st.Set("conns", map[string]interface{}{})
  3623  
  3624  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3625  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  3626  	c.Assert(snapInfo.Slots, HasLen, 1)
  3627  
  3628  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  3629  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  3630  	c.Assert(otherInfo.Plugs, HasLen, 1)
  3631  
  3632  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3633  		Active:   true,
  3634  		Sequence: []*snap.SideInfo{si},
  3635  		Current:  snap.R(1),
  3636  		SnapType: "app",
  3637  	})
  3638  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  3639  		Active:   true,
  3640  		Sequence: []*snap.SideInfo{oi},
  3641  		Current:  snap.R(1),
  3642  		SnapType: "app",
  3643  	})
  3644  
  3645  	repo := s.o.InterfaceManager().Repository()
  3646  
  3647  	// add snaps to the repo to have plugs/slots
  3648  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  3649  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  3650  
  3651  	// refresh all
  3652  	err := assertstate.RefreshSnapDeclarations(st, 0)
  3653  	c.Assert(err, IsNil)
  3654  
  3655  	ts, err := snapstate.Update(st, updateSnapName, nil, 0, snapstate.Flags{})
  3656  	c.Assert(err, IsNil)
  3657  
  3658  	// to make TaskSnapSetup work
  3659  	chg := st.NewChange("refresh", "...")
  3660  	chg.AddAll(ts)
  3661  
  3662  	// remove other-snap
  3663  	ts2, err := snapstate.Remove(st, removeSnapName, snap.R(0), &snapstate.RemoveFlags{Purge: true})
  3664  	c.Assert(err, IsNil)
  3665  	chg2 := st.NewChange("remove-snap", "...")
  3666  	chg2.AddAll(ts2)
  3667  
  3668  	// force hold state on first removal task to hit Retry error
  3669  	ts2.Tasks()[0].SetStatus(state.HoldStatus)
  3670  
  3671  	// Settle is not converging here because of the task in Hold status, therefore
  3672  	// it always hits given timeout before we carry on with the test. We're
  3673  	// interested in hitting the retry condition on auto-connect task, so
  3674  	// instead of passing a generous timeout to Settle(), repeat Settle() a number
  3675  	// of times with an aggressive timeout and break as soon as we reach the desired
  3676  	// state of auto-connect task.
  3677  	var retryCheck bool
  3678  	var autoconnectLog string
  3679  	for i := 0; i < 50 && !retryCheck; i++ {
  3680  		st.Unlock()
  3681  		s.o.Settle(aggressiveSettleTimeout)
  3682  		st.Lock()
  3683  
  3684  		for _, t := range st.Tasks() {
  3685  			if t.Kind() == "auto-connect" && t.Status() == state.DoingStatus && strings.Contains(strings.Join(t.Log(), ""), "Waiting") {
  3686  				autoconnectLog = strings.Join(t.Log(), "")
  3687  				retryCheck = true
  3688  				break
  3689  			}
  3690  		}
  3691  	}
  3692  
  3693  	c.Check(retryCheck, Equals, true)
  3694  	c.Assert(autoconnectLog, Matches, `.*Waiting for conflicting change in progress: conflicting snap.*`)
  3695  
  3696  	// back to default state, that will unblock autoconnect
  3697  	ts2.Tasks()[0].SetStatus(state.DefaultStatus)
  3698  	st.Unlock()
  3699  	err = s.o.Settle(settleTimeout)
  3700  	st.Lock()
  3701  	c.Assert(err, IsNil)
  3702  
  3703  	c.Check(chg.Err(), IsNil)
  3704  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  3705  
  3706  	// check connections
  3707  	var conns map[string]interface{}
  3708  	st.Get("conns", &conns)
  3709  	c.Assert(conns, HasLen, 0)
  3710  }
  3711  
  3712  func (s *mgrsSuite) TestUpdateWithAutoconnectRetrySlotSide(c *C) {
  3713  	s.testUpdateWithAutoconnectRetry(c, "some-snap", "other-snap")
  3714  }
  3715  
  3716  func (s *mgrsSuite) TestUpdateWithAutoconnectRetryPlugSide(c *C) {
  3717  	s.testUpdateWithAutoconnectRetry(c, "other-snap", "some-snap")
  3718  }
  3719  
  3720  func (s *mgrsSuite) TestDisconnectIgnoredOnSymmetricRemove(c *C) {
  3721  	const someSnapYaml = `name: some-snap
  3722  version: 1.0
  3723  apps:
  3724     foo:
  3725          command: bin/bar
  3726          slots: [media-hub]
  3727  hooks:
  3728     disconnect-slot-media-hub:
  3729  `
  3730  	const otherSnapYaml = `name: other-snap
  3731  version: 1.0
  3732  apps:
  3733     baz:
  3734          command: bin/bar
  3735          plugs: [media-hub]
  3736  hooks:
  3737     disconnect-plug-media-hub:
  3738  `
  3739  	st := s.o.State()
  3740  	st.Lock()
  3741  	defer st.Unlock()
  3742  
  3743  	st.Set("conns", map[string]interface{}{
  3744  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": false},
  3745  	})
  3746  
  3747  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3748  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  3749  	c.Assert(snapInfo.Slots, HasLen, 1)
  3750  
  3751  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  3752  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  3753  	c.Assert(otherInfo.Plugs, HasLen, 1)
  3754  
  3755  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3756  		Active:   true,
  3757  		Sequence: []*snap.SideInfo{si},
  3758  		Current:  snap.R(1),
  3759  		SnapType: "app",
  3760  	})
  3761  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  3762  		Active:   true,
  3763  		Sequence: []*snap.SideInfo{oi},
  3764  		Current:  snap.R(1),
  3765  		SnapType: "app",
  3766  	})
  3767  
  3768  	repo := s.o.InterfaceManager().Repository()
  3769  
  3770  	// add snaps to the repo to have plugs/slots
  3771  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  3772  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  3773  	repo.Connect(&interfaces.ConnRef{
  3774  		PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"},
  3775  		SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"},
  3776  	}, nil, nil, nil, nil, nil)
  3777  
  3778  	flags := &snapstate.RemoveFlags{Purge: true}
  3779  	ts, err := snapstate.Remove(st, "some-snap", snap.R(0), flags)
  3780  	c.Assert(err, IsNil)
  3781  	chg := st.NewChange("uninstall", "...")
  3782  	chg.AddAll(ts)
  3783  
  3784  	// remove other-snap
  3785  	ts2, err := snapstate.Remove(st, "other-snap", snap.R(0), flags)
  3786  	c.Assert(err, IsNil)
  3787  	chg2 := st.NewChange("uninstall", "...")
  3788  	chg2.AddAll(ts2)
  3789  
  3790  	st.Unlock()
  3791  	err = s.o.Settle(settleTimeout)
  3792  	st.Lock()
  3793  	c.Assert(err, IsNil)
  3794  
  3795  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  3796  
  3797  	// check connections
  3798  	var conns map[string]interface{}
  3799  	st.Get("conns", &conns)
  3800  	c.Assert(conns, HasLen, 0)
  3801  
  3802  	var disconnectInterfacesCount, slotHookCount, plugHookCount int
  3803  	for _, t := range st.Tasks() {
  3804  		if t.Kind() == "auto-disconnect" {
  3805  			disconnectInterfacesCount++
  3806  		}
  3807  		if t.Kind() == "run-hook" {
  3808  			var hsup hookstate.HookSetup
  3809  			c.Assert(t.Get("hook-setup", &hsup), IsNil)
  3810  			if hsup.Hook == "disconnect-plug-media-hub" {
  3811  				plugHookCount++
  3812  			}
  3813  			if hsup.Hook == "disconnect-slot-media-hub" {
  3814  				slotHookCount++
  3815  			}
  3816  		}
  3817  	}
  3818  	c.Assert(plugHookCount, Equals, 1)
  3819  	c.Assert(slotHookCount, Equals, 1)
  3820  	c.Assert(disconnectInterfacesCount, Equals, 2)
  3821  
  3822  	var snst snapstate.SnapState
  3823  	err = snapstate.Get(st, "other-snap", &snst)
  3824  	c.Assert(err, Equals, state.ErrNoState)
  3825  	_, err = repo.Connected("other-snap", "media-hub")
  3826  	c.Assert(err, ErrorMatches, `snap "other-snap" has no plug or slot named "media-hub"`)
  3827  }
  3828  
  3829  func (s *mgrsSuite) TestDisconnectOnUninstallRemovesAutoconnection(c *C) {
  3830  	st := s.o.State()
  3831  	st.Lock()
  3832  	defer st.Unlock()
  3833  
  3834  	st.Set("conns", map[string]interface{}{
  3835  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": true},
  3836  	})
  3837  
  3838  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  3839  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  3840  
  3841  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  3842  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  3843  
  3844  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  3845  		Active:   true,
  3846  		Sequence: []*snap.SideInfo{si},
  3847  		Current:  snap.R(1),
  3848  		SnapType: "app",
  3849  	})
  3850  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  3851  		Active:   true,
  3852  		Sequence: []*snap.SideInfo{oi},
  3853  		Current:  snap.R(1),
  3854  		SnapType: "app",
  3855  	})
  3856  
  3857  	repo := s.o.InterfaceManager().Repository()
  3858  
  3859  	// add snaps to the repo to have plugs/slots
  3860  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  3861  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  3862  	repo.Connect(&interfaces.ConnRef{
  3863  		PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"},
  3864  		SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"},
  3865  	}, nil, nil, nil, nil, nil)
  3866  
  3867  	ts, err := snapstate.Remove(st, "some-snap", snap.R(0), &snapstate.RemoveFlags{Purge: true})
  3868  	c.Assert(err, IsNil)
  3869  	chg := st.NewChange("uninstall", "...")
  3870  	chg.AddAll(ts)
  3871  
  3872  	st.Unlock()
  3873  	err = s.o.Settle(settleTimeout)
  3874  	st.Lock()
  3875  	c.Assert(err, IsNil)
  3876  
  3877  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  3878  
  3879  	// check connections; auto-connection should be removed completely from conns on uninstall.
  3880  	var conns map[string]interface{}
  3881  	st.Get("conns", &conns)
  3882  	c.Assert(conns, HasLen, 0)
  3883  }
  3884  
  3885  // TODO: add a custom checker in testutils for this and similar
  3886  func validateDownloadCheckTasks(c *C, tasks []*state.Task, name, revno, channel string) int {
  3887  	var i int
  3888  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Ensure prerequisites for "%s" are available`, name))
  3889  	i++
  3890  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Download snap "%s" (%s) from channel "%s"`, name, revno, channel))
  3891  	i++
  3892  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Fetch and check assertions for snap "%s" (%s)`, name, revno))
  3893  	i++
  3894  	return i
  3895  }
  3896  
  3897  const (
  3898  	noConfigure = 1 << iota
  3899  	isGadget
  3900  )
  3901  
  3902  func validateInstallTasks(c *C, tasks []*state.Task, name, revno string, flags int) int {
  3903  	var i int
  3904  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Mount snap "%s" (%s)`, name, revno))
  3905  	i++
  3906  	if flags&isGadget != 0 {
  3907  		c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Update assets from gadget "%s" (%s)`, name, revno))
  3908  		i++
  3909  	}
  3910  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Copy snap "%s" data`, name))
  3911  	i++
  3912  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" (%s) security profiles`, name, revno))
  3913  	i++
  3914  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make snap "%s" (%s) available to the system`, name, revno))
  3915  	i++
  3916  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Automatically connect eligible plugs and slots of snap "%s"`, name))
  3917  	i++
  3918  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Set automatic aliases for snap "%s"`, name))
  3919  	i++
  3920  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" aliases`, name))
  3921  	i++
  3922  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run install hook of "%s" snap if present`, name))
  3923  	i++
  3924  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Start snap "%s" (%s) services`, name, revno))
  3925  	i++
  3926  	if flags&noConfigure == 0 {
  3927  		c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run configure hook of "%s" snap if present`, name))
  3928  		i++
  3929  	}
  3930  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run health check of "%s" snap`, name))
  3931  	i++
  3932  	return i
  3933  }
  3934  
  3935  func validateRefreshTasks(c *C, tasks []*state.Task, name, revno string, flags int) int {
  3936  	var i int
  3937  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Mount snap "%s" (%s)`, name, revno))
  3938  	i++
  3939  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run pre-refresh hook of "%s" snap if present`, name))
  3940  	i++
  3941  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Stop snap "%s" services`, name))
  3942  	i++
  3943  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Remove aliases for snap "%s"`, name))
  3944  	i++
  3945  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make current revision for snap "%s" unavailable`, name))
  3946  	i++
  3947  	if flags&isGadget != 0 {
  3948  		c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Update assets from gadget %q (%s)`, name, revno))
  3949  		i++
  3950  
  3951  	}
  3952  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Copy snap "%s" data`, name))
  3953  	i++
  3954  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" (%s) security profiles`, name, revno))
  3955  	i++
  3956  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make snap "%s" (%s) available to the system`, name, revno))
  3957  	i++
  3958  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Automatically connect eligible plugs and slots of snap "%s"`, name))
  3959  	i++
  3960  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Set automatic aliases for snap "%s"`, name))
  3961  	i++
  3962  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" aliases`, name))
  3963  	i++
  3964  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run post-refresh hook of "%s" snap if present`, name))
  3965  	i++
  3966  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Start snap "%s" (%s) services`, name, revno))
  3967  	i++
  3968  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Clean up "%s" (%s) install`, name, revno))
  3969  	i++
  3970  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run configure hook of "%s" snap if present`, name))
  3971  	i++
  3972  	c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run health check of "%s" snap`, name))
  3973  	i++
  3974  	return i
  3975  }
  3976  
  3977  // byReadyTime sorts a list of tasks by their "ready" time
  3978  type byReadyTime []*state.Task
  3979  
  3980  func (a byReadyTime) Len() int           { return len(a) }
  3981  func (a byReadyTime) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
  3982  func (a byReadyTime) Less(i, j int) bool { return a[i].ReadyTime().Before(a[j].ReadyTime()) }
  3983  
  3984  func (s *mgrsSuite) TestRemodelRequiredSnapsAdded(c *C) {
  3985  	for _, name := range []string{"foo", "bar", "baz"} {
  3986  		s.prereqSnapAssertions(c, map[string]interface{}{
  3987  			"snap-name": name,
  3988  		})
  3989  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", name), "1")
  3990  		s.serveSnap(snapPath, "1")
  3991  	}
  3992  
  3993  	mockServer := s.mockStore(c)
  3994  	defer mockServer.Close()
  3995  
  3996  	st := s.o.State()
  3997  	st.Lock()
  3998  	defer st.Unlock()
  3999  
  4000  	// pretend we have an old required snap installed
  4001  	si1 := &snap.SideInfo{RealName: "old-required-snap-1", Revision: snap.R(1)}
  4002  	snapstate.Set(st, "old-required-snap-1", &snapstate.SnapState{
  4003  		SnapType: "app",
  4004  		Active:   true,
  4005  		Sequence: []*snap.SideInfo{si1},
  4006  		Current:  si1.Revision,
  4007  		Flags:    snapstate.Flags{Required: true},
  4008  	})
  4009  
  4010  	// create/set custom model assertion
  4011  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  4012  
  4013  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  4014  
  4015  	// setup model assertion
  4016  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4017  		Brand:  "my-brand",
  4018  		Model:  "my-model",
  4019  		Serial: "serialserialserial",
  4020  	})
  4021  	err := assertstate.Add(st, model)
  4022  	c.Assert(err, IsNil)
  4023  
  4024  	// create a new model
  4025  	newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  4026  		"required-snaps": []interface{}{"foo", "bar", "baz"},
  4027  		"revision":       "1",
  4028  	})
  4029  
  4030  	chg, err := devicestate.Remodel(st, newModel)
  4031  	c.Assert(err, IsNil)
  4032  
  4033  	c.Check(devicestate.Remodeling(st), Equals, true)
  4034  
  4035  	st.Unlock()
  4036  	err = s.o.Settle(settleTimeout)
  4037  	st.Lock()
  4038  	c.Assert(err, IsNil)
  4039  
  4040  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  4041  
  4042  	c.Check(devicestate.Remodeling(st), Equals, false)
  4043  
  4044  	// the new required-snap "foo" is installed
  4045  	var snapst snapstate.SnapState
  4046  	err = snapstate.Get(st, "foo", &snapst)
  4047  	c.Assert(err, IsNil)
  4048  	info, err := snapst.CurrentInfo()
  4049  	c.Assert(err, IsNil)
  4050  	c.Check(info.Revision, Equals, snap.R(1))
  4051  	c.Check(info.Version, Equals, "1.0")
  4052  
  4053  	// and marked required
  4054  	c.Check(snapst.Required, Equals, true)
  4055  
  4056  	// and core is still marked required
  4057  	err = snapstate.Get(st, "core", &snapst)
  4058  	c.Assert(err, IsNil)
  4059  	c.Check(snapst.Required, Equals, true)
  4060  
  4061  	// but old-required-snap-1 is no longer marked required
  4062  	err = snapstate.Get(st, "old-required-snap-1", &snapst)
  4063  	c.Assert(err, IsNil)
  4064  	c.Check(snapst.Required, Equals, false)
  4065  
  4066  	// ensure sorting is correct
  4067  	tasks := chg.Tasks()
  4068  	sort.Sort(byReadyTime(tasks))
  4069  
  4070  	var i int
  4071  	// first all downloads/checks in sequential order
  4072  	for _, name := range []string{"foo", "bar", "baz"} {
  4073  		i += validateDownloadCheckTasks(c, tasks[i:], name, "1", "stable")
  4074  	}
  4075  	// then all installs in sequential order
  4076  	for _, name := range []string{"foo", "bar", "baz"} {
  4077  		i += validateInstallTasks(c, tasks[i:], name, "1", 0)
  4078  	}
  4079  	// ensure that we only have the tasks we checked (plus the one
  4080  	// extra "set-model" task)
  4081  	c.Assert(tasks, HasLen, i+1)
  4082  }
  4083  
  4084  func (s *mgrsSuite) TestRemodelRequiredSnapsAddedUndo(c *C) {
  4085  	for _, name := range []string{"foo", "bar", "baz"} {
  4086  		s.prereqSnapAssertions(c, map[string]interface{}{
  4087  			"snap-name": name,
  4088  		})
  4089  		snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", name), "1")
  4090  		s.serveSnap(snapPath, "1")
  4091  	}
  4092  
  4093  	mockServer := s.mockStore(c)
  4094  	defer mockServer.Close()
  4095  
  4096  	st := s.o.State()
  4097  	st.Lock()
  4098  	defer st.Unlock()
  4099  
  4100  	// pretend we have an old required snap installed
  4101  	si1 := &snap.SideInfo{RealName: "old-required-snap-1", Revision: snap.R(1)}
  4102  	snapstate.Set(st, "old-required-snap-1", &snapstate.SnapState{
  4103  		SnapType: "app",
  4104  		Active:   true,
  4105  		Sequence: []*snap.SideInfo{si1},
  4106  		Current:  si1.Revision,
  4107  		Flags:    snapstate.Flags{Required: true},
  4108  	})
  4109  
  4110  	// create/set custom model assertion
  4111  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  4112  	curModel := s.brands.Model("my-brand", "my-model", modelDefaults)
  4113  
  4114  	// setup model assertion
  4115  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4116  		Brand:  "my-brand",
  4117  		Model:  "my-model",
  4118  		Serial: "serialserialserial",
  4119  	})
  4120  	err := assertstate.Add(st, curModel)
  4121  	c.Assert(err, IsNil)
  4122  
  4123  	// create a new model
  4124  	newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  4125  		"required-snaps": []interface{}{"foo", "bar", "baz"},
  4126  		"revision":       "1",
  4127  	})
  4128  
  4129  	devicestate.InjectSetModelError(fmt.Errorf("boom"))
  4130  	defer devicestate.InjectSetModelError(nil)
  4131  
  4132  	chg, err := devicestate.Remodel(st, newModel)
  4133  	c.Assert(err, IsNil)
  4134  
  4135  	st.Unlock()
  4136  	err = s.o.Settle(settleTimeout)
  4137  	st.Lock()
  4138  	c.Assert(err, IsNil)
  4139  
  4140  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  4141  
  4142  	// None of the new snaps got installed
  4143  	var snapst snapstate.SnapState
  4144  	for _, snapName := range []string{"foo", "bar", "baz"} {
  4145  		err = snapstate.Get(st, snapName, &snapst)
  4146  		c.Assert(err, Equals, state.ErrNoState)
  4147  	}
  4148  
  4149  	// old-required-snap-1 is still marked required
  4150  	err = snapstate.Get(st, "old-required-snap-1", &snapst)
  4151  	c.Assert(err, IsNil)
  4152  	c.Check(snapst.Required, Equals, true)
  4153  
  4154  	// check tasks are in undo state
  4155  	for _, t := range chg.Tasks() {
  4156  		if t.Kind() == "link-snap" {
  4157  			c.Assert(t.Status(), Equals, state.UndoneStatus)
  4158  		}
  4159  	}
  4160  
  4161  	model, err := s.o.DeviceManager().Model()
  4162  	c.Assert(err, IsNil)
  4163  	c.Assert(model, DeepEquals, curModel)
  4164  }
  4165  
  4166  func (s *mgrsSuite) TestRemodelDifferentBase(c *C) {
  4167  	// make "core18" snap available in the store
  4168  	s.prereqSnapAssertions(c, map[string]interface{}{
  4169  		"snap-name": "core18",
  4170  	})
  4171  	snapYamlContent := `name: core18
  4172  version: 18.04
  4173  type: base`
  4174  	snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "18")
  4175  	s.serveSnap(snapPath, "18")
  4176  
  4177  	mockServer := s.mockStore(c)
  4178  	defer mockServer.Close()
  4179  
  4180  	st := s.o.State()
  4181  	st.Lock()
  4182  	defer st.Unlock()
  4183  
  4184  	// create/set custom model assertion
  4185  	model := s.brands.Model("can0nical", "my-model", modelDefaults)
  4186  	// setup model assertion
  4187  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4188  		Brand:  "can0nical",
  4189  		Model:  "my-model",
  4190  		Serial: "serialserialserial",
  4191  	})
  4192  	err := assertstate.Add(st, model)
  4193  	c.Assert(err, IsNil)
  4194  
  4195  	// create a new model
  4196  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4197  		"base":     "core18",
  4198  		"revision": "1",
  4199  	})
  4200  
  4201  	chg, err := devicestate.Remodel(st, newModel)
  4202  	c.Assert(err, ErrorMatches, "cannot remodel from core to bases yet")
  4203  	c.Assert(chg, IsNil)
  4204  }
  4205  
  4206  func (ms *mgrsSuite) TestRemodelSwitchToDifferentBase(c *C) {
  4207  	bloader := bootloadertest.Mock("mock", c.MkDir())
  4208  	bootloader.Force(bloader)
  4209  	defer bootloader.Force(nil)
  4210  	bloader.SetBootVars(map[string]string{
  4211  		"snap_mode":   boot.DefaultStatus,
  4212  		"snap_core":   "core18_1.snap",
  4213  		"snap_kernel": "pc-kernel_1.snap",
  4214  	})
  4215  
  4216  	restore := release.MockOnClassic(false)
  4217  	defer restore()
  4218  
  4219  	mockServer := ms.mockStore(c)
  4220  	defer mockServer.Close()
  4221  
  4222  	st := ms.o.State()
  4223  	st.Lock()
  4224  	defer st.Unlock()
  4225  
  4226  	si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)}
  4227  	snapstate.Set(st, "core18", &snapstate.SnapState{
  4228  		Active:   true,
  4229  		Sequence: []*snap.SideInfo{si},
  4230  		Current:  snap.R(1),
  4231  		SnapType: "base",
  4232  	})
  4233  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  4234  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  4235  	snapstate.Set(st, "pc", &snapstate.SnapState{
  4236  		Active:   true,
  4237  		Sequence: []*snap.SideInfo{si2},
  4238  		Current:  snap.R(1),
  4239  		SnapType: "gadget",
  4240  	})
  4241  	gadgetYaml := `
  4242  volumes:
  4243      volume-id:
  4244          bootloader: grub
  4245  `
  4246  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  4247  		{"meta/gadget.yaml", gadgetYaml},
  4248  	})
  4249  
  4250  	// add "core20" snap to fake store
  4251  	const core20Yaml = `name: core20
  4252  type: base
  4253  version: 20.04`
  4254  	ms.prereqSnapAssertions(c, map[string]interface{}{
  4255  		"snap-name":    "core20",
  4256  		"publisher-id": "can0nical",
  4257  	})
  4258  	snapPath, _ := ms.makeStoreTestSnap(c, core20Yaml, "2")
  4259  	ms.serveSnap(snapPath, "2")
  4260  
  4261  	// add "foo" snap to fake store
  4262  	ms.prereqSnapAssertions(c, map[string]interface{}{
  4263  		"snap-name": "foo",
  4264  	})
  4265  	snapPath, _ = ms.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1")
  4266  	ms.serveSnap(snapPath, "1")
  4267  
  4268  	// create/set custom model assertion
  4269  	model := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4270  		"base": "core18",
  4271  	})
  4272  
  4273  	// setup model assertion
  4274  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4275  		Brand:  "can0nical",
  4276  		Model:  "my-model",
  4277  		Serial: "serialserialserial",
  4278  	})
  4279  	err := assertstate.Add(st, model)
  4280  	c.Assert(err, IsNil)
  4281  
  4282  	// create a new model
  4283  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4284  		"base":           "core20",
  4285  		"revision":       "1",
  4286  		"required-snaps": []interface{}{"foo"},
  4287  	})
  4288  
  4289  	chg, err := devicestate.Remodel(st, newModel)
  4290  	c.Assert(err, IsNil)
  4291  
  4292  	st.Unlock()
  4293  	err = ms.o.Settle(settleTimeout)
  4294  	st.Lock()
  4295  	c.Assert(err, IsNil)
  4296  	c.Assert(chg.Err(), IsNil)
  4297  
  4298  	// system waits for a restart because of the new base
  4299  	t := findKind(chg, "auto-connect")
  4300  	c.Assert(t, NotNil)
  4301  	c.Assert(t.Status(), Equals, state.DoingStatus)
  4302  
  4303  	// check that the boot vars got updated as expected
  4304  	bvars, err := bloader.GetBootVars("snap_mode", "snap_core", "snap_try_core", "snap_kernel", "snap_try_kernel")
  4305  	c.Assert(err, IsNil)
  4306  	c.Assert(bvars, DeepEquals, map[string]string{
  4307  		"snap_mode":       boot.TryStatus,
  4308  		"snap_core":       "core18_1.snap",
  4309  		"snap_try_core":   "core20_2.snap",
  4310  		"snap_kernel":     "pc-kernel_1.snap",
  4311  		"snap_try_kernel": "",
  4312  	})
  4313  
  4314  	// simulate successful restart happened and that the bootvars
  4315  	// got updated
  4316  	state.MockRestarting(st, state.RestartUnset)
  4317  	bloader.SetBootVars(map[string]string{
  4318  		"snap_mode":   boot.DefaultStatus,
  4319  		"snap_core":   "core20_2.snap",
  4320  		"snap_kernel": "pc-kernel_1.snap",
  4321  	})
  4322  
  4323  	// continue
  4324  	st.Unlock()
  4325  	err = ms.o.Settle(settleTimeout)
  4326  	st.Lock()
  4327  	c.Assert(err, IsNil)
  4328  
  4329  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  4330  
  4331  	// ensure tasks were run in the right order
  4332  	tasks := chg.Tasks()
  4333  	sort.Sort(byReadyTime(tasks))
  4334  
  4335  	// first all downloads/checks in sequential order
  4336  	var i int
  4337  	i += validateDownloadCheckTasks(c, tasks[i:], "core20", "2", "stable")
  4338  	i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable")
  4339  
  4340  	// then all installs in sequential order
  4341  	i += validateInstallTasks(c, tasks[i:], "core20", "2", noConfigure)
  4342  	i += validateInstallTasks(c, tasks[i:], "foo", "1", 0)
  4343  
  4344  	// ensure that we only have the tasks we checked (plus the one
  4345  	// extra "set-model" task)
  4346  	c.Assert(tasks, HasLen, i+1)
  4347  }
  4348  
  4349  func (ms *mgrsSuite) TestRemodelSwitchToDifferentBaseUndo(c *C) {
  4350  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  4351  	bootloader.Force(bloader)
  4352  	defer bootloader.Force(nil)
  4353  	bloader.SetBootVars(map[string]string{
  4354  		"snap_mode":   boot.DefaultStatus,
  4355  		"snap_core":   "core18_1.snap",
  4356  		"snap_kernel": "pc-kernel_1.snap",
  4357  	})
  4358  
  4359  	restore := release.MockOnClassic(false)
  4360  	defer restore()
  4361  
  4362  	mockServer := ms.mockStore(c)
  4363  	defer mockServer.Close()
  4364  
  4365  	st := ms.o.State()
  4366  	st.Lock()
  4367  	defer st.Unlock()
  4368  
  4369  	si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)}
  4370  	snapstate.Set(st, "core18", &snapstate.SnapState{
  4371  		Active:   true,
  4372  		Sequence: []*snap.SideInfo{si},
  4373  		Current:  snap.R(1),
  4374  		SnapType: "base",
  4375  	})
  4376  	snaptest.MockSnapWithFiles(c, "name: core18\ntype: base\nversion: 1.0", si, nil)
  4377  
  4378  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  4379  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  4380  	snapstate.Set(st, "pc", &snapstate.SnapState{
  4381  		Active:   true,
  4382  		Sequence: []*snap.SideInfo{si2},
  4383  		Current:  snap.R(1),
  4384  		SnapType: "gadget",
  4385  	})
  4386  	gadgetYaml := `
  4387  volumes:
  4388      volume-id:
  4389          bootloader: grub
  4390  `
  4391  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  4392  		{"meta/gadget.yaml", gadgetYaml},
  4393  	})
  4394  
  4395  	// add "core20" snap to fake store
  4396  	const core20Yaml = `name: core20
  4397  type: base
  4398  version: 20.04`
  4399  	ms.prereqSnapAssertions(c, map[string]interface{}{
  4400  		"snap-name":    "core20",
  4401  		"publisher-id": "can0nical",
  4402  	})
  4403  	snapPath, _ := ms.makeStoreTestSnap(c, core20Yaml, "2")
  4404  	ms.serveSnap(snapPath, "2")
  4405  
  4406  	// add "foo" snap to fake store
  4407  	ms.prereqSnapAssertions(c, map[string]interface{}{
  4408  		"snap-name": "foo",
  4409  	})
  4410  	snapPath, _ = ms.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1")
  4411  	ms.serveSnap(snapPath, "1")
  4412  
  4413  	// create/set custom model assertion
  4414  	model := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4415  		"base": "core18",
  4416  	})
  4417  
  4418  	// setup model assertion
  4419  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4420  		Brand:  "can0nical",
  4421  		Model:  "my-model",
  4422  		Serial: "serialserialserial",
  4423  	})
  4424  	err := assertstate.Add(st, model)
  4425  	c.Assert(err, IsNil)
  4426  
  4427  	// create a new model
  4428  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4429  		"base":           "core20",
  4430  		"revision":       "1",
  4431  		"required-snaps": []interface{}{"foo"},
  4432  	})
  4433  
  4434  	devicestate.InjectSetModelError(fmt.Errorf("boom"))
  4435  	defer devicestate.InjectSetModelError(nil)
  4436  
  4437  	chg, err := devicestate.Remodel(st, newModel)
  4438  	c.Assert(err, IsNil)
  4439  
  4440  	st.Unlock()
  4441  	err = ms.o.Settle(settleTimeout)
  4442  	st.Lock()
  4443  	c.Assert(err, IsNil)
  4444  	c.Assert(chg.Err(), IsNil)
  4445  
  4446  	// system waits for a restart because of the new base
  4447  	t := findKind(chg, "auto-connect")
  4448  	c.Assert(t, NotNil)
  4449  	c.Assert(t.Status(), Equals, state.DoingStatus)
  4450  
  4451  	// check that the boot vars got updated as expected
  4452  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4453  		"snap_mode":       boot.TryStatus,
  4454  		"snap_core":       "core18_1.snap",
  4455  		"snap_try_core":   "core20_2.snap",
  4456  		"snap_kernel":     "pc-kernel_1.snap",
  4457  		"snap_try_kernel": "",
  4458  	})
  4459  	// simulate successful restart happened
  4460  	ms.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeBase})
  4461  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4462  		"snap_mode":       boot.DefaultStatus,
  4463  		"snap_core":       "core20_2.snap",
  4464  		"snap_try_core":   "",
  4465  		"snap_kernel":     "pc-kernel_1.snap",
  4466  		"snap_try_kernel": "",
  4467  	})
  4468  
  4469  	// continue
  4470  	st.Unlock()
  4471  	err = ms.o.Settle(settleTimeout)
  4472  	st.Lock()
  4473  	c.Assert(err, IsNil)
  4474  
  4475  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  4476  
  4477  	// and we are in restarting state
  4478  	restarting, restartType := st.Restarting()
  4479  	c.Check(restarting, Equals, true)
  4480  	c.Check(restartType, Equals, state.RestartSystem)
  4481  
  4482  	// and the undo gave us our old kernel back
  4483  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4484  		"snap_core":       "core20_2.snap",
  4485  		"snap_try_core":   "core18_1.snap",
  4486  		"snap_kernel":     "pc-kernel_1.snap",
  4487  		"snap_try_kernel": "",
  4488  		"snap_mode":       boot.TryStatus,
  4489  	})
  4490  }
  4491  
  4492  func (ms *mgrsSuite) TestRemodelSwitchToDifferentBaseUndoOnRollback(c *C) {
  4493  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  4494  	bootloader.Force(bloader)
  4495  	defer bootloader.Force(nil)
  4496  	bloader.SetBootVars(map[string]string{
  4497  		"snap_mode":   boot.DefaultStatus,
  4498  		"snap_core":   "core18_1.snap",
  4499  		"snap_kernel": "pc-kernel_1.snap",
  4500  	})
  4501  
  4502  	restore := release.MockOnClassic(false)
  4503  	defer restore()
  4504  
  4505  	mockServer := ms.mockStore(c)
  4506  	defer mockServer.Close()
  4507  
  4508  	st := ms.o.State()
  4509  	st.Lock()
  4510  	defer st.Unlock()
  4511  
  4512  	si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)}
  4513  	snapstate.Set(st, "core18", &snapstate.SnapState{
  4514  		Active:   true,
  4515  		Sequence: []*snap.SideInfo{si},
  4516  		Current:  snap.R(1),
  4517  		SnapType: "base",
  4518  	})
  4519  	snaptest.MockSnapWithFiles(c, "name: core18\ntype: base\nversion: 1.0", si, nil)
  4520  
  4521  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  4522  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  4523  	snapstate.Set(st, "pc", &snapstate.SnapState{
  4524  		Active:   true,
  4525  		Sequence: []*snap.SideInfo{si2},
  4526  		Current:  snap.R(1),
  4527  		SnapType: "gadget",
  4528  	})
  4529  	gadgetYaml := `
  4530  volumes:
  4531      volume-id:
  4532          bootloader: grub
  4533  `
  4534  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  4535  		{"meta/gadget.yaml", gadgetYaml},
  4536  	})
  4537  
  4538  	// add "core20" snap to fake store
  4539  	const core20Yaml = `name: core20
  4540  type: base
  4541  version: 20.04`
  4542  	ms.prereqSnapAssertions(c, map[string]interface{}{
  4543  		"snap-name":    "core20",
  4544  		"publisher-id": "can0nical",
  4545  	})
  4546  	snapPath, _ := ms.makeStoreTestSnap(c, core20Yaml, "2")
  4547  	ms.serveSnap(snapPath, "2")
  4548  
  4549  	// add "foo" snap to fake store
  4550  	ms.prereqSnapAssertions(c, map[string]interface{}{
  4551  		"snap-name": "foo",
  4552  	})
  4553  	snapPath, _ = ms.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1")
  4554  	ms.serveSnap(snapPath, "1")
  4555  
  4556  	// create/set custom model assertion
  4557  	model := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4558  		"base": "core18",
  4559  	})
  4560  
  4561  	// setup model assertion
  4562  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4563  		Brand:  "can0nical",
  4564  		Model:  "my-model",
  4565  		Serial: "serialserialserial",
  4566  	})
  4567  	err := assertstate.Add(st, model)
  4568  	c.Assert(err, IsNil)
  4569  
  4570  	// create a new model
  4571  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4572  		"base":           "core20",
  4573  		"revision":       "1",
  4574  		"required-snaps": []interface{}{"foo"},
  4575  	})
  4576  
  4577  	chg, err := devicestate.Remodel(st, newModel)
  4578  	c.Assert(err, IsNil)
  4579  
  4580  	st.Unlock()
  4581  	err = ms.o.Settle(settleTimeout)
  4582  	st.Lock()
  4583  	c.Assert(err, IsNil)
  4584  	c.Assert(chg.Err(), IsNil)
  4585  
  4586  	// system waits for a restart because of the new base
  4587  	t := findKind(chg, "auto-connect")
  4588  	c.Assert(t, NotNil)
  4589  	c.Assert(t.Status(), Equals, state.DoingStatus)
  4590  
  4591  	// check that the boot vars got updated as expected
  4592  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4593  		"snap_mode":       boot.TryStatus,
  4594  		"snap_core":       "core18_1.snap",
  4595  		"snap_try_core":   "core20_2.snap",
  4596  		"snap_kernel":     "pc-kernel_1.snap",
  4597  		"snap_try_kernel": "",
  4598  	})
  4599  	// simulate successful restart happened
  4600  	ms.mockRollbackAcrossReboot(c, bloader, []snap.Type{snap.TypeBase})
  4601  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4602  		"snap_mode":       boot.DefaultStatus,
  4603  		"snap_core":       "core18_1.snap",
  4604  		"snap_try_core":   "",
  4605  		"snap_kernel":     "pc-kernel_1.snap",
  4606  		"snap_try_kernel": "",
  4607  	})
  4608  
  4609  	// continue
  4610  	st.Unlock()
  4611  	err = ms.o.Settle(settleTimeout)
  4612  	st.Lock()
  4613  	c.Assert(err, IsNil)
  4614  
  4615  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  4616  
  4617  	// and we are *not* in restarting state
  4618  	restarting, _ := st.Restarting()
  4619  	c.Check(restarting, Equals, false)
  4620  	// bootvars unchanged
  4621  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  4622  		"snap_mode":       boot.DefaultStatus,
  4623  		"snap_core":       "core18_1.snap",
  4624  		"snap_try_core":   "",
  4625  		"snap_kernel":     "pc-kernel_1.snap",
  4626  		"snap_try_kernel": "",
  4627  	})
  4628  }
  4629  
  4630  type kernelSuite struct {
  4631  	baseMgrsSuite
  4632  
  4633  	bloader *boottest.Bootenv16
  4634  }
  4635  
  4636  var _ = Suite(&kernelSuite{})
  4637  
  4638  func (s *kernelSuite) SetUpTest(c *C) {
  4639  	s.baseMgrsSuite.SetUpTest(c)
  4640  
  4641  	s.bloader = boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  4642  	s.bloader.SetBootKernel("pc-kernel_1.snap")
  4643  	s.bloader.SetBootBase("core_1.snap")
  4644  	bootloader.Force(s.bloader)
  4645  	s.AddCleanup(func() { bootloader.Force(nil) })
  4646  
  4647  	restore := release.MockOnClassic(false)
  4648  	s.AddCleanup(restore)
  4649  	mockServer := s.mockStore(c)
  4650  	s.AddCleanup(mockServer.Close)
  4651  
  4652  	st := s.o.State()
  4653  	st.Lock()
  4654  	defer st.Unlock()
  4655  
  4656  	// create/set custom model assertion
  4657  	model := s.brands.Model("can0nical", "my-model", modelDefaults)
  4658  	devicestatetest.SetDevice(st, &auth.DeviceState{
  4659  		Brand:  "can0nical",
  4660  		Model:  "my-model",
  4661  		Serial: "serialserialserial",
  4662  	})
  4663  	err := assertstate.Add(st, model)
  4664  	c.Assert(err, IsNil)
  4665  
  4666  	// make a mock "pc-kernel" kernel
  4667  	si := &snap.SideInfo{RealName: "pc-kernel", SnapID: fakeSnapID("pc-kernel"), Revision: snap.R(1)}
  4668  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  4669  		Active:   true,
  4670  		Sequence: []*snap.SideInfo{si},
  4671  		Current:  snap.R(1),
  4672  		SnapType: "kernel",
  4673  	})
  4674  	snaptest.MockSnapWithFiles(c, "name: pc-kernel\ntype: kernel\nversion: 1.0", si, nil)
  4675  
  4676  	// make a mock "pc" gadget
  4677  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  4678  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  4679  	snapstate.Set(st, "pc", &snapstate.SnapState{
  4680  		Active:   true,
  4681  		Sequence: []*snap.SideInfo{si2},
  4682  		Current:  snap.R(1),
  4683  		SnapType: "gadget",
  4684  	})
  4685  	gadgetYaml := `
  4686  volumes:
  4687      volume-id:
  4688          bootloader: grub
  4689  `
  4690  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  4691  		{"meta/gadget.yaml", gadgetYaml},
  4692  	})
  4693  
  4694  	// add some store snaps
  4695  	const kernelYaml = `name: pc-kernel
  4696  type: kernel
  4697  version: 2.0`
  4698  	snapPath, _ := s.makeStoreTestSnap(c, kernelYaml, "2")
  4699  	s.serveSnap(snapPath, "2")
  4700  
  4701  	const brandKernelYaml = `name: brand-kernel
  4702  type: kernel
  4703  version: 1.0`
  4704  	s.prereqSnapAssertions(c, map[string]interface{}{
  4705  		"snap-name":    "brand-kernel",
  4706  		"publisher-id": "can0nical",
  4707  	})
  4708  	snapPath, _ = s.makeStoreTestSnap(c, brandKernelYaml, "2")
  4709  	s.serveSnap(snapPath, "2")
  4710  
  4711  	s.prereqSnapAssertions(c, map[string]interface{}{
  4712  		"snap-name": "foo",
  4713  	})
  4714  	snapPath, _ = s.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1")
  4715  	s.serveSnap(snapPath, "1")
  4716  }
  4717  
  4718  func (s *kernelSuite) TestRemodelSwitchKernelTrack(c *C) {
  4719  	st := s.o.State()
  4720  	st.Lock()
  4721  	defer st.Unlock()
  4722  
  4723  	// create a new model
  4724  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4725  		"kernel":         "pc-kernel=18",
  4726  		"revision":       "1",
  4727  		"required-snaps": []interface{}{"foo"},
  4728  	})
  4729  
  4730  	chg, err := devicestate.Remodel(st, newModel)
  4731  	c.Assert(err, IsNil)
  4732  
  4733  	st.Unlock()
  4734  	err = s.o.Settle(settleTimeout)
  4735  	st.Lock()
  4736  	c.Assert(err, IsNil)
  4737  
  4738  	// system waits for a restart because of the new kernel
  4739  	t := findKind(chg, "auto-connect")
  4740  	c.Assert(t, NotNil)
  4741  	c.Assert(t.Status(), Equals, state.DoingStatus)
  4742  
  4743  	// simulate successful restart happened
  4744  	s.mockSuccessfulReboot(c, s.bloader, []snap.Type{snap.TypeKernel})
  4745  
  4746  	// continue
  4747  	st.Unlock()
  4748  	err = s.o.Settle(settleTimeout)
  4749  	st.Lock()
  4750  	c.Assert(err, IsNil)
  4751  
  4752  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  4753  
  4754  	// ensure tasks were run in the right order
  4755  	tasks := chg.Tasks()
  4756  	sort.Sort(byReadyTime(tasks))
  4757  
  4758  	// first all downloads/checks in sequential order
  4759  	var i int
  4760  	i += validateDownloadCheckTasks(c, tasks[i:], "pc-kernel", "2", "18")
  4761  	i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable")
  4762  
  4763  	// then all installs in sequential order
  4764  	i += validateRefreshTasks(c, tasks[i:], "pc-kernel", "2", 0)
  4765  	i += validateInstallTasks(c, tasks[i:], "foo", "1", 0)
  4766  
  4767  	// ensure that we only have the tasks we checked (plus the one
  4768  	// extra "set-model" task)
  4769  	c.Assert(tasks, HasLen, i+1)
  4770  }
  4771  
  4772  func (ms *kernelSuite) TestRemodelSwitchToDifferentKernel(c *C) {
  4773  	st := ms.o.State()
  4774  	st.Lock()
  4775  	defer st.Unlock()
  4776  
  4777  	// create a new model
  4778  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4779  		"kernel":         "brand-kernel",
  4780  		"revision":       "1",
  4781  		"required-snaps": []interface{}{"foo"},
  4782  	})
  4783  
  4784  	chg, err := devicestate.Remodel(st, newModel)
  4785  	c.Assert(err, IsNil)
  4786  
  4787  	st.Unlock()
  4788  	err = ms.o.Settle(settleTimeout)
  4789  	st.Lock()
  4790  	c.Assert(err, IsNil)
  4791  	c.Assert(chg.Err(), IsNil)
  4792  
  4793  	// system waits for a restart because of the new kernel
  4794  	t := findKind(chg, "auto-connect")
  4795  	c.Assert(t, NotNil)
  4796  	c.Assert(t.Status(), Equals, state.DoingStatus)
  4797  
  4798  	// check that the system tries to boot the new brand kernel
  4799  	c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{
  4800  		"snap_core":       "core_1.snap",
  4801  		"snap_kernel":     "pc-kernel_1.snap",
  4802  		"snap_try_kernel": "brand-kernel_2.snap",
  4803  		"snap_mode":       boot.TryStatus,
  4804  		"snap_try_core":   "",
  4805  	})
  4806  	// simulate successful system-restart bootenv updates (those
  4807  	// vars will be cleared by snapd on a restart)
  4808  	ms.mockSuccessfulReboot(c, ms.bloader, []snap.Type{snap.TypeKernel})
  4809  	// bootvars are as expected
  4810  	c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{
  4811  		"snap_core":       "core_1.snap",
  4812  		"snap_kernel":     "brand-kernel_2.snap",
  4813  		"snap_try_core":   "",
  4814  		"snap_try_kernel": "",
  4815  		"snap_mode":       boot.DefaultStatus,
  4816  	})
  4817  
  4818  	// continue
  4819  	st.Unlock()
  4820  	err = ms.o.Settle(settleTimeout)
  4821  	st.Lock()
  4822  	c.Assert(err, IsNil)
  4823  
  4824  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  4825  
  4826  	// bootvars are as expected (i.e. nothing has changed since this
  4827  	// test simulated that we booted successfully)
  4828  	c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{
  4829  		"snap_core":       "core_1.snap",
  4830  		"snap_kernel":     "brand-kernel_2.snap",
  4831  		"snap_try_kernel": "",
  4832  		"snap_try_core":   "",
  4833  		"snap_mode":       boot.DefaultStatus,
  4834  	})
  4835  
  4836  	// ensure tasks were run in the right order
  4837  	tasks := chg.Tasks()
  4838  	sort.Sort(byReadyTime(tasks))
  4839  
  4840  	// first all downloads/checks in sequential order
  4841  	var i int
  4842  	i += validateDownloadCheckTasks(c, tasks[i:], "brand-kernel", "2", "stable")
  4843  	i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable")
  4844  
  4845  	// then all installs in sequential order
  4846  	i += validateInstallTasks(c, tasks[i:], "brand-kernel", "2", 0)
  4847  	i += validateInstallTasks(c, tasks[i:], "foo", "1", 0)
  4848  
  4849  	// ensure that we only have the tasks we checked (plus the one
  4850  	// extra "set-model" task)
  4851  	c.Assert(tasks, HasLen, i+1)
  4852  
  4853  	// ensure we did not try device registration
  4854  	for _, t := range st.Tasks() {
  4855  		if t.Kind() == "request-serial" {
  4856  			c.Fatalf("test should not create a request-serial task but did")
  4857  		}
  4858  	}
  4859  }
  4860  
  4861  func (ms *kernelSuite) TestRemodelSwitchToDifferentKernelUndo(c *C) {
  4862  	st := ms.o.State()
  4863  	st.Lock()
  4864  	defer st.Unlock()
  4865  
  4866  	// create a new model
  4867  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4868  		"kernel":         "brand-kernel",
  4869  		"revision":       "1",
  4870  		"required-snaps": []interface{}{"foo"},
  4871  	})
  4872  
  4873  	devicestate.InjectSetModelError(fmt.Errorf("boom"))
  4874  	defer devicestate.InjectSetModelError(nil)
  4875  
  4876  	chg, err := devicestate.Remodel(st, newModel)
  4877  	c.Assert(err, IsNil)
  4878  
  4879  	st.Unlock()
  4880  	err = ms.o.Settle(settleTimeout)
  4881  	st.Lock()
  4882  	c.Assert(err, IsNil)
  4883  	c.Assert(chg.Err(), IsNil)
  4884  
  4885  	// system waits for a restart because of the new kernel
  4886  	t := findKind(chg, "auto-connect")
  4887  	c.Assert(t, NotNil)
  4888  	c.Assert(t.Status(), Equals, state.DoingStatus)
  4889  
  4890  	// simulate successful restart happened
  4891  	ms.mockSuccessfulReboot(c, ms.bloader, []snap.Type{snap.TypeKernel})
  4892  
  4893  	// continue
  4894  	st.Unlock()
  4895  	err = ms.o.Settle(settleTimeout)
  4896  	st.Lock()
  4897  	c.Assert(err, IsNil)
  4898  
  4899  	// the change was not successful
  4900  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  4901  
  4902  	// and we are in restarting state
  4903  	restarting, restartType := st.Restarting()
  4904  	c.Check(restarting, Equals, true)
  4905  	c.Check(restartType, Equals, state.RestartSystem)
  4906  
  4907  	// and the undo gave us our old kernel back
  4908  	c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{
  4909  		"snap_core":       "core_1.snap",
  4910  		"snap_try_core":   "",
  4911  		"snap_try_kernel": "pc-kernel_1.snap",
  4912  		"snap_kernel":     "brand-kernel_2.snap",
  4913  		"snap_mode":       boot.TryStatus,
  4914  	})
  4915  }
  4916  
  4917  func (ms *kernelSuite) TestRemodelSwitchToDifferentKernelUndoOnRollback(c *C) {
  4918  	st := ms.o.State()
  4919  	st.Lock()
  4920  	defer st.Unlock()
  4921  
  4922  	// create a new model
  4923  	newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  4924  		"kernel":         "brand-kernel",
  4925  		"revision":       "1",
  4926  		"required-snaps": []interface{}{"foo"},
  4927  	})
  4928  
  4929  	devicestate.InjectSetModelError(fmt.Errorf("boom"))
  4930  	defer devicestate.InjectSetModelError(nil)
  4931  
  4932  	chg, err := devicestate.Remodel(st, newModel)
  4933  	c.Assert(err, IsNil)
  4934  
  4935  	st.Unlock()
  4936  	err = ms.o.Settle(settleTimeout)
  4937  	st.Lock()
  4938  	c.Assert(err, IsNil)
  4939  	c.Assert(chg.Err(), IsNil)
  4940  
  4941  	// system waits for a restart because of the new kernel
  4942  	t := findKind(chg, "auto-connect")
  4943  	c.Assert(t, NotNil)
  4944  	c.Assert(t.Status(), Equals, state.DoingStatus)
  4945  
  4946  	// simulate rollback of the kernel during reboot
  4947  	ms.mockRollbackAcrossReboot(c, ms.bloader, []snap.Type{snap.TypeKernel})
  4948  
  4949  	// continue
  4950  	st.Unlock()
  4951  	err = ms.o.Settle(settleTimeout)
  4952  	st.Lock()
  4953  	c.Assert(err, IsNil)
  4954  
  4955  	// the change was not successful
  4956  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  4957  
  4958  	// and we are *not* in restarting state
  4959  	restarting, _ := st.Restarting()
  4960  	c.Check(restarting, Equals, false)
  4961  
  4962  	// and the undo gave us our old kernel back
  4963  	c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{
  4964  		"snap_core":       "core_1.snap",
  4965  		"snap_try_core":   "",
  4966  		"snap_kernel":     "pc-kernel_1.snap",
  4967  		"snap_try_kernel": "",
  4968  		"snap_mode":       boot.DefaultStatus,
  4969  	})
  4970  }
  4971  
  4972  func (s *mgrsSuite) TestRemodelStoreSwitch(c *C) {
  4973  	s.prereqSnapAssertions(c, map[string]interface{}{
  4974  		"snap-name": "foo",
  4975  	})
  4976  	snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", "foo"), "1")
  4977  	s.serveSnap(snapPath, "1")
  4978  
  4979  	// track the creation of new DeviceAndAutContext (for new Store)
  4980  	newDAC := false
  4981  
  4982  	mockServer := s.mockStore(c)
  4983  	defer mockServer.Close()
  4984  
  4985  	st := s.o.State()
  4986  	st.Lock()
  4987  	defer st.Unlock()
  4988  
  4989  	s.checkDeviceAndAuthContext = func(dac store.DeviceAndAuthContext) {
  4990  		// the DeviceAndAuthContext assumes state is unlocked
  4991  		st.Unlock()
  4992  		defer st.Lock()
  4993  		c.Check(dac, NotNil)
  4994  		stoID, err := dac.StoreID("")
  4995  		c.Assert(err, IsNil)
  4996  		c.Check(stoID, Equals, "switched-store")
  4997  		newDAC = true
  4998  	}
  4999  
  5000  	// create/set custom model assertion
  5001  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  5002  
  5003  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  5004  
  5005  	// setup model assertion
  5006  	err := assertstate.Add(st, model)
  5007  	c.Assert(err, IsNil)
  5008  
  5009  	// have a serial as well
  5010  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  5011  	c.Assert(err, IsNil)
  5012  	err = kpMgr.Put(deviceKey)
  5013  	c.Assert(err, IsNil)
  5014  
  5015  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
  5016  	c.Assert(err, IsNil)
  5017  	serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{
  5018  		"authority-id":        "my-brand",
  5019  		"brand-id":            "my-brand",
  5020  		"model":               "my-model",
  5021  		"serial":              "store-switch-serial",
  5022  		"device-key":          string(encDevKey),
  5023  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
  5024  		"timestamp":           time.Now().Format(time.RFC3339),
  5025  	}, nil, "")
  5026  	c.Assert(err, IsNil)
  5027  	err = assertstate.Add(st, serial)
  5028  	c.Assert(err, IsNil)
  5029  
  5030  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5031  		Brand:  "my-brand",
  5032  		Model:  "my-model",
  5033  		KeyID:  deviceKey.PublicKey().ID(),
  5034  		Serial: "store-switch-serial",
  5035  	})
  5036  
  5037  	// create a new model
  5038  	newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  5039  		"store":          "switched-store",
  5040  		"required-snaps": []interface{}{"foo"},
  5041  		"revision":       "1",
  5042  	})
  5043  
  5044  	s.expectedSerial = "store-switch-serial"
  5045  	s.expectedStore = "switched-store"
  5046  	s.sessionMacaroon = "switched-store-session"
  5047  
  5048  	chg, err := devicestate.Remodel(st, newModel)
  5049  	c.Assert(err, IsNil)
  5050  
  5051  	st.Unlock()
  5052  	err = s.o.Settle(settleTimeout)
  5053  	st.Lock()
  5054  	c.Assert(err, IsNil)
  5055  
  5056  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  5057  
  5058  	// the new required-snap "foo" is installed
  5059  	var snapst snapstate.SnapState
  5060  	err = snapstate.Get(st, "foo", &snapst)
  5061  	c.Assert(err, IsNil)
  5062  
  5063  	// and marked required
  5064  	c.Check(snapst.Required, Equals, true)
  5065  
  5066  	// a new store was made
  5067  	c.Check(newDAC, Equals, true)
  5068  
  5069  	// we have a session with the new store
  5070  	device, err := devicestatetest.Device(st)
  5071  	c.Assert(err, IsNil)
  5072  	c.Check(device.Serial, Equals, "store-switch-serial")
  5073  	c.Check(device.SessionMacaroon, Equals, "switched-store-session")
  5074  }
  5075  
  5076  func (s *mgrsSuite) TestRemodelSwitchGadgetTrack(c *C) {
  5077  	bloader := bootloadertest.Mock("mock", c.MkDir())
  5078  	bootloader.Force(bloader)
  5079  	defer bootloader.Force(nil)
  5080  
  5081  	restore := release.MockOnClassic(false)
  5082  	defer restore()
  5083  
  5084  	mockServer := s.mockStore(c)
  5085  	defer mockServer.Close()
  5086  
  5087  	st := s.o.State()
  5088  	st.Lock()
  5089  	defer st.Unlock()
  5090  
  5091  	si := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  5092  	snapstate.Set(st, "pc", &snapstate.SnapState{
  5093  		Active:   true,
  5094  		Sequence: []*snap.SideInfo{si},
  5095  		Current:  snap.R(1),
  5096  		SnapType: "gadget",
  5097  	})
  5098  	gadgetSnapYaml := "name: pc\nversion: 2.0\ntype: gadget"
  5099  	gadgetYaml := `
  5100  volumes:
  5101      volume-id:
  5102          bootloader: grub
  5103  `
  5104  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si, [][]string{
  5105  		{"meta/gadget.yaml", gadgetYaml},
  5106  	})
  5107  	snapPath, _ := s.makeStoreTestSnapWithFiles(c, gadgetSnapYaml, "2", [][]string{
  5108  		{"meta/gadget.yaml", gadgetYaml},
  5109  	})
  5110  	s.serveSnap(snapPath, "2")
  5111  
  5112  	// create/set custom model assertion
  5113  	model := s.brands.Model("can0nical", "my-model", modelDefaults)
  5114  
  5115  	// setup model assertion
  5116  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5117  		Brand:  "can0nical",
  5118  		Model:  "my-model",
  5119  		Serial: "serialserialserial",
  5120  	})
  5121  	err := assertstate.Add(st, model)
  5122  	c.Assert(err, IsNil)
  5123  
  5124  	// create a new model
  5125  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5126  		"gadget":   "pc=18",
  5127  		"revision": "1",
  5128  	})
  5129  
  5130  	chg, err := devicestate.Remodel(st, newModel)
  5131  	c.Assert(err, IsNil)
  5132  
  5133  	st.Unlock()
  5134  	err = s.o.Settle(settleTimeout)
  5135  	st.Lock()
  5136  	c.Assert(err, IsNil)
  5137  	c.Assert(chg.Err(), IsNil)
  5138  
  5139  	// ensure tasks were run in the right order
  5140  	tasks := chg.Tasks()
  5141  	sort.Sort(byReadyTime(tasks))
  5142  
  5143  	// first all downloads/checks in sequential order
  5144  	var i int
  5145  	i += validateDownloadCheckTasks(c, tasks[i:], "pc", "2", "18")
  5146  
  5147  	// then all installs in sequential order
  5148  	i += validateRefreshTasks(c, tasks[i:], "pc", "2", isGadget)
  5149  
  5150  	// ensure that we only have the tasks we checked (plus the one
  5151  	// extra "set-model" task)
  5152  	c.Assert(tasks, HasLen, i+1)
  5153  }
  5154  
  5155  type mockUpdater struct{}
  5156  
  5157  func (m *mockUpdater) Backup() error { return nil }
  5158  
  5159  func (m *mockUpdater) Rollback() error { return nil }
  5160  
  5161  func (m *mockUpdater) Update() error { return nil }
  5162  
  5163  func (s *mgrsSuite) TestRemodelSwitchToDifferentGadget(c *C) {
  5164  	bloader := bootloadertest.Mock("mock", c.MkDir())
  5165  	bootloader.Force(bloader)
  5166  	defer bootloader.Force(nil)
  5167  	restore := release.MockOnClassic(false)
  5168  	defer restore()
  5169  
  5170  	mockServer := s.mockStore(c)
  5171  	defer mockServer.Close()
  5172  
  5173  	st := s.o.State()
  5174  	st.Lock()
  5175  	defer st.Unlock()
  5176  
  5177  	si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)}
  5178  	snapstate.Set(st, "core18", &snapstate.SnapState{
  5179  		Active:   true,
  5180  		Sequence: []*snap.SideInfo{si},
  5181  		Current:  snap.R(1),
  5182  		SnapType: "base",
  5183  	})
  5184  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  5185  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  5186  	snapstate.Set(st, "pc", &snapstate.SnapState{
  5187  		Active:   true,
  5188  		Sequence: []*snap.SideInfo{si2},
  5189  		Current:  snap.R(1),
  5190  		SnapType: "gadget",
  5191  	})
  5192  	gadgetYaml := `
  5193  volumes:
  5194      volume-id:
  5195          bootloader: grub
  5196          structure:
  5197            - name: foo
  5198              type: bare
  5199              size: 1M
  5200              content:
  5201                - image: foo.img
  5202  `
  5203  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  5204  		{"meta/gadget.yaml", gadgetYaml},
  5205  		{"foo.img", "foo"},
  5206  	})
  5207  
  5208  	// add new gadget "other-pc" snap to fake store
  5209  	const otherPcYaml = `name: other-pc
  5210  type: gadget
  5211  version: 2`
  5212  	s.prereqSnapAssertions(c, map[string]interface{}{
  5213  		"snap-name":    "other-pc",
  5214  		"publisher-id": "can0nical",
  5215  	})
  5216  	otherGadgetYaml := `
  5217  volumes:
  5218      volume-id:
  5219          bootloader: grub
  5220          structure:
  5221            - name: foo
  5222              type: bare
  5223              size: 1M
  5224              content:
  5225                - image: new-foo.img
  5226  `
  5227  	snapPath, _ := s.makeStoreTestSnapWithFiles(c, otherPcYaml, "2", [][]string{
  5228  		// use a compatible gadget YAML
  5229  		{"meta/gadget.yaml", otherGadgetYaml},
  5230  		{"new-foo.img", "new foo"},
  5231  	})
  5232  	s.serveSnap(snapPath, "2")
  5233  
  5234  	updaterForStructureCalls := 0
  5235  	restore = gadget.MockUpdaterForStructure(func(ps *gadget.LaidOutStructure, rootDir, rollbackDir string, observer gadget.ContentUpdateObserver) (gadget.Updater, error) {
  5236  		updaterForStructureCalls++
  5237  		c.Assert(ps.Name, Equals, "foo")
  5238  		return &mockUpdater{}, nil
  5239  	})
  5240  	defer restore()
  5241  
  5242  	// create/set custom model assertion
  5243  	model := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5244  		"gadget": "pc",
  5245  	})
  5246  
  5247  	// setup model assertion
  5248  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5249  		Brand:  "can0nical",
  5250  		Model:  "my-model",
  5251  		Serial: "serialserialserial",
  5252  	})
  5253  	err := assertstate.Add(st, model)
  5254  	c.Assert(err, IsNil)
  5255  
  5256  	// create a new model
  5257  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5258  		"gadget":   "other-pc=18",
  5259  		"revision": "1",
  5260  	})
  5261  
  5262  	chg, err := devicestate.Remodel(st, newModel)
  5263  	c.Assert(err, IsNil)
  5264  
  5265  	st.Unlock()
  5266  	err = s.o.Settle(settleTimeout)
  5267  	st.Lock()
  5268  	c.Assert(err, IsNil)
  5269  	c.Assert(chg.Err(), IsNil)
  5270  
  5271  	// gadget updater was set up
  5272  	c.Check(updaterForStructureCalls, Equals, 1)
  5273  
  5274  	// gadget update requests a restart
  5275  	restarting, _ := st.Restarting()
  5276  	c.Check(restarting, Equals, true)
  5277  
  5278  	// simulate successful restart happened
  5279  	state.MockRestarting(st, state.RestartUnset)
  5280  
  5281  	st.Unlock()
  5282  	err = s.o.Settle(settleTimeout)
  5283  	st.Lock()
  5284  	c.Assert(err, IsNil)
  5285  
  5286  	// ensure tasks were run in the right order
  5287  	tasks := chg.Tasks()
  5288  	sort.Sort(byReadyTime(tasks))
  5289  
  5290  	// first all downloads/checks
  5291  	var i int
  5292  	i += validateDownloadCheckTasks(c, tasks[i:], "other-pc", "2", "18")
  5293  
  5294  	// then all installs
  5295  	i += validateInstallTasks(c, tasks[i:], "other-pc", "2", isGadget)
  5296  
  5297  	// ensure that we only have the tasks we checked (plus the one
  5298  	// extra "set-model" task)
  5299  	c.Assert(tasks, HasLen, i+1)
  5300  }
  5301  
  5302  func (s *mgrsSuite) TestRemodelSwitchToIncompatibleGadget(c *C) {
  5303  	bloader := bootloadertest.Mock("mock", c.MkDir())
  5304  	bootloader.Force(bloader)
  5305  	defer bootloader.Force(nil)
  5306  	restore := release.MockOnClassic(false)
  5307  	defer restore()
  5308  
  5309  	mockServer := s.mockStore(c)
  5310  	defer mockServer.Close()
  5311  
  5312  	st := s.o.State()
  5313  	st.Lock()
  5314  	defer st.Unlock()
  5315  
  5316  	si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)}
  5317  	snapstate.Set(st, "core18", &snapstate.SnapState{
  5318  		Active:   true,
  5319  		Sequence: []*snap.SideInfo{si},
  5320  		Current:  snap.R(1),
  5321  		SnapType: "base",
  5322  	})
  5323  	si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)}
  5324  	gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget"
  5325  	snapstate.Set(st, "pc", &snapstate.SnapState{
  5326  		Active:   true,
  5327  		Sequence: []*snap.SideInfo{si2},
  5328  		Current:  snap.R(1),
  5329  		SnapType: "gadget",
  5330  	})
  5331  	gadgetYaml := `
  5332  volumes:
  5333      volume-id:
  5334          bootloader: grub
  5335          structure:
  5336            - name: foo
  5337              type: 00000000-0000-0000-0000-0000deadcafe
  5338              size: 10M
  5339  `
  5340  	snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{
  5341  		{"meta/gadget.yaml", gadgetYaml},
  5342  	})
  5343  
  5344  	// add new gadget "other-pc" snap to fake store
  5345  	const otherPcYaml = `name: other-pc
  5346  type: gadget
  5347  version: 2`
  5348  	// new gadget layout is incompatible, a structure that exited before has
  5349  	// a different size now
  5350  	otherGadgetYaml := `
  5351  volumes:
  5352      volume-id:
  5353          bootloader: grub
  5354          structure:
  5355            - name: foo
  5356              type: 00000000-0000-0000-0000-0000deadcafe
  5357              size: 20M
  5358  `
  5359  	s.prereqSnapAssertions(c, map[string]interface{}{
  5360  		"snap-name":    "other-pc",
  5361  		"publisher-id": "can0nical",
  5362  	})
  5363  	snapPath, _ := s.makeStoreTestSnapWithFiles(c, otherPcYaml, "2", [][]string{
  5364  		{"meta/gadget.yaml", otherGadgetYaml},
  5365  	})
  5366  	s.serveSnap(snapPath, "2")
  5367  
  5368  	// create/set custom model assertion
  5369  	model := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5370  		"gadget": "pc",
  5371  	})
  5372  
  5373  	// setup model assertion
  5374  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5375  		Brand:  "can0nical",
  5376  		Model:  "my-model",
  5377  		Serial: "serialserialserial",
  5378  	})
  5379  	err := assertstate.Add(st, model)
  5380  	c.Assert(err, IsNil)
  5381  
  5382  	// create a new model
  5383  	newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{
  5384  		"gadget":   "other-pc=18",
  5385  		"revision": "1",
  5386  	})
  5387  
  5388  	chg, err := devicestate.Remodel(st, newModel)
  5389  	c.Assert(err, IsNil)
  5390  
  5391  	st.Unlock()
  5392  	err = s.o.Settle(settleTimeout)
  5393  	st.Lock()
  5394  	c.Assert(err, IsNil)
  5395  	c.Assert(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*cannot remodel to an incompatible gadget: .*cannot change structure size.*`)
  5396  }
  5397  
  5398  func (s *mgrsSuite) TestHappyDeviceRegistrationWithPrepareDeviceHook(c *C) {
  5399  	// just to 404 locally eager account-key requests
  5400  	mockStoreServer := s.mockStore(c)
  5401  	defer mockStoreServer.Close()
  5402  
  5403  	model := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  5404  		"gadget": "gadget",
  5405  	})
  5406  
  5407  	// reset as seeded but not registered
  5408  	// shortcut: have already device key
  5409  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  5410  	c.Assert(err, IsNil)
  5411  	err = kpMgr.Put(deviceKey)
  5412  	c.Assert(err, IsNil)
  5413  
  5414  	st := s.o.State()
  5415  	st.Lock()
  5416  	defer st.Unlock()
  5417  
  5418  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  5419  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5420  		Brand: "my-brand",
  5421  		Model: "my-model",
  5422  		KeyID: deviceKey.PublicKey().ID(),
  5423  	})
  5424  	err = assertstate.Add(st, model)
  5425  	c.Assert(err, IsNil)
  5426  
  5427  	signSerial := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) {
  5428  		brandID := headers["brand-id"].(string)
  5429  		model := headers["model"].(string)
  5430  		c.Check(brandID, Equals, "my-brand")
  5431  		c.Check(model, Equals, "my-model")
  5432  		headers["authority-id"] = brandID
  5433  		a, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, headers, body, "")
  5434  		return a, nil, err
  5435  	}
  5436  
  5437  	bhv := &devicestatetest.DeviceServiceBehavior{
  5438  		ReqID:            "REQID-1",
  5439  		RequestIDURLPath: "/svc/request-id",
  5440  		SerialURLPath:    "/svc/serial",
  5441  		SignSerial:       signSerial,
  5442  	}
  5443  
  5444  	mockServer := devicestatetest.MockDeviceService(c, bhv)
  5445  	defer mockServer.Close()
  5446  
  5447  	pDBhv := &devicestatetest.PrepareDeviceBehavior{
  5448  		DeviceSvcURL: mockServer.URL + "/svc/",
  5449  		Headers: map[string]string{
  5450  			"x-extra-header": "extra",
  5451  		},
  5452  		RegBody: map[string]string{
  5453  			"mac": "00:00:00:00:ff:00",
  5454  		},
  5455  		ProposedSerial: "12000",
  5456  	}
  5457  
  5458  	r := devicestatetest.MockGadget(c, st, "gadget", snap.R(2), pDBhv)
  5459  	defer r()
  5460  
  5461  	// run the whole device registration process
  5462  	st.Unlock()
  5463  	err = s.o.Settle(settleTimeout)
  5464  	st.Lock()
  5465  	c.Assert(err, IsNil)
  5466  
  5467  	var becomeOperational *state.Change
  5468  	for _, chg := range st.Changes() {
  5469  		if chg.Kind() == "become-operational" {
  5470  			becomeOperational = chg
  5471  			break
  5472  		}
  5473  	}
  5474  	c.Assert(becomeOperational, NotNil)
  5475  
  5476  	c.Check(becomeOperational.Status().Ready(), Equals, true)
  5477  	c.Check(becomeOperational.Err(), IsNil)
  5478  
  5479  	device, err := devicestatetest.Device(st)
  5480  	c.Assert(err, IsNil)
  5481  	c.Check(device.Brand, Equals, "my-brand")
  5482  	c.Check(device.Model, Equals, "my-model")
  5483  	c.Check(device.Serial, Equals, "12000")
  5484  
  5485  	a, err := assertstate.DB(st).Find(asserts.SerialType, map[string]string{
  5486  		"brand-id": "my-brand",
  5487  		"model":    "my-model",
  5488  		"serial":   "12000",
  5489  	})
  5490  	c.Assert(err, IsNil)
  5491  	serial := a.(*asserts.Serial)
  5492  
  5493  	var details map[string]interface{}
  5494  	err = yaml.Unmarshal(serial.Body(), &details)
  5495  	c.Assert(err, IsNil)
  5496  
  5497  	c.Check(details, DeepEquals, map[string]interface{}{
  5498  		"mac": "00:00:00:00:ff:00",
  5499  	})
  5500  
  5501  	c.Check(serial.DeviceKey().ID(), Equals, device.KeyID)
  5502  }
  5503  
  5504  func (s *mgrsSuite) TestRemodelReregistration(c *C) {
  5505  	s.prereqSnapAssertions(c, map[string]interface{}{
  5506  		"snap-name": "foo",
  5507  	})
  5508  	snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", "foo"), "1")
  5509  	s.serveSnap(snapPath, "1")
  5510  
  5511  	// track the creation of new DeviceAndAutContext (for new Store)
  5512  	newDAC := false
  5513  
  5514  	mockServer := s.mockStore(c)
  5515  	defer mockServer.Close()
  5516  
  5517  	st := s.o.State()
  5518  	st.Lock()
  5519  	defer st.Unlock()
  5520  
  5521  	s.checkDeviceAndAuthContext = func(dac store.DeviceAndAuthContext) {
  5522  		// the DeviceAndAuthContext assumes state is unlocked
  5523  		st.Unlock()
  5524  		defer st.Lock()
  5525  		c.Check(dac, NotNil)
  5526  		stoID, err := dac.StoreID("")
  5527  		c.Assert(err, IsNil)
  5528  		c.Check(stoID, Equals, "my-brand-substore")
  5529  		newDAC = true
  5530  	}
  5531  
  5532  	model := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{
  5533  		"gadget": "gadget",
  5534  	})
  5535  
  5536  	// setup initial device identity
  5537  	kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir)
  5538  	c.Assert(err, IsNil)
  5539  	err = kpMgr.Put(deviceKey)
  5540  	c.Assert(err, IsNil)
  5541  
  5542  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  5543  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5544  		Brand:  "my-brand",
  5545  		Model:  "my-model",
  5546  		KeyID:  deviceKey.PublicKey().ID(),
  5547  		Serial: "orig-serial",
  5548  	})
  5549  	err = assertstate.Add(st, model)
  5550  	c.Assert(err, IsNil)
  5551  
  5552  	encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey())
  5553  	c.Assert(err, IsNil)
  5554  	serialHeaders := map[string]interface{}{
  5555  		"brand-id":            "my-brand",
  5556  		"model":               "my-model",
  5557  		"serial":              "orig-serial",
  5558  		"device-key":          string(encDevKey),
  5559  		"device-key-sha3-384": deviceKey.PublicKey().ID(),
  5560  		"timestamp":           time.Now().Format(time.RFC3339),
  5561  	}
  5562  	serialA, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, serialHeaders, nil, "")
  5563  	c.Assert(err, IsNil)
  5564  	serial := serialA.(*asserts.Serial)
  5565  	err = assertstate.Add(st, serial)
  5566  	c.Assert(err, IsNil)
  5567  
  5568  	signSerial := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) {
  5569  		brandID := headers["brand-id"].(string)
  5570  		model := headers["model"].(string)
  5571  		c.Check(brandID, Equals, "my-brand")
  5572  		c.Check(model, Equals, "other-model")
  5573  		headers["authority-id"] = brandID
  5574  		a, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, headers, body, "")
  5575  		return a, nil, err
  5576  	}
  5577  
  5578  	bhv := &devicestatetest.DeviceServiceBehavior{
  5579  		ReqID:            "REQID-1",
  5580  		RequestIDURLPath: "/svc/request-id",
  5581  		SerialURLPath:    "/svc/serial",
  5582  		SignSerial:       signSerial,
  5583  	}
  5584  
  5585  	mockDeviceService := devicestatetest.MockDeviceService(c, bhv)
  5586  	defer mockDeviceService.Close()
  5587  
  5588  	r := devicestatetest.MockGadget(c, st, "gadget", snap.R(2), nil)
  5589  	defer r()
  5590  
  5591  	// set registration config on gadget
  5592  	tr := config.NewTransaction(st)
  5593  	c.Assert(tr.Set("gadget", "device-service.url", mockDeviceService.URL+"/svc/"), IsNil)
  5594  	c.Assert(tr.Set("gadget", "registration.proposed-serial", "orig-serial"), IsNil)
  5595  	tr.Commit()
  5596  
  5597  	// run the remodel
  5598  	// create a new model
  5599  	newModel := s.brands.Model("my-brand", "other-model", modelDefaults, map[string]interface{}{
  5600  		"store":          "my-brand-substore",
  5601  		"gadget":         "gadget",
  5602  		"required-snaps": []interface{}{"foo"},
  5603  	})
  5604  
  5605  	s.expectedSerial = "orig-serial"
  5606  	s.expectedStore = "my-brand-substore"
  5607  	s.sessionMacaroon = "other-store-session"
  5608  
  5609  	chg, err := devicestate.Remodel(st, newModel)
  5610  	c.Assert(err, IsNil)
  5611  
  5612  	st.Unlock()
  5613  	err = s.o.Settle(settleTimeout)
  5614  	st.Lock()
  5615  	c.Assert(err, IsNil)
  5616  
  5617  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err()))
  5618  
  5619  	device, err := devicestatetest.Device(st)
  5620  	c.Assert(err, IsNil)
  5621  	c.Check(device.Brand, Equals, "my-brand")
  5622  	c.Check(device.Model, Equals, "other-model")
  5623  	c.Check(device.Serial, Equals, "orig-serial")
  5624  
  5625  	a, err := assertstate.DB(st).Find(asserts.SerialType, map[string]string{
  5626  		"brand-id": "my-brand",
  5627  		"model":    "other-model",
  5628  		"serial":   "orig-serial",
  5629  	})
  5630  	c.Assert(err, IsNil)
  5631  	serial = a.(*asserts.Serial)
  5632  
  5633  	c.Check(serial.Body(), HasLen, 0)
  5634  	c.Check(serial.DeviceKey().ID(), Equals, device.KeyID)
  5635  
  5636  	// the new required-snap "foo" is installed
  5637  	var snapst snapstate.SnapState
  5638  	err = snapstate.Get(st, "foo", &snapst)
  5639  	c.Assert(err, IsNil)
  5640  
  5641  	// and marked required
  5642  	c.Check(snapst.Required, Equals, true)
  5643  
  5644  	// a new store was made
  5645  	c.Check(newDAC, Equals, true)
  5646  
  5647  	// we have a session with the new store
  5648  	c.Check(device.SessionMacaroon, Equals, "other-store-session")
  5649  }
  5650  
  5651  func (s *mgrsSuite) TestCheckRefreshFailureWithConcurrentRemoveOfConnectedSnap(c *C) {
  5652  	hookMgr := s.o.HookManager()
  5653  	c.Assert(hookMgr, NotNil)
  5654  
  5655  	// force configure hook failure for some-snap.
  5656  	hookMgr.RegisterHijack("configure", "some-snap", func(ctx *hookstate.Context) error {
  5657  		return fmt.Errorf("failing configure hook")
  5658  	})
  5659  
  5660  	snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40")
  5661  	s.serveSnap(snapPath, "40")
  5662  	snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50")
  5663  	s.serveSnap(snapPath, "50")
  5664  
  5665  	mockServer := s.mockStore(c)
  5666  	defer mockServer.Close()
  5667  
  5668  	st := s.o.State()
  5669  	st.Lock()
  5670  	defer st.Unlock()
  5671  
  5672  	st.Set("conns", map[string]interface{}{
  5673  		"other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": false},
  5674  	})
  5675  
  5676  	si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)}
  5677  	snapInfo := snaptest.MockSnap(c, someSnapYaml, si)
  5678  
  5679  	oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)}
  5680  	otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi)
  5681  
  5682  	snapstate.Set(st, "some-snap", &snapstate.SnapState{
  5683  		Active:   true,
  5684  		Sequence: []*snap.SideInfo{si},
  5685  		Current:  snap.R(1),
  5686  		SnapType: "app",
  5687  	})
  5688  	snapstate.Set(st, "other-snap", &snapstate.SnapState{
  5689  		Active:   true,
  5690  		Sequence: []*snap.SideInfo{oi},
  5691  		Current:  snap.R(1),
  5692  		SnapType: "app",
  5693  	})
  5694  
  5695  	// add snaps to the repo and connect them
  5696  	repo := s.o.InterfaceManager().Repository()
  5697  	c.Assert(repo.AddSnap(snapInfo), IsNil)
  5698  	c.Assert(repo.AddSnap(otherInfo), IsNil)
  5699  	_, err := repo.Connect(&interfaces.ConnRef{
  5700  		PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"},
  5701  		SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"},
  5702  	}, nil, nil, nil, nil, nil)
  5703  	c.Assert(err, IsNil)
  5704  
  5705  	// refresh all
  5706  	c.Assert(assertstate.RefreshSnapDeclarations(st, 0), IsNil)
  5707  
  5708  	ts, err := snapstate.Update(st, "some-snap", nil, 0, snapstate.Flags{})
  5709  	c.Assert(err, IsNil)
  5710  	chg := st.NewChange("refresh", "...")
  5711  	chg.AddAll(ts)
  5712  
  5713  	// remove other-snap
  5714  	ts2, err := snapstate.Remove(st, "other-snap", snap.R(0), &snapstate.RemoveFlags{Purge: true})
  5715  	c.Assert(err, IsNil)
  5716  	chg2 := st.NewChange("remove-snap", "...")
  5717  	chg2.AddAll(ts2)
  5718  
  5719  	st.Unlock()
  5720  	err = s.o.Settle(settleTimeout)
  5721  	st.Lock()
  5722  
  5723  	c.Check(err, IsNil)
  5724  
  5725  	// the refresh change has failed due to configure hook error
  5726  	c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*failing configure hook.*`)
  5727  	c.Check(chg.Status(), Equals, state.ErrorStatus)
  5728  
  5729  	// download-snap is one of the first tasks in the refresh change, check that it was undone
  5730  	var downloadSnapStatus state.Status
  5731  	for _, t := range chg.Tasks() {
  5732  		if t.Kind() == "download-snap" {
  5733  			downloadSnapStatus = t.Status()
  5734  			break
  5735  		}
  5736  	}
  5737  	c.Check(downloadSnapStatus, Equals, state.UndoneStatus)
  5738  
  5739  	// the remove change succeeded
  5740  	c.Check(chg2.Err(), IsNil)
  5741  	c.Check(chg2.Status(), Equals, state.DoneStatus)
  5742  }
  5743  
  5744  func (s *mgrsSuite) TestInstallKernelSnapRollbackUpdatesBootloaderEnv(c *C) {
  5745  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  5746  	bootloader.Force(bloader)
  5747  	defer bootloader.Force(nil)
  5748  
  5749  	restore := release.MockOnClassic(false)
  5750  	defer restore()
  5751  
  5752  	model := s.brands.Model("my-brand", "my-model", modelDefaults)
  5753  
  5754  	const packageKernel = `
  5755  name: pc-kernel
  5756  version: 4.0-1
  5757  type: kernel`
  5758  
  5759  	files := [][]string{
  5760  		{"kernel.img", "I'm a kernel"},
  5761  		{"initrd.img", "...and I'm an initrd"},
  5762  		{"meta/kernel.yaml", "version: 4.2"},
  5763  	}
  5764  	snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files)
  5765  
  5766  	st := s.o.State()
  5767  	st.Lock()
  5768  	defer st.Unlock()
  5769  
  5770  	// pretend we have core18/pc-kernel
  5771  	bloader.BootVars = map[string]string{
  5772  		"snap_core":   "core18_2.snap",
  5773  		"snap_kernel": "pc-kernel_123.snap",
  5774  		"snap_mode":   boot.DefaultStatus,
  5775  	}
  5776  	si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(123)}
  5777  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
  5778  		SnapType: "kernel",
  5779  		Active:   true,
  5780  		Sequence: []*snap.SideInfo{si1},
  5781  		Current:  si1.Revision,
  5782  	})
  5783  	snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{
  5784  		{"meta/kernel.yaml", ""},
  5785  	})
  5786  	si2 := &snap.SideInfo{RealName: "core18", Revision: snap.R(2)}
  5787  	snapstate.Set(st, "core18", &snapstate.SnapState{
  5788  		SnapType: "base",
  5789  		Active:   true,
  5790  		Sequence: []*snap.SideInfo{si2},
  5791  		Current:  si2.Revision,
  5792  	})
  5793  
  5794  	// setup model assertion
  5795  	assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...)
  5796  	devicestatetest.SetDevice(st, &auth.DeviceState{
  5797  		Brand:  "my-brand",
  5798  		Model:  "my-model",
  5799  		Serial: "serialserialserial",
  5800  	})
  5801  	err := assertstate.Add(st, model)
  5802  	c.Assert(err, IsNil)
  5803  
  5804  	ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, snapPath, "", "", snapstate.Flags{})
  5805  	c.Assert(err, IsNil)
  5806  
  5807  	chg := st.NewChange("install-snap", "...")
  5808  	chg.AddAll(ts)
  5809  
  5810  	// run, this will trigger a wait for the restart
  5811  	st.Unlock()
  5812  	err = s.o.Settle(settleTimeout)
  5813  	st.Lock()
  5814  	c.Assert(err, IsNil)
  5815  
  5816  	c.Assert(bloader.BootVars, DeepEquals, map[string]string{
  5817  		"snap_core":       "core18_2.snap",
  5818  		"snap_try_core":   "",
  5819  		"snap_kernel":     "pc-kernel_123.snap",
  5820  		"snap_try_kernel": "pc-kernel_x1.snap",
  5821  		"snap_mode":       boot.TryStatus,
  5822  	})
  5823  
  5824  	// we are in restarting state and the change is not done yet
  5825  	restarting, _ := st.Restarting()
  5826  	c.Check(restarting, Equals, true)
  5827  	c.Check(chg.Status(), Equals, state.DoingStatus)
  5828  	s.mockRollbackAcrossReboot(c, bloader, []snap.Type{snap.TypeKernel})
  5829  
  5830  	// the kernel revision got rolled back
  5831  	var snapst snapstate.SnapState
  5832  	snapstate.Get(st, "pc-kernel", &snapst)
  5833  	info, err := snapst.CurrentInfo()
  5834  	c.Assert(err, IsNil)
  5835  	c.Assert(info.Revision, Equals, snap.R(123))
  5836  
  5837  	st.Unlock()
  5838  	err = s.o.Settle(settleTimeout)
  5839  	st.Lock()
  5840  	c.Assert(err, IsNil)
  5841  
  5842  	c.Assert(chg.Status(), Equals, state.ErrorStatus)
  5843  	c.Assert(chg.Err(), ErrorMatches, `(?ms).*cannot finish pc-kernel installation, there was a rollback across reboot\)`)
  5844  
  5845  	// and the bootvars are reset
  5846  	c.Check(bloader.BootVars, DeepEquals, map[string]string{
  5847  		"snap_core":       "core18_2.snap",
  5848  		"snap_kernel":     "pc-kernel_123.snap",
  5849  		"snap_mode":       boot.DefaultStatus,
  5850  		"snap_try_core":   "",
  5851  		"snap_try_kernel": "",
  5852  	})
  5853  }