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