github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/overlord/devicestate/devicestate_test.go (about)

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