github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/devicestate/remodel_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 "time" 24 25 . "gopkg.in/check.v1" 26 27 "github.com/snapcore/snapd/asserts" 28 "github.com/snapcore/snapd/asserts/assertstest" 29 "github.com/snapcore/snapd/dirs" 30 "github.com/snapcore/snapd/overlord" 31 "github.com/snapcore/snapd/overlord/assertstate" 32 "github.com/snapcore/snapd/overlord/assertstate/assertstatetest" 33 "github.com/snapcore/snapd/overlord/auth" 34 "github.com/snapcore/snapd/overlord/devicestate" 35 "github.com/snapcore/snapd/overlord/devicestate/devicestatetest" 36 "github.com/snapcore/snapd/overlord/hookstate" 37 "github.com/snapcore/snapd/overlord/snapstate" 38 "github.com/snapcore/snapd/overlord/state" 39 "github.com/snapcore/snapd/overlord/storecontext" 40 "github.com/snapcore/snapd/store/storetest" 41 ) 42 43 type remodelLogicSuite struct { 44 state *state.State 45 mgr *devicestate.DeviceManager 46 47 storeSigning *assertstest.StoreStack 48 brands *assertstest.SigningAccounts 49 50 capturedDevBE storecontext.DeviceBackend 51 dummyStore snapstate.StoreService 52 } 53 54 var _ = Suite(&remodelLogicSuite{}) 55 56 func (s *remodelLogicSuite) SetUpTest(c *C) { 57 dirs.SetRootDir(c.MkDir()) 58 59 o := overlord.Mock() 60 s.state = o.State() 61 62 s.storeSigning = assertstest.NewStoreStack("canonical", nil) 63 s.brands = assertstest.NewSigningAccounts(s.storeSigning) 64 s.brands.Register("my-brand", brandPrivKey, nil) 65 66 db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 67 Backstore: asserts.NewMemoryBackstore(), 68 Trusted: s.storeSigning.Trusted, 69 }) 70 c.Assert(err, IsNil) 71 72 func() { 73 s.state.Lock() 74 defer s.state.Unlock() 75 assertstate.ReplaceDB(s.state, db) 76 77 assertstatetest.AddMany(s.state, s.storeSigning.StoreAccountKey("")) 78 assertstatetest.AddMany(s.state, s.brands.AccountsAndKeys("my-brand")...) 79 }() 80 81 s.dummyStore = new(storetest.Store) 82 83 newStore := func(devBE storecontext.DeviceBackend) snapstate.StoreService { 84 s.capturedDevBE = devBE 85 return s.dummyStore 86 } 87 88 hookMgr, err := hookstate.Manager(s.state, o.TaskRunner()) 89 c.Assert(err, IsNil) 90 s.mgr, err = devicestate.Manager(s.state, hookMgr, o.TaskRunner(), newStore) 91 c.Assert(err, IsNil) 92 } 93 94 func (s *remodelLogicSuite) TearDownTest(c *C) { 95 dirs.SetRootDir("") 96 } 97 98 var modelDefaults = map[string]interface{}{ 99 "architecture": "amd64", 100 "kernel": "my-brand-kernel", 101 "gadget": "my-brand-gadget", 102 "store": "my-brand-store", 103 "required-snaps": []interface{}{"required1"}, 104 } 105 106 func fakeRemodelingModel(extra map[string]interface{}) *asserts.Model { 107 primary := map[string]interface{}{ 108 "type": "model", 109 "authority-id": "my-brand", 110 "series": "16", 111 "brand-id": "my-brand", 112 "model": "my-model", 113 } 114 return assertstest.FakeAssertion(primary, modelDefaults, extra).(*asserts.Model) 115 } 116 117 func (s *remodelLogicSuite) TestClassifyRemodel(c *C) { 118 oldModel := fakeRemodelingModel(nil) 119 120 cases := []struct { 121 newHeaders map[string]interface{} 122 kind devicestate.RemodelKind 123 }{ 124 {map[string]interface{}{}, devicestate.UpdateRemodel}, 125 {map[string]interface{}{ 126 "required-snaps": []interface{}{"required1", "required2"}, 127 "revision": "1", 128 }, devicestate.UpdateRemodel}, 129 {map[string]interface{}{ 130 "store": "my-other-store", 131 "revision": "1", 132 }, devicestate.StoreSwitchRemodel}, 133 {map[string]interface{}{ 134 "model": "my-other-model", 135 "store": "my-other-store", 136 }, devicestate.ReregRemodel}, 137 {map[string]interface{}{ 138 "authority-id": "other-brand", 139 "brand-id": "other-brand", 140 "model": "other-model", 141 }, devicestate.ReregRemodel}, 142 {map[string]interface{}{ 143 "authority-id": "other-brand", 144 "brand-id": "other-brand", 145 "model": "other-model", 146 "required-snaps": []interface{}{"other-required1"}, 147 }, devicestate.ReregRemodel}, 148 {map[string]interface{}{ 149 "authority-id": "other-brand", 150 "brand-id": "other-brand", 151 "model": "other-model", 152 "store": "my-other-store", 153 }, devicestate.ReregRemodel}, 154 } 155 156 for _, t := range cases { 157 newModel := fakeRemodelingModel(t.newHeaders) 158 c.Check(devicestate.ClassifyRemodel(oldModel, newModel), Equals, t.kind) 159 } 160 } 161 162 func (s *remodelLogicSuite) TestUpdateRemodelContext(c *C) { 163 oldModel := fakeRemodelingModel(nil) 164 newModel := fakeRemodelingModel(map[string]interface{}{ 165 "required-snaps": []interface{}{"required1", "required2"}, 166 "revision": "1", 167 }) 168 169 s.state.Lock() 170 defer s.state.Unlock() 171 172 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 173 c.Assert(err, IsNil) 174 175 c.Check(remodCtx.ForRemodeling(), Equals, true) 176 c.Check(remodCtx.Kind(), Equals, devicestate.UpdateRemodel) 177 groundCtx := remodCtx.GroundContext() 178 c.Check(groundCtx.ForRemodeling(), Equals, false) 179 c.Check(groundCtx.Model().Revision(), Equals, 0) 180 c.Check(groundCtx.Store, PanicMatches, `retrieved ground context is not intended to drive store operations`) 181 182 chg := s.state.NewChange("remodel", "...") 183 184 remodCtx.Init(chg) 185 186 var encNewModel string 187 c.Assert(chg.Get("new-model", &encNewModel), IsNil) 188 189 c.Check(encNewModel, Equals, string(asserts.Encode(newModel))) 190 191 c.Check(remodCtx.Model(), DeepEquals, newModel) 192 // an update remodel does not need a new/dedicated store 193 c.Check(remodCtx.Store(), IsNil) 194 } 195 196 func (s *remodelLogicSuite) TestNewStoreRemodelContextInit(c *C) { 197 oldModel := fakeRemodelingModel(nil) 198 newModel := fakeRemodelingModel(map[string]interface{}{ 199 "store": "my-other-store", 200 "revision": "1", 201 }) 202 203 s.state.Lock() 204 defer s.state.Unlock() 205 206 // we have a device state 207 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 208 Brand: "my-brand", 209 Model: "my-model", 210 Serial: "serialserialserial", 211 SessionMacaroon: "prev-session", 212 }) 213 214 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 215 c.Assert(err, IsNil) 216 217 c.Check(remodCtx.ForRemodeling(), Equals, true) 218 c.Check(remodCtx.Kind(), Equals, devicestate.StoreSwitchRemodel) 219 groundCtx := remodCtx.GroundContext() 220 c.Check(groundCtx.ForRemodeling(), Equals, false) 221 c.Check(groundCtx.Model().Revision(), Equals, 0) 222 223 chg := s.state.NewChange("remodel", "...") 224 225 remodCtx.Init(chg) 226 227 var encNewModel string 228 c.Assert(chg.Get("new-model", &encNewModel), IsNil) 229 230 c.Check(encNewModel, Equals, string(asserts.Encode(newModel))) 231 232 var device *auth.DeviceState 233 c.Assert(chg.Get("device", &device), IsNil) 234 // session macaroon was reset 235 c.Check(device, DeepEquals, &auth.DeviceState{ 236 Brand: "my-brand", 237 Model: "my-model", 238 Serial: "serialserialserial", 239 }) 240 241 c.Check(remodCtx.Model(), DeepEquals, newModel) 242 } 243 244 func (s *remodelLogicSuite) TestRemodelDeviceBackendNoChangeYet(c *C) { 245 oldModel := fakeRemodelingModel(nil) 246 newModel := fakeRemodelingModel(map[string]interface{}{ 247 "store": "my-other-store", 248 "revision": "1", 249 }) 250 251 s.state.Lock() 252 defer s.state.Unlock() 253 254 // we have a device state 255 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 256 Brand: "my-brand", 257 Model: "my-model", 258 Serial: "serialserialserial", 259 }) 260 261 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 262 c.Assert(err, IsNil) 263 264 devBE := s.capturedDevBE 265 c.Check(devBE, NotNil) 266 267 device, err := devBE.Device() 268 c.Assert(err, IsNil) 269 c.Check(device, DeepEquals, &auth.DeviceState{ 270 Brand: "my-brand", 271 Model: "my-model", 272 Serial: "serialserialserial", 273 }) 274 275 mod, err := devBE.Model() 276 c.Assert(err, IsNil) 277 c.Check(mod, DeepEquals, newModel) 278 279 // set device state for the context 280 device1 := &auth.DeviceState{ 281 Brand: "my-brand", 282 Model: "my-model", 283 Serial: "serialserialserial", 284 SessionMacaroon: "session", 285 } 286 287 err = devBE.SetDevice(device1) 288 c.Assert(err, IsNil) 289 290 device, err = devBE.Device() 291 c.Assert(err, IsNil) 292 c.Check(device, DeepEquals, device1) 293 294 // have a change 295 chg := s.state.NewChange("remodel", "...") 296 297 remodCtx.Init(chg) 298 299 // check device state is preserved across association with a Change 300 device, err = devBE.Device() 301 c.Assert(err, IsNil) 302 c.Check(device, DeepEquals, device1) 303 } 304 305 func (s *remodelLogicSuite) TestRemodelDeviceBackend(c *C) { 306 oldModel := fakeRemodelingModel(nil) 307 newModel := fakeRemodelingModel(map[string]interface{}{ 308 "store": "my-other-store", 309 "revision": "1", 310 }) 311 312 s.state.Lock() 313 defer s.state.Unlock() 314 315 // we have a device state 316 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 317 Brand: "my-brand", 318 Model: "my-model", 319 Serial: "serialserialserial", 320 }) 321 322 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 323 c.Assert(err, IsNil) 324 325 devBE := devicestate.RemodelDeviceBackend(remodCtx) 326 327 chg := s.state.NewChange("remodel", "...") 328 329 remodCtx.Init(chg) 330 331 device, err := devBE.Device() 332 c.Assert(err, IsNil) 333 c.Check(device, DeepEquals, &auth.DeviceState{ 334 Brand: "my-brand", 335 Model: "my-model", 336 Serial: "serialserialserial", 337 }) 338 339 mod, err := devBE.Model() 340 c.Assert(err, IsNil) 341 c.Check(mod, DeepEquals, newModel) 342 343 // set a device state for the context 344 device1 := &auth.DeviceState{ 345 Brand: "my-brand", 346 Model: "my-model", 347 Serial: "serialserialserial", 348 SessionMacaroon: "session", 349 } 350 351 err = devBE.SetDevice(device1) 352 c.Assert(err, IsNil) 353 354 // it's stored on change now 355 var device2 *auth.DeviceState 356 c.Assert(chg.Get("device", &device2), IsNil) 357 c.Check(device2, DeepEquals, device1) 358 359 device, err = devBE.Device() 360 c.Assert(err, IsNil) 361 c.Check(device, DeepEquals, device1) 362 } 363 364 func (s *remodelLogicSuite) TestRemodelDeviceBackendIsolation(c *C) { 365 oldModel := fakeRemodelingModel(nil) 366 newModel := fakeRemodelingModel(map[string]interface{}{ 367 "store": "my-other-store", 368 "revision": "1", 369 }) 370 371 s.state.Lock() 372 defer s.state.Unlock() 373 374 // we have a device state 375 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 376 Brand: "my-brand", 377 Model: "my-model", 378 Serial: "serialserialserial", 379 }) 380 381 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 382 c.Assert(err, IsNil) 383 384 chg := s.state.NewChange("remodel", "...") 385 386 remodCtx.Init(chg) 387 388 devBE := devicestate.RemodelDeviceBackend(remodCtx) 389 390 err = devBE.SetDevice(&auth.DeviceState{ 391 Brand: "my-brand", 392 Model: "my-model", 393 Serial: "serialserialserial", 394 SessionMacaroon: "remodel-session", 395 }) 396 c.Assert(err, IsNil) 397 398 // the global device state is as before 399 expectedGlobalDevice := &auth.DeviceState{ 400 Brand: "my-brand", 401 Model: "my-model", 402 Serial: "serialserialserial", 403 } 404 405 device, err := s.mgr.StoreContextBackend().Device() 406 c.Assert(err, IsNil) 407 c.Check(device, DeepEquals, expectedGlobalDevice) 408 } 409 func (s *remodelLogicSuite) TestNewStoreRemodelContextStore(c *C) { 410 oldModel := fakeRemodelingModel(nil) 411 newModel := fakeRemodelingModel(map[string]interface{}{ 412 "store": "my-other-store", 413 "revision": "1", 414 }) 415 416 s.state.Lock() 417 defer s.state.Unlock() 418 419 // we have a device state 420 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 421 Brand: "my-brand", 422 Model: "my-model", 423 Serial: "serialserialserial", 424 SessionMacaroon: "prev-session", 425 }) 426 427 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 428 c.Assert(err, IsNil) 429 430 c.Check(s.capturedDevBE, NotNil) 431 432 // new store remodel context device state built ignoring the 433 // previous session 434 device1, err := s.capturedDevBE.Device() 435 c.Assert(err, IsNil) 436 c.Check(device1, DeepEquals, &auth.DeviceState{ 437 Brand: "my-brand", 438 Model: "my-model", 439 Serial: "serialserialserial", 440 }) 441 442 sto := remodCtx.Store() 443 c.Check(sto, Equals, s.dummyStore) 444 445 // store is kept and not rebuilt 446 s.dummyStore = nil 447 448 sto1 := remodCtx.Store() 449 c.Check(sto1, Equals, sto) 450 } 451 452 func (s *remodelLogicSuite) TestNewStoreRemodelContextFinish(c *C) { 453 oldModel := fakeRemodelingModel(nil) 454 newModel := fakeRemodelingModel(map[string]interface{}{ 455 "store": "my-other-store", 456 "revision": "1", 457 }) 458 459 s.state.Lock() 460 defer s.state.Unlock() 461 462 // we have a device state 463 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 464 Brand: "my-brand", 465 Model: "my-model", 466 Serial: "serialserialserial", 467 SessionMacaroon: "orig-session", 468 }) 469 470 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 471 c.Assert(err, IsNil) 472 473 chg := s.state.NewChange("remodel", "...") 474 475 remodCtx.Init(chg) 476 477 devBE := devicestate.RemodelDeviceBackend(remodCtx) 478 479 err = devBE.SetDevice(&auth.DeviceState{ 480 Brand: "my-brand", 481 Model: "my-model", 482 Serial: "serialserialserial", 483 SessionMacaroon: "new-session", 484 }) 485 c.Assert(err, IsNil) 486 487 err = remodCtx.Finish() 488 c.Assert(err, IsNil) 489 490 // the global device now matches the state reached in the remodel 491 expectedGlobalDevice := &auth.DeviceState{ 492 Brand: "my-brand", 493 Model: "my-model", 494 Serial: "serialserialserial", 495 SessionMacaroon: "new-session", 496 } 497 498 device, err := s.mgr.StoreContextBackend().Device() 499 c.Assert(err, IsNil) 500 c.Check(device, DeepEquals, expectedGlobalDevice) 501 } 502 503 func (s *remodelLogicSuite) TestNewStoreRemodelContextFinishVsGlobalUpdateDeviceAuth(c *C) { 504 oldModel := fakeRemodelingModel(nil) 505 newModel := fakeRemodelingModel(map[string]interface{}{ 506 "store": "my-other-store", 507 "revision": "1", 508 }) 509 510 s.state.Lock() 511 defer s.state.Unlock() 512 513 // we have a device state 514 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 515 Brand: "my-brand", 516 Model: "my-model", 517 Serial: "serialserialserial", 518 SessionMacaroon: "old-session", 519 }) 520 521 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 522 c.Assert(err, IsNil) 523 524 chg := s.state.NewChange("remodel", "...") 525 526 remodCtx.Init(chg) 527 528 devBE := devicestate.RemodelDeviceBackend(remodCtx) 529 530 err = devBE.SetDevice(&auth.DeviceState{ 531 Brand: "my-brand", 532 Model: "my-model", 533 Serial: "serialserialserial", 534 SessionMacaroon: "remodel-session", 535 }) 536 c.Assert(err, IsNil) 537 538 // global store device and auth context 539 scb := s.mgr.StoreContextBackend() 540 dac := storecontext.New(s.state, scb) 541 // this is the unlikely start of the global store trying to 542 // refresh the session 543 s.state.Unlock() 544 globalDevice, err := dac.Device() 545 s.state.Lock() 546 c.Assert(err, IsNil) 547 c.Check(globalDevice.SessionMacaroon, Equals, "old-session") 548 549 err = remodCtx.Finish() 550 c.Assert(err, IsNil) 551 552 s.state.Unlock() 553 device1, err := dac.UpdateDeviceAuth(globalDevice, "fresh-session") 554 s.state.Lock() 555 c.Assert(err, IsNil) 556 557 // the global device now matches the state reached in the remodel 558 expectedGlobalDevice := &auth.DeviceState{ 559 Brand: "my-brand", 560 Model: "my-model", 561 Serial: "serialserialserial", 562 SessionMacaroon: "remodel-session", 563 } 564 565 s.state.Unlock() 566 device, err := dac.Device() 567 s.state.Lock() 568 c.Assert(err, IsNil) 569 c.Check(device, DeepEquals, expectedGlobalDevice) 570 571 // also this was already the case 572 c.Check(device1, DeepEquals, expectedGlobalDevice) 573 } 574 575 func (s *remodelLogicSuite) TestRemodelDeviceBackendKeptSerial(c *C) { 576 oldModel := fakeRemodelingModel(nil) 577 newModel := fakeRemodelingModel(map[string]interface{}{ 578 "store": "my-other-store", 579 "revision": "1", 580 }) 581 582 s.state.Lock() 583 defer s.state.Unlock() 584 585 // we have a device state and serial 586 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 587 Brand: "my-brand", 588 Model: "my-model", 589 Serial: "serialserialserial1", 590 }) 591 592 makeSerialAssertionInState(c, s.brands, s.state, "my-brand", "my-model", "serialserialserial1") 593 594 serial, err := s.mgr.Serial() 595 c.Assert(err, IsNil) 596 c.Check(serial.Serial(), Equals, "serialserialserial1") 597 598 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 599 c.Assert(err, IsNil) 600 601 devBE := devicestate.RemodelDeviceBackend(remodCtx) 602 603 serial0, err := devBE.Serial() 604 c.Assert(err, IsNil) 605 c.Check(serial0.Serial(), Equals, "serialserialserial1") 606 607 chg := s.state.NewChange("remodel", "...") 608 609 remodCtx.Init(chg) 610 611 serial0, err = devBE.Serial() 612 c.Assert(err, IsNil) 613 c.Check(serial0.Serial(), Equals, "serialserialserial1") 614 } 615 616 func (s *remodelLogicSuite) TestRemodelContextSystemModeDefaultRun(c *C) { 617 oldModel := s.brands.Model("my-brand", "my-model", modelDefaults) 618 newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{"revision": "2"}) 619 620 s.state.Lock() 621 defer s.state.Unlock() 622 623 assertstatetest.AddMany(s.state, oldModel) 624 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 625 Brand: "my-brand", 626 Model: "my-model", 627 Serial: "serialserialserial", 628 }) 629 630 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 631 c.Assert(err, IsNil) 632 c.Check(remodCtx.SystemMode(), Equals, "run") 633 } 634 635 func (s *remodelLogicSuite) TestRemodelContextSystemModeWorks(c *C) { 636 oldModel := s.brands.Model("my-brand", "my-model", modelDefaults) 637 newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{"revision": "2"}) 638 639 s.state.Lock() 640 defer s.state.Unlock() 641 642 assertstatetest.AddMany(s.state, oldModel) 643 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 644 Brand: "my-brand", 645 Model: "my-model", 646 Serial: "serialserialserial", 647 }) 648 devicestate.SetSystemMode(s.mgr, "install") 649 650 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 651 c.Assert(err, IsNil) 652 c.Check(remodCtx.SystemMode(), Equals, "install") 653 } 654 655 func (s *remodelLogicSuite) TestRemodelContextForTaskAndCaching(c *C) { 656 oldModel := s.brands.Model("my-brand", "my-model", modelDefaults) 657 newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 658 "store": "my-other-store", 659 "revision": "1", 660 }) 661 662 s.state.Lock() 663 defer s.state.Unlock() 664 665 // we have a device state 666 assertstatetest.AddMany(s.state, oldModel) 667 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 668 Brand: "my-brand", 669 Model: "my-model", 670 Serial: "serialserialserial", 671 }) 672 673 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 674 c.Assert(err, IsNil) 675 676 c.Check(remodCtx.ForRemodeling(), Equals, true) 677 678 chg := s.state.NewChange("remodel", "...") 679 680 remodCtx.Init(chg) 681 682 t := s.state.NewTask("remodel-task-1", "...") 683 chg.AddTask(t) 684 685 // caching, internally this use remodelCtxFromTask 686 remodCtx1, err := devicestate.DeviceCtx(s.state, t, nil) 687 c.Assert(err, IsNil) 688 c.Check(remodCtx1, Equals, remodCtx) 689 690 // if the context goes away (e.g. because of restart) we 691 // compute a new one 692 devicestate.CleanupRemodelCtx(chg) 693 694 remodCtx2, err := devicestate.DeviceCtx(s.state, t, nil) 695 c.Assert(err, IsNil) 696 c.Check(remodCtx2 != remodCtx, Equals, true) 697 c.Check(remodCtx2.Model(), DeepEquals, newModel) 698 } 699 700 func (s *remodelLogicSuite) TestRemodelContextForTaskNo(c *C) { 701 s.state.Lock() 702 defer s.state.Unlock() 703 704 // internally these use remodelCtxFromTask 705 706 // task is nil 707 remodCtx1, err := devicestate.DeviceCtx(s.state, nil, nil) 708 c.Check(err, Equals, state.ErrNoState) 709 c.Check(remodCtx1, IsNil) 710 711 // no change 712 t := s.state.NewTask("random-task", "...") 713 _, err = devicestate.DeviceCtx(s.state, t, nil) 714 c.Check(err, Equals, state.ErrNoState) 715 716 // not a remodel change 717 chg := s.state.NewChange("not-remodel", "...") 718 chg.AddTask(t) 719 _, err = devicestate.DeviceCtx(s.state, t, nil) 720 c.Check(err, Equals, state.ErrNoState) 721 } 722 723 func (s *remodelLogicSuite) setupForRereg(c *C) (oldModel, newModel *asserts.Model) { 724 oldModel = s.brands.Model("my-brand", "my-model", modelDefaults) 725 newModel = s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 726 "authority-id": "other-brand", 727 "brand-id": "other-brand", 728 "model": "other-model", 729 "store": "other-store", 730 }) 731 732 s.state.Lock() 733 defer s.state.Unlock() 734 735 encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey()) 736 c.Assert(err, IsNil) 737 serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{ 738 "authority-id": "my-brand", 739 "brand-id": "my-brand", 740 "model": "my-model", 741 "serial": "orig-serial", 742 "device-key": string(encDevKey), 743 "device-key-sha3-384": devKey.PublicKey().ID(), 744 "timestamp": time.Now().Format(time.RFC3339), 745 }, nil, "") 746 c.Assert(err, IsNil) 747 748 assertstatetest.AddMany(s.state, oldModel, serial) 749 750 return oldModel, newModel 751 } 752 753 func (s *remodelLogicSuite) TestReregRemodelContextInit(c *C) { 754 oldModel, newModel := s.setupForRereg(c) 755 756 s.state.Lock() 757 defer s.state.Unlock() 758 759 // we have a device state 760 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 761 Brand: "my-brand", 762 Model: "my-model", 763 Serial: "orig-serial", 764 KeyID: "device-key-id", 765 SessionMacaroon: "prev-session", 766 }) 767 768 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 769 c.Assert(err, IsNil) 770 771 c.Check(remodCtx.ForRemodeling(), Equals, true) 772 c.Check(remodCtx.Kind(), Equals, devicestate.ReregRemodel) 773 groundCtx := remodCtx.GroundContext() 774 c.Check(groundCtx.ForRemodeling(), Equals, false) 775 c.Check(groundCtx.Model().BrandID(), Equals, "my-brand") 776 777 chg := s.state.NewChange("remodel", "...") 778 779 remodCtx.Init(chg) 780 781 var encNewModel string 782 c.Assert(chg.Get("new-model", &encNewModel), IsNil) 783 784 c.Check(encNewModel, Equals, string(asserts.Encode(newModel))) 785 786 var device *auth.DeviceState 787 c.Assert(chg.Get("device", &device), IsNil) 788 // fresh device state before registration but with device-key 789 c.Check(device, DeepEquals, &auth.DeviceState{ 790 Brand: "other-brand", 791 Model: "other-model", 792 KeyID: "device-key-id", 793 }) 794 795 c.Check(remodCtx.Model(), DeepEquals, newModel) 796 797 // caching 798 t := s.state.NewTask("remodel-task-1", "...") 799 chg.AddTask(t) 800 801 remodCtx1, err := devicestate.DeviceCtx(s.state, t, nil) 802 c.Assert(err, IsNil) 803 c.Check(remodCtx1, Equals, remodCtx) 804 } 805 806 func (s *remodelLogicSuite) TestReregRemodelContextAsRegistrationContext(c *C) { 807 oldModel, newModel := s.setupForRereg(c) 808 809 s.state.Lock() 810 defer s.state.Unlock() 811 812 // we have a device state 813 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 814 Brand: "my-brand", 815 Model: "my-model", 816 Serial: "orig-serial", 817 KeyID: "device-key-id", 818 SessionMacaroon: "prev-session", 819 }) 820 821 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 822 c.Assert(err, IsNil) 823 824 c.Check(remodCtx.Kind(), Equals, devicestate.ReregRemodel) 825 826 chg := s.state.NewChange("remodel", "...") 827 828 remodCtx.Init(chg) 829 830 regCtx := remodCtx.(devicestate.RegistrationContext) 831 832 c.Check(regCtx.ForRemodeling(), Equals, true) 833 device1, err := regCtx.Device() 834 c.Assert(err, IsNil) 835 // fresh device state before registration but with device-key 836 c.Check(device1, DeepEquals, &auth.DeviceState{ 837 Brand: "other-brand", 838 Model: "other-model", 839 KeyID: "device-key-id", 840 }) 841 c.Check(regCtx.GadgetForSerialRequestConfig(), Equals, "my-brand-gadget") 842 c.Check(regCtx.SerialRequestExtraHeaders(), DeepEquals, map[string]interface{}{ 843 "original-brand-id": "my-brand", 844 "original-model": "my-model", 845 "original-serial": "orig-serial", 846 }) 847 848 serial, err := s.mgr.Serial() 849 c.Assert(err, IsNil) 850 c.Check(regCtx.SerialRequestAncillaryAssertions(), DeepEquals, []asserts.Assertion{newModel, serial}) 851 } 852 853 func (s *remodelLogicSuite) TestReregRemodelContextNewSerial(c *C) { 854 // re-registration case 855 oldModel := s.brands.Model("my-brand", "my-model", modelDefaults) 856 newModel := fakeRemodelingModel(map[string]interface{}{ 857 "model": "other-model", 858 }) 859 860 s.state.Lock() 861 defer s.state.Unlock() 862 863 assertstatetest.AddMany(s.state, oldModel) 864 865 // we have a device state and serial 866 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 867 Brand: "my-brand", 868 Model: "my-model", 869 Serial: "serialserialserial1", 870 }) 871 872 makeSerialAssertionInState(c, s.brands, s.state, "my-brand", "my-model", "serialserialserial1") 873 874 serial, err := s.mgr.Serial() 875 c.Assert(err, IsNil) 876 c.Check(serial.Serial(), Equals, "serialserialserial1") 877 878 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 879 c.Assert(err, IsNil) 880 881 devBE := devicestate.RemodelDeviceBackend(remodCtx) 882 883 // no new serial yet 884 _, err = devBE.Serial() 885 c.Assert(err, Equals, state.ErrNoState) 886 887 chg := s.state.NewChange("remodel", "...") 888 889 remodCtx.Init(chg) 890 891 // sanity check 892 device1, err := devBE.Device() 893 c.Assert(err, IsNil) 894 c.Check(device1, DeepEquals, &auth.DeviceState{ 895 Brand: "my-brand", 896 Model: "other-model", 897 }) 898 899 // still no new serial 900 _, err = devBE.Serial() 901 c.Assert(err, Equals, state.ErrNoState) 902 903 newSerial := makeSerialAssertionInState(c, s.brands, s.state, "my-brand", "other-model", "serialserialserial2") 904 905 // same 906 _, err = devBE.Serial() 907 c.Check(err, Equals, state.ErrNoState) 908 909 // finish registration 910 regCtx := remodCtx.(devicestate.RegistrationContext) 911 err = regCtx.FinishRegistration(newSerial) 912 c.Assert(err, IsNil) 913 914 serial, err = devBE.Serial() 915 c.Check(err, IsNil) 916 c.Check(serial.Model(), Equals, "other-model") 917 c.Check(serial.Serial(), Equals, "serialserialserial2") 918 919 // not exposed yet 920 serial, err = s.mgr.Serial() 921 c.Assert(err, IsNil) 922 c.Check(serial.Model(), Equals, "my-model") 923 c.Check(serial.Serial(), Equals, "serialserialserial1") 924 925 // finish 926 err = remodCtx.Finish() 927 c.Assert(err, IsNil) 928 929 serial, err = s.mgr.Serial() 930 c.Assert(err, IsNil) 931 c.Check(serial.Model(), Equals, "other-model") 932 c.Check(serial.Serial(), Equals, "serialserialserial2") 933 }