github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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/overlord"
    42  	"github.com/snapcore/snapd/overlord/assertstate"
    43  	"github.com/snapcore/snapd/overlord/assertstate/assertstatetest"
    44  	"github.com/snapcore/snapd/overlord/auth"
    45  	"github.com/snapcore/snapd/overlord/configstate/config"
    46  	"github.com/snapcore/snapd/overlord/devicestate"
    47  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    48  	"github.com/snapcore/snapd/overlord/hookstate"
    49  	"github.com/snapcore/snapd/overlord/ifacestate/ifacerepo"
    50  	"github.com/snapcore/snapd/overlord/snapstate"
    51  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    52  	"github.com/snapcore/snapd/overlord/state"
    53  	"github.com/snapcore/snapd/overlord/storecontext"
    54  	"github.com/snapcore/snapd/release"
    55  	"github.com/snapcore/snapd/snap"
    56  	"github.com/snapcore/snapd/snap/snaptest"
    57  	"github.com/snapcore/snapd/snapdenv"
    58  	"github.com/snapcore/snapd/store/storetest"
    59  	"github.com/snapcore/snapd/sysconfig"
    60  	"github.com/snapcore/snapd/testutil"
    61  	"github.com/snapcore/snapd/timings"
    62  )
    63  
    64  var (
    65  	settleTimeout = testutil.HostScaledTimeout(15 * time.Second)
    66  )
    67  
    68  func TestDeviceManager(t *testing.T) { TestingT(t) }
    69  
    70  type deviceMgrBaseSuite struct {
    71  	testutil.BaseTest
    72  
    73  	o       *overlord.Overlord
    74  	state   *state.State
    75  	se      *overlord.StateEngine
    76  	hookMgr *hookstate.HookManager
    77  	mgr     *devicestate.DeviceManager
    78  	db      *asserts.Database
    79  
    80  	bootloader *bootloadertest.MockBootloader
    81  
    82  	storeSigning *assertstest.StoreStack
    83  	brands       *assertstest.SigningAccounts
    84  
    85  	ancillary []asserts.Assertion
    86  
    87  	restartRequests []state.RestartType
    88  
    89  	newFakeStore func(storecontext.DeviceBackend) snapstate.StoreService
    90  
    91  	// saved so that if a derived suite wants to undo the cloud-init mocking to
    92  	// test the actual functions, it can just call this in it's SetUpTest, see
    93  	// devicestate_cloudinit_test.go for details
    94  	restoreCloudInitStatusRestore func()
    95  }
    96  
    97  type deviceMgrSuite struct {
    98  	deviceMgrBaseSuite
    99  }
   100  
   101  var _ = Suite(&deviceMgrSuite{})
   102  
   103  type fakeStore struct {
   104  	storetest.Store
   105  
   106  	state *state.State
   107  	db    asserts.RODatabase
   108  }
   109  
   110  func (sto *fakeStore) pokeStateLock() {
   111  	// the store should be called without the state lock held. Try
   112  	// to acquire it.
   113  	sto.state.Lock()
   114  	sto.state.Unlock()
   115  }
   116  
   117  func (sto *fakeStore) Assertion(assertType *asserts.AssertionType, key []string, _ *auth.UserState) (asserts.Assertion, error) {
   118  	sto.pokeStateLock()
   119  	ref := &asserts.Ref{Type: assertType, PrimaryKey: key}
   120  	return ref.Resolve(sto.db.Find)
   121  }
   122  
   123  var (
   124  	brandPrivKey, _  = assertstest.GenerateKey(752)
   125  	brandPrivKey2, _ = assertstest.GenerateKey(752)
   126  	brandPrivKey3, _ = assertstest.GenerateKey(752)
   127  )
   128  
   129  func (s *deviceMgrBaseSuite) SetUpTest(c *C) {
   130  	s.BaseTest.SetUpTest(c)
   131  
   132  	dirs.SetRootDir(c.MkDir())
   133  	s.AddCleanup(func() { dirs.SetRootDir("") })
   134  	os.MkdirAll(dirs.SnapRunDir, 0755)
   135  
   136  	s.restartRequests = nil
   137  
   138  	s.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
   139  
   140  	s.bootloader = bootloadertest.Mock("mock", c.MkDir())
   141  	bootloader.Force(s.bootloader)
   142  	s.AddCleanup(func() { bootloader.Force(nil) })
   143  
   144  	s.AddCleanup(release.MockOnClassic(false))
   145  
   146  	s.storeSigning = assertstest.NewStoreStack("canonical", nil)
   147  	s.o = overlord.MockWithStateAndRestartHandler(nil, func(req state.RestartType) {
   148  		s.restartRequests = append(s.restartRequests, req)
   149  	})
   150  	s.state = s.o.State()
   151  	s.state.Lock()
   152  	s.state.VerifyReboot("boot-id-0")
   153  	s.state.Unlock()
   154  	s.se = s.o.StateEngine()
   155  
   156  	s.AddCleanup(sysdb.MockGenericClassicModel(s.storeSigning.GenericClassicModel))
   157  
   158  	s.brands = assertstest.NewSigningAccounts(s.storeSigning)
   159  	s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{
   160  		"display-name": "fancy model publisher",
   161  		"validation":   "certified",
   162  	})
   163  	s.brands.Register("rereg-brand", brandPrivKey2, nil)
   164  
   165  	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   166  		Backstore:       asserts.NewMemoryBackstore(),
   167  		Trusted:         s.storeSigning.Trusted,
   168  		OtherPredefined: s.storeSigning.Generic,
   169  	})
   170  	c.Assert(err, IsNil)
   171  
   172  	s.state.Lock()
   173  	assertstate.ReplaceDB(s.state, db)
   174  	s.state.Unlock()
   175  	s.AddCleanup(func() {
   176  		s.state.Lock()
   177  		assertstate.ReplaceDB(s.state, nil)
   178  		s.state.Unlock()
   179  	})
   180  
   181  	err = db.Add(s.storeSigning.StoreAccountKey(""))
   182  	c.Assert(err, IsNil)
   183  
   184  	hookMgr, err := hookstate.Manager(s.state, s.o.TaskRunner())
   185  	c.Assert(err, IsNil)
   186  	mgr, err := devicestate.Manager(s.state, hookMgr, s.o.TaskRunner(), s.newStore)
   187  	c.Assert(err, IsNil)
   188  
   189  	s.db = db
   190  	s.hookMgr = hookMgr
   191  	s.o.AddManager(s.hookMgr)
   192  	s.mgr = mgr
   193  	s.o.AddManager(s.mgr)
   194  	s.o.AddManager(s.o.TaskRunner())
   195  
   196  	// For triggering errors
   197  	erroringHandler := func(task *state.Task, _ *tomb.Tomb) error {
   198  		return errors.New("error out")
   199  	}
   200  	s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil)
   201  
   202  	c.Assert(s.o.StartUp(), IsNil)
   203  
   204  	s.state.Lock()
   205  	snapstate.ReplaceStore(s.state, &fakeStore{
   206  		state: s.state,
   207  		db:    s.storeSigning,
   208  	})
   209  	s.state.Unlock()
   210  
   211  	s.restoreCloudInitStatusRestore = devicestate.MockCloudInitStatus(func() (sysconfig.CloudInitState, error) {
   212  		return sysconfig.CloudInitRestrictedBySnapd, nil
   213  	})
   214  	s.AddCleanup(s.restoreCloudInitStatusRestore)
   215  
   216  	s.AddCleanup(func() { s.ancillary = nil })
   217  }
   218  
   219  func (s *deviceMgrBaseSuite) newStore(devBE storecontext.DeviceBackend) snapstate.StoreService {
   220  	return s.newFakeStore(devBE)
   221  }
   222  
   223  func (s *deviceMgrBaseSuite) settle(c *C) {
   224  	err := s.o.Settle(settleTimeout)
   225  	c.Assert(err, IsNil)
   226  }
   227  
   228  // seeding avoids triggering a real full seeding, it simulates having it in process instead
   229  func (s *deviceMgrBaseSuite) seeding() {
   230  	chg := s.state.NewChange("seed", "Seed system")
   231  	chg.SetStatus(state.DoingStatus)
   232  }
   233  
   234  func (s *deviceMgrSuite) TestDeviceManagerSetTimeOnce(c *C) {
   235  	s.state.Lock()
   236  	defer s.state.Unlock()
   237  
   238  	// set first time
   239  	now := time.Now()
   240  	err := devicestate.SetTimeOnce(s.mgr, "key-name", now)
   241  	c.Assert(err, IsNil)
   242  
   243  	later := now.Add(1 * time.Minute)
   244  	// setting again doesn't change value
   245  	err = devicestate.SetTimeOnce(s.mgr, "key-name", later)
   246  	c.Assert(err, IsNil)
   247  
   248  	var t time.Time
   249  	s.state.Get("key-name", &t)
   250  
   251  	c.Assert(t.Equal(now), Equals, true)
   252  }
   253  
   254  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeededAlreadySeeded(c *C) {
   255  	s.state.Lock()
   256  	s.state.Set("seeded", true)
   257  	s.state.Unlock()
   258  
   259  	called := false
   260  	restore := devicestate.MockPopulateStateFromSeed(func(*state.State, *devicestate.PopulateStateFromSeedOptions, timings.Measurer) ([]*state.TaskSet, error) {
   261  		called = true
   262  		return nil, nil
   263  	})
   264  	defer restore()
   265  
   266  	err := devicestate.EnsureSeeded(s.mgr)
   267  	c.Assert(err, IsNil)
   268  	c.Assert(called, Equals, false)
   269  }
   270  
   271  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeededChangeInFlight(c *C) {
   272  	s.state.Lock()
   273  	chg := s.state.NewChange("seed", "just for testing")
   274  	chg.AddTask(s.state.NewTask("test-task", "the change needs a task"))
   275  	s.state.Unlock()
   276  
   277  	called := false
   278  	restore := devicestate.MockPopulateStateFromSeed(func(*state.State, *devicestate.PopulateStateFromSeedOptions, timings.Measurer) ([]*state.TaskSet, error) {
   279  		called = true
   280  		return nil, nil
   281  	})
   282  	defer restore()
   283  
   284  	err := devicestate.EnsureSeeded(s.mgr)
   285  	c.Assert(err, IsNil)
   286  	c.Assert(called, Equals, false)
   287  }
   288  
   289  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeededAlsoOnClassic(c *C) {
   290  	release.OnClassic = true
   291  
   292  	called := false
   293  	restore := devicestate.MockPopulateStateFromSeed(func(st *state.State, opts *devicestate.PopulateStateFromSeedOptions, tm timings.Measurer) ([]*state.TaskSet, error) {
   294  		called = true
   295  		c.Check(opts, IsNil)
   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, true)
   303  }
   304  
   305  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeededHappy(c *C) {
   306  	restore := devicestate.MockPopulateStateFromSeed(func(st *state.State, opts *devicestate.PopulateStateFromSeedOptions, tm timings.Measurer) (ts []*state.TaskSet, err error) {
   307  		c.Assert(opts, IsNil)
   308  		t := s.state.NewTask("test-task", "a random task")
   309  		ts = append(ts, state.NewTaskSet(t))
   310  		return ts, nil
   311  	})
   312  	defer restore()
   313  
   314  	err := devicestate.EnsureSeeded(s.mgr)
   315  	c.Assert(err, IsNil)
   316  
   317  	s.state.Lock()
   318  	defer s.state.Unlock()
   319  
   320  	c.Check(s.state.Changes(), HasLen, 1)
   321  
   322  	var seedStartTime time.Time
   323  	c.Assert(s.state.Get("seed-start-time", &seedStartTime), IsNil)
   324  	c.Check(seedStartTime.Equal(devicestate.StartTime()), Equals, true)
   325  }
   326  
   327  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkSkippedOnClassic(c *C) {
   328  	s.bootloader.GetErr = fmt.Errorf("should not be called")
   329  	release.OnClassic = true
   330  
   331  	err := devicestate.EnsureBootOk(s.mgr)
   332  	c.Assert(err, IsNil)
   333  }
   334  
   335  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkSkippedOnNonRunModes(c *C) {
   336  	s.bootloader.GetErr = fmt.Errorf("should not be called")
   337  	devicestate.SetSystemMode(s.mgr, "install")
   338  
   339  	err := devicestate.EnsureBootOk(s.mgr)
   340  	c.Assert(err, IsNil)
   341  }
   342  
   343  func (s *deviceMgrSuite) TestDeviceManagerEnsureSeededHappyWithModeenv(c *C) {
   344  	n := 0
   345  	restore := devicestate.MockPopulateStateFromSeed(func(st *state.State, opts *devicestate.PopulateStateFromSeedOptions, tm timings.Measurer) (ts []*state.TaskSet, err error) {
   346  		c.Assert(opts, NotNil)
   347  		c.Check(opts.Label, Equals, "20191127")
   348  		c.Check(opts.Mode, Equals, "install")
   349  
   350  		t := s.state.NewTask("test-task", "a random task")
   351  		ts = append(ts, state.NewTaskSet(t))
   352  
   353  		n++
   354  		return ts, nil
   355  	})
   356  	defer restore()
   357  
   358  	// mock the modeenv file
   359  	m := boot.Modeenv{
   360  		Mode:           "install",
   361  		RecoverySystem: "20191127",
   362  	}
   363  	err := m.WriteTo("")
   364  	c.Assert(err, IsNil)
   365  
   366  	// re-create manager so that modeenv file is-read
   367  	s.mgr, err = devicestate.Manager(s.state, s.hookMgr, s.o.TaskRunner(), s.newStore)
   368  	c.Assert(err, IsNil)
   369  
   370  	err = devicestate.EnsureSeeded(s.mgr)
   371  	c.Assert(err, IsNil)
   372  
   373  	s.state.Lock()
   374  	defer s.state.Unlock()
   375  
   376  	c.Check(s.state.Changes(), HasLen, 1)
   377  	c.Check(n, Equals, 1)
   378  }
   379  
   380  func (s *deviceMgrBaseSuite) makeModelAssertionInState(c *C, brandID, model string, extras map[string]interface{}) *asserts.Model {
   381  	modelAs := s.brands.Model(brandID, model, extras)
   382  
   383  	s.setupBrands(c)
   384  	assertstatetest.AddMany(s.state, modelAs)
   385  	return modelAs
   386  }
   387  
   388  func (s *deviceMgrBaseSuite) setPCModelInState(c *C) {
   389  	s.state.Lock()
   390  	defer s.state.Unlock()
   391  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   392  		"architecture": "amd64",
   393  		"kernel":       "pc-kernel",
   394  		"gadget":       "pc",
   395  	})
   396  
   397  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   398  		Brand:  "canonical",
   399  		Model:  "pc",
   400  		Serial: "serialserialserial",
   401  	})
   402  }
   403  
   404  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkBootloaderHappy(c *C) {
   405  	s.setPCModelInState(c)
   406  
   407  	s.bootloader.SetBootVars(map[string]string{
   408  		"snap_mode":     boot.TryingStatus,
   409  		"snap_try_core": "core_1.snap",
   410  	})
   411  
   412  	s.state.Lock()
   413  	defer s.state.Unlock()
   414  	siCore1 := &snap.SideInfo{RealName: "core", Revision: snap.R(1)}
   415  	snapstate.Set(s.state, "core", &snapstate.SnapState{
   416  		SnapType: "os",
   417  		Active:   true,
   418  		Sequence: []*snap.SideInfo{siCore1},
   419  		Current:  siCore1.Revision,
   420  	})
   421  
   422  	s.state.Unlock()
   423  	err := devicestate.EnsureBootOk(s.mgr)
   424  	s.state.Lock()
   425  	c.Assert(err, IsNil)
   426  
   427  	m, err := s.bootloader.GetBootVars("snap_mode")
   428  	c.Assert(err, IsNil)
   429  	c.Assert(m, DeepEquals, map[string]string{"snap_mode": ""})
   430  }
   431  
   432  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkUpdateBootRevisionsHappy(c *C) {
   433  	s.setPCModelInState(c)
   434  
   435  	// simulate that we have a new core_2, tried to boot it but that failed
   436  	s.bootloader.SetBootVars(map[string]string{
   437  		"snap_mode":     "",
   438  		"snap_kernel":   "kernel_1.snap",
   439  		"snap_try_core": "core_2.snap",
   440  		"snap_core":     "core_1.snap",
   441  	})
   442  
   443  	s.state.Lock()
   444  	defer s.state.Unlock()
   445  	siKernel1 := &snap.SideInfo{RealName: "kernel", Revision: snap.R(1)}
   446  	snapstate.Set(s.state, "kernel", &snapstate.SnapState{
   447  		SnapType: "kernel",
   448  		Active:   true,
   449  		Sequence: []*snap.SideInfo{siKernel1},
   450  		Current:  siKernel1.Revision,
   451  	})
   452  
   453  	siCore1 := &snap.SideInfo{RealName: "core", Revision: snap.R(1)}
   454  	siCore2 := &snap.SideInfo{RealName: "core", Revision: snap.R(2)}
   455  	snapstate.Set(s.state, "core", &snapstate.SnapState{
   456  		SnapType: "os",
   457  		Active:   true,
   458  		Sequence: []*snap.SideInfo{siCore1, siCore2},
   459  		Current:  siCore2.Revision,
   460  	})
   461  
   462  	s.state.Unlock()
   463  	err := devicestate.EnsureBootOk(s.mgr)
   464  	s.state.Lock()
   465  	c.Assert(err, IsNil)
   466  
   467  	c.Check(s.state.Changes(), HasLen, 1)
   468  	c.Check(s.state.Changes()[0].Kind(), Equals, "update-revisions")
   469  }
   470  
   471  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkNotRunAgain(c *C) {
   472  	s.setPCModelInState(c)
   473  
   474  	s.bootloader.SetBootVars(map[string]string{
   475  		"snap_mode":     boot.TryingStatus,
   476  		"snap_try_core": "core_1.snap",
   477  	})
   478  	s.bootloader.SetErr = fmt.Errorf("ensure bootloader is not used")
   479  
   480  	devicestate.SetBootOkRan(s.mgr, true)
   481  
   482  	err := devicestate.EnsureBootOk(s.mgr)
   483  	c.Assert(err, IsNil)
   484  }
   485  
   486  func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkError(c *C) {
   487  	s.setPCModelInState(c)
   488  
   489  	s.state.Lock()
   490  	// seeded
   491  	s.state.Set("seeded", true)
   492  	// has serial
   493  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   494  		Brand:  "canonical",
   495  		Model:  "pc",
   496  		Serial: "8989",
   497  	})
   498  	s.state.Unlock()
   499  
   500  	s.bootloader.GetErr = fmt.Errorf("bootloader err")
   501  
   502  	devicestate.SetBootOkRan(s.mgr, false)
   503  
   504  	err := s.mgr.Ensure()
   505  	c.Assert(err, ErrorMatches, "devicemgr: cannot mark boot successful: bootloader err")
   506  }
   507  
   508  func (s *deviceMgrBaseSuite) setupBrands(c *C) {
   509  	assertstatetest.AddMany(s.state, s.brands.AccountsAndKeys("my-brand")...)
   510  	otherAcct := assertstest.NewAccount(s.storeSigning, "other-brand", map[string]interface{}{
   511  		"account-id": "other-brand",
   512  	}, "")
   513  	assertstatetest.AddMany(s.state, otherAcct)
   514  }
   515  
   516  func (s *deviceMgrSuite) setupSnapDecl(c *C, info *snap.Info, publisherID string) {
   517  	snapDecl, err := s.storeSigning.Sign(asserts.SnapDeclarationType, map[string]interface{}{
   518  		"series":       "16",
   519  		"snap-name":    info.SnapName(),
   520  		"snap-id":      info.SnapID,
   521  		"publisher-id": publisherID,
   522  		"timestamp":    time.Now().UTC().Format(time.RFC3339),
   523  	}, nil, "")
   524  	c.Assert(err, IsNil)
   525  	assertstatetest.AddMany(s.state, snapDecl)
   526  }
   527  
   528  func fakeMyModel(extra map[string]interface{}) *asserts.Model {
   529  	model := map[string]interface{}{
   530  		"type":         "model",
   531  		"authority-id": "my-brand",
   532  		"series":       "16",
   533  		"brand-id":     "my-brand",
   534  		"model":        "my-model",
   535  	}
   536  	return assertstest.FakeAssertion(model, extra).(*asserts.Model)
   537  }
   538  
   539  func (s *deviceMgrSuite) TestCheckGadget(c *C) {
   540  	s.state.Lock()
   541  	defer s.state.Unlock()
   542  
   543  	gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: other-gadget, version: 0}", nil)
   544  
   545  	s.setupBrands(c)
   546  	// model assertion in device context
   547  	model := fakeMyModel(map[string]interface{}{
   548  		"architecture": "amd64",
   549  		"gadget":       "gadget",
   550  		"kernel":       "krnl",
   551  	})
   552  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
   553  
   554  	err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   555  	c.Check(err, ErrorMatches, `cannot install gadget "other-gadget", model assertion requests "gadget"`)
   556  
   557  	// brand gadget
   558  	brandGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   559  	brandGadgetInfo.SnapID = "brand-gadget-id"
   560  	s.setupSnapDecl(c, brandGadgetInfo, "my-brand")
   561  
   562  	// canonical gadget
   563  	canonicalGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   564  	canonicalGadgetInfo.SnapID = "canonical-gadget-id"
   565  	s.setupSnapDecl(c, canonicalGadgetInfo, "canonical")
   566  
   567  	// other gadget
   568  	otherGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   569  	otherGadgetInfo.SnapID = "other-gadget-id"
   570  	s.setupSnapDecl(c, otherGadgetInfo, "other-brand")
   571  
   572  	// install brand gadget ok
   573  	err = devicestate.CheckGadgetOrKernel(s.state, brandGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   574  	c.Check(err, IsNil)
   575  
   576  	// install canonical gadget ok
   577  	err = devicestate.CheckGadgetOrKernel(s.state, canonicalGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   578  	c.Check(err, IsNil)
   579  
   580  	// install other gadget fails
   581  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   582  	c.Check(err, ErrorMatches, `cannot install gadget "gadget" published by "other-brand" for model by "my-brand"`)
   583  
   584  	// unasserted installation of other works
   585  	otherGadgetInfo.SnapID = ""
   586  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   587  	c.Check(err, IsNil)
   588  
   589  	// parallel install fails
   590  	otherGadgetInfo.InstanceKey = "foo"
   591  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   592  	c.Check(err, ErrorMatches, `cannot install "gadget_foo", parallel installation of kernel or gadget snaps is not supported`)
   593  }
   594  
   595  func (s *deviceMgrSuite) TestCheckGadgetOnClassic(c *C) {
   596  	release.OnClassic = true
   597  
   598  	s.state.Lock()
   599  	defer s.state.Unlock()
   600  
   601  	gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: other-gadget, version: 0}", nil)
   602  
   603  	s.setupBrands(c)
   604  	// model assertion in device context
   605  	model := fakeMyModel(map[string]interface{}{
   606  		"classic": "true",
   607  		"gadget":  "gadget",
   608  	})
   609  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
   610  
   611  	err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   612  	c.Check(err, ErrorMatches, `cannot install gadget "other-gadget", model assertion requests "gadget"`)
   613  
   614  	// brand gadget
   615  	brandGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   616  	brandGadgetInfo.SnapID = "brand-gadget-id"
   617  	s.setupSnapDecl(c, brandGadgetInfo, "my-brand")
   618  
   619  	// canonical gadget
   620  	canonicalGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   621  	canonicalGadgetInfo.SnapID = "canonical-gadget-id"
   622  	s.setupSnapDecl(c, canonicalGadgetInfo, "canonical")
   623  
   624  	// other gadget
   625  	otherGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   626  	otherGadgetInfo.SnapID = "other-gadget-id"
   627  	s.setupSnapDecl(c, otherGadgetInfo, "other-brand")
   628  
   629  	// install brand gadget ok
   630  	err = devicestate.CheckGadgetOrKernel(s.state, brandGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   631  	c.Check(err, IsNil)
   632  
   633  	// install canonical gadget ok
   634  	err = devicestate.CheckGadgetOrKernel(s.state, canonicalGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   635  	c.Check(err, IsNil)
   636  
   637  	// install other gadget fails
   638  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   639  	c.Check(err, ErrorMatches, `cannot install gadget "gadget" published by "other-brand" for model by "my-brand"`)
   640  
   641  	// unasserted installation of other works
   642  	otherGadgetInfo.SnapID = ""
   643  	err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   644  	c.Check(err, IsNil)
   645  }
   646  
   647  func (s *deviceMgrSuite) TestCheckGadgetOnClassicGadgetNotSpecified(c *C) {
   648  	release.OnClassic = true
   649  
   650  	s.state.Lock()
   651  	defer s.state.Unlock()
   652  
   653  	gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   654  
   655  	s.setupBrands(c)
   656  	// model assertion in device context
   657  	model := fakeMyModel(map[string]interface{}{
   658  		"classic": "true",
   659  	})
   660  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
   661  
   662  	err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   663  	c.Check(err, ErrorMatches, `cannot install gadget snap on classic if not requested by the model`)
   664  }
   665  
   666  func (s *deviceMgrSuite) TestCheckGadgetValid(c *C) {
   667  	s.state.Lock()
   668  	defer s.state.Unlock()
   669  
   670  	// model assertion in device context
   671  	model := fakeMyModel(map[string]interface{}{
   672  		"architecture": "amd64",
   673  		"gadget":       "gadget",
   674  		"kernel":       "krnl",
   675  	})
   676  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
   677  
   678  	gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil)
   679  
   680  	// valid gadget.yaml
   681  	cont := snaptest.MockContainer(c, [][]string{
   682  		{"meta/gadget.yaml", gadgetYaml},
   683  	})
   684  	err := devicestate.CheckGadgetValid(s.state, gadgetInfo, nil, cont, snapstate.Flags{}, deviceCtx)
   685  	c.Check(err, IsNil)
   686  
   687  	// invalid gadget.yaml
   688  	cont = snaptest.MockContainer(c, [][]string{
   689  		{"meta/gadget.yaml", `defaults:`},
   690  	})
   691  	err = devicestate.CheckGadgetValid(s.state, gadgetInfo, nil, cont, snapstate.Flags{}, deviceCtx)
   692  	c.Check(err, ErrorMatches, `bootloader not declared in any volume`)
   693  
   694  }
   695  
   696  func (s *deviceMgrSuite) TestCheckKernel(c *C) {
   697  	s.state.Lock()
   698  	defer s.state.Unlock()
   699  	kernelInfo := snaptest.MockInfo(c, "{type: kernel, name: lnrk, version: 0}", nil)
   700  
   701  	// not on classic
   702  	release.OnClassic = true
   703  	err := devicestate.CheckGadgetOrKernel(s.state, kernelInfo, nil, nil, snapstate.Flags{}, nil)
   704  	c.Check(err, ErrorMatches, `cannot install a kernel snap on classic`)
   705  	release.OnClassic = false
   706  
   707  	s.setupBrands(c)
   708  	// model assertion in device context
   709  	model := fakeMyModel(map[string]interface{}{
   710  		"architecture": "amd64",
   711  		"gadget":       "gadget",
   712  		"kernel":       "krnl",
   713  	})
   714  	deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model}
   715  
   716  	err = devicestate.CheckGadgetOrKernel(s.state, kernelInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   717  	c.Check(err, ErrorMatches, `cannot install kernel "lnrk", model assertion requests "krnl"`)
   718  
   719  	// brand kernel
   720  	brandKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil)
   721  	brandKrnlInfo.SnapID = "brand-krnl-id"
   722  	s.setupSnapDecl(c, brandKrnlInfo, "my-brand")
   723  
   724  	// canonical kernel
   725  	canonicalKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil)
   726  	canonicalKrnlInfo.SnapID = "canonical-krnl-id"
   727  	s.setupSnapDecl(c, canonicalKrnlInfo, "canonical")
   728  
   729  	// other kernel
   730  	otherKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil)
   731  	otherKrnlInfo.SnapID = "other-krnl-id"
   732  	s.setupSnapDecl(c, otherKrnlInfo, "other-brand")
   733  
   734  	// install brand kernel ok
   735  	err = devicestate.CheckGadgetOrKernel(s.state, brandKrnlInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   736  	c.Check(err, IsNil)
   737  
   738  	// install canonical kernel ok
   739  	err = devicestate.CheckGadgetOrKernel(s.state, canonicalKrnlInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   740  	c.Check(err, IsNil)
   741  
   742  	// install other kernel fails
   743  	err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   744  	c.Check(err, ErrorMatches, `cannot install kernel "krnl" published by "other-brand" for model by "my-brand"`)
   745  
   746  	// unasserted installation of other works
   747  	otherKrnlInfo.SnapID = ""
   748  	err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   749  	c.Check(err, IsNil)
   750  
   751  	// parallel install fails
   752  	otherKrnlInfo.InstanceKey = "foo"
   753  	err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, nil, snapstate.Flags{}, deviceCtx)
   754  	c.Check(err, ErrorMatches, `cannot install "krnl_foo", parallel installation of kernel or gadget snaps is not supported`)
   755  }
   756  
   757  func makeSerialAssertionInState(c *C, brands *assertstest.SigningAccounts, st *state.State, brandID, model, serialN string) *asserts.Serial {
   758  	encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey())
   759  	c.Assert(err, IsNil)
   760  	serial, err := brands.Signing(brandID).Sign(asserts.SerialType, map[string]interface{}{
   761  		"brand-id":            brandID,
   762  		"model":               model,
   763  		"serial":              serialN,
   764  		"device-key":          string(encDevKey),
   765  		"device-key-sha3-384": devKey.PublicKey().ID(),
   766  		"timestamp":           time.Now().Format(time.RFC3339),
   767  	}, nil, "")
   768  	c.Assert(err, IsNil)
   769  	err = assertstate.Add(st, serial)
   770  	c.Assert(err, IsNil)
   771  	return serial.(*asserts.Serial)
   772  }
   773  
   774  func (s *deviceMgrBaseSuite) makeSerialAssertionInState(c *C, brandID, model, serialN string) *asserts.Serial {
   775  	return makeSerialAssertionInState(c, s.brands, s.state, brandID, model, serialN)
   776  }
   777  
   778  func (s *deviceMgrSuite) TestCanAutoRefreshOnCore(c *C) {
   779  	s.state.Lock()
   780  	defer s.state.Unlock()
   781  
   782  	canAutoRefresh := func() bool {
   783  		ok, err := devicestate.CanAutoRefresh(s.state)
   784  		c.Assert(err, IsNil)
   785  		return ok
   786  	}
   787  
   788  	// not seeded, no model, no serial -> no auto-refresh
   789  	s.state.Set("seeded", false)
   790  	c.Check(canAutoRefresh(), Equals, false)
   791  
   792  	// seeded, model, no serial -> no auto-refresh
   793  	s.state.Set("seeded", true)
   794  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   795  		Brand: "canonical",
   796  		Model: "pc",
   797  	})
   798  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   799  		"architecture": "amd64",
   800  		"kernel":       "pc-kernel",
   801  		"gadget":       "pc",
   802  	})
   803  	c.Check(canAutoRefresh(), Equals, false)
   804  
   805  	// seeded, model, serial -> auto-refresh
   806  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   807  		Brand:  "canonical",
   808  		Model:  "pc",
   809  		Serial: "8989",
   810  	})
   811  	s.makeSerialAssertionInState(c, "canonical", "pc", "8989")
   812  	c.Check(canAutoRefresh(), Equals, true)
   813  
   814  	// not seeded, model, serial -> no auto-refresh
   815  	s.state.Set("seeded", false)
   816  	c.Check(canAutoRefresh(), Equals, false)
   817  }
   818  
   819  func (s *deviceMgrSuite) TestCanAutoRefreshNoSerialFallback(c *C) {
   820  	s.state.Lock()
   821  	defer s.state.Unlock()
   822  
   823  	canAutoRefresh := func() bool {
   824  		ok, err := devicestate.CanAutoRefresh(s.state)
   825  		c.Assert(err, IsNil)
   826  		return ok
   827  	}
   828  
   829  	// seeded, model, no serial, two attempts at getting serial
   830  	// -> no auto-refresh
   831  	devicestate.IncEnsureOperationalAttempts(s.state)
   832  	devicestate.IncEnsureOperationalAttempts(s.state)
   833  	s.state.Set("seeded", true)
   834  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   835  		Brand: "canonical",
   836  		Model: "pc",
   837  	})
   838  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   839  		"architecture": "amd64",
   840  		"kernel":       "pc-kernel",
   841  		"gadget":       "pc",
   842  	})
   843  	c.Check(canAutoRefresh(), Equals, false)
   844  
   845  	// third attempt ongoing, or done
   846  	// fallback, try auto-refresh
   847  	devicestate.IncEnsureOperationalAttempts(s.state)
   848  	// sanity
   849  	c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 3)
   850  	c.Check(canAutoRefresh(), Equals, true)
   851  }
   852  
   853  func (s *deviceMgrSuite) TestCanAutoRefreshOnClassic(c *C) {
   854  	release.OnClassic = true
   855  
   856  	s.state.Lock()
   857  	defer s.state.Unlock()
   858  
   859  	canAutoRefresh := func() bool {
   860  		ok, err := devicestate.CanAutoRefresh(s.state)
   861  		c.Assert(err, IsNil)
   862  		return ok
   863  	}
   864  
   865  	// not seeded, no model, no serial -> no auto-refresh
   866  	s.state.Set("seeded", false)
   867  	c.Check(canAutoRefresh(), Equals, false)
   868  
   869  	// seeded, no model -> auto-refresh
   870  	s.state.Set("seeded", true)
   871  	c.Check(canAutoRefresh(), Equals, false)
   872  
   873  	// seeded, model, no serial -> no auto-refresh
   874  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   875  		Brand: "canonical",
   876  		Model: "pc",
   877  	})
   878  	s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{
   879  		"classic": "true",
   880  	})
   881  	c.Check(canAutoRefresh(), Equals, false)
   882  
   883  	// seeded, model, serial -> auto-refresh
   884  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   885  		Brand:  "canonical",
   886  		Model:  "pc",
   887  		Serial: "8989",
   888  	})
   889  	s.makeSerialAssertionInState(c, "canonical", "pc", "8989")
   890  	c.Check(canAutoRefresh(), Equals, true)
   891  
   892  	// not seeded, model, serial -> no auto-refresh
   893  	s.state.Set("seeded", false)
   894  	c.Check(canAutoRefresh(), Equals, false)
   895  }
   896  
   897  func makeInstalledMockCoreSnapWithSnapdControl(c *C, st *state.State) *snap.Info {
   898  	sideInfoCore11 := &snap.SideInfo{RealName: "core", Revision: snap.R(11), SnapID: "core-id"}
   899  	snapstate.Set(st, "core", &snapstate.SnapState{
   900  		Active:   true,
   901  		Sequence: []*snap.SideInfo{sideInfoCore11},
   902  		Current:  sideInfoCore11.Revision,
   903  		SnapType: "os",
   904  	})
   905  	core11 := snaptest.MockSnap(c, `
   906  name: core
   907  version: 1.0
   908  slots:
   909   snapd-control:
   910  `, sideInfoCore11)
   911  	c.Assert(core11.Slots, HasLen, 1)
   912  
   913  	return core11
   914  }
   915  
   916  var snapWithSnapdControlRefreshScheduleManagedYAML = `
   917  name: snap-with-snapd-control
   918  version: 1.0
   919  plugs:
   920   snapd-control:
   921    refresh-schedule: managed
   922  `
   923  
   924  var snapWithSnapdControlOnlyYAML = `
   925  name: snap-with-snapd-control
   926  version: 1.0
   927  plugs:
   928   snapd-control:
   929  `
   930  
   931  func makeInstalledMockSnap(c *C, st *state.State, yml string) *snap.Info {
   932  	sideInfo11 := &snap.SideInfo{RealName: "snap-with-snapd-control", Revision: snap.R(11), SnapID: "snap-with-snapd-control-id"}
   933  	snapstate.Set(st, "snap-with-snapd-control", &snapstate.SnapState{
   934  		Active:   true,
   935  		Sequence: []*snap.SideInfo{sideInfo11},
   936  		Current:  sideInfo11.Revision,
   937  		SnapType: "app",
   938  	})
   939  	info11 := snaptest.MockSnap(c, yml, sideInfo11)
   940  	c.Assert(info11.Plugs, HasLen, 1)
   941  
   942  	return info11
   943  }
   944  
   945  func makeMockRepoWithConnectedSnaps(c *C, st *state.State, info11, core11 *snap.Info, ifname string) {
   946  	repo := interfaces.NewRepository()
   947  	for _, iface := range builtin.Interfaces() {
   948  		err := repo.AddInterface(iface)
   949  		c.Assert(err, IsNil)
   950  	}
   951  	err := repo.AddSnap(info11)
   952  	c.Assert(err, IsNil)
   953  	err = repo.AddSnap(core11)
   954  	c.Assert(err, IsNil)
   955  	_, err = repo.Connect(&interfaces.ConnRef{
   956  		PlugRef: interfaces.PlugRef{Snap: info11.InstanceName(), Name: ifname},
   957  		SlotRef: interfaces.SlotRef{Snap: core11.InstanceName(), Name: ifname},
   958  	}, nil, nil, nil, nil, nil)
   959  	c.Assert(err, IsNil)
   960  	conns, err := repo.Connected("snap-with-snapd-control", "snapd-control")
   961  	c.Assert(err, IsNil)
   962  	c.Assert(conns, HasLen, 1)
   963  	ifacerepo.Replace(st, repo)
   964  }
   965  
   966  func (s *deviceMgrSuite) TestCanManageRefreshes(c *C) {
   967  	st := s.state
   968  	st.Lock()
   969  	defer st.Unlock()
   970  
   971  	// not possbile to manage by default
   972  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
   973  
   974  	// not possible with just a snap with "snapd-control" plug with the
   975  	// right attribute
   976  	info11 := makeInstalledMockSnap(c, st, snapWithSnapdControlRefreshScheduleManagedYAML)
   977  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
   978  
   979  	// not possible with a core snap with snapd control
   980  	core11 := makeInstalledMockCoreSnapWithSnapdControl(c, st)
   981  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
   982  
   983  	// not possible even with connected interfaces
   984  	makeMockRepoWithConnectedSnaps(c, st, info11, core11, "snapd-control")
   985  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
   986  
   987  	// if all of the above plus a snap declaration are in place we can
   988  	// manage schedules
   989  	s.setupSnapDecl(c, info11, "canonical")
   990  	c.Check(devicestate.CanManageRefreshes(st), Equals, true)
   991  
   992  	// works if the snap is not active as well (to fix race when a
   993  	// snap is refreshed)
   994  	var sideInfo11 snapstate.SnapState
   995  	err := snapstate.Get(st, "snap-with-snapd-control", &sideInfo11)
   996  	c.Assert(err, IsNil)
   997  	sideInfo11.Active = false
   998  	snapstate.Set(st, "snap-with-snapd-control", &sideInfo11)
   999  	c.Check(devicestate.CanManageRefreshes(st), Equals, true)
  1000  }
  1001  
  1002  func (s *deviceMgrSuite) TestCanManageRefreshesNoRefreshScheduleManaged(c *C) {
  1003  	st := s.state
  1004  	st.Lock()
  1005  	defer st.Unlock()
  1006  
  1007  	// just having a connected "snapd-control" interface is not enough
  1008  	// for setting refresh.schedule=managed
  1009  	info11 := makeInstalledMockSnap(c, st, snapWithSnapdControlOnlyYAML)
  1010  	core11 := makeInstalledMockCoreSnapWithSnapdControl(c, st)
  1011  	makeMockRepoWithConnectedSnaps(c, st, info11, core11, "snapd-control")
  1012  	s.setupSnapDecl(c, info11, "canonical")
  1013  
  1014  	c.Check(devicestate.CanManageRefreshes(st), Equals, false)
  1015  }
  1016  
  1017  func (s *deviceMgrSuite) TestReloadRegistered(c *C) {
  1018  	st := state.New(nil)
  1019  
  1020  	runner1 := state.NewTaskRunner(st)
  1021  	hookMgr1, err := hookstate.Manager(st, runner1)
  1022  	c.Assert(err, IsNil)
  1023  	mgr1, err := devicestate.Manager(st, hookMgr1, runner1, nil)
  1024  	c.Assert(err, IsNil)
  1025  
  1026  	ok := false
  1027  	select {
  1028  	case <-mgr1.Registered():
  1029  	default:
  1030  		ok = true
  1031  	}
  1032  	c.Check(ok, Equals, true)
  1033  
  1034  	st.Lock()
  1035  	devicestatetest.SetDevice(st, &auth.DeviceState{
  1036  		Brand:  "canonical",
  1037  		Model:  "pc",
  1038  		Serial: "serial",
  1039  	})
  1040  	st.Unlock()
  1041  
  1042  	runner2 := state.NewTaskRunner(st)
  1043  	hookMgr2, err := hookstate.Manager(st, runner2)
  1044  	c.Assert(err, IsNil)
  1045  	mgr2, err := devicestate.Manager(st, hookMgr2, runner2, nil)
  1046  	c.Assert(err, IsNil)
  1047  
  1048  	ok = false
  1049  	select {
  1050  	case <-mgr2.Registered():
  1051  		ok = true
  1052  	case <-time.After(5 * time.Second):
  1053  		c.Fatal("should have been marked registered")
  1054  	}
  1055  	c.Check(ok, Equals, true)
  1056  }
  1057  
  1058  func (s *deviceMgrSuite) TestMarkSeededInConfig(c *C) {
  1059  	st := s.state
  1060  	st.Lock()
  1061  	defer st.Unlock()
  1062  
  1063  	// avoid device registration
  1064  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
  1065  		Serial: "123",
  1066  	})
  1067  
  1068  	// avoid full seeding
  1069  	s.seeding()
  1070  
  1071  	// not seeded -> no config is set
  1072  	s.state.Unlock()
  1073  	s.mgr.Ensure()
  1074  	s.state.Lock()
  1075  
  1076  	var seedLoaded bool
  1077  	tr := config.NewTransaction(st)
  1078  	tr.Get("core", "seed.loaded", &seedLoaded)
  1079  	c.Check(seedLoaded, Equals, false)
  1080  
  1081  	// pretend we are seeded now
  1082  	s.state.Set("seeded", true)
  1083  
  1084  	// seeded -> config got updated
  1085  	s.state.Unlock()
  1086  	s.mgr.Ensure()
  1087  	s.state.Lock()
  1088  
  1089  	tr = config.NewTransaction(st)
  1090  	tr.Get("core", "seed.loaded", &seedLoaded)
  1091  	c.Check(seedLoaded, Equals, true)
  1092  
  1093  	// only the fake seeding change is in the state, no further
  1094  	// changes
  1095  	c.Check(s.state.Changes(), HasLen, 1)
  1096  }
  1097  
  1098  func (s *deviceMgrSuite) TestDevicemgrCanStandby(c *C) {
  1099  	st := state.New(nil)
  1100  
  1101  	runner := state.NewTaskRunner(st)
  1102  	hookMgr, err := hookstate.Manager(st, runner)
  1103  	c.Assert(err, IsNil)
  1104  	mgr, err := devicestate.Manager(st, hookMgr, runner, nil)
  1105  	c.Assert(err, IsNil)
  1106  
  1107  	st.Lock()
  1108  	defer st.Unlock()
  1109  	c.Check(mgr.CanStandby(), Equals, false)
  1110  
  1111  	st.Set("seeded", true)
  1112  	c.Check(mgr.CanStandby(), Equals, true)
  1113  }
  1114  
  1115  func (s *deviceMgrSuite) TestDeviceManagerReadsModeenv(c *C) {
  1116  	modeEnv := &boot.Modeenv{Mode: "install"}
  1117  	err := modeEnv.WriteTo("")
  1118  	c.Assert(err, IsNil)
  1119  
  1120  	runner := s.o.TaskRunner()
  1121  	mgr, err := devicestate.Manager(s.state, s.hookMgr, runner, s.newStore)
  1122  	c.Assert(err, IsNil)
  1123  	c.Assert(mgr, NotNil)
  1124  	c.Assert(mgr.SystemMode(), Equals, "install")
  1125  }
  1126  
  1127  func (s *deviceMgrSuite) TestDeviceManagerEmptySystemModeRun(c *C) {
  1128  	// set empty system mode
  1129  	devicestate.SetSystemMode(s.mgr, "")
  1130  
  1131  	// empty is returned as "run"
  1132  	c.Check(s.mgr.SystemMode(), Equals, "run")
  1133  }
  1134  
  1135  type startOfOperationTimeSuite struct {
  1136  	state  *state.State
  1137  	mgr    *devicestate.DeviceManager
  1138  	runner *state.TaskRunner
  1139  }
  1140  
  1141  var _ = Suite(&startOfOperationTimeSuite{})
  1142  
  1143  func (s *startOfOperationTimeSuite) SetUpTest(c *C) {
  1144  	dirs.SetRootDir(c.MkDir())
  1145  	os.MkdirAll(dirs.SnapRunDir, 0755)
  1146  
  1147  	s.state = state.New(nil)
  1148  	s.runner = state.NewTaskRunner(s.state)
  1149  	s.mgr = nil
  1150  }
  1151  
  1152  func (s *startOfOperationTimeSuite) TearDownTest(c *C) {
  1153  	dirs.SetRootDir("")
  1154  }
  1155  
  1156  func (s *startOfOperationTimeSuite) manager(c *C) *devicestate.DeviceManager {
  1157  	if s.mgr == nil {
  1158  		hookMgr, err := hookstate.Manager(s.state, s.runner)
  1159  		c.Assert(err, IsNil)
  1160  		mgr, err := devicestate.Manager(s.state, hookMgr, s.runner, nil)
  1161  		c.Assert(err, IsNil)
  1162  		s.mgr = mgr
  1163  	}
  1164  	return s.mgr
  1165  }
  1166  
  1167  func (s *startOfOperationTimeSuite) TestStartOfOperationTimeFromSeedTime(c *C) {
  1168  	mgr := s.manager(c)
  1169  
  1170  	st := s.state
  1171  	st.Lock()
  1172  	defer st.Unlock()
  1173  
  1174  	seedTime := time.Now().AddDate(0, -1, 0)
  1175  	st.Set("seed-time", seedTime)
  1176  
  1177  	operationTime, err := mgr.StartOfOperationTime()
  1178  	c.Assert(err, IsNil)
  1179  	c.Check(operationTime.Equal(seedTime), Equals, true)
  1180  
  1181  	var op time.Time
  1182  	st.Get("start-of-operation-time", &op)
  1183  	c.Check(op.Equal(operationTime), Equals, true)
  1184  }
  1185  
  1186  func (s *startOfOperationTimeSuite) TestStartOfOperationTimeAlreadySet(c *C) {
  1187  	mgr := s.manager(c)
  1188  
  1189  	st := s.state
  1190  	st.Lock()
  1191  	defer st.Unlock()
  1192  
  1193  	op := time.Now().AddDate(0, -1, 0)
  1194  	st.Set("start-of-operation-time", op)
  1195  
  1196  	operationTime, err := mgr.StartOfOperationTime()
  1197  	c.Assert(err, IsNil)
  1198  	c.Check(operationTime.Equal(op), Equals, true)
  1199  }
  1200  
  1201  func (s *startOfOperationTimeSuite) TestStartOfOperationTimeNoSeedTime(c *C) {
  1202  	mgr := s.manager(c)
  1203  
  1204  	st := s.state
  1205  	st.Lock()
  1206  	defer st.Unlock()
  1207  
  1208  	now := time.Now().Add(-1 * time.Second)
  1209  	devicestate.MockTimeNow(func() time.Time {
  1210  		return now
  1211  	})
  1212  
  1213  	operationTime, err := mgr.StartOfOperationTime()
  1214  	c.Assert(err, IsNil)
  1215  	c.Check(operationTime.Equal(now), Equals, true)
  1216  
  1217  	// repeated call returns already set time
  1218  	prev := now
  1219  	now = time.Now().Add(-10 * time.Hour)
  1220  	operationTime, err = s.manager(c).StartOfOperationTime()
  1221  	c.Assert(err, IsNil)
  1222  	c.Check(operationTime.Equal(prev), Equals, true)
  1223  }
  1224  
  1225  func (s *startOfOperationTimeSuite) TestStartOfOperationErrorIfPreseed(c *C) {
  1226  	restore := snapdenv.MockPreseeding(true)
  1227  	defer restore()
  1228  
  1229  	mgr := s.manager(c)
  1230  	st := s.state
  1231  
  1232  	st.Lock()
  1233  	defer st.Unlock()
  1234  	_, err := mgr.StartOfOperationTime()
  1235  	c.Assert(err, ErrorMatches, `internal error: unexpected call to StartOfOperationTime in preseed mode`)
  1236  }