github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/managers_test.go (about)

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