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