github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/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 }