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