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