github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/overlord/devicestate/devicestate_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2019 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package devicestate_test
    21  
    22  import (
    23  	"errors"
    24  	"fmt"
    25  	"os"
    26  	"testing"
    27  	"time"
    28  
    29  	. "gopkg.in/check.v1"
    30  	"gopkg.in/tomb.v2"
    31  
    32  	"github.com/snapcore/snapd/asserts"
    33  	"github.com/snapcore/snapd/asserts/assertstest"
    34  	"github.com/snapcore/snapd/asserts/sysdb"
    35  	"github.com/snapcore/snapd/boot"
    36  	"github.com/snapcore/snapd/bootloader"
    37  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    38  	"github.com/snapcore/snapd/dirs"
    39  	"github.com/snapcore/snapd/interfaces"
    40  	"github.com/snapcore/snapd/interfaces/builtin"
    41  	"github.com/snapcore/snapd/osutil"
    42  	"github.com/snapcore/snapd/overlord"
    43  	"github.com/snapcore/snapd/overlord/assertstate"
    44  	"github.com/snapcore/snapd/overlord/assertstate/assertstatetest"
    45  	"github.com/snapcore/snapd/overlord/auth"
    46  	"github.com/snapcore/snapd/overlord/configstate/config"
    47  	"github.com/snapcore/snapd/overlord/devicestate"
    48  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    49  	"github.com/snapcore/snapd/overlord/devicestate/fde"
    50  	"github.com/snapcore/snapd/overlord/hookstate"
    51  	"github.com/snapcore/snapd/overlord/ifacestate/ifacerepo"
    52  	"github.com/snapcore/snapd/overlord/snapstate"
    53  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    54  	"github.com/snapcore/snapd/overlord/state"
    55  	"github.com/snapcore/snapd/overlord/storecontext"
    56  	"github.com/snapcore/snapd/release"
    57  	"github.com/snapcore/snapd/secboot"
    58  	"github.com/snapcore/snapd/snap"
    59  	"github.com/snapcore/snapd/snap/snaptest"
    60  	"github.com/snapcore/snapd/snapdenv"
    61  	"github.com/snapcore/snapd/store/storetest"
    62  	"github.com/snapcore/snapd/sysconfig"
    63  	"github.com/snapcore/snapd/testutil"
    64  	"github.com/snapcore/snapd/timings"
    65  )
    66  
    67  var (
    68  	settleTimeout = testutil.HostScaledTimeout(15 * time.Second)
    69  )
    70  
    71  func TestDeviceManager(t *testing.T) { TestingT(t) }
    72  
    73  type deviceMgrBaseSuite struct {
    74  	testutil.BaseTest
    75  
    76  	o       *overlord.Overlord
    77  	state   *state.State
    78  	se      *overlord.StateEngine
    79  	hookMgr *hookstate.HookManager
    80  	mgr     *devicestate.DeviceManager
    81  	db      *asserts.Database
    82  
    83  	bootloader *bootloadertest.MockBootloader
    84  
    85  	storeSigning *assertstest.StoreStack
    86  	brands       *assertstest.SigningAccounts
    87  
    88  	ancillary []asserts.Assertion
    89  
    90  	restartRequests []state.RestartType
    91  
    92  	newFakeStore func(storecontext.DeviceBackend) snapstate.StoreService
    93  
    94  	// saved so that if a derived suite wants to undo the cloud-init mocking to
    95  	// test the actual functions, it can just call this in it's SetUpTest, see
    96  	// devicestate_cloudinit_test.go for details
    97  	restoreCloudInitStatusRestore func()
    98  }
    99  
   100  type deviceMgrSuite struct {
   101  	deviceMgrBaseSuite
   102  }
   103  
   104  var _ = Suite(&deviceMgrSuite{})
   105  
   106  type fakeStore struct {
   107  	storetest.Store
   108  
   109  	state *state.State
   110  	db    asserts.RODatabase
   111  }
   112  
   113  func (sto *fakeStore) pokeStateLock() {
   114  	// the store should be called without the state lock held. Try
   115  	// to acquire it.
   116  	sto.state.Lock()
   117  	sto.state.Unlock()
   118  }
   119  
   120  func (sto *fakeStore) Assertion(assertType *asserts.AssertionType, key []string, _ *auth.UserState) (asserts.Assertion, error) {
   121  	sto.pokeStateLock()
   122  	ref := &asserts.Ref{Type: assertType, PrimaryKey: key}
   123  	return ref.Resolve(sto.db.Find)
   124  }
   125  
   126  var (
   127  	brandPrivKey, _  = assertstest.GenerateKey(752)
   128  	brandPrivKey2, _ = assertstest.GenerateKey(752)
   129  	brandPrivKey3, _ = assertstest.GenerateKey(752)
   130  )
   131  
   132  func (s *deviceMgrBaseSuite) SetUpTest(c *C) {
   133  	s.BaseTest.SetUpTest(c)
   134  
   135  	dirs.SetRootDir(c.MkDir())
   136  	s.AddCleanup(func() { dirs.SetRootDir("") })
   137  
   138  	err := os.MkdirAll(dirs.SnapRunDir, 0755)
   139  	c.Assert(err, IsNil)
   140  	err = os.MkdirAll(dirs.SnapdStateDir(dirs.GlobalRootDir), 0755)
   141  	c.Assert(err, IsNil)
   142  
   143  	s.AddCleanup(osutil.MockMountInfo(``))
   144  
   145  	s.restartRequests = nil
   146  
   147  	s.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
   148  
   149  	s.bootloader = bootloadertest.Mock("mock", c.MkDir())
   150  	bootloader.Force(s.bootloader)
   151  	s.AddCleanup(func() { bootloader.Force(nil) })
   152  
   153  	s.AddCleanup(release.MockOnClassic(false))
   154  
   155  	s.storeSigning = assertstest.NewStoreStack("canonical", nil)
   156  	s.o = overlord.MockWithStateAndRestartHandler(nil, func(req state.RestartType) {
   157  		s.restartRequests = append(s.restartRequests, req)
   158  	})
   159  	s.state = s.o.State()
   160  	s.state.Lock()
   161  	s.state.VerifyReboot("boot-id-0")
   162  	s.state.Unlock()
   163  	s.se = s.o.StateEngine()
   164  
   165  	s.AddCleanup(sysdb.MockGenericClassicModel(s.storeSigning.GenericClassicModel))
   166  
   167  	s.brands = assertstest.NewSigningAccounts(s.storeSigning)
   168  	s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{
   169  		"display-name": "fancy model publisher",
   170  		"validation":   "certified",
   171  	})
   172  	s.brands.Register("rereg-brand", brandPrivKey2, nil)
   173  
   174  	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   175  		Backstore:       asserts.NewMemoryBackstore(),
   176  		Trusted:         s.storeSigning.Trusted,
   177  		OtherPredefined: s.storeSigning.Generic,
   178  	})
   179  	c.Assert(err, IsNil)
   180  
   181  	s.state.Lock()
   182  	assertstate.ReplaceDB(s.state, db)
   183  	s.state.Unlock()
   184  	s.AddCleanup(func() {
   185  		s.state.Lock()
   186  		assertstate.ReplaceDB(s.state, nil)
   187  		s.state.Unlock()
   188  	})
   189  
   190  	err = db.Add(s.storeSigning.StoreAccountKey(""))
   191  	c.Assert(err, IsNil)
   192  
   193  	hookMgr, err := hookstate.Manager(s.state, s.o.TaskRunner())
   194  	c.Assert(err, IsNil)
   195  	mgr, err := devicestate.Manager(s.state, hookMgr, s.o.TaskRunner(), s.newStore)
   196  	c.Assert(err, IsNil)
   197  
   198  	s.db = db
   199  	s.hookMgr = hookMgr
   200  	s.o.AddManager(s.hookMgr)
   201  	s.mgr = mgr
   202  	s.o.AddManager(s.mgr)
   203  	s.o.AddManager(s.o.TaskRunner())
   204  
   205  	// For triggering errors
   206  	erroringHandler := func(task *state.Task, _ *tomb.Tomb) error {
   207  		return errors.New("error out")
   208  	}
   209  	s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil)
   210  
   211  	c.Assert(s.o.StartUp(), IsNil)
   212  
   213  	s.state.Lock()
   214  	snapstate.ReplaceStore(s.state, &fakeStore{
   215  		state: s.state,
   216  		db:    s.storeSigning,
   217  	})
   218  	s.state.Unlock()
   219  
   220  	s.restoreCloudInitStatusRestore = devicestate.MockCloudInitStatus(func() (sysconfig.CloudInitState, error) {
   221  		return sysconfig.CloudInitRestrictedBySnapd, nil
   222  	})
   223  	s.AddCleanup(s.restoreCloudInitStatusRestore)
   224  
   225  	s.AddCleanup(func() { s.ancillary = nil })
   226  }
   227  
   228  func (s *deviceMgrBaseSuite) newStore(devBE storecontext.DeviceBackend) snapstate.StoreService {
   229  	return s.newFakeStore(devBE)
   230  }
   231  
   232  func (s *deviceMgrBaseSuite) settle(c *C) {
   233  	err := s.o.Settle(settleTimeout)
   234  	c.Assert(err, IsNil)
   235  }
   236  
   237  // seeding avoids triggering a real full seeding, it simulates having it in process instead
   238  func (s *deviceMgrBaseSuite) seeding() {
   239  	chg := s.state.NewChange("seed", "Seed system")
   240  	chg.SetStatus(state.DoingStatus)
   241  }
   242  
   243  func (s *deviceMgrSuite) TestDeviceManagerSetTimeOnce(c *C) {
   244  	s.state.Lock()
   245  	defer s.state.Unlock()
   246  
   247  	// set first time
   248  	now := time.Now()
   249  	err := devicestate.SetTimeOnce(s.mgr, "key-name", now)
   250  	c.Assert(err, IsNil)
   251  
   252  	later := now.Add(1 * time.Minute)
   253  	// setting again doesn't change value
   254  	err = devicestate.SetTimeOnce(s.mgr, "key-name", later)
   255  	c.Assert(err, IsNil)
   256  
   257  	var t time.Time
   258  	s.state.Get("key-name", &t)
   259  
   260  	c.Assert(t.Equal(now), Equals, true)
   261  }
   262  
   263  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeededAlreadySeeded(c *C) {
   264  	s.state.Lock()
   265  	s.state.Set("seeded", true)
   266  	s.state.Unlock()
   267  
   268  	called := false
   269  	restore := devicestate.MockPopulateStateFromSeed(func(*state.State, *devicestate.PopulateStateFromSeedOptions, timings.Measurer) ([]*state.TaskSet, error) {
   270  		called = true
   271  		return nil, nil
   272  	})
   273  	defer restore()
   274  
   275  	err := devicestate.EnsureSeeded(s.mgr)
   276  	c.Assert(err, IsNil)
   277  	c.Assert(called, Equals, false)
   278  }
   279  
   280  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeededChangeInFlight(c *C) {
   281  	s.state.Lock()
   282  	chg := s.state.NewChange("seed", "just for testing")
   283  	chg.AddTask(s.state.NewTask("test-task", "the change needs a task"))
   284  	s.state.Unlock()
   285  
   286  	called := false
   287  	restore := devicestate.MockPopulateStateFromSeed(func(*state.State, *devicestate.PopulateStateFromSeedOptions, timings.Measurer) ([]*state.TaskSet, error) {
   288  		called = true
   289  		return nil, nil
   290  	})
   291  	defer restore()
   292  
   293  	err := devicestate.EnsureSeeded(s.mgr)
   294  	c.Assert(err, IsNil)
   295  	c.Assert(called, Equals, false)
   296  }
   297  
   298  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeededAlsoOnClassic(c *C) {
   299  	release.OnClassic = true
   300  
   301  	called := false
   302  	restore := devicestate.MockPopulateStateFromSeed(func(st *state.State, opts *devicestate.PopulateStateFromSeedOptions, tm timings.Measurer) ([]*state.TaskSet, error) {
   303  		called = true
   304  		c.Check(opts, IsNil)
   305  		return nil, nil
   306  	})
   307  	defer restore()
   308  
   309  	err := devicestate.EnsureSeeded(s.mgr)
   310  	c.Assert(err, IsNil)
   311  	c.Assert(called, Equals, true)
   312  }
   313  
   314  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeededHappy(c *C) {
   315  	restore := devicestate.MockPopulateStateFromSeed(func(st *state.State, opts *devicestate.PopulateStateFromSeedOptions, tm timings.Measurer) (ts []*state.TaskSet, err error) {
   316  		c.Assert(opts, IsNil)
   317  		t := s.state.NewTask("test-task", "a random task")
   318  		ts = append(ts, state.NewTaskSet(t))
   319  		return ts, nil
   320  	})
   321  	defer restore()
   322  
   323  	err := devicestate.EnsureSeeded(s.mgr)
   324  	c.Assert(err, IsNil)
   325  
   326  	s.state.Lock()
   327  	defer s.state.Unlock()
   328  
   329  	c.Check(s.state.Changes(), HasLen, 1)
   330  
   331  	var seedStartTime time.Time
   332  	c.Assert(s.state.Get("seed-start-time", &seedStartTime), IsNil)
   333  	c.Check(seedStartTime.Equal(devicestate.StartTime()), Equals, true)
   334  }
   335  
   336  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkSkippedOnClassic(c *C) {
   337  	s.bootloader.GetErr = fmt.Errorf("should not be called")
   338  	release.OnClassic = true
   339  
   340  	err := devicestate.EnsureBootOk(s.mgr)
   341  	c.Assert(err, IsNil)
   342  }
   343  
   344  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkSkippedOnNonRunModes(c *C) {
   345  	s.bootloader.GetErr = fmt.Errorf("should not be called")
   346  	devicestate.SetSystemMode(s.mgr, "install")
   347  
   348  	err := devicestate.EnsureBootOk(s.mgr)
   349  	c.Assert(err, IsNil)
   350  }
   351  
   352  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeededHappyWithModeenv(c *C) {
   353  	n := 0
   354  	restore := devicestate.MockPopulateStateFromSeed(func(st *state.State, opts *devicestate.PopulateStateFromSeedOptions, tm timings.Measurer) (ts []*state.TaskSet, err error) {
   355  		c.Assert(opts, NotNil)
   356  		c.Check(opts.Label, Equals, "20191127")
   357  		c.Check(opts.Mode, Equals, "install")
   358  
   359  		t := s.state.NewTask("test-task", "a random task")
   360  		ts = append(ts, state.NewTaskSet(t))
   361  
   362  		n++
   363  		return ts, nil
   364  	})
   365  	defer restore()
   366  
   367  	// mock the modeenv file
   368  	m := boot.Modeenv{
   369  		Mode:           "install",
   370  		RecoverySystem: "20191127",
   371  	}
   372  	err := m.WriteTo("")
   373  	c.Assert(err, IsNil)
   374  
   375  	// re-create manager so that modeenv file is-read
   376  	s.mgr, err = devicestate.Manager(s.state, s.hookMgr, s.o.TaskRunner(), s.newStore)
   377  	c.Assert(err, IsNil)
   378  
   379  	err = devicestate.EnsureSeeded(s.mgr)
   380  	c.Assert(err, IsNil)
   381  
   382  	s.state.Lock()
   383  	defer s.state.Unlock()
   384  
   385  	c.Check(s.state.Changes(), HasLen, 1)
   386  	c.Check(n, Equals, 1)
   387  }
   388  
   389  func (s *deviceMgrBaseSuite) makeModelAssertionInState(c *C, brandID, model string, extras map[string]interface{}) *asserts.Model {
   390  	modelAs := s.brands.Model(brandID, model, extras)
   391  
   392  	s.setupBrands(c)
   393  	assertstatetest.AddMany(s.state, modelAs)
   394  	return modelAs
   395  }
   396  
   397  func (s *deviceMgrBaseSuite) setPCModelInState(c *C) {
   398  	s.state.Lock()
   399  	defer s.state.Unlock()
   400  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   401  		"architecture": "amd64",
   402  		"kernel":       "pc-kernel",
   403  		"gadget":       "pc",
   404  	})
   405  
   406  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   407  		Brand:  "canonical",
   408  		Model:  "pc",
   409  		Serial: "serialserialserial",
   410  	})
   411  }
   412  
   413  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkBootloaderHappy(c *C) {
   414  	s.setPCModelInState(c)
   415  
   416  	s.bootloader.SetBootVars(map[string]string{
   417  		"snap_mode":     boot.TryingStatus,
   418  		"snap_try_core": "core_1.snap",
   419  	})
   420  
   421  	s.state.Lock()
   422  	defer s.state.Unlock()
   423  	siCore1 := &snap.SideInfo{RealName: "core", Revision: snap.R(1)}
   424  	snapstate.Set(s.state, "core", &snapstate.SnapState{
   425  		SnapType: "os",
   426  		Active:   true,
   427  		Sequence: []*snap.SideInfo{siCore1},
   428  		Current:  siCore1.Revision,
   429  	})
   430  
   431  	s.state.Unlock()
   432  	err := devicestate.EnsureBootOk(s.mgr)
   433  	s.state.Lock()
   434  	c.Assert(err, IsNil)
   435  
   436  	m, err := s.bootloader.GetBootVars("snap_mode")
   437  	c.Assert(err, IsNil)
   438  	c.Assert(m, DeepEquals, map[string]string{"snap_mode": ""})
   439  }
   440  
   441  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkUpdateBootRevisionsHappy(c *C) {
   442  	s.setPCModelInState(c)
   443  
   444  	// simulate that we have a new core_2, tried to boot it but that failed
   445  	s.bootloader.SetBootVars(map[string]string{
   446  		"snap_mode":     "",
   447  		"snap_kernel":   "kernel_1.snap",
   448  		"snap_try_core": "core_2.snap",
   449  		"snap_core":     "core_1.snap",
   450  	})
   451  
   452  	s.state.Lock()
   453  	defer s.state.Unlock()
   454  	siKernel1 := &snap.SideInfo{RealName: "kernel", Revision: snap.R(1)}
   455  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
   456  		SnapType: "kernel",
   457  		Active:   true,
   458  		Sequence: []*snap.SideInfo{siKernel1},
   459  		Current:  siKernel1.Revision,
   460  	})
   461  
   462  	siCore1 := &snap.SideInfo{RealName: "core", Revision: snap.R(1)}
   463  	siCore2 := &snap.SideInfo{RealName: "core", Revision: snap.R(2)}
   464  	snapstate.Set(s.state, "core", &snapstate.SnapState{
   465  		SnapType: "os",
   466  		Active:   true,
   467  		Sequence: []*snap.SideInfo{siCore1, siCore2},
   468  		Current:  siCore2.Revision,
   469  	})
   470  
   471  	s.state.Unlock()
   472  	err := devicestate.EnsureBootOk(s.mgr)
   473  	s.state.Lock()
   474  	c.Assert(err, IsNil)
   475  
   476  	c.Check(s.state.Changes(), HasLen, 1)
   477  	c.Check(s.state.Changes()[0].Kind(), Equals, "update-revisions")
   478  }
   479  
   480  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkNotRunAgain(c *C) {
   481  	s.setPCModelInState(c)
   482  
   483  	s.bootloader.SetBootVars(map[string]string{
   484  		"snap_mode":     boot.TryingStatus,
   485  		"snap_try_core": "core_1.snap",
   486  	})
   487  	s.bootloader.SetErr = fmt.Errorf("ensure bootloader is not used")
   488  
   489  	devicestate.SetBootOkRan(s.mgr, true)
   490  
   491  	err := devicestate.EnsureBootOk(s.mgr)
   492  	c.Assert(err, IsNil)
   493  }
   494  
   495  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkError(c *C) {
   496  	s.setPCModelInState(c)
   497  
   498  	s.state.Lock()
   499  	// seeded
   500  	s.state.Set("seeded", true)
   501  	// has serial
   502  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   503  		Brand:  "canonical",
   504  		Model:  "pc",
   505  		Serial: "8989",
   506  	})
   507  	s.state.Unlock()
   508  
   509  	s.bootloader.GetErr = fmt.Errorf("bootloader err")
   510  
   511  	devicestate.SetBootOkRan(s.mgr, false)
   512  
   513  	err := s.mgr.Ensure()
   514  	c.Assert(err, ErrorMatches, "devicemgr: cannot mark boot successful: bootloader err")
   515  }
   516  
   517  func (s *deviceMgrBaseSuite) setupBrands(c *C) {
   518  	assertstatetest.AddMany(s.state, s.brands.AccountsAndKeys("my-brand")...)
   519  	otherAcct := assertstest.NewAccount(s.storeSigning, "other-brand", map[string]interface{}{
   520  		"account-id": "other-brand",
   521  	}, "")
   522  	assertstatetest.AddMany(s.state, otherAcct)
   523  }
   524  
   525  func (s *deviceMgrSuite) setupSnapDecl(c *C, info *snap.Info, publisherID string) {
   526  	snapDecl, err := s.storeSigning.Sign(asserts.SnapDeclarationType, map[string]interface{}{
   527  		"series":       "16",
   528  		"snap-name":    info.SnapName(),
   529  		"snap-id":      info.SnapID,
   530  		"publisher-id": publisherID,
   531  		"timestamp":    time.Now().UTC().Format(time.RFC3339),
   532  	}, nil, "")
   533  	c.Assert(err, IsNil)
   534  	assertstatetest.AddMany(s.state, snapDecl)
   535  }
   536  
   537  func fakeMyModel(extra map[string]interface{}) *asserts.Model {
   538  	model := map[string]interface{}{
   539  		"type":         "model",
   540  		"authority-id": "my-brand",
   541  		"series":       "16",
   542  		"brand-id":     "my-brand",
   543  		"model":        "my-model",
   544  	}
   545  	return assertstest.FakeAssertion(model, extra).(*asserts.Model)
   546  }
   547  
   548  func (s *deviceMgrSuite) TestCheckGadget(c *C) {
   549  	s.state.Lock()
   550  	defer s.state.Unlock()
   551  
   552  	gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: other-gadget, version: 0}", nil)
   553  
   554  	s.setupBrands(c)
   555  	// model assertion in device context
   556  	model := fakeMyModel(map[string]interface{}{
   557  		"architecture": "amd64",
   558  		"gadget":       "gadget",
   559  		"kernel":       "krnl",
   560  	})
   561  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
   562  
   563  	err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   564  	c.Check(err, ErrorMatches, `cannot install gadget "other-gadget", model assertion requests "gadget"`)
   565  
   566  	// brand gadget
   567  	brandGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   568  	brandGadgetInfo.SnapID = "brand-gadget-id"
   569  	s.setupSnapDecl(c, brandGadgetInfo, "my-brand")
   570  
   571  	// canonical gadget
   572  	canonicalGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   573  	canonicalGadgetInfo.SnapID = "canonical-gadget-id"
   574  	s.setupSnapDecl(c, canonicalGadgetInfo, "canonical")
   575  
   576  	// other gadget
   577  	otherGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   578  	otherGadgetInfo.SnapID = "other-gadget-id"
   579  	s.setupSnapDecl(c, otherGadgetInfo, "other-brand")
   580  
   581  	// install brand gadget ok
   582  	err = devicestate.CheckGadgetOrKernel(s.state, brandGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   583  	c.Check(err, IsNil)
   584  
   585  	// install canonical gadget ok
   586  	err = devicestate.CheckGadgetOrKernel(s.state, canonicalGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   587  	c.Check(err, IsNil)
   588  
   589  	// install other gadget fails
   590  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   591  	c.Check(err, ErrorMatches, `cannot install gadget "gadget" published by "other-brand" for model by "my-brand"`)
   592  
   593  	// unasserted installation of other works
   594  	otherGadgetInfo.SnapID = ""
   595  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   596  	c.Check(err, IsNil)
   597  
   598  	// parallel install fails
   599  	otherGadgetInfo.InstanceKey = "foo"
   600  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   601  	c.Check(err, ErrorMatches, `cannot install "gadget_foo", parallel installation of kernel or gadget snaps is not supported`)
   602  }
   603  
   604  func (s *deviceMgrSuite) TestCheckGadgetOnClassic(c *C) {
   605  	release.OnClassic = true
   606  
   607  	s.state.Lock()
   608  	defer s.state.Unlock()
   609  
   610  	gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: other-gadget, version: 0}", nil)
   611  
   612  	s.setupBrands(c)
   613  	// model assertion in device context
   614  	model := fakeMyModel(map[string]interface{}{
   615  		"classic": "true",
   616  		"gadget":  "gadget",
   617  	})
   618  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
   619  
   620  	err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   621  	c.Check(err, ErrorMatches, `cannot install gadget "other-gadget", model assertion requests "gadget"`)
   622  
   623  	// brand gadget
   624  	brandGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   625  	brandGadgetInfo.SnapID = "brand-gadget-id"
   626  	s.setupSnapDecl(c, brandGadgetInfo, "my-brand")
   627  
   628  	// canonical gadget
   629  	canonicalGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   630  	canonicalGadgetInfo.SnapID = "canonical-gadget-id"
   631  	s.setupSnapDecl(c, canonicalGadgetInfo, "canonical")
   632  
   633  	// other gadget
   634  	otherGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   635  	otherGadgetInfo.SnapID = "other-gadget-id"
   636  	s.setupSnapDecl(c, otherGadgetInfo, "other-brand")
   637  
   638  	// install brand gadget ok
   639  	err = devicestate.CheckGadgetOrKernel(s.state, brandGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   640  	c.Check(err, IsNil)
   641  
   642  	// install canonical gadget ok
   643  	err = devicestate.CheckGadgetOrKernel(s.state, canonicalGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   644  	c.Check(err, IsNil)
   645  
   646  	// install other gadget fails
   647  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   648  	c.Check(err, ErrorMatches, `cannot install gadget "gadget" published by "other-brand" for model by "my-brand"`)
   649  
   650  	// unasserted installation of other works
   651  	otherGadgetInfo.SnapID = ""
   652  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   653  	c.Check(err, IsNil)
   654  }
   655  
   656  func (s *deviceMgrSuite) TestCheckGadgetOnClassicGadgetNotSpecified(c *C) {
   657  	release.OnClassic = true
   658  
   659  	s.state.Lock()
   660  	defer s.state.Unlock()
   661  
   662  	gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   663  
   664  	s.setupBrands(c)
   665  	// model assertion in device context
   666  	model := fakeMyModel(map[string]interface{}{
   667  		"classic": "true",
   668  	})
   669  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
   670  
   671  	err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   672  	c.Check(err, ErrorMatches, `cannot install gadget snap on classic if not requested by the model`)
   673  }
   674  
   675  func (s *deviceMgrSuite) TestCheckGadgetValid(c *C) {
   676  	s.state.Lock()
   677  	defer s.state.Unlock()
   678  
   679  	// model assertion in device context
   680  	model := fakeMyModel(map[string]interface{}{
   681  		"architecture": "amd64",
   682  		"gadget":       "gadget",
   683  		"kernel":       "krnl",
   684  	})
   685  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
   686  
   687  	gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   688  
   689  	// valid gadget.yaml
   690  	cont := snaptest.MockContainer(c, [][]string{
   691  		{"meta/gadget.yaml", gadgetYaml},
   692  	})
   693  	err := devicestate.CheckGadgetValid(s.state, gadgetInfo, nil, cont, snapstate.Flags{}, deviceCtx)
   694  	c.Check(err, IsNil)
   695  
   696  	// invalid gadget.yaml
   697  	cont = snaptest.MockContainer(c, [][]string{
   698  		{"meta/gadget.yaml", `defaults:`},
   699  	})
   700  	err = devicestate.CheckGadgetValid(s.state, gadgetInfo, nil, cont, snapstate.Flags{}, deviceCtx)
   701  	c.Check(err, ErrorMatches, `bootloader not declared in any volume`)
   702  
   703  }
   704  
   705  func (s *deviceMgrSuite) TestCheckKernel(c *C) {
   706  	s.state.Lock()
   707  	defer s.state.Unlock()
   708  	kernelInfo := snaptest.MockInfo(c, "{type: kernel, name: lnrk, version: 0}", nil)
   709  
   710  	// not on classic
   711  	release.OnClassic = true
   712  	err := devicestate.CheckGadgetOrKernel(s.state, kernelInfo, nil, nil, snapstate.Flags{}, nil)
   713  	c.Check(err, ErrorMatches, `cannot install a kernel snap on classic`)
   714  	release.OnClassic = false
   715  
   716  	s.setupBrands(c)
   717  	// model assertion in device context
   718  	model := fakeMyModel(map[string]interface{}{
   719  		"architecture": "amd64",
   720  		"gadget":       "gadget",
   721  		"kernel":       "krnl",
   722  	})
   723  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
   724  
   725  	err = devicestate.CheckGadgetOrKernel(s.state, kernelInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   726  	c.Check(err, ErrorMatches, `cannot install kernel "lnrk", model assertion requests "krnl"`)
   727  
   728  	// brand kernel
   729  	brandKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil)
   730  	brandKrnlInfo.SnapID = "brand-krnl-id"
   731  	s.setupSnapDecl(c, brandKrnlInfo, "my-brand")
   732  
   733  	// canonical kernel
   734  	canonicalKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil)
   735  	canonicalKrnlInfo.SnapID = "canonical-krnl-id"
   736  	s.setupSnapDecl(c, canonicalKrnlInfo, "canonical")
   737  
   738  	// other kernel
   739  	otherKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil)
   740  	otherKrnlInfo.SnapID = "other-krnl-id"
   741  	s.setupSnapDecl(c, otherKrnlInfo, "other-brand")
   742  
   743  	// install brand kernel ok
   744  	err = devicestate.CheckGadgetOrKernel(s.state, brandKrnlInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   745  	c.Check(err, IsNil)
   746  
   747  	// install canonical kernel ok
   748  	err = devicestate.CheckGadgetOrKernel(s.state, canonicalKrnlInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   749  	c.Check(err, IsNil)
   750  
   751  	// install other kernel fails
   752  	err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   753  	c.Check(err, ErrorMatches, `cannot install kernel "krnl" published by "other-brand" for model by "my-brand"`)
   754  
   755  	// unasserted installation of other works
   756  	otherKrnlInfo.SnapID = ""
   757  	err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   758  	c.Check(err, IsNil)
   759  
   760  	// parallel install fails
   761  	otherKrnlInfo.InstanceKey = "foo"
   762  	err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   763  	c.Check(err, ErrorMatches, `cannot install "krnl_foo", parallel installation of kernel or gadget snaps is not supported`)
   764  }
   765  
   766  func makeSerialAssertionInState(c *C, brands *assertstest.SigningAccounts, st *state.State, brandID, model, serialN string) *asserts.Serial {
   767  	encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey())
   768  	c.Assert(err, IsNil)
   769  	serial, err := brands.Signing(brandID).Sign(asserts.SerialType, map[string]interface{}{
   770  		"brand-id":            brandID,
   771  		"model":               model,
   772  		"serial":              serialN,
   773  		"device-key":          string(encDevKey),
   774  		"device-key-sha3-384": devKey.PublicKey().ID(),
   775  		"timestamp":           time.Now().Format(time.RFC3339),
   776  	}, nil, "")
   777  	c.Assert(err, IsNil)
   778  	err = assertstate.Add(st, serial)
   779  	c.Assert(err, IsNil)
   780  	return serial.(*asserts.Serial)
   781  }
   782  
   783  func (s *deviceMgrBaseSuite) makeSerialAssertionInState(c *C, brandID, model, serialN string) *asserts.Serial {
   784  	return makeSerialAssertionInState(c, s.brands, s.state, brandID, model, serialN)
   785  }
   786  
   787  func (s *deviceMgrSuite) TestCanAutoRefreshOnCore(c *C) {
   788  	s.state.Lock()
   789  	defer s.state.Unlock()
   790  
   791  	canAutoRefresh := func() bool {
   792  		ok, err := devicestate.CanAutoRefresh(s.state)
   793  		c.Assert(err, IsNil)
   794  		return ok
   795  	}
   796  
   797  	// not seeded, no model, no serial -> no auto-refresh
   798  	s.state.Set("seeded", false)
   799  	c.Check(canAutoRefresh(), Equals, false)
   800  
   801  	// seeded, model, no serial -> no auto-refresh
   802  	s.state.Set("seeded", true)
   803  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   804  		Brand: "canonical",
   805  		Model: "pc",
   806  	})
   807  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   808  		"architecture": "amd64",
   809  		"kernel":       "pc-kernel",
   810  		"gadget":       "pc",
   811  	})
   812  	c.Check(canAutoRefresh(), Equals, false)
   813  
   814  	// seeded, model, serial -> auto-refresh
   815  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   816  		Brand:  "canonical",
   817  		Model:  "pc",
   818  		Serial: "8989",
   819  	})
   820  	s.makeSerialAssertionInState(c, "canonical", "pc", "8989")
   821  	c.Check(canAutoRefresh(), Equals, true)
   822  
   823  	// not seeded, model, serial -> no auto-refresh
   824  	s.state.Set("seeded", false)
   825  	c.Check(canAutoRefresh(), Equals, false)
   826  }
   827  
   828  func (s *deviceMgrSuite) TestCanAutoRefreshNoSerialFallback(c *C) {
   829  	s.state.Lock()
   830  	defer s.state.Unlock()
   831  
   832  	canAutoRefresh := func() bool {
   833  		ok, err := devicestate.CanAutoRefresh(s.state)
   834  		c.Assert(err, IsNil)
   835  		return ok
   836  	}
   837  
   838  	// seeded, model, no serial, two attempts at getting serial
   839  	// -> no auto-refresh
   840  	devicestate.IncEnsureOperationalAttempts(s.state)
   841  	devicestate.IncEnsureOperationalAttempts(s.state)
   842  	s.state.Set("seeded", true)
   843  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   844  		Brand: "canonical",
   845  		Model: "pc",
   846  	})
   847  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   848  		"architecture": "amd64",
   849  		"kernel":       "pc-kernel",
   850  		"gadget":       "pc",
   851  	})
   852  	c.Check(canAutoRefresh(), Equals, false)
   853  
   854  	// third attempt ongoing, or done
   855  	// fallback, try auto-refresh
   856  	devicestate.IncEnsureOperationalAttempts(s.state)
   857  	// sanity
   858  	c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 3)
   859  	c.Check(canAutoRefresh(), Equals, true)
   860  }
   861  
   862  func (s *deviceMgrSuite) TestCanAutoRefreshOnClassic(c *C) {
   863  	release.OnClassic = true
   864  
   865  	s.state.Lock()
   866  	defer s.state.Unlock()
   867  
   868  	canAutoRefresh := func() bool {
   869  		ok, err := devicestate.CanAutoRefresh(s.state)
   870  		c.Assert(err, IsNil)
   871  		return ok
   872  	}
   873  
   874  	// not seeded, no model, no serial -> no auto-refresh
   875  	s.state.Set("seeded", false)
   876  	c.Check(canAutoRefresh(), Equals, false)
   877  
   878  	// seeded, no model -> auto-refresh
   879  	s.state.Set("seeded", true)
   880  	c.Check(canAutoRefresh(), Equals, false)
   881  
   882  	// seeded, model, no serial -> no auto-refresh
   883  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   884  		Brand: "canonical",
   885  		Model: "pc",
   886  	})
   887  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   888  		"classic": "true",
   889  	})
   890  	c.Check(canAutoRefresh(), Equals, false)
   891  
   892  	// seeded, model, serial -> auto-refresh
   893  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   894  		Brand:  "canonical",
   895  		Model:  "pc",
   896  		Serial: "8989",
   897  	})
   898  	s.makeSerialAssertionInState(c, "canonical", "pc", "8989")
   899  	c.Check(canAutoRefresh(), Equals, true)
   900  
   901  	// not seeded, model, serial -> no auto-refresh
   902  	s.state.Set("seeded", false)
   903  	c.Check(canAutoRefresh(), Equals, false)
   904  }
   905  
   906  func makeInstalledMockCoreSnapWithSnapdControl(c *C, st *state.State) *snap.Info {
   907  	sideInfoCore11 := &snap.SideInfo{RealName: "core", Revision: snap.R(11), SnapID: "core-id"}
   908  	snapstate.Set(st, "core", &snapstate.SnapState{
   909  		Active:   true,
   910  		Sequence: []*snap.SideInfo{sideInfoCore11},
   911  		Current:  sideInfoCore11.Revision,
   912  		SnapType: "os",
   913  	})
   914  	core11 := snaptest.MockSnap(c, `
   915  name: core
   916  version: 1.0
   917  slots:
   918   snapd-control:
   919  `, sideInfoCore11)
   920  	c.Assert(core11.Slots, HasLen, 1)
   921  
   922  	return core11
   923  }
   924  
   925  var snapWithSnapdControlRefreshScheduleManagedYAML = `
   926  name: snap-with-snapd-control
   927  version: 1.0
   928  plugs:
   929   snapd-control:
   930    refresh-schedule: managed
   931  `
   932  
   933  var snapWithSnapdControlOnlyYAML = `
   934  name: snap-with-snapd-control
   935  version: 1.0
   936  plugs:
   937   snapd-control:
   938  `
   939  
   940  func makeInstalledMockSnap(c *C, st *state.State, yml string) *snap.Info {
   941  	sideInfo11 := &snap.SideInfo{RealName: "snap-with-snapd-control", Revision: snap.R(11), SnapID: "snap-with-snapd-control-id"}
   942  	snapstate.Set(st, "snap-with-snapd-control", &snapstate.SnapState{
   943  		Active:   true,
   944  		Sequence: []*snap.SideInfo{sideInfo11},
   945  		Current:  sideInfo11.Revision,
   946  		SnapType: "app",
   947  	})
   948  	info11 := snaptest.MockSnap(c, yml, sideInfo11)
   949  	c.Assert(info11.Plugs, HasLen, 1)
   950  
   951  	return info11
   952  }
   953  
   954  func makeInstalledMockKernelSnap(c *C, st *state.State, yml string) *snap.Info {
   955  	sideInfo11 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(11), SnapID: "pc-kernel-id"}
   956  	snapstate.Set(st, "pc-kernel", &snapstate.SnapState{
   957  		Active:   true,
   958  		Sequence: []*snap.SideInfo{sideInfo11},
   959  		Current:  sideInfo11.Revision,
   960  		SnapType: "kernel",
   961  	})
   962  	info11 := snaptest.MockSnap(c, yml, sideInfo11)
   963  
   964  	return info11
   965  }
   966  
   967  func makeMockRepoWithConnectedSnaps(c *C, st *state.State, info11, core11 *snap.Info, ifname string) {
   968  	repo := interfaces.NewRepository()
   969  	for _, iface := range builtin.Interfaces() {
   970  		err := repo.AddInterface(iface)
   971  		c.Assert(err, IsNil)
   972  	}
   973  	err := repo.AddSnap(info11)
   974  	c.Assert(err, IsNil)
   975  	err = repo.AddSnap(core11)
   976  	c.Assert(err, IsNil)
   977  	_, err = repo.Connect(&interfaces.ConnRef{
   978  		PlugRef: interfaces.PlugRef{Snap: info11.InstanceName(), Name: ifname},
   979  		SlotRef: interfaces.SlotRef{Snap: core11.InstanceName(), Name: ifname},
   980  	}, nil, nil, nil, nil, nil)
   981  	c.Assert(err, IsNil)
   982  	conns, err := repo.Connected("snap-with-snapd-control", "snapd-control")
   983  	c.Assert(err, IsNil)
   984  	c.Assert(conns, HasLen, 1)
   985  	ifacerepo.Replace(st, repo)
   986  }
   987  
   988  func (s *deviceMgrSuite) TestCanManageRefreshes(c *C) {
   989  	st := s.state
   990  	st.Lock()
   991  	defer st.Unlock()
   992  
   993  	// not possbile to manage by default
   994  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
   995  
   996  	// not possible with just a snap with "snapd-control" plug with the
   997  	// right attribute
   998  	info11 := makeInstalledMockSnap(c, st, snapWithSnapdControlRefreshScheduleManagedYAML)
   999  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
  1000  
  1001  	// not possible with a core snap with snapd control
  1002  	core11 := makeInstalledMockCoreSnapWithSnapdControl(c, st)
  1003  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
  1004  
  1005  	// not possible even with connected interfaces
  1006  	makeMockRepoWithConnectedSnaps(c, st, info11, core11, "snapd-control")
  1007  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
  1008  
  1009  	// if all of the above plus a snap declaration are in place we can
  1010  	// manage schedules
  1011  	s.setupSnapDecl(c, info11, "canonical")
  1012  	c.Check(devicestate.CanManageRefreshes(st), Equals, true)
  1013  
  1014  	// works if the snap is not active as well (to fix race when a
  1015  	// snap is refreshed)
  1016  	var sideInfo11 snapstate.SnapState
  1017  	err := snapstate.Get(st, "snap-with-snapd-control", &sideInfo11)
  1018  	c.Assert(err, IsNil)
  1019  	sideInfo11.Active = false
  1020  	snapstate.Set(st, "snap-with-snapd-control", &sideInfo11)
  1021  	c.Check(devicestate.CanManageRefreshes(st), Equals, true)
  1022  }
  1023  
  1024  func (s *deviceMgrSuite) TestCanManageRefreshesNoRefreshScheduleManaged(c *C) {
  1025  	st := s.state
  1026  	st.Lock()
  1027  	defer st.Unlock()
  1028  
  1029  	// just having a connected "snapd-control" interface is not enough
  1030  	// for setting refresh.schedule=managed
  1031  	info11 := makeInstalledMockSnap(c, st, snapWithSnapdControlOnlyYAML)
  1032  	core11 := makeInstalledMockCoreSnapWithSnapdControl(c, st)
  1033  	makeMockRepoWithConnectedSnaps(c, st, info11, core11, "snapd-control")
  1034  	s.setupSnapDecl(c, info11, "canonical")
  1035  
  1036  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
  1037  }
  1038  
  1039  func (s *deviceMgrSuite) TestReloadRegistered(c *C) {
  1040  	st := state.New(nil)
  1041  
  1042  	runner1 := state.NewTaskRunner(st)
  1043  	hookMgr1, err := hookstate.Manager(st, runner1)
  1044  	c.Assert(err, IsNil)
  1045  	mgr1, err := devicestate.Manager(st, hookMgr1, runner1, nil)
  1046  	c.Assert(err, IsNil)
  1047  
  1048  	ok := false
  1049  	select {
  1050  	case <-mgr1.Registered():
  1051  	default:
  1052  		ok = true
  1053  	}
  1054  	c.Check(ok, Equals, true)
  1055  
  1056  	st.Lock()
  1057  	devicestatetest.SetDevice(st, &auth.DeviceState{
  1058  		Brand:  "canonical",
  1059  		Model:  "pc",
  1060  		Serial: "serial",
  1061  	})
  1062  	st.Unlock()
  1063  
  1064  	runner2 := state.NewTaskRunner(st)
  1065  	hookMgr2, err := hookstate.Manager(st, runner2)
  1066  	c.Assert(err, IsNil)
  1067  	mgr2, err := devicestate.Manager(st, hookMgr2, runner2, nil)
  1068  	c.Assert(err, IsNil)
  1069  
  1070  	ok = false
  1071  	select {
  1072  	case <-mgr2.Registered():
  1073  		ok = true
  1074  	case <-time.After(5 * time.Second):
  1075  		c.Fatal("should have been marked registered")
  1076  	}
  1077  	c.Check(ok, Equals, true)
  1078  }
  1079  
  1080  func (s *deviceMgrSuite) TestMarkSeededInConfig(c *C) {
  1081  	st := s.state
  1082  	st.Lock()
  1083  	defer st.Unlock()
  1084  
  1085  	// avoid device registration
  1086  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1087  		Serial: "123",
  1088  	})
  1089  
  1090  	// avoid full seeding
  1091  	s.seeding()
  1092  
  1093  	// not seeded -> no config is set
  1094  	s.state.Unlock()
  1095  	s.mgr.Ensure()
  1096  	s.state.Lock()
  1097  
  1098  	var seedLoaded bool
  1099  	tr := config.NewTransaction(st)
  1100  	tr.Get("core", "seed.loaded", &seedLoaded)
  1101  	c.Check(seedLoaded, Equals, false)
  1102  
  1103  	// pretend we are seeded now
  1104  	s.state.Set("seeded", true)
  1105  
  1106  	// seeded -> config got updated
  1107  	s.state.Unlock()
  1108  	s.mgr.Ensure()
  1109  	s.state.Lock()
  1110  
  1111  	tr = config.NewTransaction(st)
  1112  	tr.Get("core", "seed.loaded", &seedLoaded)
  1113  	c.Check(seedLoaded, Equals, true)
  1114  
  1115  	// only the fake seeding change is in the state, no further
  1116  	// changes
  1117  	c.Check(s.state.Changes(), HasLen, 1)
  1118  }
  1119  
  1120  func (s *deviceMgrSuite) TestDevicemgrCanStandby(c *C) {
  1121  	st := state.New(nil)
  1122  
  1123  	runner := state.NewTaskRunner(st)
  1124  	hookMgr, err := hookstate.Manager(st, runner)
  1125  	c.Assert(err, IsNil)
  1126  	mgr, err := devicestate.Manager(st, hookMgr, runner, nil)
  1127  	c.Assert(err, IsNil)
  1128  
  1129  	st.Lock()
  1130  	defer st.Unlock()
  1131  	c.Check(mgr.CanStandby(), Equals, false)
  1132  
  1133  	st.Set("seeded", true)
  1134  	c.Check(mgr.CanStandby(), Equals, true)
  1135  }
  1136  
  1137  func (s *deviceMgrSuite) TestDeviceManagerReadsModeenv(c *C) {
  1138  	modeEnv := &boot.Modeenv{Mode: "install"}
  1139  	err := modeEnv.WriteTo("")
  1140  	c.Assert(err, IsNil)
  1141  
  1142  	runner := s.o.TaskRunner()
  1143  	mgr, err := devicestate.Manager(s.state, s.hookMgr, runner, s.newStore)
  1144  	c.Assert(err, IsNil)
  1145  	c.Assert(mgr, NotNil)
  1146  	c.Assert(mgr.SystemMode(), Equals, "install")
  1147  }
  1148  
  1149  func (s *deviceMgrSuite) TestDeviceManagerEmptySystemModeRun(c *C) {
  1150  	// set empty system mode
  1151  	devicestate.SetSystemMode(s.mgr, "")
  1152  
  1153  	// empty is returned as "run"
  1154  	c.Check(s.mgr.SystemMode(), Equals, "run")
  1155  }
  1156  
  1157  const (
  1158  	mountRunMntUbuntuSaveFmt = `26 27 8:3 / %s/run/mnt/ubuntu-save rw,relatime shared:7 - ext4 /dev/fakedevice0p1 rw,data=ordered`
  1159  	mountSnapSaveFmt         = `26 27 8:3 / %s/var/lib/snapd/save rw,relatime shared:7 - ext4 /dev/fakedevice0p1 rw,data=ordered`
  1160  )
  1161  
  1162  func (s *deviceMgrSuite) TestDeviceManagerStartupUC20UbuntuSaveFullHappy(c *C) {
  1163  	modeEnv := &boot.Modeenv{Mode: "run"}
  1164  	err := modeEnv.WriteTo("")
  1165  	c.Assert(err, IsNil)
  1166  	// create a new manager so that the modeenv we mocked in read
  1167  	mgr, err := devicestate.Manager(s.state, s.hookMgr, s.o.TaskRunner(), s.newStore)
  1168  	c.Assert(err, IsNil)
  1169  
  1170  	cmd := testutil.MockCommand(c, "systemd-mount", "")
  1171  	defer cmd.Restore()
  1172  
  1173  	// ubuntu-save not mounted
  1174  	err = mgr.StartUp()
  1175  	c.Assert(err, IsNil)
  1176  	c.Check(cmd.Calls(), HasLen, 0)
  1177  
  1178  	restore := osutil.MockMountInfo(fmt.Sprintf(mountRunMntUbuntuSaveFmt, dirs.GlobalRootDir))
  1179  	defer restore()
  1180  
  1181  	err = mgr.StartUp()
  1182  	c.Assert(err, IsNil)
  1183  	c.Check(cmd.Calls(), DeepEquals, [][]string{
  1184  		{"systemd-mount", "-o", "bind", boot.InitramfsUbuntuSaveDir, dirs.SnapSaveDir},
  1185  	})
  1186  
  1187  	// known as available
  1188  	c.Check(devicestate.SaveAvailable(mgr), Equals, true)
  1189  }
  1190  
  1191  func (s *deviceMgrSuite) TestDeviceManagerStartupUC20UbuntuSaveAlreadyMounted(c *C) {
  1192  	modeEnv := &boot.Modeenv{Mode: "run"}
  1193  	err := modeEnv.WriteTo("")
  1194  	c.Assert(err, IsNil)
  1195  	// create a new manager so that the modeenv we mocked in read
  1196  	mgr, err := devicestate.Manager(s.state, s.hookMgr, s.o.TaskRunner(), s.newStore)
  1197  	c.Assert(err, IsNil)
  1198  
  1199  	cmd := testutil.MockCommand(c, "systemd-mount", "")
  1200  	defer cmd.Restore()
  1201  
  1202  	// already mounted
  1203  	restore := osutil.MockMountInfo(fmt.Sprintf(mountRunMntUbuntuSaveFmt, dirs.GlobalRootDir) + "\n" +
  1204  		fmt.Sprintf(mountSnapSaveFmt, dirs.GlobalRootDir))
  1205  	defer restore()
  1206  
  1207  	err = mgr.StartUp()
  1208  	c.Assert(err, IsNil)
  1209  	c.Check(cmd.Calls(), HasLen, 0)
  1210  
  1211  	// known as available
  1212  	c.Check(devicestate.SaveAvailable(mgr), Equals, true)
  1213  }
  1214  
  1215  func (s *deviceMgrSuite) TestDeviceManagerStartupUC20NoUbuntuSave(c *C) {
  1216  	modeEnv := &boot.Modeenv{Mode: "run"}
  1217  	err := modeEnv.WriteTo("")
  1218  	c.Assert(err, IsNil)
  1219  	// create a new manager so that the modeenv we mocked in read
  1220  	mgr, err := devicestate.Manager(s.state, s.hookMgr, s.o.TaskRunner(), s.newStore)
  1221  	c.Assert(err, IsNil)
  1222  
  1223  	cmd := testutil.MockCommand(c, "systemd-mount", "")
  1224  	defer cmd.Restore()
  1225  
  1226  	// ubuntu-save not mounted
  1227  	err = mgr.StartUp()
  1228  	c.Assert(err, IsNil)
  1229  	c.Check(cmd.Calls(), HasLen, 0)
  1230  
  1231  	// known as available
  1232  	c.Check(devicestate.SaveAvailable(mgr), Equals, true)
  1233  }
  1234  
  1235  func (s *deviceMgrSuite) TestDeviceManagerStartupUC20UbuntuSaveErr(c *C) {
  1236  	modeEnv := &boot.Modeenv{Mode: "run"}
  1237  	err := modeEnv.WriteTo("")
  1238  	c.Assert(err, IsNil)
  1239  	// create a new manager so that the modeenv we mocked in read
  1240  	mgr, err := devicestate.Manager(s.state, s.hookMgr, s.o.TaskRunner(), s.newStore)
  1241  	c.Assert(err, IsNil)
  1242  
  1243  	cmd := testutil.MockCommand(c, "systemd-mount", "echo failed; exit 1")
  1244  	defer cmd.Restore()
  1245  
  1246  	restore := osutil.MockMountInfo(fmt.Sprintf(mountRunMntUbuntuSaveFmt, dirs.GlobalRootDir))
  1247  	defer restore()
  1248  
  1249  	err = mgr.StartUp()
  1250  	c.Assert(err, ErrorMatches, "cannot set up ubuntu-save: cannot bind mount .*/run/mnt/ubuntu-save under .*/var/lib/snapd/save: failed")
  1251  	c.Check(cmd.Calls(), DeepEquals, [][]string{
  1252  		{"systemd-mount", "-o", "bind", boot.InitramfsUbuntuSaveDir, dirs.SnapSaveDir},
  1253  	})
  1254  
  1255  	// known as not available
  1256  	c.Check(devicestate.SaveAvailable(mgr), Equals, false)
  1257  }
  1258  
  1259  func (s *deviceMgrSuite) TestDeviceManagerStartupNonUC20NoUbuntuSave(c *C) {
  1260  	err := os.RemoveAll(dirs.SnapModeenvFileUnder(dirs.GlobalRootDir))
  1261  	c.Assert(err, IsNil)
  1262  	// create a new manager so that we know it does not see the modeenv
  1263  	mgr, err := devicestate.Manager(s.state, s.hookMgr, s.o.TaskRunner(), s.newStore)
  1264  	c.Assert(err, IsNil)
  1265  
  1266  	cmd := testutil.MockCommand(c, "systemd-mount", "")
  1267  	defer cmd.Restore()
  1268  
  1269  	// ubuntu-save not mounted
  1270  	err = mgr.StartUp()
  1271  	c.Assert(err, IsNil)
  1272  	c.Check(cmd.Calls(), HasLen, 0)
  1273  
  1274  	// known as not available
  1275  	c.Check(devicestate.SaveAvailable(mgr), Equals, false)
  1276  }
  1277  
  1278  var kernelYamlNoFdeSetup = `name: pc-kernel
  1279  version: 1.0
  1280  type: kernel
  1281  `
  1282  
  1283  var kernelYamlWithFdeSetup = `name: pc-kernel
  1284  version: 1.0
  1285  type: kernel
  1286  hooks:
  1287   fde-setup:
  1288  `
  1289  
  1290  func (s *deviceMgrSuite) TestHasFdeSetupHook(c *C) {
  1291  	st := s.state
  1292  	st.Lock()
  1293  	defer st.Unlock()
  1294  
  1295  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
  1296  		"architecture": "amd64",
  1297  		"kernel":       "pc-kernel",
  1298  		"gadget":       "pc",
  1299  	})
  1300  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1301  		Brand: "canonical",
  1302  		Model: "pc",
  1303  	})
  1304  
  1305  	for _, tc := range []struct {
  1306  		kernelYaml      string
  1307  		hasFdeSetupHook bool
  1308  	}{
  1309  		{kernelYamlNoFdeSetup, false},
  1310  		{kernelYamlWithFdeSetup, true},
  1311  	} {
  1312  		makeInstalledMockKernelSnap(c, st, tc.kernelYaml)
  1313  
  1314  		hasHook, err := devicestate.DeviceManagerHasFDESetupHook(s.mgr)
  1315  		c.Assert(err, IsNil)
  1316  		c.Check(hasHook, Equals, tc.hasFdeSetupHook)
  1317  	}
  1318  }
  1319  
  1320  func (s *deviceMgrSuite) TestRunFdeSetupHookOpInitialSetup(c *C) {
  1321  	st := s.state
  1322  
  1323  	st.Lock()
  1324  	makeInstalledMockKernelSnap(c, st, kernelYamlWithFdeSetup)
  1325  	mockModel := s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
  1326  		"architecture": "amd64",
  1327  		"kernel":       "pc-kernel",
  1328  		"gadget":       "pc",
  1329  	})
  1330  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1331  		Brand: "canonical",
  1332  		Model: "pc",
  1333  	})
  1334  	st.Unlock()
  1335  
  1336  	mockKey := secboot.EncryptionKey{1, 2, 3, 4}
  1337  
  1338  	var hookCalled []string
  1339  	hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) {
  1340  		ctx.Lock()
  1341  		defer ctx.Unlock()
  1342  
  1343  		// check that the context has the right data
  1344  		c.Check(ctx.HookName(), Equals, "fde-setup")
  1345  		var fdeSetup fde.SetupRequest
  1346  		ctx.Get("fde-setup-request", &fdeSetup)
  1347  		c.Check(fdeSetup, DeepEquals, fde.SetupRequest{
  1348  			Op:      "initial-setup",
  1349  			Key:     &mockKey,
  1350  			KeyName: "some-key-name",
  1351  			Models: []map[string]string{
  1352  				{
  1353  					"series":     "16",
  1354  					"brand-id":   "canonical",
  1355  					"model":      "pc",
  1356  					"grade":      "unset",
  1357  					"signkey-id": mockModel.SignKeyID(),
  1358  				},
  1359  			},
  1360  		})
  1361  
  1362  		// the snapctl fde-setup-result will set the data
  1363  		ctx.Set("fde-setup-result", []byte("sealed-key"))
  1364  		hookCalled = append(hookCalled, ctx.InstanceName())
  1365  		return nil, nil
  1366  	}
  1367  
  1368  	rhk := hookstate.MockRunHook(hookInvoke)
  1369  	defer rhk()
  1370  
  1371  	s.o.Loop()
  1372  	defer s.o.Stop()
  1373  
  1374  	params := &boot.FDESetupHookParams{
  1375  		Key:     mockKey,
  1376  		KeyName: "some-key-name",
  1377  		Models:  []*asserts.Model{mockModel},
  1378  	}
  1379  	st.Lock()
  1380  	data, err := devicestate.DeviceManagerRunFDESetupHook(s.mgr, "initial-setup", params)
  1381  	st.Unlock()
  1382  	c.Assert(err, IsNil)
  1383  	c.Check(string(data), Equals, "sealed-key")
  1384  	c.Check(hookCalled, DeepEquals, []string{"pc-kernel"})
  1385  }
  1386  
  1387  func (s *deviceMgrSuite) TestRunFdeSetupHookOpInitialSetupErrors(c *C) {
  1388  	st := s.state
  1389  
  1390  	st.Lock()
  1391  	makeInstalledMockKernelSnap(c, st, kernelYamlWithFdeSetup)
  1392  	mockModel := s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
  1393  		"architecture": "amd64",
  1394  		"kernel":       "pc-kernel",
  1395  		"gadget":       "pc",
  1396  	})
  1397  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1398  		Brand: "canonical",
  1399  		Model: "pc",
  1400  	})
  1401  	st.Unlock()
  1402  
  1403  	hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) {
  1404  		return nil, fmt.Errorf("hook failed")
  1405  	}
  1406  
  1407  	rhk := hookstate.MockRunHook(hookInvoke)
  1408  	defer rhk()
  1409  
  1410  	s.o.Loop()
  1411  	defer s.o.Stop()
  1412  
  1413  	params := &boot.FDESetupHookParams{
  1414  		Key:     secboot.EncryptionKey{1, 2, 3, 4},
  1415  		KeyName: "some-key-name",
  1416  		Models:  []*asserts.Model{mockModel},
  1417  	}
  1418  	st.Lock()
  1419  	_, err := devicestate.DeviceManagerRunFDESetupHook(s.mgr, "initial-setup", params)
  1420  	st.Unlock()
  1421  	c.Assert(err, ErrorMatches, `cannot run hook for "initial-setup": run hook "fde-setup": hook failed`)
  1422  }
  1423  
  1424  func (s *deviceMgrSuite) TestRunFdeSetupHookOpInitialSetupErrorResult(c *C) {
  1425  	st := s.state
  1426  
  1427  	st.Lock()
  1428  	makeInstalledMockKernelSnap(c, st, kernelYamlWithFdeSetup)
  1429  	mockModel := s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
  1430  		"architecture": "amd64",
  1431  		"kernel":       "pc-kernel",
  1432  		"gadget":       "pc",
  1433  	})
  1434  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1435  		Brand: "canonical",
  1436  		Model: "pc",
  1437  	})
  1438  	st.Unlock()
  1439  
  1440  	hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) {
  1441  		// Simulate an incorrect type for fde-setup-result here to
  1442  		// test error string from runFDESetupHook.
  1443  		// This should never happen in practice, "snapctl fde-setup"
  1444  		// will always set this to []byte
  1445  		ctx.Lock()
  1446  		ctx.Set("fde-setup-result", "not-bytes")
  1447  		ctx.Unlock()
  1448  		return nil, nil
  1449  	}
  1450  
  1451  	rhk := hookstate.MockRunHook(hookInvoke)
  1452  	defer rhk()
  1453  
  1454  	s.o.Loop()
  1455  	defer s.o.Stop()
  1456  
  1457  	params := &boot.FDESetupHookParams{
  1458  		Key:     secboot.EncryptionKey{1, 2, 3, 4},
  1459  		KeyName: "some-key-name",
  1460  		Models:  []*asserts.Model{mockModel},
  1461  	}
  1462  	st.Lock()
  1463  	_, err := devicestate.DeviceManagerRunFDESetupHook(s.mgr, "initial-setup", params)
  1464  	st.Unlock()
  1465  	c.Assert(err, ErrorMatches, `cannot get result from fde-setup hook "initial-setup": cannot unmarshal context value for "fde-setup-result": illegal base64 data at input byte 3`)
  1466  }
  1467  
  1468  type startOfOperationTimeSuite struct {
  1469  	state  *state.State
  1470  	mgr    *devicestate.DeviceManager
  1471  	runner *state.TaskRunner
  1472  }
  1473  
  1474  var _ = Suite(&startOfOperationTimeSuite{})
  1475  
  1476  func (s *startOfOperationTimeSuite) SetUpTest(c *C) {
  1477  	dirs.SetRootDir(c.MkDir())
  1478  	os.MkdirAll(dirs.SnapRunDir, 0755)
  1479  
  1480  	s.state = state.New(nil)
  1481  	s.runner = state.NewTaskRunner(s.state)
  1482  	s.mgr = nil
  1483  }
  1484  
  1485  func (s *startOfOperationTimeSuite) TearDownTest(c *C) {
  1486  	dirs.SetRootDir("")
  1487  }
  1488  
  1489  func (s *startOfOperationTimeSuite) manager(c *C) *devicestate.DeviceManager {
  1490  	if s.mgr == nil {
  1491  		hookMgr, err := hookstate.Manager(s.state, s.runner)
  1492  		c.Assert(err, IsNil)
  1493  		mgr, err := devicestate.Manager(s.state, hookMgr, s.runner, nil)
  1494  		c.Assert(err, IsNil)
  1495  		s.mgr = mgr
  1496  	}
  1497  	return s.mgr
  1498  }
  1499  
  1500  func (s *startOfOperationTimeSuite) TestStartOfOperationTimeFromSeedTime(c *C) {
  1501  	mgr := s.manager(c)
  1502  
  1503  	st := s.state
  1504  	st.Lock()
  1505  	defer st.Unlock()
  1506  
  1507  	seedTime := time.Now().AddDate(0, -1, 0)
  1508  	st.Set("seed-time", seedTime)
  1509  
  1510  	operationTime, err := mgr.StartOfOperationTime()
  1511  	c.Assert(err, IsNil)
  1512  	c.Check(operationTime.Equal(seedTime), Equals, true)
  1513  
  1514  	var op time.Time
  1515  	st.Get("start-of-operation-time", &op)
  1516  	c.Check(op.Equal(operationTime), Equals, true)
  1517  }
  1518  
  1519  func (s *startOfOperationTimeSuite) TestStartOfOperationTimeAlreadySet(c *C) {
  1520  	mgr := s.manager(c)
  1521  
  1522  	st := s.state
  1523  	st.Lock()
  1524  	defer st.Unlock()
  1525  
  1526  	op := time.Now().AddDate(0, -1, 0)
  1527  	st.Set("start-of-operation-time", op)
  1528  
  1529  	operationTime, err := mgr.StartOfOperationTime()
  1530  	c.Assert(err, IsNil)
  1531  	c.Check(operationTime.Equal(op), Equals, true)
  1532  }
  1533  
  1534  func (s *startOfOperationTimeSuite) TestStartOfOperationTimeNoSeedTime(c *C) {
  1535  	mgr := s.manager(c)
  1536  
  1537  	st := s.state
  1538  	st.Lock()
  1539  	defer st.Unlock()
  1540  
  1541  	now := time.Now().Add(-1 * time.Second)
  1542  	devicestate.MockTimeNow(func() time.Time {
  1543  		return now
  1544  	})
  1545  
  1546  	operationTime, err := mgr.StartOfOperationTime()
  1547  	c.Assert(err, IsNil)
  1548  	c.Check(operationTime.Equal(now), Equals, true)
  1549  
  1550  	// repeated call returns already set time
  1551  	prev := now
  1552  	now = time.Now().Add(-10 * time.Hour)
  1553  	operationTime, err = s.manager(c).StartOfOperationTime()
  1554  	c.Assert(err, IsNil)
  1555  	c.Check(operationTime.Equal(prev), Equals, true)
  1556  }
  1557  
  1558  func (s *startOfOperationTimeSuite) TestStartOfOperationErrorIfPreseed(c *C) {
  1559  	restore := snapdenv.MockPreseeding(true)
  1560  	defer restore()
  1561  
  1562  	mgr := s.manager(c)
  1563  	st := s.state
  1564  
  1565  	st.Lock()
  1566  	defer st.Unlock()
  1567  	_, err := mgr.StartOfOperationTime()
  1568  	c.Assert(err, ErrorMatches, `internal error: unexpected call to StartOfOperationTime in preseed mode`)
  1569  }