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