github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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 178 chg := s.state.NewChange("remodel", "...") 179 180 remodCtx.Init(chg) 181 182 var encNewModel string 183 c.Assert(chg.Get("new-model", &encNewModel), IsNil) 184 185 c.Check(encNewModel, Equals, string(asserts.Encode(newModel))) 186 187 c.Check(remodCtx.Model(), DeepEquals, newModel) 188 // an update remodel does not need a new/dedicated store 189 c.Check(remodCtx.Store(), IsNil) 190 } 191 192 func (s *remodelLogicSuite) TestNewStoreRemodelContextInit(c *C) { 193 oldModel := fakeRemodelingModel(nil) 194 newModel := fakeRemodelingModel(map[string]interface{}{ 195 "store": "my-other-store", 196 "revision": "1", 197 }) 198 199 s.state.Lock() 200 defer s.state.Unlock() 201 202 // we have a device state 203 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 204 Brand: "my-brand", 205 Model: "my-model", 206 Serial: "serialserialserial", 207 SessionMacaroon: "prev-session", 208 }) 209 210 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 211 c.Assert(err, IsNil) 212 213 c.Check(remodCtx.ForRemodeling(), Equals, true) 214 c.Check(remodCtx.Kind(), Equals, devicestate.StoreSwitchRemodel) 215 216 chg := s.state.NewChange("remodel", "...") 217 218 remodCtx.Init(chg) 219 220 var encNewModel string 221 c.Assert(chg.Get("new-model", &encNewModel), IsNil) 222 223 c.Check(encNewModel, Equals, string(asserts.Encode(newModel))) 224 225 var device *auth.DeviceState 226 c.Assert(chg.Get("device", &device), IsNil) 227 // session macaroon was reset 228 c.Check(device, DeepEquals, &auth.DeviceState{ 229 Brand: "my-brand", 230 Model: "my-model", 231 Serial: "serialserialserial", 232 }) 233 234 c.Check(remodCtx.Model(), DeepEquals, newModel) 235 } 236 237 func (s *remodelLogicSuite) TestRemodelDeviceBackendNoChangeYet(c *C) { 238 oldModel := fakeRemodelingModel(nil) 239 newModel := fakeRemodelingModel(map[string]interface{}{ 240 "store": "my-other-store", 241 "revision": "1", 242 }) 243 244 s.state.Lock() 245 defer s.state.Unlock() 246 247 // we have a device state 248 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 249 Brand: "my-brand", 250 Model: "my-model", 251 Serial: "serialserialserial", 252 }) 253 254 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 255 c.Assert(err, IsNil) 256 257 devBE := s.capturedDevBE 258 c.Check(devBE, NotNil) 259 260 device, err := devBE.Device() 261 c.Assert(err, IsNil) 262 c.Check(device, DeepEquals, &auth.DeviceState{ 263 Brand: "my-brand", 264 Model: "my-model", 265 Serial: "serialserialserial", 266 }) 267 268 mod, err := devBE.Model() 269 c.Assert(err, IsNil) 270 c.Check(mod, DeepEquals, newModel) 271 272 // set device state for the context 273 device1 := &auth.DeviceState{ 274 Brand: "my-brand", 275 Model: "my-model", 276 Serial: "serialserialserial", 277 SessionMacaroon: "session", 278 } 279 280 err = devBE.SetDevice(device1) 281 c.Assert(err, IsNil) 282 283 device, err = devBE.Device() 284 c.Assert(err, IsNil) 285 c.Check(device, DeepEquals, device1) 286 287 // have a change 288 chg := s.state.NewChange("remodel", "...") 289 290 remodCtx.Init(chg) 291 292 // check device state is preserved across association with a Change 293 device, err = devBE.Device() 294 c.Assert(err, IsNil) 295 c.Check(device, DeepEquals, device1) 296 } 297 298 func (s *remodelLogicSuite) TestRemodelDeviceBackend(c *C) { 299 oldModel := fakeRemodelingModel(nil) 300 newModel := fakeRemodelingModel(map[string]interface{}{ 301 "store": "my-other-store", 302 "revision": "1", 303 }) 304 305 s.state.Lock() 306 defer s.state.Unlock() 307 308 // we have a device state 309 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 310 Brand: "my-brand", 311 Model: "my-model", 312 Serial: "serialserialserial", 313 }) 314 315 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 316 c.Assert(err, IsNil) 317 318 devBE := devicestate.RemodelDeviceBackend(remodCtx) 319 320 chg := s.state.NewChange("remodel", "...") 321 322 remodCtx.Init(chg) 323 324 device, err := devBE.Device() 325 c.Assert(err, IsNil) 326 c.Check(device, DeepEquals, &auth.DeviceState{ 327 Brand: "my-brand", 328 Model: "my-model", 329 Serial: "serialserialserial", 330 }) 331 332 mod, err := devBE.Model() 333 c.Assert(err, IsNil) 334 c.Check(mod, DeepEquals, newModel) 335 336 // set a device state for the context 337 device1 := &auth.DeviceState{ 338 Brand: "my-brand", 339 Model: "my-model", 340 Serial: "serialserialserial", 341 SessionMacaroon: "session", 342 } 343 344 err = devBE.SetDevice(device1) 345 c.Assert(err, IsNil) 346 347 // it's stored on change now 348 var device2 *auth.DeviceState 349 c.Assert(chg.Get("device", &device2), IsNil) 350 c.Check(device2, DeepEquals, device1) 351 352 device, err = devBE.Device() 353 c.Assert(err, IsNil) 354 c.Check(device, DeepEquals, device1) 355 } 356 357 func (s *remodelLogicSuite) TestRemodelDeviceBackendIsolation(c *C) { 358 oldModel := fakeRemodelingModel(nil) 359 newModel := fakeRemodelingModel(map[string]interface{}{ 360 "store": "my-other-store", 361 "revision": "1", 362 }) 363 364 s.state.Lock() 365 defer s.state.Unlock() 366 367 // we have a device state 368 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 369 Brand: "my-brand", 370 Model: "my-model", 371 Serial: "serialserialserial", 372 }) 373 374 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 375 c.Assert(err, IsNil) 376 377 chg := s.state.NewChange("remodel", "...") 378 379 remodCtx.Init(chg) 380 381 devBE := devicestate.RemodelDeviceBackend(remodCtx) 382 383 err = devBE.SetDevice(&auth.DeviceState{ 384 Brand: "my-brand", 385 Model: "my-model", 386 Serial: "serialserialserial", 387 SessionMacaroon: "remodel-session", 388 }) 389 c.Assert(err, IsNil) 390 391 // the global device state is as before 392 expectedGlobalDevice := &auth.DeviceState{ 393 Brand: "my-brand", 394 Model: "my-model", 395 Serial: "serialserialserial", 396 } 397 398 device, err := s.mgr.StoreContextBackend().Device() 399 c.Assert(err, IsNil) 400 c.Check(device, DeepEquals, expectedGlobalDevice) 401 } 402 func (s *remodelLogicSuite) TestNewStoreRemodelContextStore(c *C) { 403 oldModel := fakeRemodelingModel(nil) 404 newModel := fakeRemodelingModel(map[string]interface{}{ 405 "store": "my-other-store", 406 "revision": "1", 407 }) 408 409 s.state.Lock() 410 defer s.state.Unlock() 411 412 // we have a device state 413 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 414 Brand: "my-brand", 415 Model: "my-model", 416 Serial: "serialserialserial", 417 SessionMacaroon: "prev-session", 418 }) 419 420 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 421 c.Assert(err, IsNil) 422 423 c.Check(s.capturedDevBE, NotNil) 424 425 // new store remodel context device state built ignoring the 426 // previous session 427 device1, err := s.capturedDevBE.Device() 428 c.Assert(err, IsNil) 429 c.Check(device1, DeepEquals, &auth.DeviceState{ 430 Brand: "my-brand", 431 Model: "my-model", 432 Serial: "serialserialserial", 433 }) 434 435 sto := remodCtx.Store() 436 c.Check(sto, Equals, s.dummyStore) 437 438 // store is kept and not rebuilt 439 s.dummyStore = nil 440 441 sto1 := remodCtx.Store() 442 c.Check(sto1, Equals, sto) 443 } 444 445 func (s *remodelLogicSuite) TestNewStoreRemodelContextFinish(c *C) { 446 oldModel := fakeRemodelingModel(nil) 447 newModel := fakeRemodelingModel(map[string]interface{}{ 448 "store": "my-other-store", 449 "revision": "1", 450 }) 451 452 s.state.Lock() 453 defer s.state.Unlock() 454 455 // we have a device state 456 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 457 Brand: "my-brand", 458 Model: "my-model", 459 Serial: "serialserialserial", 460 SessionMacaroon: "orig-session", 461 }) 462 463 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 464 c.Assert(err, IsNil) 465 466 chg := s.state.NewChange("remodel", "...") 467 468 remodCtx.Init(chg) 469 470 devBE := devicestate.RemodelDeviceBackend(remodCtx) 471 472 err = devBE.SetDevice(&auth.DeviceState{ 473 Brand: "my-brand", 474 Model: "my-model", 475 Serial: "serialserialserial", 476 SessionMacaroon: "new-session", 477 }) 478 c.Assert(err, IsNil) 479 480 err = remodCtx.Finish() 481 c.Assert(err, IsNil) 482 483 // the global device now matches the state reached in the remodel 484 expectedGlobalDevice := &auth.DeviceState{ 485 Brand: "my-brand", 486 Model: "my-model", 487 Serial: "serialserialserial", 488 SessionMacaroon: "new-session", 489 } 490 491 device, err := s.mgr.StoreContextBackend().Device() 492 c.Assert(err, IsNil) 493 c.Check(device, DeepEquals, expectedGlobalDevice) 494 } 495 496 func (s *remodelLogicSuite) TestNewStoreRemodelContextFinishVsGlobalUpdateDeviceAuth(c *C) { 497 oldModel := fakeRemodelingModel(nil) 498 newModel := fakeRemodelingModel(map[string]interface{}{ 499 "store": "my-other-store", 500 "revision": "1", 501 }) 502 503 s.state.Lock() 504 defer s.state.Unlock() 505 506 // we have a device state 507 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 508 Brand: "my-brand", 509 Model: "my-model", 510 Serial: "serialserialserial", 511 SessionMacaroon: "old-session", 512 }) 513 514 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 515 c.Assert(err, IsNil) 516 517 chg := s.state.NewChange("remodel", "...") 518 519 remodCtx.Init(chg) 520 521 devBE := devicestate.RemodelDeviceBackend(remodCtx) 522 523 err = devBE.SetDevice(&auth.DeviceState{ 524 Brand: "my-brand", 525 Model: "my-model", 526 Serial: "serialserialserial", 527 SessionMacaroon: "remodel-session", 528 }) 529 c.Assert(err, IsNil) 530 531 // global store device and auth context 532 scb := s.mgr.StoreContextBackend() 533 dac := storecontext.New(s.state, scb) 534 // this is the unlikely start of the global store trying to 535 // refresh the session 536 s.state.Unlock() 537 globalDevice, err := dac.Device() 538 s.state.Lock() 539 c.Assert(err, IsNil) 540 c.Check(globalDevice.SessionMacaroon, Equals, "old-session") 541 542 err = remodCtx.Finish() 543 c.Assert(err, IsNil) 544 545 s.state.Unlock() 546 device1, err := dac.UpdateDeviceAuth(globalDevice, "fresh-session") 547 s.state.Lock() 548 c.Assert(err, IsNil) 549 550 // the global device now matches the state reached in the remodel 551 expectedGlobalDevice := &auth.DeviceState{ 552 Brand: "my-brand", 553 Model: "my-model", 554 Serial: "serialserialserial", 555 SessionMacaroon: "remodel-session", 556 } 557 558 s.state.Unlock() 559 device, err := dac.Device() 560 s.state.Lock() 561 c.Assert(err, IsNil) 562 c.Check(device, DeepEquals, expectedGlobalDevice) 563 564 // also this was already the case 565 c.Check(device1, DeepEquals, expectedGlobalDevice) 566 } 567 568 func (s *remodelLogicSuite) TestRemodelDeviceBackendKeptSerial(c *C) { 569 oldModel := fakeRemodelingModel(nil) 570 newModel := fakeRemodelingModel(map[string]interface{}{ 571 "store": "my-other-store", 572 "revision": "1", 573 }) 574 575 s.state.Lock() 576 defer s.state.Unlock() 577 578 // we have a device state and serial 579 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 580 Brand: "my-brand", 581 Model: "my-model", 582 Serial: "serialserialserial1", 583 }) 584 585 makeSerialAssertionInState(c, s.brands, s.state, "my-brand", "my-model", "serialserialserial1") 586 587 serial, err := s.mgr.Serial() 588 c.Assert(err, IsNil) 589 c.Check(serial.Serial(), Equals, "serialserialserial1") 590 591 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 592 c.Assert(err, IsNil) 593 594 devBE := devicestate.RemodelDeviceBackend(remodCtx) 595 596 serial0, err := devBE.Serial() 597 c.Assert(err, IsNil) 598 c.Check(serial0.Serial(), Equals, "serialserialserial1") 599 600 chg := s.state.NewChange("remodel", "...") 601 602 remodCtx.Init(chg) 603 604 serial0, err = devBE.Serial() 605 c.Assert(err, IsNil) 606 c.Check(serial0.Serial(), Equals, "serialserialserial1") 607 } 608 609 func (s *remodelLogicSuite) TestRemodelContextForTaskAndCaching(c *C) { 610 oldModel := s.brands.Model("my-brand", "my-model", modelDefaults) 611 newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 612 "store": "my-other-store", 613 "revision": "1", 614 }) 615 616 s.state.Lock() 617 defer s.state.Unlock() 618 619 // we have a device state 620 assertstatetest.AddMany(s.state, oldModel) 621 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 622 Brand: "my-brand", 623 Model: "my-model", 624 Serial: "serialserialserial", 625 }) 626 627 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 628 c.Assert(err, IsNil) 629 630 c.Check(remodCtx.ForRemodeling(), Equals, true) 631 632 chg := s.state.NewChange("remodel", "...") 633 634 remodCtx.Init(chg) 635 636 t := s.state.NewTask("remodel-task-1", "...") 637 chg.AddTask(t) 638 639 // caching, internally this use remodelCtxFromTask 640 remodCtx1, err := devicestate.DeviceCtx(s.state, t, nil) 641 c.Assert(err, IsNil) 642 c.Check(remodCtx1, Equals, remodCtx) 643 644 // if the context goes away (e.g. because of restart) we 645 // compute a new one 646 devicestate.CleanupRemodelCtx(chg) 647 648 remodCtx2, err := devicestate.DeviceCtx(s.state, t, nil) 649 c.Assert(err, IsNil) 650 c.Check(remodCtx2 != remodCtx, Equals, true) 651 c.Check(remodCtx2.Model(), DeepEquals, newModel) 652 } 653 654 func (s *remodelLogicSuite) TestRemodelContextForTaskNo(c *C) { 655 s.state.Lock() 656 defer s.state.Unlock() 657 658 // internally these use remodelCtxFromTask 659 660 // task is nil 661 remodCtx1, err := devicestate.DeviceCtx(s.state, nil, nil) 662 c.Check(err, Equals, state.ErrNoState) 663 c.Check(remodCtx1, IsNil) 664 665 // no change 666 t := s.state.NewTask("random-task", "...") 667 _, err = devicestate.DeviceCtx(s.state, t, nil) 668 c.Check(err, Equals, state.ErrNoState) 669 670 // not a remodel change 671 chg := s.state.NewChange("not-remodel", "...") 672 chg.AddTask(t) 673 _, err = devicestate.DeviceCtx(s.state, t, nil) 674 c.Check(err, Equals, state.ErrNoState) 675 } 676 677 func (s *remodelLogicSuite) setupForRereg(c *C) (oldModel, newModel *asserts.Model) { 678 oldModel = s.brands.Model("my-brand", "my-model", modelDefaults) 679 newModel = s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 680 "authority-id": "other-brand", 681 "brand-id": "other-brand", 682 "model": "other-model", 683 "store": "other-store", 684 }) 685 686 s.state.Lock() 687 defer s.state.Unlock() 688 689 encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey()) 690 c.Assert(err, IsNil) 691 serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{ 692 "authority-id": "my-brand", 693 "brand-id": "my-brand", 694 "model": "my-model", 695 "serial": "orig-serial", 696 "device-key": string(encDevKey), 697 "device-key-sha3-384": devKey.PublicKey().ID(), 698 "timestamp": time.Now().Format(time.RFC3339), 699 }, nil, "") 700 c.Assert(err, IsNil) 701 702 assertstatetest.AddMany(s.state, oldModel, serial) 703 704 return oldModel, newModel 705 } 706 707 func (s *remodelLogicSuite) TestReregRemodelContextInit(c *C) { 708 oldModel, newModel := s.setupForRereg(c) 709 710 s.state.Lock() 711 defer s.state.Unlock() 712 713 // we have a device state 714 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 715 Brand: "my-brand", 716 Model: "my-model", 717 Serial: "orig-serial", 718 KeyID: "device-key-id", 719 SessionMacaroon: "prev-session", 720 }) 721 722 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 723 c.Assert(err, IsNil) 724 725 c.Check(remodCtx.ForRemodeling(), Equals, true) 726 c.Check(remodCtx.Kind(), Equals, devicestate.ReregRemodel) 727 728 chg := s.state.NewChange("remodel", "...") 729 730 remodCtx.Init(chg) 731 732 var encNewModel string 733 c.Assert(chg.Get("new-model", &encNewModel), IsNil) 734 735 c.Check(encNewModel, Equals, string(asserts.Encode(newModel))) 736 737 var device *auth.DeviceState 738 c.Assert(chg.Get("device", &device), IsNil) 739 // fresh device state before registration but with device-key 740 c.Check(device, DeepEquals, &auth.DeviceState{ 741 Brand: "other-brand", 742 Model: "other-model", 743 KeyID: "device-key-id", 744 }) 745 746 c.Check(remodCtx.Model(), DeepEquals, newModel) 747 748 // caching 749 t := s.state.NewTask("remodel-task-1", "...") 750 chg.AddTask(t) 751 752 remodCtx1, err := devicestate.DeviceCtx(s.state, t, nil) 753 c.Assert(err, IsNil) 754 c.Check(remodCtx1, Equals, remodCtx) 755 } 756 757 func (s *remodelLogicSuite) TestReregRemodelContextAsRegistrationContext(c *C) { 758 oldModel, newModel := s.setupForRereg(c) 759 760 s.state.Lock() 761 defer s.state.Unlock() 762 763 // we have a device state 764 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 765 Brand: "my-brand", 766 Model: "my-model", 767 Serial: "orig-serial", 768 KeyID: "device-key-id", 769 SessionMacaroon: "prev-session", 770 }) 771 772 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 773 c.Assert(err, IsNil) 774 775 c.Check(remodCtx.Kind(), Equals, devicestate.ReregRemodel) 776 777 chg := s.state.NewChange("remodel", "...") 778 779 remodCtx.Init(chg) 780 781 regCtx := remodCtx.(devicestate.RegistrationContext) 782 783 c.Check(regCtx.ForRemodeling(), Equals, true) 784 device1, err := regCtx.Device() 785 c.Assert(err, IsNil) 786 // fresh device state before registration but with device-key 787 c.Check(device1, DeepEquals, &auth.DeviceState{ 788 Brand: "other-brand", 789 Model: "other-model", 790 KeyID: "device-key-id", 791 }) 792 c.Check(regCtx.GadgetForSerialRequestConfig(), Equals, "my-brand-gadget") 793 c.Check(regCtx.SerialRequestExtraHeaders(), DeepEquals, map[string]interface{}{ 794 "original-brand-id": "my-brand", 795 "original-model": "my-model", 796 "original-serial": "orig-serial", 797 }) 798 799 serial, err := s.mgr.Serial() 800 c.Assert(err, IsNil) 801 c.Check(regCtx.SerialRequestAncillaryAssertions(), DeepEquals, []asserts.Assertion{newModel, serial}) 802 } 803 804 func (s *remodelLogicSuite) TestReregRemodelContextNewSerial(c *C) { 805 // re-registration case 806 oldModel := s.brands.Model("my-brand", "my-model", modelDefaults) 807 newModel := fakeRemodelingModel(map[string]interface{}{ 808 "model": "other-model", 809 }) 810 811 s.state.Lock() 812 defer s.state.Unlock() 813 814 assertstatetest.AddMany(s.state, oldModel) 815 816 // we have a device state and serial 817 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 818 Brand: "my-brand", 819 Model: "my-model", 820 Serial: "serialserialserial1", 821 }) 822 823 makeSerialAssertionInState(c, s.brands, s.state, "my-brand", "my-model", "serialserialserial1") 824 825 serial, err := s.mgr.Serial() 826 c.Assert(err, IsNil) 827 c.Check(serial.Serial(), Equals, "serialserialserial1") 828 829 remodCtx, err := devicestate.RemodelCtx(s.state, oldModel, newModel) 830 c.Assert(err, IsNil) 831 832 devBE := devicestate.RemodelDeviceBackend(remodCtx) 833 834 // no new serial yet 835 _, err = devBE.Serial() 836 c.Assert(err, Equals, state.ErrNoState) 837 838 chg := s.state.NewChange("remodel", "...") 839 840 remodCtx.Init(chg) 841 842 // sanity check 843 device1, err := devBE.Device() 844 c.Assert(err, IsNil) 845 c.Check(device1, DeepEquals, &auth.DeviceState{ 846 Brand: "my-brand", 847 Model: "other-model", 848 }) 849 850 // still no new serial 851 _, err = devBE.Serial() 852 c.Assert(err, Equals, state.ErrNoState) 853 854 newSerial := makeSerialAssertionInState(c, s.brands, s.state, "my-brand", "other-model", "serialserialserial2") 855 856 // same 857 _, err = devBE.Serial() 858 c.Check(err, Equals, state.ErrNoState) 859 860 // finish registration 861 regCtx := remodCtx.(devicestate.RegistrationContext) 862 err = regCtx.FinishRegistration(newSerial) 863 c.Assert(err, IsNil) 864 865 serial, err = devBE.Serial() 866 c.Check(err, IsNil) 867 c.Check(serial.Model(), Equals, "other-model") 868 c.Check(serial.Serial(), Equals, "serialserialserial2") 869 870 // not exposed yet 871 serial, err = s.mgr.Serial() 872 c.Assert(err, IsNil) 873 c.Check(serial.Model(), Equals, "my-model") 874 c.Check(serial.Serial(), Equals, "serialserialserial1") 875 876 // finish 877 err = remodCtx.Finish() 878 c.Assert(err, IsNil) 879 880 serial, err = s.mgr.Serial() 881 c.Assert(err, IsNil) 882 c.Check(serial.Model(), Equals, "other-model") 883 c.Check(serial.Serial(), Equals, "serialserialserial2") 884 }