github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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 "context" 24 "errors" 25 "fmt" 26 "io/ioutil" 27 "net" 28 "net/http" 29 "net/http/httptest" 30 "net/url" 31 "os" 32 "path/filepath" 33 "strings" 34 "testing" 35 "time" 36 37 . "gopkg.in/check.v1" 38 "gopkg.in/tomb.v2" 39 "gopkg.in/yaml.v2" 40 41 "github.com/snapcore/snapd/asserts" 42 "github.com/snapcore/snapd/asserts/assertstest" 43 "github.com/snapcore/snapd/asserts/sysdb" 44 "github.com/snapcore/snapd/bootloader" 45 "github.com/snapcore/snapd/bootloader/bootloadertest" 46 "github.com/snapcore/snapd/dirs" 47 "github.com/snapcore/snapd/gadget" 48 "github.com/snapcore/snapd/httputil" 49 "github.com/snapcore/snapd/interfaces" 50 "github.com/snapcore/snapd/interfaces/builtin" 51 "github.com/snapcore/snapd/logger" 52 "github.com/snapcore/snapd/osutil" 53 "github.com/snapcore/snapd/overlord" 54 "github.com/snapcore/snapd/overlord/assertstate" 55 "github.com/snapcore/snapd/overlord/assertstate/assertstatetest" 56 "github.com/snapcore/snapd/overlord/auth" 57 "github.com/snapcore/snapd/overlord/configstate/config" 58 "github.com/snapcore/snapd/overlord/devicestate" 59 "github.com/snapcore/snapd/overlord/devicestate/devicestatetest" 60 "github.com/snapcore/snapd/overlord/hookstate" 61 "github.com/snapcore/snapd/overlord/ifacestate/ifacerepo" 62 "github.com/snapcore/snapd/overlord/snapstate" 63 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 64 "github.com/snapcore/snapd/overlord/state" 65 "github.com/snapcore/snapd/overlord/storecontext" 66 "github.com/snapcore/snapd/release" 67 "github.com/snapcore/snapd/snap" 68 "github.com/snapcore/snapd/snap/snaptest" 69 "github.com/snapcore/snapd/store/storetest" 70 "github.com/snapcore/snapd/strutil" 71 "github.com/snapcore/snapd/testutil" 72 "github.com/snapcore/snapd/timings" 73 ) 74 75 func TestDeviceManager(t *testing.T) { TestingT(t) } 76 77 type deviceMgrSuite struct { 78 o *overlord.Overlord 79 state *state.State 80 se *overlord.StateEngine 81 hookMgr *hookstate.HookManager 82 mgr *devicestate.DeviceManager 83 db *asserts.Database 84 85 bootloader *bootloadertest.MockBootloader 86 87 storeSigning *assertstest.StoreStack 88 brands *assertstest.SigningAccounts 89 90 reqID string 91 92 ancillary []asserts.Assertion 93 94 restartRequests []state.RestartType 95 96 restoreOnClassic func() 97 restoreGenericClassicMod func() 98 restoreSanitize func() 99 100 newFakeStore func(storecontext.DeviceBackend) snapstate.StoreService 101 } 102 103 var _ = Suite(&deviceMgrSuite{}) 104 var testKeyLength = 1024 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 ) 130 131 func (s *deviceMgrSuite) SetUpTest(c *C) { 132 dirs.SetRootDir(c.MkDir()) 133 os.MkdirAll(dirs.SnapRunDir, 0755) 134 135 s.restartRequests = nil 136 137 s.restoreSanitize = snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) 138 139 s.bootloader = bootloadertest.Mock("mock", c.MkDir()) 140 bootloader.Force(s.bootloader) 141 142 s.restoreOnClassic = release.MockOnClassic(false) 143 144 s.storeSigning = assertstest.NewStoreStack("canonical", nil) 145 s.o = overlord.MockWithRestartHandler(func(req state.RestartType) { 146 s.restartRequests = append(s.restartRequests, req) 147 }) 148 s.state = s.o.State() 149 s.state.Lock() 150 s.state.VerifyReboot("boot-id-0") 151 s.state.Unlock() 152 s.se = s.o.StateEngine() 153 154 s.restoreGenericClassicMod = sysdb.MockGenericClassicModel(s.storeSigning.GenericClassicModel) 155 156 s.brands = assertstest.NewSigningAccounts(s.storeSigning) 157 s.brands.Register("my-brand", brandPrivKey, nil) 158 s.brands.Register("rereg-brand", brandPrivKey2, nil) 159 160 db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 161 Backstore: asserts.NewMemoryBackstore(), 162 Trusted: s.storeSigning.Trusted, 163 OtherPredefined: s.storeSigning.Generic, 164 }) 165 c.Assert(err, IsNil) 166 167 s.state.Lock() 168 assertstate.ReplaceDB(s.state, db) 169 s.state.Unlock() 170 171 err = db.Add(s.storeSigning.StoreAccountKey("")) 172 c.Assert(err, IsNil) 173 174 hookMgr, err := hookstate.Manager(s.state, s.o.TaskRunner()) 175 c.Assert(err, IsNil) 176 mgr, err := devicestate.Manager(s.state, hookMgr, s.o.TaskRunner(), s.newStore) 177 c.Assert(err, IsNil) 178 179 s.db = db 180 s.hookMgr = hookMgr 181 s.o.AddManager(s.hookMgr) 182 s.mgr = mgr 183 s.o.AddManager(s.mgr) 184 s.o.AddManager(s.o.TaskRunner()) 185 186 // For triggering errors 187 erroringHandler := func(task *state.Task, _ *tomb.Tomb) error { 188 return errors.New("error out") 189 } 190 s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil) 191 192 c.Assert(s.o.StartUp(), IsNil) 193 194 s.state.Lock() 195 snapstate.ReplaceStore(s.state, &fakeStore{ 196 state: s.state, 197 db: s.storeSigning, 198 }) 199 s.state.Unlock() 200 } 201 202 func (s *deviceMgrSuite) newStore(devBE storecontext.DeviceBackend) snapstate.StoreService { 203 return s.newFakeStore(devBE) 204 } 205 206 func (s *deviceMgrSuite) TearDownTest(c *C) { 207 s.ancillary = nil 208 s.state.Lock() 209 assertstate.ReplaceDB(s.state, nil) 210 s.state.Unlock() 211 bootloader.Force(nil) 212 dirs.SetRootDir("") 213 s.restoreGenericClassicMod() 214 s.restoreOnClassic() 215 s.restoreSanitize() 216 } 217 218 var settleTimeout = 15 * time.Second 219 220 func (s *deviceMgrSuite) settle(c *C) { 221 err := s.o.Settle(settleTimeout) 222 c.Assert(err, IsNil) 223 } 224 225 // seeding avoids triggering a real full seeding, it simulates having it in process instead 226 func (s *deviceMgrSuite) seeding() { 227 chg := s.state.NewChange("seed", "Seed system") 228 chg.SetStatus(state.DoingStatus) 229 } 230 231 func (s *deviceMgrSuite) signSerial(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) { 232 brandID := headers["brand-id"].(string) 233 model := headers["model"].(string) 234 keyID := "" 235 236 var signing assertstest.SignerDB = s.storeSigning 237 238 switch model { 239 case "pc", "pc2": 240 case "classic-alt-store": 241 c.Check(brandID, Equals, "canonical") 242 case "generic-classic": 243 c.Check(brandID, Equals, "generic") 244 headers["authority-id"] = "generic" 245 keyID = s.storeSigning.GenericKey.PublicKeyID() 246 case "rereg-model": 247 headers["authority-id"] = "rereg-brand" 248 signing = s.brands.Signing("rereg-brand") 249 default: 250 c.Fatalf("unknown model: %s", model) 251 } 252 a, err := signing.Sign(asserts.SerialType, headers, body, keyID) 253 return a, s.ancillary, err 254 } 255 256 func (s *deviceMgrSuite) mockServer(c *C, reqID string, bhv *devicestatetest.DeviceServiceBehavior) *httptest.Server { 257 if bhv == nil { 258 bhv = &devicestatetest.DeviceServiceBehavior{} 259 } 260 261 bhv.ReqID = reqID 262 bhv.SignSerial = s.signSerial 263 bhv.ExpectedCapabilities = "serial-stream" 264 265 return devicestatetest.MockDeviceService(c, bhv) 266 } 267 268 func (s *deviceMgrSuite) setupCore(c *C, name, snapYaml string, snapContents string) { 269 sideInfoCore := &snap.SideInfo{ 270 RealName: name, 271 Revision: snap.R(3), 272 } 273 snaptest.MockSnap(c, snapYaml, sideInfoCore) 274 snapstate.Set(s.state, name, &snapstate.SnapState{ 275 SnapType: "os", 276 Active: true, 277 Sequence: []*snap.SideInfo{sideInfoCore}, 278 Current: sideInfoCore.Revision, 279 }) 280 } 281 282 func (s *deviceMgrSuite) findBecomeOperationalChange(skipIDs ...string) *state.Change { 283 for _, chg := range s.state.Changes() { 284 if chg.Kind() == "become-operational" && !strutil.ListContains(skipIDs, chg.ID()) { 285 return chg 286 } 287 } 288 return nil 289 } 290 291 func (s *deviceMgrSuite) TestFullDeviceRegistrationHappy(c *C) { 292 r1 := devicestate.MockKeyLength(testKeyLength) 293 defer r1() 294 295 mockServer := s.mockServer(c, "REQID-1", nil) 296 defer mockServer.Close() 297 298 r2 := devicestate.MockBaseStoreURL(mockServer.URL) 299 defer r2() 300 301 // setup state as will be done by first-boot 302 s.state.Lock() 303 defer s.state.Unlock() 304 305 s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ 306 "architecture": "amd64", 307 "kernel": "pc-kernel", 308 "gadget": "pc", 309 }) 310 311 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 312 Brand: "canonical", 313 Model: "pc", 314 }) 315 316 // avoid full seeding 317 s.seeding() 318 319 // not started if not seeded 320 s.state.Unlock() 321 s.se.Ensure() 322 s.state.Lock() 323 324 becomeOperational := s.findBecomeOperationalChange() 325 c.Check(becomeOperational, IsNil) 326 327 devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil) 328 // mark it as seeded 329 s.state.Set("seeded", true) 330 331 // runs the whole device registration process 332 s.state.Unlock() 333 s.settle(c) 334 s.state.Lock() 335 336 becomeOperational = s.findBecomeOperationalChange() 337 c.Assert(becomeOperational, NotNil) 338 339 c.Check(becomeOperational.Status().Ready(), Equals, true) 340 c.Check(becomeOperational.Err(), IsNil) 341 342 device, err := devicestatetest.Device(s.state) 343 c.Assert(err, IsNil) 344 c.Check(device.Brand, Equals, "canonical") 345 c.Check(device.Model, Equals, "pc") 346 c.Check(device.Serial, Equals, "9999") 347 348 ok := false 349 select { 350 case <-s.mgr.Registered(): 351 ok = true 352 case <-time.After(5 * time.Second): 353 c.Fatal("should have been marked registered") 354 } 355 c.Check(ok, Equals, true) 356 357 a, err := s.db.Find(asserts.SerialType, map[string]string{ 358 "brand-id": "canonical", 359 "model": "pc", 360 "serial": "9999", 361 }) 362 c.Assert(err, IsNil) 363 serial := a.(*asserts.Serial) 364 365 privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID()) 366 c.Assert(err, IsNil) 367 c.Check(privKey, NotNil) 368 369 c.Check(device.KeyID, Equals, privKey.PublicKey().ID()) 370 } 371 372 func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyWithProxy(c *C) { 373 r1 := devicestate.MockKeyLength(testKeyLength) 374 defer r1() 375 376 mockServer := s.mockServer(c, "REQID-1", nil) 377 defer mockServer.Close() 378 379 // as core.proxy.store is set, should not need to do this but just in case 380 r2 := devicestate.MockBaseStoreURL(mockServer.URL + "/direct/baaad/") 381 defer r2() 382 383 // setup state as will be done by first-boot 384 s.state.Lock() 385 defer s.state.Unlock() 386 387 tr := config.NewTransaction(s.state) 388 c.Assert(tr.Set("core", "proxy.store", "foo"), IsNil) 389 tr.Commit() 390 operatorAcct := assertstest.NewAccount(s.storeSigning, "foo-operator", nil, "") 391 392 // have a store assertion. 393 stoAs, err := s.storeSigning.Sign(asserts.StoreType, map[string]interface{}{ 394 "store": "foo", 395 "url": mockServer.URL, 396 "operator-id": operatorAcct.AccountID(), 397 "timestamp": time.Now().Format(time.RFC3339), 398 }, nil, "") 399 c.Assert(err, IsNil) 400 401 assertstatetest.AddMany(s.state, operatorAcct, stoAs) 402 403 s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ 404 "architecture": "amd64", 405 "kernel": "pc-kernel", 406 "gadget": "pc", 407 }) 408 409 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 410 Brand: "canonical", 411 Model: "pc", 412 }) 413 414 devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil) 415 // mark as seeded 416 s.state.Set("seeded", true) 417 418 // runs the whole device registration process 419 s.state.Unlock() 420 s.settle(c) 421 s.state.Lock() 422 423 becomeOperational := s.findBecomeOperationalChange() 424 c.Assert(becomeOperational, NotNil) 425 426 c.Check(becomeOperational.Status().Ready(), Equals, true) 427 c.Check(becomeOperational.Err(), IsNil) 428 429 device, err := devicestatetest.Device(s.state) 430 c.Assert(err, IsNil) 431 c.Check(device.Brand, Equals, "canonical") 432 c.Check(device.Model, Equals, "pc") 433 c.Check(device.Serial, Equals, "9999") 434 435 ok := false 436 select { 437 case <-s.mgr.Registered(): 438 ok = true 439 case <-time.After(5 * time.Second): 440 c.Fatal("should have been marked registered") 441 } 442 c.Check(ok, Equals, true) 443 444 a, err := s.db.Find(asserts.SerialType, map[string]string{ 445 "brand-id": "canonical", 446 "model": "pc", 447 "serial": "9999", 448 }) 449 c.Assert(err, IsNil) 450 serial := a.(*asserts.Serial) 451 452 privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID()) 453 c.Assert(err, IsNil) 454 c.Check(privKey, NotNil) 455 456 c.Check(device.KeyID, Equals, privKey.PublicKey().ID()) 457 } 458 459 func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyClassicNoGadget(c *C) { 460 restore := release.MockOnClassic(true) 461 defer restore() 462 463 r1 := devicestate.MockKeyLength(testKeyLength) 464 defer r1() 465 466 mockServer := s.mockServer(c, "REQID-1", nil) 467 defer mockServer.Close() 468 469 r2 := devicestate.MockBaseStoreURL(mockServer.URL) 470 defer r2() 471 472 // setup state as will be done by first-boot 473 s.state.Lock() 474 defer s.state.Unlock() 475 476 s.makeModelAssertionInState(c, "canonical", "classic-alt-store", map[string]interface{}{ 477 "classic": "true", 478 "store": "alt-store", 479 }) 480 481 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 482 Brand: "canonical", 483 Model: "classic-alt-store", 484 }) 485 486 // avoid full seeding 487 s.seeding() 488 489 // runs the whole device registration process 490 s.state.Unlock() 491 s.settle(c) 492 s.state.Lock() 493 494 becomeOperational := s.findBecomeOperationalChange() 495 c.Assert(becomeOperational, NotNil) 496 497 c.Check(becomeOperational.Status().Ready(), Equals, true) 498 c.Check(becomeOperational.Err(), IsNil) 499 500 device, err := devicestatetest.Device(s.state) 501 c.Assert(err, IsNil) 502 c.Check(device.Brand, Equals, "canonical") 503 c.Check(device.Model, Equals, "classic-alt-store") 504 c.Check(device.Serial, Equals, "9999") 505 506 a, err := s.db.Find(asserts.SerialType, map[string]string{ 507 "brand-id": "canonical", 508 "model": "classic-alt-store", 509 "serial": "9999", 510 }) 511 c.Assert(err, IsNil) 512 serial := a.(*asserts.Serial) 513 514 privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID()) 515 c.Assert(err, IsNil) 516 c.Check(privKey, NotNil) 517 518 c.Check(device.KeyID, Equals, privKey.PublicKey().ID()) 519 } 520 521 func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyClassicFallback(c *C) { 522 restore := release.MockOnClassic(true) 523 defer restore() 524 525 r1 := devicestate.MockKeyLength(testKeyLength) 526 defer r1() 527 528 mockServer := s.mockServer(c, "REQID-1", nil) 529 defer mockServer.Close() 530 531 r2 := devicestate.MockBaseStoreURL(mockServer.URL) 532 defer r2() 533 534 // setup state as will be done by first-boot 535 s.state.Lock() 536 defer s.state.Unlock() 537 538 // in this case is just marked seeded without snaps 539 s.state.Set("seeded", true) 540 541 // not started without some installation happening or happened 542 s.state.Unlock() 543 s.se.Ensure() 544 s.state.Lock() 545 546 becomeOperational := s.findBecomeOperationalChange() 547 c.Check(becomeOperational, IsNil) 548 549 // have a in-progress installation 550 inst := s.state.NewChange("install", "...") 551 task := s.state.NewTask("mount-snap", "...") 552 inst.AddTask(task) 553 554 // runs the whole device registration process 555 s.state.Unlock() 556 s.settle(c) 557 s.state.Lock() 558 559 becomeOperational = s.findBecomeOperationalChange() 560 c.Assert(becomeOperational, NotNil) 561 562 c.Check(becomeOperational.Status().Ready(), Equals, true) 563 c.Check(becomeOperational.Err(), IsNil) 564 565 device, err := devicestatetest.Device(s.state) 566 c.Assert(err, IsNil) 567 c.Check(device.Brand, Equals, "generic") 568 c.Check(device.Model, Equals, "generic-classic") 569 c.Check(device.Serial, Equals, "9999") 570 571 // model was installed 572 _, err = s.db.Find(asserts.ModelType, map[string]string{ 573 "series": "16", 574 "brand-id": "generic", 575 "model": "generic-classic", 576 "classic": "true", 577 }) 578 c.Assert(err, IsNil) 579 580 a, err := s.db.Find(asserts.SerialType, map[string]string{ 581 "brand-id": "generic", 582 "model": "generic-classic", 583 "serial": "9999", 584 }) 585 c.Assert(err, IsNil) 586 serial := a.(*asserts.Serial) 587 588 privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID()) 589 c.Assert(err, IsNil) 590 c.Check(privKey, NotNil) 591 592 c.Check(device.KeyID, Equals, privKey.PublicKey().ID()) 593 594 // auto-refreshes are possible 595 ok, err := devicestate.CanAutoRefresh(s.state) 596 c.Assert(err, IsNil) 597 c.Check(ok, Equals, true) 598 } 599 600 func (s *deviceMgrSuite) TestFullDeviceRegistrationAltBrandHappy(c *C) { 601 c.Skip("not yet supported") 602 r1 := devicestate.MockKeyLength(testKeyLength) 603 defer r1() 604 605 mockServer := s.mockServer(c, "REQID-1", nil) 606 defer mockServer.Close() 607 608 r2 := devicestate.MockBaseStoreURL(mockServer.URL) 609 defer r2() 610 611 // setup state as will be done by first-boot 612 s.state.Lock() 613 defer s.state.Unlock() 614 615 s.makeModelAssertionInState(c, "my-brand", "my-model", map[string]interface{}{ 616 "classic": "true", 617 "store": "alt-store", 618 }) 619 620 devicestatetest.MockGadget(c, s.state, "gadget", snap.R(2), nil) 621 622 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 623 Brand: "my-brand", 624 Model: "my-model", 625 }) 626 627 // avoid full seeding 628 s.seeding() 629 630 // runs the whole device registration process 631 s.state.Unlock() 632 s.settle(c) 633 s.state.Lock() 634 635 becomeOperational := s.findBecomeOperationalChange() 636 c.Assert(becomeOperational, NotNil) 637 638 c.Check(becomeOperational.Status().Ready(), Equals, true) 639 c.Check(becomeOperational.Err(), IsNil) 640 641 device, err := devicestatetest.Device(s.state) 642 c.Assert(err, IsNil) 643 c.Check(device.Brand, Equals, "my-brand") 644 c.Check(device.Model, Equals, "my-model") 645 c.Check(device.Serial, Equals, "9999") 646 647 a, err := s.db.Find(asserts.SerialType, map[string]string{ 648 "brand-id": "my-brand", 649 "model": "my-model", 650 "serial": "9999", 651 }) 652 c.Assert(err, IsNil) 653 serial := a.(*asserts.Serial) 654 655 privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID()) 656 c.Assert(err, IsNil) 657 c.Check(privKey, NotNil) 658 659 c.Check(device.KeyID, Equals, privKey.PublicKey().ID()) 660 } 661 662 func (s *deviceMgrSuite) TestDoRequestSerialIdempotentAfterAddSerial(c *C) { 663 privKey, _ := assertstest.GenerateKey(testKeyLength) 664 665 mockServer := s.mockServer(c, "REQID-1", nil) 666 defer mockServer.Close() 667 668 restore := devicestate.MockBaseStoreURL(mockServer.URL) 669 defer restore() 670 671 restore = devicestate.MockRepeatRequestSerial("after-add-serial") 672 defer restore() 673 674 // setup state as done by first-boot/Ensure/doGenerateDeviceKey 675 s.state.Lock() 676 defer s.state.Unlock() 677 678 s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ 679 "architecture": "amd64", 680 "kernel": "pc-kernel", 681 "gadget": "pc", 682 }) 683 684 devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil) 685 686 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 687 Brand: "canonical", 688 Model: "pc", 689 KeyID: privKey.PublicKey().ID(), 690 }) 691 devicestate.KeypairManager(s.mgr).Put(privKey) 692 693 t := s.state.NewTask("request-serial", "test") 694 chg := s.state.NewChange("become-operational", "...") 695 chg.AddTask(t) 696 697 // avoid full seeding 698 s.seeding() 699 700 s.state.Unlock() 701 s.se.Ensure() 702 s.se.Wait() 703 s.state.Lock() 704 705 c.Check(chg.Status(), Equals, state.DoingStatus) 706 c.Check(chg.Err(), IsNil) 707 device, err := devicestatetest.Device(s.state) 708 c.Check(err, IsNil) 709 _, err = s.db.Find(asserts.SerialType, map[string]string{ 710 "brand-id": "canonical", 711 "model": "pc", 712 "serial": "9999", 713 }) 714 c.Assert(err, IsNil) 715 716 ok := false 717 select { 718 case <-s.mgr.Registered(): 719 default: 720 ok = true 721 } 722 c.Check(ok, Equals, true) 723 724 s.state.Unlock() 725 s.se.Ensure() 726 s.se.Wait() 727 s.state.Lock() 728 729 // Repeated handler run but set original serial. 730 c.Check(chg.Status(), Equals, state.DoneStatus) 731 device, err = devicestatetest.Device(s.state) 732 c.Check(err, IsNil) 733 c.Check(device.Serial, Equals, "9999") 734 735 ok = false 736 select { 737 case <-s.mgr.Registered(): 738 ok = true 739 case <-time.After(5 * time.Second): 740 c.Fatal("should have been marked registered") 741 } 742 c.Check(ok, Equals, true) 743 } 744 745 func (s *deviceMgrSuite) TestDoRequestSerialIdempotentAfterGotSerial(c *C) { 746 privKey, _ := assertstest.GenerateKey(testKeyLength) 747 748 mockServer := s.mockServer(c, "REQID-1", nil) 749 defer mockServer.Close() 750 751 restore := devicestate.MockBaseStoreURL(mockServer.URL) 752 defer restore() 753 754 restore = devicestate.MockRepeatRequestSerial("after-got-serial") 755 defer restore() 756 757 // setup state as done by first-boot/Ensure/doGenerateDeviceKey 758 s.state.Lock() 759 defer s.state.Unlock() 760 761 s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ 762 "architecture": "amd64", 763 "kernel": "pc-kernel", 764 "gadget": "pc", 765 }) 766 767 devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil) 768 769 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 770 Brand: "canonical", 771 Model: "pc", 772 KeyID: privKey.PublicKey().ID(), 773 }) 774 devicestate.KeypairManager(s.mgr).Put(privKey) 775 776 t := s.state.NewTask("request-serial", "test") 777 chg := s.state.NewChange("become-operational", "...") 778 chg.AddTask(t) 779 780 // avoid full seeding 781 s.seeding() 782 783 s.state.Unlock() 784 s.se.Ensure() 785 s.se.Wait() 786 s.state.Lock() 787 788 c.Check(chg.Status(), Equals, state.DoingStatus) 789 device, err := devicestatetest.Device(s.state) 790 c.Check(err, IsNil) 791 _, err = s.db.Find(asserts.SerialType, map[string]string{ 792 "brand-id": "canonical", 793 "model": "pc", 794 "serial": "9999", 795 }) 796 c.Assert(asserts.IsNotFound(err), Equals, true) 797 798 s.state.Unlock() 799 s.se.Ensure() 800 s.se.Wait() 801 s.state.Lock() 802 803 // Repeated handler run but set original serial. 804 c.Check(chg.Status(), Equals, state.DoneStatus) 805 c.Check(chg.Err(), IsNil) 806 device, err = devicestatetest.Device(s.state) 807 c.Check(err, IsNil) 808 c.Check(device.Serial, Equals, "9999") 809 } 810 811 func (s *deviceMgrSuite) TestDoRequestSerialErrorsOnNoHost(c *C) { 812 if os.Getenv("http_proxy") != "" { 813 c.Skip("cannot run test when http proxy is in use, the error pattern is different") 814 } 815 816 const nonexistent_host = "nowhere.nowhere.test" 817 818 // check internet access 819 _, err := net.LookupHost(nonexistent_host) 820 if netErr, ok := err.(net.Error); !ok || netErr.Temporary() { 821 c.Skip("cannot run test with no internet access, the error pattern is different") 822 } 823 824 privKey, _ := assertstest.GenerateKey(testKeyLength) 825 826 nowhere := "http://" + nonexistent_host 827 828 restore := devicestate.MockBaseStoreURL(nowhere) 829 defer restore() 830 831 // setup state as done by first-boot/Ensure/doGenerateDeviceKey 832 s.state.Lock() 833 defer s.state.Unlock() 834 835 devicestatetest.MockGadget(c, s.state, "gadget", snap.R(2), nil) 836 837 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 838 Brand: "canonical", 839 Model: "pc", 840 KeyID: privKey.PublicKey().ID(), 841 }) 842 devicestate.KeypairManager(s.mgr).Put(privKey) 843 844 t := s.state.NewTask("request-serial", "test") 845 chg := s.state.NewChange("become-operational", "...") 846 chg.AddTask(t) 847 848 // avoid full seeding 849 s.seeding() 850 851 s.state.Unlock() 852 s.se.Ensure() 853 s.se.Wait() 854 s.state.Lock() 855 856 c.Check(chg.Status(), Equals, state.ErrorStatus) 857 } 858 859 func (s *deviceMgrSuite) TestDoRequestSerialMaxTentatives(c *C) { 860 privKey, _ := assertstest.GenerateKey(testKeyLength) 861 862 // immediate 863 r := devicestate.MockRetryInterval(0) 864 defer r() 865 866 r = devicestate.MockMaxTentatives(2) 867 defer r() 868 869 mockServer := s.mockServer(c, devicestatetest.ReqIDFailID501, nil) 870 defer mockServer.Close() 871 872 restore := devicestate.MockBaseStoreURL(mockServer.URL) 873 defer restore() 874 875 restore = devicestate.MockRepeatRequestSerial("after-add-serial") 876 defer restore() 877 878 // setup state as done by first-boot/Ensure/doGenerateDeviceKey 879 s.state.Lock() 880 defer s.state.Unlock() 881 882 s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ 883 "architecture": "amd64", 884 "kernel": "pc-kernel", 885 "gadget": "pc", 886 }) 887 888 devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil) 889 890 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 891 Brand: "canonical", 892 Model: "pc", 893 KeyID: privKey.PublicKey().ID(), 894 }) 895 devicestate.KeypairManager(s.mgr).Put(privKey) 896 897 t := s.state.NewTask("request-serial", "test") 898 chg := s.state.NewChange("become-operational", "...") 899 chg.AddTask(t) 900 901 // avoid full seeding 902 s.seeding() 903 904 s.state.Unlock() 905 s.se.Ensure() 906 s.se.Wait() 907 s.state.Lock() 908 909 c.Check(chg.Status(), Equals, state.DoingStatus) 910 911 s.state.Unlock() 912 s.se.Ensure() 913 s.se.Wait() 914 s.state.Lock() 915 916 c.Check(chg.Status(), Equals, state.ErrorStatus) 917 c.Check(chg.Err(), ErrorMatches, `(?s).*cannot retrieve request-id for making a request for a serial: unexpected status 501.*`) 918 } 919 920 func (s *deviceMgrSuite) TestFullDeviceRegistrationPollHappy(c *C) { 921 r1 := devicestate.MockKeyLength(testKeyLength) 922 defer r1() 923 924 mockServer := s.mockServer(c, devicestatetest.ReqIDPoll, nil) 925 defer mockServer.Close() 926 927 r2 := devicestate.MockBaseStoreURL(mockServer.URL) 928 defer r2() 929 930 // immediately 931 r3 := devicestate.MockRetryInterval(0) 932 defer r3() 933 934 // setup state as will be done by first-boot 935 s.state.Lock() 936 defer s.state.Unlock() 937 938 s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ 939 "architecture": "amd64", 940 "kernel": "pc-kernel", 941 "gadget": "pc", 942 }) 943 944 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 945 Brand: "canonical", 946 Model: "pc", 947 }) 948 949 devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil) 950 // mark as seeded 951 s.state.Set("seeded", true) 952 953 // runs the whole device registration process with polling 954 s.state.Unlock() 955 s.settle(c) 956 s.state.Lock() 957 958 becomeOperational := s.findBecomeOperationalChange() 959 c.Assert(becomeOperational, NotNil) 960 961 // needs 3 more Retry passes of polling 962 for i := 0; i < 3; i++ { 963 s.state.Unlock() 964 s.settle(c) 965 s.state.Lock() 966 } 967 968 c.Check(becomeOperational.Status().Ready(), Equals, true) 969 c.Check(becomeOperational.Err(), IsNil) 970 971 device, err := devicestatetest.Device(s.state) 972 c.Assert(err, IsNil) 973 c.Check(device.Brand, Equals, "canonical") 974 c.Check(device.Model, Equals, "pc") 975 c.Check(device.Serial, Equals, "10002") 976 977 a, err := s.db.Find(asserts.SerialType, map[string]string{ 978 "brand-id": "canonical", 979 "model": "pc", 980 "serial": "10002", 981 }) 982 c.Assert(err, IsNil) 983 serial := a.(*asserts.Serial) 984 985 privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID()) 986 c.Assert(err, IsNil) 987 c.Check(privKey, NotNil) 988 989 c.Check(device.KeyID, Equals, privKey.PublicKey().ID()) 990 } 991 992 func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyPrepareDeviceHook(c *C) { 993 r1 := devicestate.MockKeyLength(testKeyLength) 994 defer r1() 995 996 bhv := &devicestatetest.DeviceServiceBehavior{ 997 RequestIDURLPath: "/svc/request-id", 998 SerialURLPath: "/svc/serial", 999 } 1000 bhv.PostPreflight = func(c *C, bhv *devicestatetest.DeviceServiceBehavior, w http.ResponseWriter, r *http.Request) { 1001 c.Check(r.Header.Get("X-Extra-Header"), Equals, "extra") 1002 } 1003 1004 mockServer := s.mockServer(c, "REQID-1", bhv) 1005 defer mockServer.Close() 1006 1007 // setup state as will be done by first-boot 1008 // & have a gadget with a prepare-device hook 1009 s.state.Lock() 1010 defer s.state.Unlock() 1011 1012 pDBhv := &devicestatetest.PrepareDeviceBehavior{ 1013 DeviceSvcURL: mockServer.URL + "/svc/", 1014 Headers: map[string]string{ 1015 "x-extra-header": "extra", 1016 }, 1017 RegBody: map[string]string{ 1018 "mac": "00:00:00:00:ff:00", 1019 }, 1020 ProposedSerial: "Y9999", 1021 } 1022 1023 r2 := devicestatetest.MockGadget(c, s.state, "gadget", snap.R(2), pDBhv) 1024 defer r2() 1025 1026 // as device-service.url is set, should not need to do this but just in case 1027 r3 := devicestate.MockBaseStoreURL(mockServer.URL + "/direct/baad/") 1028 defer r3() 1029 1030 s.makeModelAssertionInState(c, "canonical", "pc2", map[string]interface{}{ 1031 "architecture": "amd64", 1032 "kernel": "pc-kernel", 1033 "gadget": "gadget", 1034 }) 1035 1036 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 1037 Brand: "canonical", 1038 Model: "pc2", 1039 }) 1040 1041 // avoid full seeding 1042 s.seeding() 1043 1044 // runs the whole device registration process, note that the 1045 // device is not seeded yet 1046 s.state.Unlock() 1047 s.settle(c) 1048 s.state.Lock() 1049 1050 // without a seeded device, there is no become-operational change 1051 becomeOperational := s.findBecomeOperationalChange() 1052 c.Assert(becomeOperational, IsNil) 1053 1054 // now mark it as seeded 1055 s.state.Set("seeded", true) 1056 // and run the device registration again 1057 s.state.Unlock() 1058 s.settle(c) 1059 s.state.Lock() 1060 1061 becomeOperational = s.findBecomeOperationalChange() 1062 c.Assert(becomeOperational, NotNil) 1063 1064 c.Check(becomeOperational.Status().Ready(), Equals, true) 1065 c.Check(becomeOperational.Err(), IsNil) 1066 1067 device, err := devicestatetest.Device(s.state) 1068 c.Assert(err, IsNil) 1069 c.Check(device.Brand, Equals, "canonical") 1070 c.Check(device.Model, Equals, "pc2") 1071 c.Check(device.Serial, Equals, "Y9999") 1072 1073 a, err := s.db.Find(asserts.SerialType, map[string]string{ 1074 "brand-id": "canonical", 1075 "model": "pc2", 1076 "serial": "Y9999", 1077 }) 1078 c.Assert(err, IsNil) 1079 serial := a.(*asserts.Serial) 1080 1081 var details map[string]interface{} 1082 err = yaml.Unmarshal(serial.Body(), &details) 1083 c.Assert(err, IsNil) 1084 1085 c.Check(details, DeepEquals, map[string]interface{}{ 1086 "mac": "00:00:00:00:ff:00", 1087 }) 1088 1089 privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID()) 1090 c.Assert(err, IsNil) 1091 c.Check(privKey, NotNil) 1092 1093 c.Check(device.KeyID, Equals, privKey.PublicKey().ID()) 1094 } 1095 1096 func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyWithHookAndNewProxy(c *C) { 1097 s.testFullDeviceRegistrationHappyWithHookAndProxy(c, true) 1098 } 1099 1100 func (s *deviceMgrSuite) TestFullDeviceRegistrationHappyWithHookAndOldProxy(c *C) { 1101 s.testFullDeviceRegistrationHappyWithHookAndProxy(c, false) 1102 } 1103 1104 func (s *deviceMgrSuite) testFullDeviceRegistrationHappyWithHookAndProxy(c *C, newEnough bool) { 1105 r1 := devicestate.MockKeyLength(testKeyLength) 1106 defer r1() 1107 1108 var reqID string 1109 var storeVersion string 1110 head := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, w http.ResponseWriter, r *http.Request) { 1111 w.Header().Set("Snap-Store-Version", storeVersion) 1112 } 1113 bhv := &devicestatetest.DeviceServiceBehavior{ 1114 Head: head, 1115 } 1116 svcPath := "/svc/" 1117 if newEnough { 1118 reqID = "REQID-42" 1119 storeVersion = "6" 1120 bhv.PostPreflight = func(c *C, bhv *devicestatetest.DeviceServiceBehavior, w http.ResponseWriter, r *http.Request) { 1121 c.Check(r.Header.Get("X-Snap-Device-Service-URL"), Matches, "http://[^/]*/bad/svc/") 1122 c.Check(r.Header.Get("X-Extra-Header"), Equals, "extra") 1123 } 1124 svcPath = "/bad/svc/" 1125 } else { 1126 reqID = "REQID-41" 1127 storeVersion = "5" 1128 bhv.RequestIDURLPath = "/svc/request-id" 1129 bhv.SerialURLPath = "/svc/serial" 1130 bhv.PostPreflight = func(c *C, bhv *devicestatetest.DeviceServiceBehavior, w http.ResponseWriter, r *http.Request) { 1131 c.Check(r.Header.Get("X-Extra-Header"), Equals, "extra") 1132 } 1133 } 1134 1135 mockServer := s.mockServer(c, reqID, bhv) 1136 defer mockServer.Close() 1137 1138 // setup state as will be done by first-boot 1139 // & have a gadget with a prepare-device hook 1140 s.state.Lock() 1141 defer s.state.Unlock() 1142 1143 pDBhv := &devicestatetest.PrepareDeviceBehavior{ 1144 DeviceSvcURL: mockServer.URL + svcPath, 1145 Headers: map[string]string{ 1146 "x-extra-header": "extra", 1147 }, 1148 } 1149 r2 := devicestatetest.MockGadget(c, s.state, "gadget", snap.R(2), pDBhv) 1150 defer r2() 1151 1152 // as device-service.url is set, should not need to do this but just in case 1153 r3 := devicestate.MockBaseStoreURL(mockServer.URL + "/direct/baad/") 1154 defer r3() 1155 1156 tr := config.NewTransaction(s.state) 1157 c.Assert(tr.Set("core", "proxy.store", "foo"), IsNil) 1158 tr.Commit() 1159 operatorAcct := assertstest.NewAccount(s.storeSigning, "foo-operator", nil, "") 1160 1161 // have a store assertion. 1162 stoAs, err := s.storeSigning.Sign(asserts.StoreType, map[string]interface{}{ 1163 "store": "foo", 1164 "url": mockServer.URL, 1165 "operator-id": operatorAcct.AccountID(), 1166 "timestamp": time.Now().Format(time.RFC3339), 1167 }, nil, "") 1168 c.Assert(err, IsNil) 1169 1170 assertstatetest.AddMany(s.state, operatorAcct, stoAs) 1171 1172 s.makeModelAssertionInState(c, "canonical", "pc2", map[string]interface{}{ 1173 "architecture": "amd64", 1174 "kernel": "pc-kernel", 1175 "gadget": "gadget", 1176 }) 1177 1178 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 1179 Brand: "canonical", 1180 Model: "pc2", 1181 }) 1182 1183 // mark it as seeded 1184 s.state.Set("seeded", true) 1185 1186 // runs the whole device registration process 1187 s.state.Unlock() 1188 s.settle(c) 1189 s.state.Lock() 1190 1191 becomeOperational := s.findBecomeOperationalChange() 1192 c.Assert(becomeOperational, NotNil) 1193 1194 c.Check(becomeOperational.Status().Ready(), Equals, true) 1195 c.Check(becomeOperational.Err(), IsNil) 1196 1197 device, err := devicestatetest.Device(s.state) 1198 c.Assert(err, IsNil) 1199 c.Check(device.Brand, Equals, "canonical") 1200 c.Check(device.Model, Equals, "pc2") 1201 c.Check(device.Serial, Equals, "9999") 1202 1203 a, err := s.db.Find(asserts.SerialType, map[string]string{ 1204 "brand-id": "canonical", 1205 "model": "pc2", 1206 "serial": "9999", 1207 }) 1208 c.Assert(err, IsNil) 1209 serial := a.(*asserts.Serial) 1210 1211 privKey, err := devicestate.KeypairManager(s.mgr).Get(serial.DeviceKey().ID()) 1212 c.Assert(err, IsNil) 1213 c.Check(privKey, NotNil) 1214 1215 c.Check(device.KeyID, Equals, privKey.PublicKey().ID()) 1216 } 1217 1218 func (s *deviceMgrSuite) TestFullDeviceRegistrationErrorBackoff(c *C) { 1219 r1 := devicestate.MockKeyLength(testKeyLength) 1220 defer r1() 1221 1222 bhv := &devicestatetest.DeviceServiceBehavior{} 1223 mockServer := s.mockServer(c, devicestatetest.ReqIDBadRequest, bhv) 1224 defer mockServer.Close() 1225 1226 r2 := devicestate.MockBaseStoreURL(mockServer.URL) 1227 defer r2() 1228 1229 // setup state as will be done by first-boot 1230 s.state.Lock() 1231 defer s.state.Unlock() 1232 1233 // sanity 1234 c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 0) 1235 1236 s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ 1237 "architecture": "amd64", 1238 "kernel": "pc-kernel", 1239 "gadget": "pc", 1240 }) 1241 1242 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 1243 Brand: "canonical", 1244 Model: "pc", 1245 }) 1246 1247 devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil) 1248 // mark as seeded 1249 s.state.Set("seeded", true) 1250 1251 // try the whole device registration process 1252 s.state.Unlock() 1253 s.settle(c) 1254 s.state.Lock() 1255 1256 becomeOperational := s.findBecomeOperationalChange() 1257 c.Assert(becomeOperational, NotNil) 1258 firstTryID := becomeOperational.ID() 1259 1260 c.Check(becomeOperational.Status().Ready(), Equals, true) 1261 c.Check(becomeOperational.Err(), ErrorMatches, `(?s).*cannot deliver device serial request: bad serial-request.*`) 1262 1263 device, err := devicestatetest.Device(s.state) 1264 c.Assert(err, IsNil) 1265 c.Check(device.KeyID, Not(Equals), "") 1266 keyID := device.KeyID 1267 1268 c.Check(devicestate.EnsureOperationalShouldBackoff(s.mgr, time.Now()), Equals, true) 1269 c.Check(devicestate.EnsureOperationalShouldBackoff(s.mgr, time.Now().Add(6*time.Minute)), Equals, false) 1270 c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 1) 1271 1272 // try again the whole device registration process 1273 bhv.ReqID = "REQID-1" 1274 devicestate.SetLastBecomeOperationalAttempt(s.mgr, time.Now().Add(-15*time.Minute)) 1275 s.state.Unlock() 1276 s.settle(c) 1277 s.state.Lock() 1278 1279 becomeOperational = s.findBecomeOperationalChange(firstTryID) 1280 c.Assert(becomeOperational, NotNil) 1281 1282 c.Check(becomeOperational.Status().Ready(), Equals, true) 1283 c.Check(becomeOperational.Err(), IsNil) 1284 1285 c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 2) 1286 1287 device, err = devicestatetest.Device(s.state) 1288 c.Assert(err, IsNil) 1289 c.Check(device.KeyID, Equals, keyID) 1290 c.Check(device.Serial, Equals, "10000") 1291 } 1292 1293 func (s *deviceMgrSuite) TestEnsureBecomeOperationalShouldBackoff(c *C) { 1294 t0 := time.Now() 1295 c.Check(devicestate.EnsureOperationalShouldBackoff(s.mgr, t0), Equals, false) 1296 c.Check(devicestate.BecomeOperationalBackoff(s.mgr), Equals, 5*time.Minute) 1297 1298 backoffs := []time.Duration{5, 10, 20, 40, 80, 160, 320, 640, 1440, 1440} 1299 t1 := t0 1300 for _, m := range backoffs { 1301 c.Check(devicestate.EnsureOperationalShouldBackoff(s.mgr, t1.Add(time.Duration(m-1)*time.Minute)), Equals, true) 1302 1303 t1 = t1.Add(time.Duration(m+1) * time.Minute) 1304 c.Check(devicestate.EnsureOperationalShouldBackoff(s.mgr, t1), Equals, false) 1305 m *= 2 1306 if m > (12 * 60) { 1307 m = 24 * 60 1308 } 1309 c.Check(devicestate.BecomeOperationalBackoff(s.mgr), Equals, m*time.Minute) 1310 } 1311 } 1312 1313 func (s *deviceMgrSuite) TestFullDeviceRegistrationMismatchedSerial(c *C) { 1314 r1 := devicestate.MockKeyLength(testKeyLength) 1315 defer r1() 1316 1317 mockServer := s.mockServer(c, devicestatetest.ReqIDSerialWithBadModel, nil) 1318 defer mockServer.Close() 1319 1320 r2 := devicestate.MockBaseStoreURL(mockServer.URL) 1321 defer r2() 1322 1323 // setup state as will be done by first-boot 1324 s.state.Lock() 1325 defer s.state.Unlock() 1326 1327 // sanity 1328 c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 0) 1329 1330 devicestatetest.MockGadget(c, s.state, "gadget", snap.R(2), nil) 1331 1332 s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ 1333 "architecture": "amd64", 1334 "kernel": "pc-kernel", 1335 "gadget": "gadget", 1336 }) 1337 1338 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 1339 Brand: "canonical", 1340 Model: "pc", 1341 }) 1342 1343 // mark as seeded 1344 s.state.Set("seeded", true) 1345 1346 // try the whole device registration process 1347 s.state.Unlock() 1348 s.settle(c) 1349 s.state.Lock() 1350 1351 becomeOperational := s.findBecomeOperationalChange() 1352 c.Assert(becomeOperational, NotNil) 1353 1354 c.Check(becomeOperational.Status().Ready(), Equals, true) 1355 c.Check(becomeOperational.Err(), ErrorMatches, `(?s).*obtained serial assertion does not match provided device identity information.*`) 1356 } 1357 1358 func (s *deviceMgrSuite) TestModelAndSerial(c *C) { 1359 s.state.Lock() 1360 defer s.state.Unlock() 1361 // nothing in the state 1362 _, err := s.mgr.Model() 1363 c.Check(err, Equals, state.ErrNoState) 1364 _, err = s.mgr.Serial() 1365 c.Check(err, Equals, state.ErrNoState) 1366 1367 // just brand and model 1368 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 1369 Brand: "canonical", 1370 Model: "pc", 1371 }) 1372 _, err = s.mgr.Model() 1373 c.Check(err, Equals, state.ErrNoState) 1374 _, err = s.mgr.Serial() 1375 c.Check(err, Equals, state.ErrNoState) 1376 1377 // have a model assertion 1378 model := s.brands.Model("canonical", "pc", map[string]interface{}{ 1379 "gadget": "pc", 1380 "kernel": "kernel", 1381 "architecture": "amd64", 1382 }) 1383 assertstatetest.AddMany(s.state, model) 1384 1385 mod, err := s.mgr.Model() 1386 c.Assert(err, IsNil) 1387 c.Assert(mod.BrandID(), Equals, "canonical") 1388 1389 _, err = s.mgr.Serial() 1390 c.Check(err, Equals, state.ErrNoState) 1391 1392 // have a serial as well 1393 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 1394 Brand: "canonical", 1395 Model: "pc", 1396 Serial: "8989", 1397 }) 1398 _, err = s.mgr.Model() 1399 c.Assert(err, IsNil) 1400 _, err = s.mgr.Serial() 1401 c.Check(err, Equals, state.ErrNoState) 1402 1403 // have a serial assertion 1404 s.makeSerialAssertionInState(c, "canonical", "pc", "8989") 1405 1406 _, err = s.mgr.Model() 1407 c.Assert(err, IsNil) 1408 ser, err := s.mgr.Serial() 1409 c.Assert(err, IsNil) 1410 c.Check(ser.Serial(), Equals, "8989") 1411 } 1412 1413 func (s *deviceMgrSuite) TestStoreContextBackendSetDevice(c *C) { 1414 s.state.Lock() 1415 defer s.state.Unlock() 1416 1417 scb := s.mgr.StoreContextBackend() 1418 1419 device, err := scb.Device() 1420 c.Check(err, IsNil) 1421 c.Check(device, DeepEquals, &auth.DeviceState{}) 1422 1423 err = scb.SetDevice(&auth.DeviceState{Brand: "some-brand"}) 1424 c.Check(err, IsNil) 1425 device, err = scb.Device() 1426 c.Check(err, IsNil) 1427 c.Check(device, DeepEquals, &auth.DeviceState{Brand: "some-brand"}) 1428 } 1429 1430 func (s *deviceMgrSuite) TestStoreContextBackendModelAndSerial(c *C) { 1431 s.state.Lock() 1432 defer s.state.Unlock() 1433 1434 scb := s.mgr.StoreContextBackend() 1435 1436 // nothing in the state 1437 _, err := scb.Model() 1438 c.Check(err, Equals, state.ErrNoState) 1439 _, err = scb.Serial() 1440 c.Check(err, Equals, state.ErrNoState) 1441 1442 // just brand and model 1443 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 1444 Brand: "canonical", 1445 Model: "pc", 1446 }) 1447 _, err = scb.Model() 1448 c.Check(err, Equals, state.ErrNoState) 1449 _, err = scb.Serial() 1450 c.Check(err, Equals, state.ErrNoState) 1451 1452 // have a model assertion 1453 model := s.brands.Model("canonical", "pc", map[string]interface{}{ 1454 "gadget": "pc", 1455 "kernel": "kernel", 1456 "architecture": "amd64", 1457 }) 1458 assertstatetest.AddMany(s.state, model) 1459 1460 mod, err := scb.Model() 1461 c.Assert(err, IsNil) 1462 c.Assert(mod.BrandID(), Equals, "canonical") 1463 1464 _, err = scb.Serial() 1465 c.Check(err, Equals, state.ErrNoState) 1466 1467 // have a serial as well 1468 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 1469 Brand: "canonical", 1470 Model: "pc", 1471 Serial: "8989", 1472 }) 1473 _, err = scb.Model() 1474 c.Assert(err, IsNil) 1475 _, err = scb.Serial() 1476 c.Check(err, Equals, state.ErrNoState) 1477 1478 // have a serial assertion 1479 s.makeSerialAssertionInState(c, "canonical", "pc", "8989") 1480 1481 _, err = scb.Model() 1482 c.Assert(err, IsNil) 1483 ser, err := scb.Serial() 1484 c.Assert(err, IsNil) 1485 c.Check(ser.Serial(), Equals, "8989") 1486 } 1487 1488 var ( 1489 devKey, _ = assertstest.GenerateKey(testKeyLength) 1490 ) 1491 1492 func (s *deviceMgrSuite) TestStoreContextBackendDeviceSessionRequestParams(c *C) { 1493 s.state.Lock() 1494 defer s.state.Unlock() 1495 1496 scb := s.mgr.StoreContextBackend() 1497 1498 // nothing there 1499 _, err := scb.SignDeviceSessionRequest(nil, "NONCE-1") 1500 c.Check(err, ErrorMatches, "internal error: cannot sign a session request without a serial") 1501 1502 // setup state as done by device initialisation 1503 encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey()) 1504 c.Check(err, IsNil) 1505 seriala, err := s.storeSigning.Sign(asserts.SerialType, map[string]interface{}{ 1506 "brand-id": "canonical", 1507 "model": "pc", 1508 "serial": "8989", 1509 "device-key": string(encDevKey), 1510 "device-key-sha3-384": devKey.PublicKey().ID(), 1511 "timestamp": time.Now().Format(time.RFC3339), 1512 }, nil, "") 1513 c.Assert(err, IsNil) 1514 assertstatetest.AddMany(s.state, seriala) 1515 serial := seriala.(*asserts.Serial) 1516 1517 _, err = scb.SignDeviceSessionRequest(serial, "NONCE-1") 1518 c.Check(err, ErrorMatches, "internal error: inconsistent state with serial but no device key") 1519 1520 // have a key 1521 devicestate.KeypairManager(s.mgr).Put(devKey) 1522 1523 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 1524 Brand: "canonical", 1525 Model: "pc", 1526 Serial: "8989", 1527 KeyID: devKey.PublicKey().ID(), 1528 }) 1529 1530 sessReq, err := scb.SignDeviceSessionRequest(serial, "NONCE-1") 1531 c.Assert(err, IsNil) 1532 1533 // correctly signed with device key 1534 err = asserts.SignatureCheck(sessReq, devKey.PublicKey()) 1535 c.Check(err, IsNil) 1536 1537 c.Check(sessReq.BrandID(), Equals, "canonical") 1538 c.Check(sessReq.Model(), Equals, "pc") 1539 c.Check(sessReq.Serial(), Equals, "8989") 1540 c.Check(sessReq.Nonce(), Equals, "NONCE-1") 1541 } 1542 1543 func (s *deviceMgrSuite) TestStoreContextBackendProxyStore(c *C) { 1544 mockServer := s.mockServer(c, "", nil) 1545 defer mockServer.Close() 1546 s.state.Lock() 1547 defer s.state.Unlock() 1548 1549 scb := s.mgr.StoreContextBackend() 1550 1551 // nothing in the state 1552 _, err := scb.ProxyStore() 1553 c.Check(err, Equals, state.ErrNoState) 1554 1555 // have a store referenced 1556 tr := config.NewTransaction(s.state) 1557 err = tr.Set("core", "proxy.store", "foo") 1558 tr.Commit() 1559 c.Assert(err, IsNil) 1560 1561 _, err = scb.ProxyStore() 1562 c.Check(err, Equals, state.ErrNoState) 1563 1564 operatorAcct := assertstest.NewAccount(s.storeSigning, "foo-operator", nil, "") 1565 1566 // have a store assertion. 1567 stoAs, err := s.storeSigning.Sign(asserts.StoreType, map[string]interface{}{ 1568 "store": "foo", 1569 "operator-id": operatorAcct.AccountID(), 1570 "url": mockServer.URL, 1571 "timestamp": time.Now().Format(time.RFC3339), 1572 }, nil, "") 1573 c.Assert(err, IsNil) 1574 1575 assertstatetest.AddMany(s.state, operatorAcct, stoAs) 1576 1577 sto, err := scb.ProxyStore() 1578 c.Assert(err, IsNil) 1579 c.Assert(sto.Store(), Equals, "foo") 1580 c.Assert(sto.URL().String(), Equals, mockServer.URL) 1581 } 1582 1583 func (s *deviceMgrSuite) TestInitialRegistrationContext(c *C) { 1584 s.state.Lock() 1585 defer s.state.Unlock() 1586 1587 // have a model assertion 1588 model, err := s.storeSigning.Sign(asserts.ModelType, map[string]interface{}{ 1589 "series": "16", 1590 "brand-id": "canonical", 1591 "model": "pc", 1592 "gadget": "pc-gadget", 1593 "kernel": "kernel", 1594 "architecture": "amd64", 1595 "timestamp": time.Now().Format(time.RFC3339), 1596 }, nil, "") 1597 c.Assert(err, IsNil) 1598 err = assertstate.Add(s.state, model) 1599 c.Assert(err, IsNil) 1600 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 1601 Brand: "canonical", 1602 Model: "pc", 1603 }) 1604 1605 // TODO: will need to pass in a task later 1606 regCtx, err := devicestate.RegistrationCtx(s.mgr, nil) 1607 c.Assert(err, IsNil) 1608 c.Assert(regCtx, NotNil) 1609 1610 c.Check(regCtx.ForRemodeling(), Equals, false) 1611 1612 device, err := regCtx.Device() 1613 c.Check(err, IsNil) 1614 c.Check(device, DeepEquals, &auth.DeviceState{ 1615 Brand: "canonical", 1616 Model: "pc", 1617 }) 1618 1619 c.Check(regCtx.GadgetForSerialRequestConfig(), Equals, "pc-gadget") 1620 c.Check(regCtx.SerialRequestExtraHeaders(), HasLen, 0) 1621 c.Check(regCtx.SerialRequestAncillaryAssertions(), HasLen, 0) 1622 1623 } 1624 1625 func (s *deviceMgrSuite) TestDeviceManagerEnsureSeedYamlAlreadySeeded(c *C) { 1626 s.state.Lock() 1627 s.state.Set("seeded", true) 1628 s.state.Unlock() 1629 1630 called := false 1631 restore := devicestate.MockPopulateStateFromSeed(func(*state.State, timings.Measurer) ([]*state.TaskSet, error) { 1632 called = true 1633 return nil, nil 1634 }) 1635 defer restore() 1636 1637 err := devicestate.EnsureSeedYaml(s.mgr) 1638 c.Assert(err, IsNil) 1639 c.Assert(called, Equals, false) 1640 } 1641 1642 func (s *deviceMgrSuite) TestDeviceManagerEnsureSeedYamlChangeInFlight(c *C) { 1643 s.state.Lock() 1644 chg := s.state.NewChange("seed", "just for testing") 1645 chg.AddTask(s.state.NewTask("test-task", "the change needs a task")) 1646 s.state.Unlock() 1647 1648 called := false 1649 restore := devicestate.MockPopulateStateFromSeed(func(*state.State, timings.Measurer) ([]*state.TaskSet, error) { 1650 called = true 1651 return nil, nil 1652 }) 1653 defer restore() 1654 1655 err := devicestate.EnsureSeedYaml(s.mgr) 1656 c.Assert(err, IsNil) 1657 c.Assert(called, Equals, false) 1658 } 1659 1660 func (s *deviceMgrSuite) TestDeviceManagerEnsureSeedYamlAlsoOnClassic(c *C) { 1661 release.OnClassic = true 1662 1663 called := false 1664 restore := devicestate.MockPopulateStateFromSeed(func(*state.State, timings.Measurer) ([]*state.TaskSet, error) { 1665 called = true 1666 return nil, nil 1667 }) 1668 defer restore() 1669 1670 err := devicestate.EnsureSeedYaml(s.mgr) 1671 c.Assert(err, IsNil) 1672 c.Assert(called, Equals, true) 1673 } 1674 1675 func (s *deviceMgrSuite) TestDeviceManagerEnsureSeedYamlHappy(c *C) { 1676 restore := devicestate.MockPopulateStateFromSeed(func(*state.State, timings.Measurer) (ts []*state.TaskSet, err error) { 1677 t := s.state.NewTask("test-task", "a random task") 1678 ts = append(ts, state.NewTaskSet(t)) 1679 return ts, nil 1680 }) 1681 defer restore() 1682 1683 err := devicestate.EnsureSeedYaml(s.mgr) 1684 c.Assert(err, IsNil) 1685 1686 s.state.Lock() 1687 defer s.state.Unlock() 1688 1689 c.Check(s.state.Changes(), HasLen, 1) 1690 } 1691 1692 func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkSkippedOnClassic(c *C) { 1693 release.OnClassic = true 1694 1695 err := devicestate.EnsureBootOk(s.mgr) 1696 c.Assert(err, IsNil) 1697 } 1698 1699 func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkBootloaderHappy(c *C) { 1700 s.bootloader.SetBootVars(map[string]string{ 1701 "snap_mode": "trying", 1702 "snap_try_core": "core_1.snap", 1703 }) 1704 1705 s.state.Lock() 1706 defer s.state.Unlock() 1707 siCore1 := &snap.SideInfo{RealName: "core", Revision: snap.R(1)} 1708 snapstate.Set(s.state, "core", &snapstate.SnapState{ 1709 SnapType: "os", 1710 Active: true, 1711 Sequence: []*snap.SideInfo{siCore1}, 1712 Current: siCore1.Revision, 1713 }) 1714 1715 s.state.Unlock() 1716 err := devicestate.EnsureBootOk(s.mgr) 1717 s.state.Lock() 1718 c.Assert(err, IsNil) 1719 1720 m, err := s.bootloader.GetBootVars("snap_mode") 1721 c.Assert(err, IsNil) 1722 c.Assert(m, DeepEquals, map[string]string{"snap_mode": ""}) 1723 } 1724 1725 func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkUpdateBootRevisionsHappy(c *C) { 1726 // simulate that we have a new core_2, tried to boot it but that failed 1727 s.bootloader.SetBootVars(map[string]string{ 1728 "snap_mode": "", 1729 "snap_kernel": "kernel_1.snap", 1730 "snap_try_core": "core_2.snap", 1731 "snap_core": "core_1.snap", 1732 }) 1733 1734 s.state.Lock() 1735 defer s.state.Unlock() 1736 siKernel1 := &snap.SideInfo{RealName: "kernel", Revision: snap.R(1)} 1737 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 1738 SnapType: "kernel", 1739 Active: true, 1740 Sequence: []*snap.SideInfo{siKernel1}, 1741 Current: siKernel1.Revision, 1742 }) 1743 1744 siCore1 := &snap.SideInfo{RealName: "core", Revision: snap.R(1)} 1745 siCore2 := &snap.SideInfo{RealName: "core", Revision: snap.R(2)} 1746 snapstate.Set(s.state, "core", &snapstate.SnapState{ 1747 SnapType: "os", 1748 Active: true, 1749 Sequence: []*snap.SideInfo{siCore1, siCore2}, 1750 Current: siCore2.Revision, 1751 }) 1752 1753 s.state.Unlock() 1754 err := devicestate.EnsureBootOk(s.mgr) 1755 s.state.Lock() 1756 c.Assert(err, IsNil) 1757 1758 c.Check(s.state.Changes(), HasLen, 1) 1759 c.Check(s.state.Changes()[0].Kind(), Equals, "update-revisions") 1760 } 1761 1762 func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkNotRunAgain(c *C) { 1763 s.bootloader.SetBootVars(map[string]string{ 1764 "snap_mode": "trying", 1765 "snap_try_core": "core_1.snap", 1766 }) 1767 s.bootloader.SetErr = fmt.Errorf("ensure bootloader is not used") 1768 1769 devicestate.SetBootOkRan(s.mgr, true) 1770 1771 err := devicestate.EnsureBootOk(s.mgr) 1772 c.Assert(err, IsNil) 1773 } 1774 1775 func (s *deviceMgrSuite) TestDeviceManagerEnsureBootOkError(c *C) { 1776 s.state.Lock() 1777 // seeded 1778 s.state.Set("seeded", true) 1779 // has serial 1780 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 1781 Brand: "canonical", 1782 Model: "pc", 1783 Serial: "8989", 1784 }) 1785 s.state.Unlock() 1786 1787 s.bootloader.GetErr = fmt.Errorf("bootloader err") 1788 1789 devicestate.SetBootOkRan(s.mgr, false) 1790 1791 err := s.mgr.Ensure() 1792 c.Assert(err, ErrorMatches, "devicemgr: bootloader err") 1793 } 1794 1795 func (s *deviceMgrSuite) setupBrands(c *C) { 1796 assertstatetest.AddMany(s.state, s.brands.AccountsAndKeys("my-brand")...) 1797 otherAcct := assertstest.NewAccount(s.storeSigning, "other-brand", map[string]interface{}{ 1798 "account-id": "other-brand", 1799 }, "") 1800 assertstatetest.AddMany(s.state, otherAcct) 1801 } 1802 1803 func (s *deviceMgrSuite) setupSnapDecl(c *C, info *snap.Info, publisherID string) { 1804 snapDecl, err := s.storeSigning.Sign(asserts.SnapDeclarationType, map[string]interface{}{ 1805 "series": "16", 1806 "snap-name": info.SnapName(), 1807 "snap-id": info.SnapID, 1808 "publisher-id": publisherID, 1809 "timestamp": time.Now().UTC().Format(time.RFC3339), 1810 }, nil, "") 1811 c.Assert(err, IsNil) 1812 assertstatetest.AddMany(s.state, snapDecl) 1813 } 1814 1815 func fakeMyModel(extra map[string]interface{}) *asserts.Model { 1816 model := map[string]interface{}{ 1817 "type": "model", 1818 "authority-id": "my-brand", 1819 "series": "16", 1820 "brand-id": "my-brand", 1821 "model": "my-model", 1822 } 1823 return assertstest.FakeAssertion(model, extra).(*asserts.Model) 1824 } 1825 1826 func (s *deviceMgrSuite) TestCheckGadget(c *C) { 1827 s.state.Lock() 1828 defer s.state.Unlock() 1829 1830 gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: other-gadget, version: 0}", nil) 1831 1832 s.setupBrands(c) 1833 // model assertion in device context 1834 model := fakeMyModel(map[string]interface{}{ 1835 "architecture": "amd64", 1836 "gadget": "gadget", 1837 "kernel": "krnl", 1838 }) 1839 deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model} 1840 1841 err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1842 c.Check(err, ErrorMatches, `cannot install gadget "other-gadget", model assertion requests "gadget"`) 1843 1844 // brand gadget 1845 brandGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil) 1846 brandGadgetInfo.SnapID = "brand-gadget-id" 1847 s.setupSnapDecl(c, brandGadgetInfo, "my-brand") 1848 1849 // canonical gadget 1850 canonicalGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil) 1851 canonicalGadgetInfo.SnapID = "canonical-gadget-id" 1852 s.setupSnapDecl(c, canonicalGadgetInfo, "canonical") 1853 1854 // other gadget 1855 otherGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil) 1856 otherGadgetInfo.SnapID = "other-gadget-id" 1857 s.setupSnapDecl(c, otherGadgetInfo, "other-brand") 1858 1859 // install brand gadget ok 1860 err = devicestate.CheckGadgetOrKernel(s.state, brandGadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1861 c.Check(err, IsNil) 1862 1863 // install canonical gadget ok 1864 err = devicestate.CheckGadgetOrKernel(s.state, canonicalGadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1865 c.Check(err, IsNil) 1866 1867 // install other gadget fails 1868 err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1869 c.Check(err, ErrorMatches, `cannot install gadget "gadget" published by "other-brand" for model by "my-brand"`) 1870 1871 // unasserted installation of other works 1872 otherGadgetInfo.SnapID = "" 1873 err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1874 c.Check(err, IsNil) 1875 1876 // parallel install fails 1877 otherGadgetInfo.InstanceKey = "foo" 1878 err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1879 c.Check(err, ErrorMatches, `cannot install "gadget_foo", parallel installation of kernel or gadget snaps is not supported`) 1880 } 1881 1882 func (s *deviceMgrSuite) TestCheckGadgetOnClassic(c *C) { 1883 release.OnClassic = true 1884 1885 s.state.Lock() 1886 defer s.state.Unlock() 1887 1888 gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: other-gadget, version: 0}", nil) 1889 1890 s.setupBrands(c) 1891 // model assertion in device context 1892 model := fakeMyModel(map[string]interface{}{ 1893 "classic": "true", 1894 "gadget": "gadget", 1895 }) 1896 deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model} 1897 1898 err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1899 c.Check(err, ErrorMatches, `cannot install gadget "other-gadget", model assertion requests "gadget"`) 1900 1901 // brand gadget 1902 brandGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil) 1903 brandGadgetInfo.SnapID = "brand-gadget-id" 1904 s.setupSnapDecl(c, brandGadgetInfo, "my-brand") 1905 1906 // canonical gadget 1907 canonicalGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil) 1908 canonicalGadgetInfo.SnapID = "canonical-gadget-id" 1909 s.setupSnapDecl(c, canonicalGadgetInfo, "canonical") 1910 1911 // other gadget 1912 otherGadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil) 1913 otherGadgetInfo.SnapID = "other-gadget-id" 1914 s.setupSnapDecl(c, otherGadgetInfo, "other-brand") 1915 1916 // install brand gadget ok 1917 err = devicestate.CheckGadgetOrKernel(s.state, brandGadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1918 c.Check(err, IsNil) 1919 1920 // install canonical gadget ok 1921 err = devicestate.CheckGadgetOrKernel(s.state, canonicalGadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1922 c.Check(err, IsNil) 1923 1924 // install other gadget fails 1925 err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1926 c.Check(err, ErrorMatches, `cannot install gadget "gadget" published by "other-brand" for model by "my-brand"`) 1927 1928 // unasserted installation of other works 1929 otherGadgetInfo.SnapID = "" 1930 err = devicestate.CheckGadgetOrKernel(s.state, otherGadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1931 c.Check(err, IsNil) 1932 } 1933 1934 func (s *deviceMgrSuite) TestCheckGadgetOnClassicGadgetNotSpecified(c *C) { 1935 release.OnClassic = true 1936 1937 s.state.Lock() 1938 defer s.state.Unlock() 1939 1940 gadgetInfo := snaptest.MockInfo(c, "{type: gadget, name: gadget, version: 0}", nil) 1941 1942 s.setupBrands(c) 1943 // model assertion in device context 1944 model := fakeMyModel(map[string]interface{}{ 1945 "classic": "true", 1946 }) 1947 deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model} 1948 1949 err := devicestate.CheckGadgetOrKernel(s.state, gadgetInfo, nil, snapstate.Flags{}, deviceCtx) 1950 c.Check(err, ErrorMatches, `cannot install gadget snap on classic if not requested by the model`) 1951 } 1952 1953 func (s *deviceMgrSuite) TestCheckKernel(c *C) { 1954 s.state.Lock() 1955 defer s.state.Unlock() 1956 kernelInfo := snaptest.MockInfo(c, "{type: kernel, name: lnrk, version: 0}", nil) 1957 1958 // not on classic 1959 release.OnClassic = true 1960 err := devicestate.CheckGadgetOrKernel(s.state, kernelInfo, nil, snapstate.Flags{}, nil) 1961 c.Check(err, ErrorMatches, `cannot install a kernel snap on classic`) 1962 release.OnClassic = false 1963 1964 s.setupBrands(c) 1965 // model assertion in device context 1966 model := fakeMyModel(map[string]interface{}{ 1967 "architecture": "amd64", 1968 "gadget": "gadget", 1969 "kernel": "krnl", 1970 }) 1971 deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model} 1972 1973 err = devicestate.CheckGadgetOrKernel(s.state, kernelInfo, nil, snapstate.Flags{}, deviceCtx) 1974 c.Check(err, ErrorMatches, `cannot install kernel "lnrk", model assertion requests "krnl"`) 1975 1976 // brand kernel 1977 brandKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil) 1978 brandKrnlInfo.SnapID = "brand-krnl-id" 1979 s.setupSnapDecl(c, brandKrnlInfo, "my-brand") 1980 1981 // canonical kernel 1982 canonicalKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil) 1983 canonicalKrnlInfo.SnapID = "canonical-krnl-id" 1984 s.setupSnapDecl(c, canonicalKrnlInfo, "canonical") 1985 1986 // other kernel 1987 otherKrnlInfo := snaptest.MockInfo(c, "{type: kernel, name: krnl, version: 0}", nil) 1988 otherKrnlInfo.SnapID = "other-krnl-id" 1989 s.setupSnapDecl(c, otherKrnlInfo, "other-brand") 1990 1991 // install brand kernel ok 1992 err = devicestate.CheckGadgetOrKernel(s.state, brandKrnlInfo, nil, snapstate.Flags{}, deviceCtx) 1993 c.Check(err, IsNil) 1994 1995 // install canonical kernel ok 1996 err = devicestate.CheckGadgetOrKernel(s.state, canonicalKrnlInfo, nil, snapstate.Flags{}, deviceCtx) 1997 c.Check(err, IsNil) 1998 1999 // install other kernel fails 2000 err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, snapstate.Flags{}, deviceCtx) 2001 c.Check(err, ErrorMatches, `cannot install kernel "krnl" published by "other-brand" for model by "my-brand"`) 2002 2003 // unasserted installation of other works 2004 otherKrnlInfo.SnapID = "" 2005 err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, snapstate.Flags{}, deviceCtx) 2006 c.Check(err, IsNil) 2007 2008 // parallel install fails 2009 otherKrnlInfo.InstanceKey = "foo" 2010 err = devicestate.CheckGadgetOrKernel(s.state, otherKrnlInfo, nil, snapstate.Flags{}, deviceCtx) 2011 c.Check(err, ErrorMatches, `cannot install "krnl_foo", parallel installation of kernel or gadget snaps is not supported`) 2012 } 2013 2014 func (s *deviceMgrSuite) makeModelAssertionInState(c *C, brandID, model string, extras map[string]interface{}) { 2015 modelAs := s.brands.Model(brandID, model, extras) 2016 2017 s.setupBrands(c) 2018 assertstatetest.AddMany(s.state, modelAs) 2019 } 2020 2021 func makeSerialAssertionInState(c *C, brands *assertstest.SigningAccounts, st *state.State, brandID, model, serialN string) *asserts.Serial { 2022 encDevKey, err := asserts.EncodePublicKey(devKey.PublicKey()) 2023 c.Assert(err, IsNil) 2024 serial, err := brands.Signing(brandID).Sign(asserts.SerialType, map[string]interface{}{ 2025 "brand-id": brandID, 2026 "model": model, 2027 "serial": serialN, 2028 "device-key": string(encDevKey), 2029 "device-key-sha3-384": devKey.PublicKey().ID(), 2030 "timestamp": time.Now().Format(time.RFC3339), 2031 }, nil, "") 2032 c.Assert(err, IsNil) 2033 err = assertstate.Add(st, serial) 2034 c.Assert(err, IsNil) 2035 return serial.(*asserts.Serial) 2036 } 2037 2038 func (s *deviceMgrSuite) makeSerialAssertionInState(c *C, brandID, model, serialN string) *asserts.Serial { 2039 return makeSerialAssertionInState(c, s.brands, s.state, brandID, model, serialN) 2040 } 2041 2042 func (s *deviceMgrSuite) TestCanAutoRefreshOnCore(c *C) { 2043 s.state.Lock() 2044 defer s.state.Unlock() 2045 2046 canAutoRefresh := func() bool { 2047 ok, err := devicestate.CanAutoRefresh(s.state) 2048 c.Assert(err, IsNil) 2049 return ok 2050 } 2051 2052 // not seeded, no model, no serial -> no auto-refresh 2053 s.state.Set("seeded", false) 2054 c.Check(canAutoRefresh(), Equals, false) 2055 2056 // seeded, model, no serial -> no auto-refresh 2057 s.state.Set("seeded", true) 2058 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2059 Brand: "canonical", 2060 Model: "pc", 2061 }) 2062 s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ 2063 "architecture": "amd64", 2064 "kernel": "pc-kernel", 2065 "gadget": "pc", 2066 }) 2067 c.Check(canAutoRefresh(), Equals, false) 2068 2069 // seeded, model, serial -> auto-refresh 2070 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2071 Brand: "canonical", 2072 Model: "pc", 2073 Serial: "8989", 2074 }) 2075 s.makeSerialAssertionInState(c, "canonical", "pc", "8989") 2076 c.Check(canAutoRefresh(), Equals, true) 2077 2078 // not seeded, model, serial -> no auto-refresh 2079 s.state.Set("seeded", false) 2080 c.Check(canAutoRefresh(), Equals, false) 2081 } 2082 2083 func (s *deviceMgrSuite) TestCanAutoRefreshNoSerialFallback(c *C) { 2084 s.state.Lock() 2085 defer s.state.Unlock() 2086 2087 canAutoRefresh := func() bool { 2088 ok, err := devicestate.CanAutoRefresh(s.state) 2089 c.Assert(err, IsNil) 2090 return ok 2091 } 2092 2093 // seeded, model, no serial, two attempts at getting serial 2094 // -> no auto-refresh 2095 devicestate.IncEnsureOperationalAttempts(s.state) 2096 devicestate.IncEnsureOperationalAttempts(s.state) 2097 s.state.Set("seeded", true) 2098 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2099 Brand: "canonical", 2100 Model: "pc", 2101 }) 2102 s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ 2103 "architecture": "amd64", 2104 "kernel": "pc-kernel", 2105 "gadget": "pc", 2106 }) 2107 c.Check(canAutoRefresh(), Equals, false) 2108 2109 // third attempt ongoing, or done 2110 // fallback, try auto-refresh 2111 devicestate.IncEnsureOperationalAttempts(s.state) 2112 // sanity 2113 c.Check(devicestate.EnsureOperationalAttempts(s.state), Equals, 3) 2114 c.Check(canAutoRefresh(), Equals, true) 2115 } 2116 2117 func (s *deviceMgrSuite) TestCanAutoRefreshOnClassic(c *C) { 2118 release.OnClassic = true 2119 2120 s.state.Lock() 2121 defer s.state.Unlock() 2122 2123 canAutoRefresh := func() bool { 2124 ok, err := devicestate.CanAutoRefresh(s.state) 2125 c.Assert(err, IsNil) 2126 return ok 2127 } 2128 2129 // not seeded, no model, no serial -> no auto-refresh 2130 s.state.Set("seeded", false) 2131 c.Check(canAutoRefresh(), Equals, false) 2132 2133 // seeded, no model -> auto-refresh 2134 s.state.Set("seeded", true) 2135 c.Check(canAutoRefresh(), Equals, false) 2136 2137 // seeded, model, no serial -> no auto-refresh 2138 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2139 Brand: "canonical", 2140 Model: "pc", 2141 }) 2142 s.makeModelAssertionInState(c, "canonical", "pc", map[string]interface{}{ 2143 "classic": "true", 2144 }) 2145 c.Check(canAutoRefresh(), Equals, false) 2146 2147 // seeded, model, serial -> auto-refresh 2148 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2149 Brand: "canonical", 2150 Model: "pc", 2151 Serial: "8989", 2152 }) 2153 s.makeSerialAssertionInState(c, "canonical", "pc", "8989") 2154 c.Check(canAutoRefresh(), Equals, true) 2155 2156 // not seeded, model, serial -> no auto-refresh 2157 s.state.Set("seeded", false) 2158 c.Check(canAutoRefresh(), Equals, false) 2159 } 2160 2161 func makeInstalledMockCoreSnapWithSnapdControl(c *C, st *state.State) *snap.Info { 2162 sideInfoCore11 := &snap.SideInfo{RealName: "core", Revision: snap.R(11), SnapID: "core-id"} 2163 snapstate.Set(st, "core", &snapstate.SnapState{ 2164 Active: true, 2165 Sequence: []*snap.SideInfo{sideInfoCore11}, 2166 Current: sideInfoCore11.Revision, 2167 SnapType: "os", 2168 }) 2169 core11 := snaptest.MockSnap(c, ` 2170 name: core 2171 version: 1.0 2172 slots: 2173 snapd-control: 2174 `, sideInfoCore11) 2175 c.Assert(core11.Slots, HasLen, 1) 2176 2177 return core11 2178 } 2179 2180 var snapWithSnapdControlRefreshScheduleManagedYAML = ` 2181 name: snap-with-snapd-control 2182 version: 1.0 2183 plugs: 2184 snapd-control: 2185 refresh-schedule: managed 2186 ` 2187 2188 var snapWithSnapdControlOnlyYAML = ` 2189 name: snap-with-snapd-control 2190 version: 1.0 2191 plugs: 2192 snapd-control: 2193 ` 2194 2195 func makeInstalledMockSnap(c *C, st *state.State, yml string) *snap.Info { 2196 sideInfo11 := &snap.SideInfo{RealName: "snap-with-snapd-control", Revision: snap.R(11), SnapID: "snap-with-snapd-control-id"} 2197 snapstate.Set(st, "snap-with-snapd-control", &snapstate.SnapState{ 2198 Active: true, 2199 Sequence: []*snap.SideInfo{sideInfo11}, 2200 Current: sideInfo11.Revision, 2201 SnapType: "app", 2202 }) 2203 info11 := snaptest.MockSnap(c, yml, sideInfo11) 2204 c.Assert(info11.Plugs, HasLen, 1) 2205 2206 return info11 2207 } 2208 2209 func makeMockRepoWithConnectedSnaps(c *C, st *state.State, info11, core11 *snap.Info, ifname string) { 2210 repo := interfaces.NewRepository() 2211 for _, iface := range builtin.Interfaces() { 2212 err := repo.AddInterface(iface) 2213 c.Assert(err, IsNil) 2214 } 2215 err := repo.AddSnap(info11) 2216 c.Assert(err, IsNil) 2217 err = repo.AddSnap(core11) 2218 c.Assert(err, IsNil) 2219 _, err = repo.Connect(&interfaces.ConnRef{ 2220 PlugRef: interfaces.PlugRef{Snap: info11.InstanceName(), Name: ifname}, 2221 SlotRef: interfaces.SlotRef{Snap: core11.InstanceName(), Name: ifname}, 2222 }, nil, nil, nil, nil, nil) 2223 c.Assert(err, IsNil) 2224 conns, err := repo.Connected("snap-with-snapd-control", "snapd-control") 2225 c.Assert(err, IsNil) 2226 c.Assert(conns, HasLen, 1) 2227 ifacerepo.Replace(st, repo) 2228 } 2229 2230 func (s *deviceMgrSuite) TestCanManageRefreshes(c *C) { 2231 st := s.state 2232 st.Lock() 2233 defer st.Unlock() 2234 2235 // not possbile to manage by default 2236 c.Check(devicestate.CanManageRefreshes(st), Equals, false) 2237 2238 // not possible with just a snap with "snapd-control" plug with the 2239 // right attribute 2240 info11 := makeInstalledMockSnap(c, st, snapWithSnapdControlRefreshScheduleManagedYAML) 2241 c.Check(devicestate.CanManageRefreshes(st), Equals, false) 2242 2243 // not possible with a core snap with snapd control 2244 core11 := makeInstalledMockCoreSnapWithSnapdControl(c, st) 2245 c.Check(devicestate.CanManageRefreshes(st), Equals, false) 2246 2247 // not possible even with connected interfaces 2248 makeMockRepoWithConnectedSnaps(c, st, info11, core11, "snapd-control") 2249 c.Check(devicestate.CanManageRefreshes(st), Equals, false) 2250 2251 // if all of the above plus a snap declaration are in place we can 2252 // manage schedules 2253 s.setupSnapDecl(c, info11, "canonical") 2254 c.Check(devicestate.CanManageRefreshes(st), Equals, true) 2255 2256 // works if the snap is not active as well (to fix race when a 2257 // snap is refreshed) 2258 var sideInfo11 snapstate.SnapState 2259 err := snapstate.Get(st, "snap-with-snapd-control", &sideInfo11) 2260 c.Assert(err, IsNil) 2261 sideInfo11.Active = false 2262 snapstate.Set(st, "snap-with-snapd-control", &sideInfo11) 2263 c.Check(devicestate.CanManageRefreshes(st), Equals, true) 2264 } 2265 2266 func (s *deviceMgrSuite) TestCanManageRefreshesNoRefreshScheduleManaged(c *C) { 2267 st := s.state 2268 st.Lock() 2269 defer st.Unlock() 2270 2271 // just having a connected "snapd-control" interface is not enough 2272 // for setting refresh.schedule=managed 2273 info11 := makeInstalledMockSnap(c, st, snapWithSnapdControlOnlyYAML) 2274 core11 := makeInstalledMockCoreSnapWithSnapdControl(c, st) 2275 makeMockRepoWithConnectedSnaps(c, st, info11, core11, "snapd-control") 2276 s.setupSnapDecl(c, info11, "canonical") 2277 2278 c.Check(devicestate.CanManageRefreshes(st), Equals, false) 2279 } 2280 2281 func (s *deviceMgrSuite) TestReloadRegistered(c *C) { 2282 st := state.New(nil) 2283 2284 runner1 := state.NewTaskRunner(st) 2285 hookMgr1, err := hookstate.Manager(st, runner1) 2286 c.Assert(err, IsNil) 2287 mgr1, err := devicestate.Manager(st, hookMgr1, runner1, nil) 2288 c.Assert(err, IsNil) 2289 2290 ok := false 2291 select { 2292 case <-mgr1.Registered(): 2293 default: 2294 ok = true 2295 } 2296 c.Check(ok, Equals, true) 2297 2298 st.Lock() 2299 devicestatetest.SetDevice(st, &auth.DeviceState{ 2300 Brand: "canonical", 2301 Model: "pc", 2302 Serial: "serial", 2303 }) 2304 st.Unlock() 2305 2306 runner2 := state.NewTaskRunner(st) 2307 hookMgr2, err := hookstate.Manager(st, runner2) 2308 c.Assert(err, IsNil) 2309 mgr2, err := devicestate.Manager(st, hookMgr2, runner2, nil) 2310 c.Assert(err, IsNil) 2311 2312 ok = false 2313 select { 2314 case <-mgr2.Registered(): 2315 ok = true 2316 case <-time.After(5 * time.Second): 2317 c.Fatal("should have been marked registered") 2318 } 2319 c.Check(ok, Equals, true) 2320 } 2321 2322 func (s *deviceMgrSuite) TestMarkSeededInConfig(c *C) { 2323 st := s.state 2324 st.Lock() 2325 defer st.Unlock() 2326 2327 // avoid device registration 2328 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2329 Serial: "123", 2330 }) 2331 2332 // avoid full seeding 2333 s.seeding() 2334 2335 // not seeded -> no config is set 2336 s.state.Unlock() 2337 s.mgr.Ensure() 2338 s.state.Lock() 2339 2340 var seedLoaded bool 2341 tr := config.NewTransaction(st) 2342 tr.Get("core", "seed.loaded", &seedLoaded) 2343 c.Check(seedLoaded, Equals, false) 2344 2345 // pretend we are seeded now 2346 s.state.Set("seeded", true) 2347 2348 // seeded -> config got updated 2349 s.state.Unlock() 2350 s.mgr.Ensure() 2351 s.state.Lock() 2352 2353 tr = config.NewTransaction(st) 2354 tr.Get("core", "seed.loaded", &seedLoaded) 2355 c.Check(seedLoaded, Equals, true) 2356 2357 // only the fake seeding change is in the state, no further 2358 // changes 2359 c.Check(s.state.Changes(), HasLen, 1) 2360 } 2361 2362 func (s *deviceMgrSuite) TestNewEnoughProxyParse(c *C) { 2363 s.state.Lock() 2364 defer s.state.Unlock() 2365 2366 log, restore := logger.MockLogger() 2367 defer restore() 2368 os.Setenv("SNAPD_DEBUG", "1") 2369 defer os.Unsetenv("SNAPD_DEBUG") 2370 2371 badURL := &url.URL{Opaque: "%a"} // url.Parse(badURL.String()) needs to fail, which isn't easy :-) 2372 c.Check(devicestate.NewEnoughProxy(s.state, badURL, http.DefaultClient), Equals, false) 2373 c.Check(log.String(), Matches, "(?m).* DEBUG: Cannot check whether proxy store supports a custom serial vault: parse .*") 2374 } 2375 2376 func (s *deviceMgrSuite) TestNewEnoughProxy(c *C) { 2377 s.state.Lock() 2378 defer s.state.Unlock() 2379 2380 expectedUserAgent := httputil.UserAgent() 2381 log, restore := logger.MockLogger() 2382 defer restore() 2383 os.Setenv("SNAPD_DEBUG", "1") 2384 defer os.Unsetenv("SNAPD_DEBUG") 2385 2386 expecteds := []string{ 2387 `Head http://\S+: EOF`, 2388 `Head request returned 403 Forbidden.`, 2389 `Bogus Snap-Store-Version header "5pre1".`, 2390 ``, 2391 } 2392 2393 n := 0 2394 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 2395 c.Check(r.Header.Get("User-Agent"), Equals, expectedUserAgent) 2396 n++ 2397 switch n { 2398 case 1: 2399 conn, _, err := w.(http.Hijacker).Hijack() 2400 c.Assert(err, IsNil) 2401 conn.Close() 2402 case 2: 2403 w.WriteHeader(403) 2404 case 3: 2405 w.Header().Set("Snap-Store-Version", "5pre1") 2406 w.WriteHeader(200) 2407 case 4: 2408 w.Header().Set("Snap-Store-Version", "5") 2409 w.WriteHeader(200) 2410 case 5: 2411 w.Header().Set("Snap-Store-Version", "6") 2412 w.WriteHeader(200) 2413 default: 2414 c.Errorf("expected %d results, now on %d", len(expecteds), n) 2415 } 2416 })) 2417 defer server.Close() 2418 2419 u, err := url.Parse(server.URL) 2420 c.Assert(err, IsNil) 2421 for _, expected := range expecteds { 2422 log.Reset() 2423 c.Check(devicestate.NewEnoughProxy(s.state, u, http.DefaultClient), Equals, false) 2424 if len(expected) > 0 { 2425 expected = "(?m).* DEBUG: Cannot check whether proxy store supports a custom serial vault: " + expected 2426 } 2427 c.Check(log.String(), Matches, expected) 2428 } 2429 c.Check(n, Equals, len(expecteds)) 2430 2431 // and success at last 2432 log.Reset() 2433 c.Check(devicestate.NewEnoughProxy(s.state, u, http.DefaultClient), Equals, true) 2434 c.Check(log.String(), Equals, "") 2435 c.Check(n, Equals, len(expecteds)+1) 2436 } 2437 2438 func (s *deviceMgrSuite) TestDevicemgrCanStandby(c *C) { 2439 st := state.New(nil) 2440 2441 runner := state.NewTaskRunner(st) 2442 hookMgr, err := hookstate.Manager(st, runner) 2443 c.Assert(err, IsNil) 2444 mgr, err := devicestate.Manager(st, hookMgr, runner, nil) 2445 c.Assert(err, IsNil) 2446 2447 st.Lock() 2448 defer st.Unlock() 2449 c.Check(mgr.CanStandby(), Equals, false) 2450 2451 st.Set("seeded", true) 2452 c.Check(mgr.CanStandby(), Equals, true) 2453 } 2454 2455 type testModel struct { 2456 brand, model string 2457 arch, base, kernel, gadget string 2458 } 2459 2460 func (s *deviceMgrSuite) TestRemodelUnhappyNotSeeded(c *C) { 2461 s.state.Lock() 2462 defer s.state.Unlock() 2463 s.state.Set("seeded", false) 2464 2465 newModel := s.brands.Model("canonical", "pc", map[string]interface{}{ 2466 "architecture": "amd64", 2467 "kernel": "pc-kernel", 2468 "gadget": "pc", 2469 }) 2470 _, err := devicestate.Remodel(s.state, newModel) 2471 c.Assert(err, ErrorMatches, "cannot remodel until fully seeded") 2472 } 2473 2474 func (s *deviceMgrSuite) TestRemodelUnhappy(c *C) { 2475 s.state.Lock() 2476 defer s.state.Unlock() 2477 s.state.Set("seeded", true) 2478 2479 // set a model assertion 2480 cur := map[string]string{ 2481 "brand": "canonical", 2482 "model": "pc-model", 2483 "architecture": "amd64", 2484 "kernel": "pc-kernel", 2485 "gadget": "pc", 2486 "base": "core18", 2487 } 2488 s.makeModelAssertionInState(c, cur["brand"], cur["model"], map[string]interface{}{ 2489 "architecture": cur["architecture"], 2490 "kernel": cur["kernel"], 2491 "gadget": cur["gadget"], 2492 "base": cur["base"], 2493 }) 2494 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2495 Brand: cur["brand"], 2496 Model: cur["model"], 2497 }) 2498 2499 // ensure all error cases are checked 2500 for _, t := range []struct { 2501 new map[string]string 2502 errStr string 2503 }{ 2504 {map[string]string{"architecture": "pdp-7"}, "cannot remodel to different architectures yet"}, 2505 {map[string]string{"base": "core20"}, "cannot remodel to different bases yet"}, 2506 {map[string]string{"gadget": "other-gadget"}, "cannot remodel to different gadgets yet"}, 2507 } { 2508 // copy current model unless new model test data is different 2509 for k, v := range cur { 2510 if t.new[k] != "" { 2511 continue 2512 } 2513 t.new[k] = v 2514 } 2515 new := s.brands.Model(t.new["brand"], t.new["model"], map[string]interface{}{ 2516 "architecture": t.new["architecture"], 2517 "kernel": t.new["kernel"], 2518 "gadget": t.new["gadget"], 2519 "base": t.new["base"], 2520 }) 2521 chg, err := devicestate.Remodel(s.state, new) 2522 c.Check(chg, IsNil) 2523 c.Check(err, ErrorMatches, t.errStr) 2524 } 2525 } 2526 2527 func (s *deviceMgrSuite) TestRemodelTasksSwitchKernelTrack(c *C) { 2528 s.state.Lock() 2529 defer s.state.Unlock() 2530 s.state.Set("seeded", true) 2531 s.state.Set("refresh-privacy-key", "some-privacy-key") 2532 2533 var testDeviceCtx snapstate.DeviceContext 2534 2535 restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) { 2536 c.Check(flags.Required, Equals, true) 2537 c.Check(deviceCtx, Equals, testDeviceCtx) 2538 c.Check(fromChange, Equals, "99") 2539 2540 tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name)) 2541 tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name)) 2542 tValidate.WaitFor(tDownload) 2543 tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name)) 2544 tInstall.WaitFor(tValidate) 2545 ts := state.NewTaskSet(tDownload, tValidate, tInstall) 2546 ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge) 2547 return ts, nil 2548 }) 2549 defer restore() 2550 2551 restore = devicestate.MockSnapstateUpdateWithDeviceContext(func(st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) { 2552 c.Check(flags.Required, Equals, false) 2553 c.Check(flags.NoReRefresh, Equals, true) 2554 c.Check(deviceCtx, Equals, testDeviceCtx) 2555 c.Check(fromChange, Equals, "99") 2556 2557 tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s to track %s", name, opts.Channel)) 2558 tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name)) 2559 tValidate.WaitFor(tDownload) 2560 tUpdate := s.state.NewTask("fake-update", fmt.Sprintf("Update %s to track %s", name, opts.Channel)) 2561 tUpdate.WaitFor(tValidate) 2562 ts := state.NewTaskSet(tDownload, tValidate, tUpdate) 2563 ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge) 2564 return ts, nil 2565 }) 2566 defer restore() 2567 2568 // set a model assertion 2569 current := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 2570 "architecture": "amd64", 2571 "kernel": "pc-kernel", 2572 "gadget": "pc", 2573 "base": "core18", 2574 }) 2575 err := assertstate.Add(s.state, current) 2576 c.Assert(err, IsNil) 2577 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2578 Brand: "canonical", 2579 Model: "pc-model", 2580 }) 2581 2582 new := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 2583 "architecture": "amd64", 2584 "kernel": "pc-kernel=18", 2585 "gadget": "pc", 2586 "base": "core18", 2587 "required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"}, 2588 "revision": "1", 2589 }) 2590 2591 testDeviceCtx = &snapstatetest.TrivialDeviceContext{Remodeling: true} 2592 2593 tss, err := devicestate.RemodelTasks(context.Background(), s.state, current, new, testDeviceCtx, "99") 2594 c.Assert(err, IsNil) 2595 // 2 snaps, plus one track switch plus the remodel task, the 2596 // wait chain is tested in TestRemodel* 2597 c.Assert(tss, HasLen, 4) 2598 } 2599 2600 func (s *deviceMgrSuite) TestRemodelTasksSwitchKernel(c *C) { 2601 s.state.Lock() 2602 defer s.state.Unlock() 2603 s.state.Set("seeded", true) 2604 s.state.Set("refresh-privacy-key", "some-privacy-key") 2605 2606 var testDeviceCtx snapstate.DeviceContext 2607 2608 restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) { 2609 c.Check(deviceCtx, Equals, testDeviceCtx) 2610 c.Check(name, Equals, "other-kernel") 2611 c.Check(opts.Channel, Equals, "18") 2612 2613 tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name)) 2614 tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name)) 2615 tValidate.WaitFor(tDownload) 2616 tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name)) 2617 tInstall.WaitFor(tValidate) 2618 ts := state.NewTaskSet(tDownload, tValidate, tInstall) 2619 ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge) 2620 return ts, nil 2621 }) 2622 defer restore() 2623 2624 // set a model assertion 2625 current := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 2626 "architecture": "amd64", 2627 "kernel": "pc-kernel", 2628 "gadget": "pc", 2629 "base": "core18", 2630 }) 2631 err := assertstate.Add(s.state, current) 2632 c.Assert(err, IsNil) 2633 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2634 Brand: "canonical", 2635 Model: "pc-model", 2636 }) 2637 2638 new := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 2639 "architecture": "amd64", 2640 "kernel": "other-kernel=18", 2641 "gadget": "pc", 2642 "base": "core18", 2643 "revision": "1", 2644 }) 2645 2646 testDeviceCtx = &snapstatetest.TrivialDeviceContext{Remodeling: true} 2647 2648 tss, err := devicestate.RemodelTasks(context.Background(), s.state, current, new, testDeviceCtx, "99") 2649 c.Assert(err, IsNil) 2650 // 1 new kernel plus the remodel task 2651 c.Assert(tss, HasLen, 2) 2652 } 2653 2654 func (s *deviceMgrSuite) TestRemodelRequiredSnaps(c *C) { 2655 s.state.Lock() 2656 defer s.state.Unlock() 2657 s.state.Set("seeded", true) 2658 s.state.Set("refresh-privacy-key", "some-privacy-key") 2659 2660 restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) { 2661 c.Check(flags.Required, Equals, true) 2662 c.Check(deviceCtx, NotNil) 2663 c.Check(deviceCtx.ForRemodeling(), Equals, true) 2664 2665 tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name)) 2666 tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name)) 2667 tValidate.WaitFor(tDownload) 2668 tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name)) 2669 tInstall.WaitFor(tValidate) 2670 ts := state.NewTaskSet(tDownload, tValidate, tInstall) 2671 ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge) 2672 return ts, nil 2673 }) 2674 defer restore() 2675 2676 // set a model assertion 2677 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 2678 "architecture": "amd64", 2679 "kernel": "pc-kernel", 2680 "gadget": "pc", 2681 "base": "core18", 2682 }) 2683 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2684 Brand: "canonical", 2685 Model: "pc-model", 2686 }) 2687 2688 new := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 2689 "architecture": "amd64", 2690 "kernel": "pc-kernel", 2691 "gadget": "pc", 2692 "base": "core18", 2693 "required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"}, 2694 "revision": "1", 2695 }) 2696 chg, err := devicestate.Remodel(s.state, new) 2697 c.Assert(err, IsNil) 2698 c.Assert(chg.Summary(), Equals, "Refresh model assertion from revision 0 to 1") 2699 2700 tl := chg.Tasks() 2701 // 2 snaps, 2702 c.Assert(tl, HasLen, 2*3+1) 2703 2704 deviceCtx, err := devicestate.DeviceCtx(s.state, tl[0], nil) 2705 c.Assert(err, IsNil) 2706 // deviceCtx is actually a remodelContext here 2707 remodCtx, ok := deviceCtx.(devicestate.RemodelContext) 2708 c.Assert(ok, Equals, true) 2709 c.Check(remodCtx.ForRemodeling(), Equals, true) 2710 c.Check(remodCtx.Kind(), Equals, devicestate.UpdateRemodel) 2711 c.Check(remodCtx.Model(), DeepEquals, new) 2712 c.Check(remodCtx.Store(), IsNil) 2713 2714 // check the tasks 2715 tDownloadSnap1 := tl[0] 2716 tValidateSnap1 := tl[1] 2717 tInstallSnap1 := tl[2] 2718 tDownloadSnap2 := tl[3] 2719 tValidateSnap2 := tl[4] 2720 tInstallSnap2 := tl[5] 2721 tSetModel := tl[6] 2722 2723 // check the tasks 2724 c.Assert(tDownloadSnap1.Kind(), Equals, "fake-download") 2725 c.Assert(tDownloadSnap1.Summary(), Equals, "Download new-required-snap-1") 2726 c.Assert(tDownloadSnap1.WaitTasks(), HasLen, 0) 2727 c.Assert(tValidateSnap1.Kind(), Equals, "validate-snap") 2728 c.Assert(tValidateSnap1.Summary(), Equals, "Validate new-required-snap-1") 2729 c.Assert(tDownloadSnap1.WaitTasks(), HasLen, 0) 2730 c.Assert(tDownloadSnap2.Kind(), Equals, "fake-download") 2731 c.Assert(tDownloadSnap2.Summary(), Equals, "Download new-required-snap-2") 2732 // check the ordering, download/validate everything first, then install 2733 2734 // snap2 downloads wait for the downloads of snap1 2735 c.Assert(tDownloadSnap1.WaitTasks(), HasLen, 0) 2736 c.Assert(tValidateSnap1.WaitTasks(), DeepEquals, []*state.Task{ 2737 tDownloadSnap1, 2738 }) 2739 c.Assert(tDownloadSnap2.WaitTasks(), DeepEquals, []*state.Task{ 2740 tValidateSnap1, 2741 }) 2742 c.Assert(tValidateSnap2.WaitTasks(), DeepEquals, []*state.Task{ 2743 tDownloadSnap2, 2744 }) 2745 c.Assert(tInstallSnap1.WaitTasks(), DeepEquals, []*state.Task{ 2746 // wait for own check-snap 2747 tValidateSnap1, 2748 // and also the last check-snap of the download chain 2749 tValidateSnap2, 2750 }) 2751 c.Assert(tInstallSnap2.WaitTasks(), DeepEquals, []*state.Task{ 2752 // last snap of the download chain 2753 tValidateSnap2, 2754 // previous install chain 2755 tInstallSnap1, 2756 }) 2757 2758 c.Assert(tSetModel.Kind(), Equals, "set-model") 2759 c.Assert(tSetModel.Summary(), Equals, "Set new model assertion") 2760 // setModel waits for everything in the change 2761 c.Assert(tSetModel.WaitTasks(), DeepEquals, []*state.Task{tDownloadSnap1, tValidateSnap1, tInstallSnap1, tDownloadSnap2, tValidateSnap2, tInstallSnap2}) 2762 } 2763 2764 func (s *deviceMgrSuite) TestRemodelSwitchKernelTrack(c *C) { 2765 s.state.Lock() 2766 defer s.state.Unlock() 2767 s.state.Set("seeded", true) 2768 s.state.Set("refresh-privacy-key", "some-privacy-key") 2769 2770 restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) { 2771 c.Check(flags.Required, Equals, true) 2772 c.Check(deviceCtx, NotNil) 2773 c.Check(deviceCtx.ForRemodeling(), Equals, true) 2774 2775 tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name)) 2776 tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name)) 2777 tValidate.WaitFor(tDownload) 2778 tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name)) 2779 tInstall.WaitFor(tValidate) 2780 ts := state.NewTaskSet(tDownload, tValidate, tInstall) 2781 ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge) 2782 return ts, nil 2783 }) 2784 defer restore() 2785 2786 restore = devicestate.MockSnapstateUpdateWithDeviceContext(func(st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) { 2787 c.Check(flags.Required, Equals, false) 2788 c.Check(flags.NoReRefresh, Equals, true) 2789 c.Check(deviceCtx, NotNil) 2790 c.Check(deviceCtx.ForRemodeling(), Equals, true) 2791 2792 tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s to track %s", name, opts.Channel)) 2793 tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name)) 2794 tValidate.WaitFor(tDownload) 2795 tUpdate := s.state.NewTask("fake-update", fmt.Sprintf("Update %s to track %s", name, opts.Channel)) 2796 tUpdate.WaitFor(tValidate) 2797 ts := state.NewTaskSet(tDownload, tValidate, tUpdate) 2798 ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge) 2799 return ts, nil 2800 }) 2801 defer restore() 2802 2803 // set a model assertion 2804 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 2805 "architecture": "amd64", 2806 "kernel": "pc-kernel", 2807 "gadget": "pc", 2808 "base": "core18", 2809 }) 2810 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2811 Brand: "canonical", 2812 Model: "pc-model", 2813 }) 2814 2815 new := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 2816 "architecture": "amd64", 2817 "kernel": "pc-kernel=18", 2818 "gadget": "pc", 2819 "base": "core18", 2820 "required-snaps": []interface{}{"new-required-snap-1"}, 2821 "revision": "1", 2822 }) 2823 chg, err := devicestate.Remodel(s.state, new) 2824 c.Assert(err, IsNil) 2825 c.Assert(chg.Summary(), Equals, "Refresh model assertion from revision 0 to 1") 2826 2827 tl := chg.Tasks() 2828 c.Assert(tl, HasLen, 2*3+1) 2829 2830 tDownloadKernel := tl[0] 2831 tValidateKernel := tl[1] 2832 tUpdateKernel := tl[2] 2833 tDownloadSnap1 := tl[3] 2834 tValidateSnap1 := tl[4] 2835 tInstallSnap1 := tl[5] 2836 tSetModel := tl[6] 2837 2838 c.Assert(tDownloadKernel.Kind(), Equals, "fake-download") 2839 c.Assert(tDownloadKernel.Summary(), Equals, "Download pc-kernel to track 18") 2840 c.Assert(tValidateKernel.Kind(), Equals, "validate-snap") 2841 c.Assert(tValidateKernel.Summary(), Equals, "Validate pc-kernel") 2842 c.Assert(tUpdateKernel.Kind(), Equals, "fake-update") 2843 c.Assert(tUpdateKernel.Summary(), Equals, "Update pc-kernel to track 18") 2844 c.Assert(tDownloadSnap1.Kind(), Equals, "fake-download") 2845 c.Assert(tDownloadSnap1.Summary(), Equals, "Download new-required-snap-1") 2846 c.Assert(tValidateSnap1.Kind(), Equals, "validate-snap") 2847 c.Assert(tValidateSnap1.Summary(), Equals, "Validate new-required-snap-1") 2848 c.Assert(tInstallSnap1.Kind(), Equals, "fake-install") 2849 c.Assert(tInstallSnap1.Summary(), Equals, "Install new-required-snap-1") 2850 2851 c.Assert(tSetModel.Kind(), Equals, "set-model") 2852 c.Assert(tSetModel.Summary(), Equals, "Set new model assertion") 2853 2854 // check the ordering 2855 c.Assert(tDownloadSnap1.WaitTasks(), DeepEquals, []*state.Task{ 2856 // previous download finished 2857 tValidateKernel, 2858 }) 2859 c.Assert(tInstallSnap1.WaitTasks(), DeepEquals, []*state.Task{ 2860 // last download in the chain finished 2861 tValidateSnap1, 2862 // and kernel got updated 2863 tUpdateKernel, 2864 }) 2865 c.Assert(tUpdateKernel.WaitTasks(), DeepEquals, []*state.Task{ 2866 // kernel is valid 2867 tValidateKernel, 2868 // and last download in the chain finished 2869 tValidateSnap1, 2870 }) 2871 } 2872 2873 func (s *deviceMgrSuite) TestRemodelLessRequiredSnaps(c *C) { 2874 s.state.Lock() 2875 defer s.state.Unlock() 2876 s.state.Set("seeded", true) 2877 s.state.Set("refresh-privacy-key", "some-privacy-key") 2878 2879 // set a model assertion 2880 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 2881 "architecture": "amd64", 2882 "kernel": "pc-kernel", 2883 "gadget": "pc", 2884 "base": "core18", 2885 "required-snaps": []interface{}{"some-required-snap"}, 2886 }) 2887 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2888 Brand: "canonical", 2889 Model: "pc-model", 2890 }) 2891 2892 new := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 2893 "architecture": "amd64", 2894 "kernel": "pc-kernel", 2895 "gadget": "pc", 2896 "base": "core18", 2897 "revision": "1", 2898 }) 2899 chg, err := devicestate.Remodel(s.state, new) 2900 c.Assert(err, IsNil) 2901 c.Assert(chg.Summary(), Equals, "Refresh model assertion from revision 0 to 1") 2902 2903 tl := chg.Tasks() 2904 c.Assert(tl, HasLen, 1) 2905 tSetModel := tl[0] 2906 c.Assert(tSetModel.Kind(), Equals, "set-model") 2907 c.Assert(tSetModel.Summary(), Equals, "Set new model assertion") 2908 } 2909 2910 type freshSessionStore struct { 2911 storetest.Store 2912 2913 ensureDeviceSession int 2914 } 2915 2916 func (sto *freshSessionStore) EnsureDeviceSession() (*auth.DeviceState, error) { 2917 sto.ensureDeviceSession += 1 2918 return nil, nil 2919 } 2920 2921 func (s *deviceMgrSuite) TestRemodelStoreSwitch(c *C) { 2922 s.state.Lock() 2923 defer s.state.Unlock() 2924 s.state.Set("seeded", true) 2925 s.state.Set("refresh-privacy-key", "some-privacy-key") 2926 2927 var testStore snapstate.StoreService 2928 2929 restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) { 2930 c.Check(flags.Required, Equals, true) 2931 c.Check(deviceCtx, NotNil) 2932 c.Check(deviceCtx.ForRemodeling(), Equals, true) 2933 2934 c.Check(deviceCtx.Store(), Equals, testStore) 2935 2936 tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name)) 2937 tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name)) 2938 tValidate.WaitFor(tDownload) 2939 tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name)) 2940 tInstall.WaitFor(tValidate) 2941 ts := state.NewTaskSet(tDownload, tValidate, tInstall) 2942 ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge) 2943 return ts, nil 2944 }) 2945 defer restore() 2946 2947 // set a model assertion 2948 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 2949 "architecture": "amd64", 2950 "kernel": "pc-kernel", 2951 "gadget": "pc", 2952 "base": "core18", 2953 }) 2954 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 2955 Brand: "canonical", 2956 Model: "pc-model", 2957 }) 2958 2959 new := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 2960 "architecture": "amd64", 2961 "kernel": "pc-kernel", 2962 "gadget": "pc", 2963 "base": "core18", 2964 "store": "switched-store", 2965 "required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"}, 2966 "revision": "1", 2967 }) 2968 2969 freshStore := &freshSessionStore{} 2970 testStore = freshStore 2971 2972 s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService { 2973 mod, err := devBE.Model() 2974 c.Check(err, IsNil) 2975 if err == nil { 2976 c.Check(mod, DeepEquals, new) 2977 } 2978 return testStore 2979 } 2980 2981 chg, err := devicestate.Remodel(s.state, new) 2982 c.Assert(err, IsNil) 2983 c.Assert(chg.Summary(), Equals, "Refresh model assertion from revision 0 to 1") 2984 2985 c.Check(freshStore.ensureDeviceSession, Equals, 1) 2986 2987 tl := chg.Tasks() 2988 // 2 snaps * 3 tasks (from the mock install above) + 2989 // 1 "set-model" task at the end 2990 c.Assert(tl, HasLen, 2*3+1) 2991 2992 deviceCtx, err := devicestate.DeviceCtx(s.state, tl[0], nil) 2993 c.Assert(err, IsNil) 2994 // deviceCtx is actually a remodelContext here 2995 remodCtx, ok := deviceCtx.(devicestate.RemodelContext) 2996 c.Assert(ok, Equals, true) 2997 c.Check(remodCtx.ForRemodeling(), Equals, true) 2998 c.Check(remodCtx.Kind(), Equals, devicestate.StoreSwitchRemodel) 2999 c.Check(remodCtx.Model(), DeepEquals, new) 3000 c.Check(remodCtx.Store(), Equals, testStore) 3001 } 3002 3003 func (s *deviceMgrSuite) TestRemodelRereg(c *C) { 3004 s.state.Lock() 3005 defer s.state.Unlock() 3006 s.state.Set("seeded", true) 3007 3008 // set a model assertion 3009 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 3010 "architecture": "amd64", 3011 "kernel": "pc-kernel", 3012 "gadget": "pc", 3013 "base": "core18", 3014 }) 3015 s.makeSerialAssertionInState(c, "canonical", "pc-model", "orig-serial") 3016 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 3017 Brand: "canonical", 3018 Model: "pc-model", 3019 Serial: "orig-serial", 3020 SessionMacaroon: "old-session", 3021 }) 3022 3023 new := s.brands.Model("canonical", "rereg-model", map[string]interface{}{ 3024 "architecture": "amd64", 3025 "kernel": "pc-kernel", 3026 "gadget": "pc", 3027 "base": "core18", 3028 "required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"}, 3029 }) 3030 3031 s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService { 3032 mod, err := devBE.Model() 3033 c.Check(err, IsNil) 3034 if err == nil { 3035 c.Check(mod, DeepEquals, new) 3036 } 3037 return nil 3038 } 3039 3040 chg, err := devicestate.Remodel(s.state, new) 3041 c.Assert(err, IsNil) 3042 3043 c.Assert(chg.Summary(), Equals, "Remodel device to canonical/rereg-model (0)") 3044 3045 tl := chg.Tasks() 3046 c.Assert(tl, HasLen, 2) 3047 3048 // check the tasks 3049 tRequestSerial := tl[0] 3050 tPrepareRemodeling := tl[1] 3051 3052 // check the tasks 3053 c.Assert(tRequestSerial.Kind(), Equals, "request-serial") 3054 c.Assert(tRequestSerial.Summary(), Equals, "Request new device serial") 3055 c.Assert(tRequestSerial.WaitTasks(), HasLen, 0) 3056 3057 c.Assert(tPrepareRemodeling.Kind(), Equals, "prepare-remodeling") 3058 c.Assert(tPrepareRemodeling.Summary(), Equals, "Prepare remodeling") 3059 c.Assert(tPrepareRemodeling.WaitTasks(), DeepEquals, []*state.Task{tRequestSerial}) 3060 } 3061 3062 func (s *deviceMgrSuite) TestRemodelClash(c *C) { 3063 s.state.Lock() 3064 defer s.state.Unlock() 3065 s.state.Set("seeded", true) 3066 s.state.Set("refresh-privacy-key", "some-privacy-key") 3067 3068 var clashing *asserts.Model 3069 3070 restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) { 3071 // simulate things changing under our feet 3072 assertstatetest.AddMany(st, clashing) 3073 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 3074 Brand: "canonical", 3075 Model: clashing.Model(), 3076 }) 3077 3078 tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name)) 3079 tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name)) 3080 tValidate.WaitFor(tDownload) 3081 tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name)) 3082 tInstall.WaitFor(tValidate) 3083 ts := state.NewTaskSet(tDownload, tValidate, tInstall) 3084 ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge) 3085 return ts, nil 3086 }) 3087 defer restore() 3088 3089 // set a model assertion 3090 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 3091 "architecture": "amd64", 3092 "kernel": "pc-kernel", 3093 "gadget": "pc", 3094 "base": "core18", 3095 }) 3096 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 3097 Brand: "canonical", 3098 Model: "pc-model", 3099 }) 3100 3101 new := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 3102 "architecture": "amd64", 3103 "kernel": "pc-kernel", 3104 "gadget": "pc", 3105 "base": "core18", 3106 "required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"}, 3107 "revision": "1", 3108 }) 3109 other := s.brands.Model("canonical", "pc-model-other", map[string]interface{}{ 3110 "architecture": "amd64", 3111 "kernel": "pc-kernel", 3112 "gadget": "pc", 3113 "base": "core18", 3114 "required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"}, 3115 }) 3116 3117 clashing = other 3118 _, err := devicestate.Remodel(s.state, new) 3119 c.Check(err, DeepEquals, &snapstate.ChangeConflictError{ 3120 Message: "cannot start remodel, clashing with concurrent remodel to canonical/pc-model-other (0)", 3121 }) 3122 3123 // reset 3124 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 3125 Brand: "canonical", 3126 Model: "pc-model", 3127 }) 3128 clashing = new 3129 _, err = devicestate.Remodel(s.state, new) 3130 c.Check(err, DeepEquals, &snapstate.ChangeConflictError{ 3131 Message: "cannot start remodel, clashing with concurrent remodel to canonical/pc-model (1)", 3132 }) 3133 } 3134 3135 func (s *deviceMgrSuite) TestRemodelClashInProgress(c *C) { 3136 s.state.Lock() 3137 defer s.state.Unlock() 3138 s.state.Set("seeded", true) 3139 s.state.Set("refresh-privacy-key", "some-privacy-key") 3140 3141 restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) { 3142 // simulate another started remodeling 3143 st.NewChange("remodel", "other remodel") 3144 3145 tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name)) 3146 tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name)) 3147 tValidate.WaitFor(tDownload) 3148 tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name)) 3149 tInstall.WaitFor(tValidate) 3150 ts := state.NewTaskSet(tDownload, tValidate, tInstall) 3151 ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge) 3152 return ts, nil 3153 }) 3154 defer restore() 3155 3156 // set a model assertion 3157 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 3158 "architecture": "amd64", 3159 "kernel": "pc-kernel", 3160 "gadget": "pc", 3161 "base": "core18", 3162 }) 3163 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 3164 Brand: "canonical", 3165 Model: "pc-model", 3166 }) 3167 3168 new := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 3169 "architecture": "amd64", 3170 "kernel": "pc-kernel", 3171 "gadget": "pc", 3172 "base": "core18", 3173 "required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"}, 3174 "revision": "1", 3175 }) 3176 3177 _, err := devicestate.Remodel(s.state, new) 3178 c.Check(err, DeepEquals, &snapstate.ChangeConflictError{ 3179 Message: "cannot start remodel, clashing with concurrent one", 3180 }) 3181 } 3182 3183 func (s *deviceMgrSuite) TestReregRemodelClashAnyChange(c *C) { 3184 s.state.Lock() 3185 defer s.state.Unlock() 3186 s.state.Set("seeded", true) 3187 3188 // set a model assertion 3189 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 3190 "architecture": "amd64", 3191 "kernel": "pc-kernel", 3192 "gadget": "pc", 3193 "base": "core18", 3194 }) 3195 s.makeSerialAssertionInState(c, "canonical", "pc-model", "orig-serial") 3196 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 3197 Brand: "canonical", 3198 Model: "pc-model", 3199 Serial: "orig-serial", 3200 SessionMacaroon: "old-session", 3201 }) 3202 3203 new := s.brands.Model("canonical", "pc-model-2", map[string]interface{}{ 3204 "architecture": "amd64", 3205 "kernel": "pc-kernel", 3206 "gadget": "pc", 3207 "base": "core18", 3208 "required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"}, 3209 "revision": "1", 3210 }) 3211 3212 // simulate any other change 3213 s.state.NewChange("chg", "other change") 3214 3215 _, err := devicestate.Remodel(s.state, new) 3216 c.Check(err, DeepEquals, &snapstate.ChangeConflictError{ 3217 Message: "cannot start complete remodel, other changes are in progress", 3218 }) 3219 } 3220 3221 func (s *deviceMgrSuite) TestRemodeling(c *C) { 3222 s.state.Lock() 3223 defer s.state.Unlock() 3224 3225 // no changes 3226 c.Check(devicestate.Remodeling(s.state), Equals, false) 3227 3228 // other change 3229 s.state.NewChange("other", "...") 3230 c.Check(devicestate.Remodeling(s.state), Equals, false) 3231 3232 // remodel change 3233 chg := s.state.NewChange("remodel", "...") 3234 c.Check(devicestate.Remodeling(s.state), Equals, true) 3235 3236 // done 3237 chg.SetStatus(state.DoneStatus) 3238 c.Check(devicestate.Remodeling(s.state), Equals, false) 3239 } 3240 3241 func (s *deviceMgrSuite) testDoRequestSerialReregistration(c *C, setAncillary func(origSerial *asserts.Serial)) *state.Task { 3242 mockServer := s.mockServer(c, "REQID-1", nil) 3243 defer mockServer.Close() 3244 3245 restore := devicestate.MockBaseStoreURL(mockServer.URL) 3246 defer restore() 3247 3248 // setup state as after initial registration 3249 s.state.Lock() 3250 defer s.state.Unlock() 3251 3252 s.makeModelAssertionInState(c, "my-brand", "my-model", map[string]interface{}{ 3253 "architecture": "amd64", 3254 "kernel": "pc-kernel", 3255 "gadget": "pc", 3256 }) 3257 3258 devicestatetest.MockGadget(c, s.state, "pc", snap.R(2), nil) 3259 3260 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 3261 Brand: "my-brand", 3262 Model: "my-model", 3263 KeyID: devKey.PublicKey().ID(), 3264 Serial: "9999", 3265 }) 3266 devicestate.KeypairManager(s.mgr).Put(devKey) 3267 3268 // have a serial assertion 3269 serial0 := s.makeSerialAssertionInState(c, "my-brand", "my-model", "9999") 3270 // give a chance to the test to setup returning a stream vs 3271 // just the serial assertion 3272 if setAncillary != nil { 3273 setAncillary(serial0) 3274 } 3275 3276 new := s.brands.Model("rereg-brand", "rereg-model", map[string]interface{}{ 3277 "architecture": "amd64", 3278 "kernel": "pc-kernel", 3279 "gadget": "pc", 3280 }) 3281 cur, err := s.mgr.Model() 3282 c.Assert(err, IsNil) 3283 3284 s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService { 3285 mod, err := devBE.Model() 3286 c.Check(err, IsNil) 3287 if err == nil { 3288 c.Check(mod, DeepEquals, new) 3289 } 3290 return nil 3291 } 3292 3293 remodCtx, err := devicestate.RemodelCtx(s.state, cur, new) 3294 c.Assert(err, IsNil) 3295 c.Check(remodCtx.Kind(), Equals, devicestate.ReregRemodel) 3296 3297 t := s.state.NewTask("request-serial", "test") 3298 chg := s.state.NewChange("remodel", "...") 3299 // associate with context 3300 remodCtx.Init(chg) 3301 chg.AddTask(t) 3302 3303 // sanity 3304 regCtx, err := devicestate.RegistrationCtx(s.mgr, t) 3305 c.Assert(err, IsNil) 3306 c.Check(regCtx, Equals, remodCtx.(devicestate.RegistrationContext)) 3307 3308 // avoid full seeding 3309 s.seeding() 3310 3311 s.state.Unlock() 3312 s.se.Ensure() 3313 s.se.Wait() 3314 s.state.Lock() 3315 3316 return t 3317 } 3318 3319 func (s *deviceMgrSuite) TestDoRequestSerialReregistration(c *C) { 3320 assertstest.AddMany(s.storeSigning, s.brands.AccountsAndKeys("rereg-brand")...) 3321 3322 t := s.testDoRequestSerialReregistration(c, nil) 3323 3324 s.state.Lock() 3325 defer s.state.Unlock() 3326 chg := t.Change() 3327 3328 c.Check(chg.Status(), Equals, state.DoneStatus, Commentf("%s", t.Log())) 3329 c.Check(chg.Err(), IsNil) 3330 device, err := devicestatetest.Device(s.state) 3331 c.Check(err, IsNil) 3332 c.Check(device.Serial, Equals, "9999") 3333 _, err = s.db.Find(asserts.SerialType, map[string]string{ 3334 "brand-id": "rereg-brand", 3335 "model": "rereg-model", 3336 "serial": "9999", 3337 }) 3338 c.Assert(err, IsNil) 3339 } 3340 3341 func (s *deviceMgrSuite) TestDoRequestSerialReregistrationStreamFromService(c *C) { 3342 setAncillary := func(_ *asserts.Serial) { 3343 // sets up such that re-registration returns a stream 3344 // of assertions 3345 s.ancillary = s.brands.AccountsAndKeys("rereg-brand") 3346 } 3347 3348 t := s.testDoRequestSerialReregistration(c, setAncillary) 3349 3350 s.state.Lock() 3351 defer s.state.Unlock() 3352 chg := t.Change() 3353 3354 c.Check(chg.Status(), Equals, state.DoneStatus, Commentf("%s", t.Log())) 3355 c.Check(chg.Err(), IsNil) 3356 device, err := devicestatetest.Device(s.state) 3357 c.Check(err, IsNil) 3358 c.Check(device.Serial, Equals, "9999") 3359 _, err = s.db.Find(asserts.SerialType, map[string]string{ 3360 "brand-id": "rereg-brand", 3361 "model": "rereg-model", 3362 "serial": "9999", 3363 }) 3364 c.Assert(err, IsNil) 3365 } 3366 3367 func (s *deviceMgrSuite) TestDoRequestSerialReregistrationIncompleteStreamFromService(c *C) { 3368 setAncillary := func(_ *asserts.Serial) { 3369 // will produce an incomplete stream! 3370 s.ancillary = s.brands.AccountsAndKeys("rereg-brand")[:1] 3371 } 3372 3373 t := s.testDoRequestSerialReregistration(c, setAncillary) 3374 3375 s.state.Lock() 3376 defer s.state.Unlock() 3377 chg := t.Change() 3378 3379 c.Check(chg.Status(), Equals, state.ErrorStatus, Commentf("%s", t.Log())) 3380 c.Check(chg.Err(), ErrorMatches, `(?ms).*cannot accept stream of assertions from device service:.*`) 3381 } 3382 3383 func (s *deviceMgrSuite) TestDoRequestSerialReregistrationDoubleSerialStreamFromService(c *C) { 3384 setAncillary := func(serial0 *asserts.Serial) { 3385 // will produce a stream with confusingly two serial 3386 // assertions 3387 s.ancillary = s.brands.AccountsAndKeys("rereg-brand") 3388 s.ancillary = append(s.ancillary, serial0) 3389 } 3390 3391 t := s.testDoRequestSerialReregistration(c, setAncillary) 3392 3393 s.state.Lock() 3394 defer s.state.Unlock() 3395 chg := t.Change() 3396 3397 c.Check(chg.Status(), Equals, state.ErrorStatus, Commentf("%s", t.Log())) 3398 c.Check(chg.Err(), ErrorMatches, `(?ms).*cannot accept more than a single device serial assertion from the device service.*`) 3399 } 3400 3401 func (s *deviceMgrSuite) TestDeviceCtxNoTask(c *C) { 3402 s.state.Lock() 3403 defer s.state.Unlock() 3404 // nothing in the state 3405 3406 _, err := devicestate.DeviceCtx(s.state, nil, nil) 3407 c.Check(err, Equals, state.ErrNoState) 3408 3409 // have a model assertion 3410 model := s.brands.Model("canonical", "pc", map[string]interface{}{ 3411 "gadget": "pc", 3412 "kernel": "kernel", 3413 "architecture": "amd64", 3414 }) 3415 assertstatetest.AddMany(s.state, model) 3416 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 3417 Brand: "canonical", 3418 Model: "pc", 3419 }) 3420 3421 deviceCtx, err := devicestate.DeviceCtx(s.state, nil, nil) 3422 c.Assert(err, IsNil) 3423 c.Assert(deviceCtx.Model().BrandID(), Equals, "canonical") 3424 } 3425 3426 func (s *deviceMgrSuite) TestDeviceCtxProvided(c *C) { 3427 s.state.Lock() 3428 defer s.state.Unlock() 3429 3430 model := assertstest.FakeAssertion(map[string]interface{}{ 3431 "type": "model", 3432 "authority-id": "canonical", 3433 "series": "16", 3434 "brand-id": "canonical", 3435 "model": "pc", 3436 "gadget": "pc", 3437 "kernel": "kernel", 3438 "architecture": "amd64", 3439 }).(*asserts.Model) 3440 3441 deviceCtx := &snapstatetest.TrivialDeviceContext{DeviceModel: model} 3442 3443 deviceCtx1, err := devicestate.DeviceCtx(s.state, nil, deviceCtx) 3444 c.Assert(err, IsNil) 3445 c.Assert(deviceCtx1, Equals, deviceCtx) 3446 } 3447 3448 var snapYaml = ` 3449 name: foo-gadget 3450 type: gadget 3451 ` 3452 3453 var gadgetYaml = ` 3454 volumes: 3455 pc: 3456 bootloader: grub 3457 ` 3458 3459 func setupGadgetUpdate(c *C, st *state.State) (chg *state.Change, tsk *state.Task) { 3460 siCurrent := &snap.SideInfo{ 3461 RealName: "foo-gadget", 3462 Revision: snap.R(33), 3463 SnapID: "foo-id", 3464 } 3465 si := &snap.SideInfo{ 3466 RealName: "foo-gadget", 3467 Revision: snap.R(34), 3468 SnapID: "foo-id", 3469 } 3470 snaptest.MockSnapWithFiles(c, snapYaml, siCurrent, [][]string{ 3471 {"meta/gadget.yaml", gadgetYaml}, 3472 }) 3473 snaptest.MockSnapWithFiles(c, snapYaml, si, [][]string{ 3474 {"meta/gadget.yaml", gadgetYaml}, 3475 }) 3476 3477 st.Lock() 3478 3479 snapstate.Set(st, "foo-gadget", &snapstate.SnapState{ 3480 SnapType: "gadget", 3481 Sequence: []*snap.SideInfo{siCurrent}, 3482 Current: siCurrent.Revision, 3483 Active: true, 3484 }) 3485 3486 tsk = st.NewTask("update-gadget-assets", "update gadget") 3487 tsk.Set("snap-setup", &snapstate.SnapSetup{ 3488 SideInfo: si, 3489 Type: snap.TypeGadget, 3490 }) 3491 chg = st.NewChange("dummy", "...") 3492 chg.AddTask(tsk) 3493 3494 st.Unlock() 3495 3496 return chg, tsk 3497 } 3498 3499 func (s *deviceMgrSuite) TestUpdateGadgetOnCoreSimple(c *C) { 3500 var updateCalled bool 3501 var passedRollbackDir string 3502 restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error { 3503 updateCalled = true 3504 passedRollbackDir = path 3505 st, err := os.Stat(path) 3506 c.Assert(err, IsNil) 3507 m := st.Mode() 3508 c.Assert(m.IsDir(), Equals, true) 3509 c.Check(m.Perm(), Equals, os.FileMode(0750)) 3510 return nil 3511 }) 3512 defer restore() 3513 3514 chg, t := setupGadgetUpdate(c, s.state) 3515 3516 for i := 0; i < 6; i++ { 3517 s.se.Ensure() 3518 s.se.Wait() 3519 } 3520 3521 s.state.Lock() 3522 defer s.state.Unlock() 3523 c.Assert(chg.IsReady(), Equals, true) 3524 c.Check(chg.Err(), IsNil) 3525 c.Check(t.Status(), Equals, state.DoneStatus) 3526 c.Check(updateCalled, Equals, true) 3527 rollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget_34") 3528 c.Check(rollbackDir, Equals, passedRollbackDir) 3529 // should have been removed right after update 3530 c.Check(osutil.IsDirectory(rollbackDir), Equals, false) 3531 c.Check(s.restartRequests, DeepEquals, []state.RestartType{state.RestartSystem}) 3532 } 3533 3534 func (s *deviceMgrSuite) TestUpdateGadgetOnCoreNoUpdateNeeded(c *C) { 3535 var called bool 3536 restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error { 3537 called = true 3538 return gadget.ErrNoUpdate 3539 }) 3540 defer restore() 3541 3542 chg, t := setupGadgetUpdate(c, s.state) 3543 3544 s.se.Ensure() 3545 s.se.Wait() 3546 3547 s.state.Lock() 3548 defer s.state.Unlock() 3549 c.Assert(chg.IsReady(), Equals, true) 3550 c.Check(chg.Err(), IsNil) 3551 c.Check(t.Status(), Equals, state.DoneStatus) 3552 c.Check(t.Log(), HasLen, 1) 3553 c.Check(t.Log()[0], Matches, ".* INFO No gadget assets update needed") 3554 c.Check(called, Equals, true) 3555 c.Check(s.restartRequests, HasLen, 0) 3556 } 3557 3558 func (s *deviceMgrSuite) TestUpdateGadgetOnCoreRollbackDirCreateFailed(c *C) { 3559 if os.Geteuid() == 0 { 3560 c.Skip("this test cannot run as root (permissions are not honored)") 3561 } 3562 3563 restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error { 3564 return errors.New("unexpected call") 3565 }) 3566 defer restore() 3567 3568 chg, t := setupGadgetUpdate(c, s.state) 3569 3570 rollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget_34") 3571 err := os.MkdirAll(dirs.SnapRollbackDir, 0000) 3572 c.Assert(err, IsNil) 3573 3574 for i := 0; i < 6; i++ { 3575 s.se.Ensure() 3576 s.se.Wait() 3577 } 3578 3579 s.state.Lock() 3580 defer s.state.Unlock() 3581 c.Assert(chg.IsReady(), Equals, true) 3582 c.Check(chg.Err(), ErrorMatches, `(?s).*cannot prepare update rollback directory: .*`) 3583 c.Check(t.Status(), Equals, state.ErrorStatus) 3584 c.Check(osutil.IsDirectory(rollbackDir), Equals, false) 3585 c.Check(s.restartRequests, HasLen, 0) 3586 } 3587 3588 func (s *deviceMgrSuite) TestUpdateGadgetOnCoreUpdateFailed(c *C) { 3589 restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error { 3590 return errors.New("gadget exploded") 3591 }) 3592 defer restore() 3593 chg, t := setupGadgetUpdate(c, s.state) 3594 3595 for i := 0; i < 6; i++ { 3596 s.se.Ensure() 3597 s.se.Wait() 3598 } 3599 3600 s.state.Lock() 3601 defer s.state.Unlock() 3602 c.Assert(chg.IsReady(), Equals, true) 3603 c.Check(chg.Err(), ErrorMatches, `(?s).*update gadget \(gadget exploded\).*`) 3604 c.Check(t.Status(), Equals, state.ErrorStatus) 3605 rollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget_34") 3606 // update rollback left for inspection 3607 c.Check(osutil.IsDirectory(rollbackDir), Equals, true) 3608 c.Check(s.restartRequests, HasLen, 0) 3609 } 3610 3611 func (s *deviceMgrSuite) TestUpdateGadgetOnCoreNotDuringFirstboot(c *C) { 3612 restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error { 3613 return errors.New("unexpected call") 3614 }) 3615 defer restore() 3616 3617 // simulate first-boot/seeding, there is no existing snap state information 3618 3619 si := &snap.SideInfo{ 3620 RealName: "foo-gadget", 3621 Revision: snap.R(34), 3622 SnapID: "foo-id", 3623 } 3624 snaptest.MockSnapWithFiles(c, snapYaml, si, [][]string{ 3625 {"meta/gadget.yaml", gadgetYaml}, 3626 }) 3627 3628 s.state.Lock() 3629 3630 t := s.state.NewTask("update-gadget-assets", "update gadget") 3631 t.Set("snap-setup", &snapstate.SnapSetup{ 3632 SideInfo: si, 3633 Type: snap.TypeGadget, 3634 }) 3635 chg := s.state.NewChange("dummy", "...") 3636 chg.AddTask(t) 3637 3638 s.state.Unlock() 3639 3640 for i := 0; i < 6; i++ { 3641 s.se.Ensure() 3642 s.se.Wait() 3643 } 3644 3645 s.state.Lock() 3646 defer s.state.Unlock() 3647 c.Assert(chg.IsReady(), Equals, true) 3648 c.Check(chg.Err(), IsNil) 3649 c.Check(t.Status(), Equals, state.DoneStatus) 3650 rollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget") 3651 c.Check(osutil.IsDirectory(rollbackDir), Equals, false) 3652 c.Check(s.restartRequests, HasLen, 0) 3653 } 3654 3655 func (s *deviceMgrSuite) TestUpdateGadgetOnCoreBadGadgetYaml(c *C) { 3656 restore := devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error { 3657 return errors.New("unexpected call") 3658 }) 3659 defer restore() 3660 siCurrent := &snap.SideInfo{ 3661 RealName: "foo-gadget", 3662 Revision: snap.R(33), 3663 SnapID: "foo-id", 3664 } 3665 si := &snap.SideInfo{ 3666 RealName: "foo-gadget", 3667 Revision: snap.R(34), 3668 SnapID: "foo-id", 3669 } 3670 snaptest.MockSnapWithFiles(c, snapYaml, siCurrent, [][]string{ 3671 {"meta/gadget.yaml", gadgetYaml}, 3672 }) 3673 // invalid gadget.yaml data 3674 snaptest.MockSnapWithFiles(c, snapYaml, si, [][]string{ 3675 {"meta/gadget.yaml", "foobar"}, 3676 }) 3677 3678 s.state.Lock() 3679 3680 snapstate.Set(s.state, "foo-gadget", &snapstate.SnapState{ 3681 SnapType: "gadget", 3682 Sequence: []*snap.SideInfo{siCurrent}, 3683 Current: siCurrent.Revision, 3684 Active: true, 3685 }) 3686 3687 t := s.state.NewTask("update-gadget-assets", "update gadget") 3688 t.Set("snap-setup", &snapstate.SnapSetup{ 3689 SideInfo: si, 3690 Type: snap.TypeGadget, 3691 }) 3692 chg := s.state.NewChange("dummy", "...") 3693 chg.AddTask(t) 3694 3695 s.state.Unlock() 3696 3697 for i := 0; i < 6; i++ { 3698 s.se.Ensure() 3699 s.se.Wait() 3700 } 3701 3702 s.state.Lock() 3703 defer s.state.Unlock() 3704 c.Assert(chg.IsReady(), Equals, true) 3705 c.Check(chg.Err(), ErrorMatches, `(?s).*update gadget \(cannot read candidate gadget snap details: .*\).*`) 3706 c.Check(t.Status(), Equals, state.ErrorStatus) 3707 rollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget") 3708 c.Check(osutil.IsDirectory(rollbackDir), Equals, false) 3709 c.Check(s.restartRequests, HasLen, 0) 3710 } 3711 3712 func (s *deviceMgrSuite) TestUpdateGadgetOnClassicErrorsOut(c *C) { 3713 restore := release.MockOnClassic(true) 3714 defer restore() 3715 3716 restore = devicestate.MockGadgetUpdate(func(current, update gadget.GadgetData, path string) error { 3717 return errors.New("unexpected call") 3718 }) 3719 defer restore() 3720 3721 s.state.Lock() 3722 3723 t := s.state.NewTask("update-gadget-assets", "update gadget") 3724 chg := s.state.NewChange("dummy", "...") 3725 chg.AddTask(t) 3726 3727 s.state.Unlock() 3728 3729 // we cannot use "s.o.Settle()" here because this change has an 3730 // error which means that the settle will never converge 3731 for i := 0; i < 50; i++ { 3732 s.se.Ensure() 3733 s.se.Wait() 3734 3735 s.state.Lock() 3736 ready := chg.IsReady() 3737 s.state.Unlock() 3738 if ready { 3739 break 3740 } 3741 } 3742 3743 s.state.Lock() 3744 defer s.state.Unlock() 3745 c.Assert(chg.IsReady(), Equals, true) 3746 c.Check(chg.Err(), ErrorMatches, `(?s).*update gadget \(cannot run update gadget assets task on a classic system\).*`) 3747 c.Check(t.Status(), Equals, state.ErrorStatus) 3748 } 3749 3750 type mockUpdater struct{} 3751 3752 func (m *mockUpdater) Backup() error { return nil } 3753 3754 func (m *mockUpdater) Rollback() error { return nil } 3755 3756 func (m *mockUpdater) Update() error { return nil } 3757 3758 func (s *deviceMgrSuite) TestUpdateGadgetCallsToGadget(c *C) { 3759 siCurrent := &snap.SideInfo{ 3760 RealName: "foo-gadget", 3761 Revision: snap.R(33), 3762 SnapID: "foo-id", 3763 } 3764 si := &snap.SideInfo{ 3765 RealName: "foo-gadget", 3766 Revision: snap.R(34), 3767 SnapID: "foo-id", 3768 } 3769 var gadgetCurrentYaml = ` 3770 volumes: 3771 pc: 3772 bootloader: grub 3773 structure: 3774 - name: foo 3775 size: 10M 3776 type: bare 3777 content: 3778 - image: content.img 3779 ` 3780 var gadgetUpdateYaml = ` 3781 volumes: 3782 pc: 3783 bootloader: grub 3784 structure: 3785 - name: foo 3786 size: 10M 3787 type: bare 3788 content: 3789 - image: content.img 3790 update: 3791 edition: 2 3792 ` 3793 snaptest.MockSnapWithFiles(c, snapYaml, siCurrent, [][]string{ 3794 {"meta/gadget.yaml", gadgetCurrentYaml}, 3795 {"content.img", "some content"}, 3796 }) 3797 updateInfo := snaptest.MockSnapWithFiles(c, snapYaml, si, [][]string{ 3798 {"meta/gadget.yaml", gadgetUpdateYaml}, 3799 {"content.img", "updated content"}, 3800 }) 3801 3802 expectedRollbackDir := filepath.Join(dirs.SnapRollbackDir, "foo-gadget_34") 3803 updaterForStructureCalls := 0 3804 gadget.MockUpdaterForStructure(func(ps *gadget.LaidOutStructure, rootDir, rollbackDir string) (gadget.Updater, error) { 3805 updaterForStructureCalls++ 3806 3807 c.Assert(ps.Name, Equals, "foo") 3808 c.Assert(rootDir, Equals, updateInfo.MountDir()) 3809 c.Assert(filepath.Join(rootDir, "content.img"), testutil.FileEquals, "updated content") 3810 c.Assert(strings.HasPrefix(rollbackDir, expectedRollbackDir), Equals, true) 3811 c.Assert(osutil.IsDirectory(rollbackDir), Equals, true) 3812 return &mockUpdater{}, nil 3813 }) 3814 3815 s.state.Lock() 3816 3817 snapstate.Set(s.state, "foo-gadget", &snapstate.SnapState{ 3818 SnapType: "gadget", 3819 Sequence: []*snap.SideInfo{siCurrent}, 3820 Current: siCurrent.Revision, 3821 Active: true, 3822 }) 3823 3824 t := s.state.NewTask("update-gadget-assets", "update gadget") 3825 t.Set("snap-setup", &snapstate.SnapSetup{ 3826 SideInfo: si, 3827 Type: snap.TypeGadget, 3828 }) 3829 chg := s.state.NewChange("dummy", "...") 3830 chg.AddTask(t) 3831 3832 s.state.Unlock() 3833 3834 for i := 0; i < 6; i++ { 3835 s.se.Ensure() 3836 s.se.Wait() 3837 } 3838 3839 s.state.Lock() 3840 defer s.state.Unlock() 3841 c.Assert(chg.IsReady(), Equals, true) 3842 c.Check(t.Status(), Equals, state.DoneStatus) 3843 c.Check(s.restartRequests, HasLen, 1) 3844 c.Check(updaterForStructureCalls, Equals, 1) 3845 } 3846 3847 func (s *deviceMgrSuite) TestCurrentAndUpdateInfo(c *C) { 3848 siCurrent := &snap.SideInfo{ 3849 RealName: "foo-gadget", 3850 Revision: snap.R(33), 3851 SnapID: "foo-id", 3852 } 3853 si := &snap.SideInfo{ 3854 RealName: "foo-gadget", 3855 Revision: snap.R(34), 3856 SnapID: "foo-id", 3857 } 3858 3859 s.state.Lock() 3860 defer s.state.Unlock() 3861 3862 snapsup := &snapstate.SnapSetup{ 3863 SideInfo: si, 3864 Type: snap.TypeGadget, 3865 } 3866 3867 current, update, err := devicestate.GadgetCurrentAndUpdate(s.state, snapsup) 3868 c.Assert(current, IsNil) 3869 c.Assert(update, IsNil) 3870 c.Assert(err, IsNil) 3871 3872 snapstate.Set(s.state, "foo-gadget", &snapstate.SnapState{ 3873 SnapType: "gadget", 3874 Sequence: []*snap.SideInfo{siCurrent}, 3875 Current: siCurrent.Revision, 3876 Active: true, 3877 }) 3878 3879 // mock current first, but gadget.yaml is still missing 3880 ci := snaptest.MockSnapWithFiles(c, snapYaml, siCurrent, nil) 3881 3882 current, update, err = devicestate.GadgetCurrentAndUpdate(s.state, snapsup) 3883 c.Assert(current, IsNil) 3884 c.Assert(update, IsNil) 3885 c.Assert(err, ErrorMatches, "cannot read current gadget snap details: .*/33/meta/gadget.yaml: no such file or directory") 3886 3887 // drop gadget.yaml for current snap 3888 ioutil.WriteFile(filepath.Join(ci.MountDir(), "meta/gadget.yaml"), []byte(gadgetYaml), 0644) 3889 3890 // update missing snap.yaml 3891 current, update, err = devicestate.GadgetCurrentAndUpdate(s.state, snapsup) 3892 c.Assert(current, IsNil) 3893 c.Assert(update, IsNil) 3894 c.Assert(err, ErrorMatches, "cannot read candidate gadget snap details: cannot find installed snap .* .*/34/meta/snap.yaml") 3895 3896 ui := snaptest.MockSnapWithFiles(c, snapYaml, si, nil) 3897 3898 current, update, err = devicestate.GadgetCurrentAndUpdate(s.state, snapsup) 3899 c.Assert(current, IsNil) 3900 c.Assert(update, IsNil) 3901 c.Assert(err, ErrorMatches, "cannot read candidate gadget snap details: .*/34/meta/gadget.yaml: no such file or directory") 3902 3903 var updateGadgetYaml = ` 3904 volumes: 3905 pc: 3906 bootloader: grub 3907 id: 123 3908 ` 3909 3910 // drop gadget.yaml for update snap 3911 ioutil.WriteFile(filepath.Join(ui.MountDir(), "meta/gadget.yaml"), []byte(updateGadgetYaml), 0644) 3912 3913 current, update, err = devicestate.GadgetCurrentAndUpdate(s.state, snapsup) 3914 c.Assert(err, IsNil) 3915 c.Assert(current, DeepEquals, &gadget.GadgetData{ 3916 Info: &gadget.Info{ 3917 Volumes: map[string]gadget.Volume{ 3918 "pc": { 3919 Bootloader: "grub", 3920 }, 3921 }, 3922 }, 3923 RootDir: ci.MountDir(), 3924 }) 3925 c.Assert(update, DeepEquals, &gadget.GadgetData{ 3926 Info: &gadget.Info{ 3927 Volumes: map[string]gadget.Volume{ 3928 "pc": { 3929 Bootloader: "grub", 3930 ID: "123", 3931 }, 3932 }, 3933 }, 3934 RootDir: ui.MountDir(), 3935 }) 3936 } 3937 3938 func (s *deviceMgrSuite) TestGadgetUpdateBlocksWhenOtherTasks(c *C) { 3939 restore := release.MockOnClassic(true) 3940 defer restore() 3941 3942 s.state.Lock() 3943 defer s.state.Unlock() 3944 3945 tUpdate := s.state.NewTask("update-gadget-assets", "update gadget") 3946 t1 := s.state.NewTask("other-task-1", "other 1") 3947 t2 := s.state.NewTask("other-task-2", "other 2") 3948 3949 // no other running tasks, does not block 3950 c.Assert(devicestate.GadgetUpdateBlocked(tUpdate, nil), Equals, false) 3951 3952 // list of running tasks actually contains ones that are in the 'running' state 3953 t1.SetStatus(state.DoingStatus) 3954 t2.SetStatus(state.UndoingStatus) 3955 // block on any other running tasks 3956 c.Assert(devicestate.GadgetUpdateBlocked(tUpdate, []*state.Task{t1, t2}), Equals, true) 3957 } 3958 3959 func (s *deviceMgrSuite) TestGadgetUpdateBlocksOtherTasks(c *C) { 3960 restore := release.MockOnClassic(true) 3961 defer restore() 3962 3963 s.state.Lock() 3964 defer s.state.Unlock() 3965 3966 tUpdate := s.state.NewTask("update-gadget-assets", "update gadget") 3967 tUpdate.SetStatus(state.DoingStatus) 3968 t1 := s.state.NewTask("other-task-1", "other 1") 3969 t2 := s.state.NewTask("other-task-2", "other 2") 3970 3971 // block on any other running tasks 3972 c.Assert(devicestate.GadgetUpdateBlocked(t1, []*state.Task{tUpdate}), Equals, true) 3973 c.Assert(devicestate.GadgetUpdateBlocked(t2, []*state.Task{tUpdate}), Equals, true) 3974 3975 t2.SetStatus(state.UndoingStatus) 3976 // update-gadget should be the only running task, for the sake of 3977 // completeness pretend it's one of many running tasks 3978 c.Assert(devicestate.GadgetUpdateBlocked(t1, []*state.Task{tUpdate, t2}), Equals, true) 3979 3980 // not blocking without gadget update task 3981 c.Assert(devicestate.GadgetUpdateBlocked(t1, []*state.Task{t2}), Equals, false) 3982 }