gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/devicestate/firstboot_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-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 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "strconv" 28 "strings" 29 "time" 30 31 . "gopkg.in/check.v1" 32 "gopkg.in/tomb.v2" 33 34 "github.com/snapcore/snapd/asserts" 35 "github.com/snapcore/snapd/asserts/assertstest" 36 "github.com/snapcore/snapd/asserts/sysdb" 37 "github.com/snapcore/snapd/boot/boottest" 38 "github.com/snapcore/snapd/bootloader" 39 "github.com/snapcore/snapd/bootloader/bootloadertest" 40 "github.com/snapcore/snapd/dirs" 41 "github.com/snapcore/snapd/gadget" 42 "github.com/snapcore/snapd/osutil" 43 "github.com/snapcore/snapd/overlord" 44 "github.com/snapcore/snapd/overlord/assertstate" 45 "github.com/snapcore/snapd/overlord/auth" 46 "github.com/snapcore/snapd/overlord/configstate" 47 "github.com/snapcore/snapd/overlord/configstate/config" 48 "github.com/snapcore/snapd/overlord/devicestate" 49 "github.com/snapcore/snapd/overlord/devicestate/devicestatetest" 50 "github.com/snapcore/snapd/overlord/hookstate" 51 "github.com/snapcore/snapd/overlord/ifacestate" 52 "github.com/snapcore/snapd/overlord/snapstate" 53 "github.com/snapcore/snapd/overlord/state" 54 "github.com/snapcore/snapd/release" 55 "github.com/snapcore/snapd/seed/seedtest" 56 "github.com/snapcore/snapd/snap" 57 "github.com/snapcore/snapd/snap/snaptest" 58 "github.com/snapcore/snapd/sysconfig" 59 "github.com/snapcore/snapd/systemd" 60 "github.com/snapcore/snapd/testutil" 61 "github.com/snapcore/snapd/timings" 62 ) 63 64 type firstBootBaseTest struct { 65 testutil.BaseTest 66 67 systemctl *testutil.MockCmd 68 69 devAcct *asserts.Account 70 71 overlord *overlord.Overlord 72 73 perfTimings timings.Measurer 74 } 75 76 func (t *firstBootBaseTest) setupBaseTest(c *C, s *seedtest.SeedSnaps) { 77 t.BaseTest.SetUpTest(c) 78 79 tempdir := c.MkDir() 80 dirs.SetRootDir(tempdir) 81 t.AddCleanup(func() { dirs.SetRootDir("/") }) 82 83 t.AddCleanup(release.MockOnClassic(false)) 84 85 restore := osutil.MockMountInfo("") 86 t.AddCleanup(restore) 87 88 // mock the world! 89 err := os.MkdirAll(filepath.Join(dirs.SnapSeedDir, "snaps"), 0755) 90 c.Assert(err, IsNil) 91 92 err = os.MkdirAll(dirs.SnapServicesDir, 0755) 93 c.Assert(err, IsNil) 94 os.Setenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS", "1") 95 t.AddCleanup(func() { os.Unsetenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") }) 96 t.systemctl = testutil.MockCommand(c, "systemctl", "") 97 t.AddCleanup(t.systemctl.Restore) 98 99 s.SetupAssertSigning("canonical") 100 s.Brands.Register("my-brand", brandPrivKey, map[string]interface{}{ 101 "verification": "verified", 102 }) 103 104 t.devAcct = assertstest.NewAccount(s.StoreSigning, "developer", map[string]interface{}{ 105 "account-id": "developerid", 106 }, "") 107 108 t.AddCleanup(sysdb.InjectTrusted([]asserts.Assertion{s.StoreSigning.TrustedKey})) 109 t.AddCleanup(ifacestate.MockSecurityBackends(nil)) 110 111 t.perfTimings = timings.New(nil) 112 113 r := devicestate.MockCloudInitStatus(func() (sysconfig.CloudInitState, error) { 114 return sysconfig.CloudInitRestrictedBySnapd, nil 115 }) 116 t.AddCleanup(r) 117 } 118 119 // startOverlord will setup and create a new overlord, note that it will not 120 // stop any pre-existing overlord and it will be overwritten, for more fine 121 // control create your own overlord 122 // also note that as long as you don't run overlord.Loop() this is safe to call 123 // multiple times to clear overlord state, if you call Loop() call Stop() on 124 // your own before calling this again 125 func (t *firstBootBaseTest) startOverlord(c *C) { 126 ovld, err := overlord.New(nil) 127 c.Assert(err, IsNil) 128 ovld.InterfaceManager().DisableUDevMonitor() 129 // avoid gadget preload in the general tests cases 130 // it requires a proper seed to be available 131 devicestate.EarlyConfig = func(st *state.State, preloadGadget func() (sysconfig.Device, *gadget.Info, error)) error { 132 return nil 133 } 134 t.AddCleanup(func() { devicestate.EarlyConfig = nil }) 135 t.overlord = ovld 136 c.Assert(ovld.StartUp(), IsNil) 137 138 // don't actually try to talk to the store on snapstate.Ensure 139 // needs doing after the call to devicestate.Manager (which happens in 140 // overlord.New) 141 snapstate.CanAutoRefresh = nil 142 } 143 144 type firstBoot16BaseTest struct { 145 *firstBootBaseTest 146 // TestingSeed16 helps populating seeds (it provides 147 // MakeAssertedSnap, WriteAssertions etc.) for tests. 148 *seedtest.TestingSeed16 149 } 150 151 func (t *firstBoot16BaseTest) setup16BaseTest(c *C, bt *firstBootBaseTest) { 152 t.firstBootBaseTest = bt 153 t.setupBaseTest(c, &t.TestingSeed16.SeedSnaps) 154 } 155 156 type firstBoot16Suite struct { 157 firstBootBaseTest 158 firstBoot16BaseTest 159 } 160 161 var _ = Suite(&firstBoot16Suite{}) 162 163 func (s *firstBoot16Suite) SetUpTest(c *C) { 164 s.TestingSeed16 = &seedtest.TestingSeed16{} 165 s.setup16BaseTest(c, &s.firstBootBaseTest) 166 167 s.startOverlord(c) 168 169 s.SeedDir = dirs.SnapSeedDir 170 171 err := os.MkdirAll(filepath.Join(dirs.SnapSeedDir, "assertions"), 0755) 172 c.Assert(err, IsNil) 173 174 // mock the snap mapper as core here to make sure that other tests don't 175 // set it inadvertently to the snapd mapper and break the 16 tests 176 s.AddCleanup(ifacestate.MockSnapMapper(&ifacestate.CoreCoreSystemMapper{})) 177 } 178 179 func checkTrivialSeeding(c *C, tsAll []*state.TaskSet) { 180 // run internal core config and mark seeded 181 c.Check(tsAll, HasLen, 2) 182 tasks := tsAll[0].Tasks() 183 c.Check(tasks, HasLen, 1) 184 c.Assert(tasks[0].Kind(), Equals, "run-hook") 185 var hooksup hookstate.HookSetup 186 err := tasks[0].Get("hook-setup", &hooksup) 187 c.Assert(err, IsNil) 188 c.Check(hooksup.Hook, Equals, "configure") 189 c.Check(hooksup.Snap, Equals, "core") 190 tasks = tsAll[1].Tasks() 191 c.Check(tasks, HasLen, 1) 192 c.Check(tasks[0].Kind(), Equals, "mark-seeded") 193 } 194 195 func modelHeaders(modelStr string, reqSnaps ...string) map[string]interface{} { 196 headers := map[string]interface{}{ 197 "architecture": "amd64", 198 "store": "canonical", 199 } 200 if strings.HasSuffix(modelStr, "-classic") { 201 headers["classic"] = "true" 202 } else { 203 headers["kernel"] = "pc-kernel" 204 headers["gadget"] = "pc" 205 } 206 if len(reqSnaps) != 0 { 207 reqs := make([]interface{}, len(reqSnaps)) 208 for i, req := range reqSnaps { 209 reqs[i] = req 210 } 211 headers["required-snaps"] = reqs 212 } 213 return headers 214 } 215 216 func (s *firstBoot16BaseTest) makeModelAssertionChain(c *C, modName string, extraHeaders map[string]interface{}, reqSnaps ...string) []asserts.Assertion { 217 return s.MakeModelAssertionChain("my-brand", modName, modelHeaders(modName, reqSnaps...), extraHeaders) 218 } 219 220 func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicNoop(c *C) { 221 restore := release.MockOnClassic(true) 222 defer restore() 223 224 st := s.overlord.State() 225 st.Lock() 226 defer st.Unlock() 227 228 err := os.Remove(filepath.Join(dirs.SnapSeedDir, "assertions")) 229 c.Assert(err, IsNil) 230 231 _, _, err = devicestate.PreloadGadget(s.overlord.DeviceManager()) 232 c.Check(err, Equals, state.ErrNoState) 233 234 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 235 c.Assert(err, IsNil) 236 checkTrivialSeeding(c, tsAll) 237 238 // already set the fallback model 239 240 // verify that the model was added 241 db := assertstate.DB(st) 242 as, err := db.Find(asserts.ModelType, map[string]string{ 243 "series": "16", 244 "brand-id": "generic", 245 "model": "generic-classic", 246 }) 247 c.Assert(err, IsNil) 248 _, ok := as.(*asserts.Model) 249 c.Check(ok, Equals, true) 250 251 ds, err := devicestatetest.Device(st) 252 c.Assert(err, IsNil) 253 c.Check(ds.Brand, Equals, "generic") 254 c.Check(ds.Model, Equals, "generic-classic") 255 } 256 257 func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicNoSeedYaml(c *C) { 258 restore := release.MockOnClassic(true) 259 defer restore() 260 261 ovld, err := overlord.New(nil) 262 c.Assert(err, IsNil) 263 st := ovld.State() 264 265 // add the model assertion and its chain 266 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil) 267 s.WriteAssertions("model.asserts", assertsChain...) 268 269 st.Lock() 270 defer st.Unlock() 271 272 _, _, err = devicestate.PreloadGadget(ovld.DeviceManager()) 273 c.Check(err, Equals, state.ErrNoState) 274 275 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 276 c.Assert(err, IsNil) 277 checkTrivialSeeding(c, tsAll) 278 279 ds, err := devicestatetest.Device(st) 280 c.Assert(err, IsNil) 281 c.Check(ds.Brand, Equals, "my-brand") 282 c.Check(ds.Model, Equals, "my-model-classic") 283 } 284 285 func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicEmptySeedYaml(c *C) { 286 restore := release.MockOnClassic(true) 287 defer restore() 288 289 ovld, err := overlord.New(nil) 290 c.Assert(err, IsNil) 291 st := ovld.State() 292 293 // add the model assertion and its chain 294 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil) 295 s.WriteAssertions("model.asserts", assertsChain...) 296 297 // create an empty seed.yaml 298 err = ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), nil, 0644) 299 c.Assert(err, IsNil) 300 301 st.Lock() 302 defer st.Unlock() 303 304 _, err = devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 305 c.Assert(err, ErrorMatches, "cannot proceed, no snaps to seed") 306 st.Unlock() 307 st.Lock() 308 // note, cannot use st.Tasks() here as it filters out tasks with no change 309 c.Check(st.TaskCount(), Equals, 0) 310 } 311 312 func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicNoSeedYamlWithCloudInstanceData(c *C) { 313 restore := release.MockOnClassic(true) 314 defer restore() 315 316 st := s.overlord.State() 317 318 // add the model assertion and its chain 319 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil) 320 s.WriteAssertions("model.asserts", assertsChain...) 321 322 // write cloud instance data 323 const instData = `{ 324 "v1": { 325 "availability-zone": "us-east-2b", 326 "cloud-name": "aws", 327 "instance-id": "i-03bdbe0d89f4c8ec9", 328 "local-hostname": "ip-10-41-41-143", 329 "region": "us-east-2" 330 } 331 }` 332 err := os.MkdirAll(filepath.Dir(dirs.CloudInstanceDataFile), 0755) 333 c.Assert(err, IsNil) 334 err = ioutil.WriteFile(dirs.CloudInstanceDataFile, []byte(instData), 0600) 335 c.Assert(err, IsNil) 336 337 st.Lock() 338 defer st.Unlock() 339 340 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 341 c.Assert(err, IsNil) 342 checkTrivialSeeding(c, tsAll) 343 344 ds, err := devicestatetest.Device(st) 345 c.Assert(err, IsNil) 346 c.Check(ds.Brand, Equals, "my-brand") 347 c.Check(ds.Model, Equals, "my-model-classic") 348 349 // now run the change and check the result 350 // use the expected kind otherwise settle will start another one 351 chg := st.NewChange("seed", "run the populate from seed changes") 352 for _, ts := range tsAll { 353 chg.AddAll(ts) 354 } 355 c.Assert(st.Changes(), HasLen, 1) 356 357 // avoid device reg 358 chg1 := st.NewChange("become-operational", "init device") 359 chg1.SetStatus(state.DoingStatus) 360 361 st.Unlock() 362 err = s.overlord.Settle(settleTimeout) 363 st.Lock() 364 c.Assert(chg.Err(), IsNil) 365 c.Assert(err, IsNil) 366 367 // check marked seeded 368 var seeded bool 369 err = st.Get("seeded", &seeded) 370 c.Assert(err, IsNil) 371 c.Check(seeded, Equals, true) 372 373 // check captured cloud information 374 tr := config.NewTransaction(st) 375 var cloud auth.CloudInfo 376 err = tr.Get("core", "cloud", &cloud) 377 c.Assert(err, IsNil) 378 c.Check(cloud.Name, Equals, "aws") 379 c.Check(cloud.Region, Equals, "us-east-2") 380 c.Check(cloud.AvailabilityZone, Equals, "us-east-2b") 381 } 382 383 func (s *firstBoot16Suite) TestPopulateFromSeedErrorsOnState(c *C) { 384 st := s.overlord.State() 385 st.Lock() 386 defer st.Unlock() 387 st.Set("seeded", true) 388 389 _, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 390 c.Assert(err, ErrorMatches, "cannot populate state: already seeded") 391 // note, cannot use st.Tasks() here as it filters out tasks with no change 392 c.Check(st.TaskCount(), Equals, 0) 393 } 394 395 func (s *firstBoot16BaseTest) makeCoreSnaps(c *C, extraGadgetYaml string) (coreFname, kernelFname, gadgetFname string) { 396 files := [][]string{} 397 if strings.Contains(extraGadgetYaml, "defaults:") { 398 files = [][]string{{"meta/hooks/configure", ""}} 399 } 400 401 // put core snap into the SnapBlobDir 402 snapYaml := `name: core 403 version: 1.0 404 type: os` 405 coreFname, coreDecl, coreRev := s.MakeAssertedSnap(c, snapYaml, files, snap.R(1), "canonical") 406 s.WriteAssertions("core.asserts", coreRev, coreDecl) 407 408 // put kernel snap into the SnapBlobDir 409 snapYaml = `name: pc-kernel 410 version: 1.0 411 type: kernel` 412 kernelFname, kernelDecl, kernelRev := s.MakeAssertedSnap(c, snapYaml, files, snap.R(1), "canonical") 413 s.WriteAssertions("kernel.asserts", kernelRev, kernelDecl) 414 415 gadgetYaml := ` 416 volumes: 417 volume-id: 418 bootloader: grub 419 ` 420 gadgetYaml += extraGadgetYaml 421 422 // put gadget snap into the SnapBlobDir 423 files = append(files, []string{"meta/gadget.yaml", gadgetYaml}) 424 425 snapYaml = `name: pc 426 version: 1.0 427 type: gadget` 428 gadgetFname, gadgetDecl, gadgetRev := s.MakeAssertedSnap(c, snapYaml, files, snap.R(1), "canonical") 429 s.WriteAssertions("gadget.asserts", gadgetRev, gadgetDecl) 430 431 return coreFname, kernelFname, gadgetFname 432 } 433 434 func checkOrder(c *C, tsAll []*state.TaskSet, snaps ...string) { 435 matched := 0 436 var prevTask *state.Task 437 for i, ts := range tsAll { 438 task0 := ts.Tasks()[0] 439 waitTasks := task0.WaitTasks() 440 if i == 0 { 441 c.Check(waitTasks, HasLen, 0) 442 } else { 443 c.Check(waitTasks, testutil.Contains, prevTask) 444 } 445 prevTask = task0 446 if task0.Kind() != "prerequisites" { 447 continue 448 } 449 snapsup, err := snapstate.TaskSnapSetup(task0) 450 c.Assert(err, IsNil, Commentf("%#v", task0)) 451 c.Check(snapsup.InstanceName(), Equals, snaps[matched]) 452 matched++ 453 } 454 c.Check(matched, Equals, len(snaps)) 455 } 456 457 func checkSeedTasks(c *C, tsAll []*state.TaskSet) { 458 // the last taskset is just mark-seeded 459 lastTasks := tsAll[len(tsAll)-1].Tasks() 460 c.Check(lastTasks, HasLen, 1) 461 markSeededTask := lastTasks[0] 462 c.Check(markSeededTask.Kind(), Equals, "mark-seeded") 463 // and mark-seeded must wait for the other tasks 464 prevTasks := tsAll[len(tsAll)-2].Tasks() 465 otherTask := prevTasks[len(prevTasks)-1] 466 c.Check(markSeededTask.WaitTasks(), testutil.Contains, otherTask) 467 } 468 469 func (s *firstBoot16BaseTest) makeSeedChange(c *C, st *state.State, opts *devicestate.PopulateStateFromSeedOptions, 470 checkTasks func(c *C, tsAll []*state.TaskSet), checkOrder func(c *C, tsAll []*state.TaskSet, snaps ...string)) (*state.Change, *asserts.Model) { 471 472 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "") 473 474 s.WriteAssertions("developer.account", s.devAcct) 475 476 // put a firstboot snap into the SnapBlobDir 477 snapYaml := `name: foo 478 version: 1.0` 479 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 480 s.WriteAssertions("foo.snap-declaration", fooDecl) 481 s.WriteAssertions("foo.snap-revision", fooRev) 482 483 // put a firstboot local snap into the SnapBlobDir 484 snapYaml = `name: local 485 version: 1.0` 486 mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml, nil) 487 targetSnapFile2 := filepath.Join(dirs.SnapSeedDir, "snaps", filepath.Base(mockSnapFile)) 488 err := os.Rename(mockSnapFile, targetSnapFile2) 489 c.Assert(err, IsNil) 490 491 // add a model assertion and its chain 492 var model *asserts.Model = nil 493 assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo") 494 for i, as := range assertsChain { 495 if as.Type() == asserts.ModelType { 496 model = as.(*asserts.Model) 497 } 498 s.WriteAssertions(strconv.Itoa(i), as) 499 } 500 c.Assert(model, NotNil) 501 502 // create a seed.yaml 503 content := []byte(fmt.Sprintf(` 504 snaps: 505 - name: core 506 file: %s 507 - name: pc-kernel 508 file: %s 509 - name: pc 510 file: %s 511 - name: foo 512 file: %s 513 devmode: true 514 contact: mailto:some.guy@example.com 515 - name: local 516 unasserted: true 517 file: %s 518 `, coreFname, kernelFname, gadgetFname, fooFname, filepath.Base(targetSnapFile2))) 519 err = ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 520 c.Assert(err, IsNil) 521 522 // run the firstboot stuff 523 st.Lock() 524 defer st.Unlock() 525 526 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, opts, s.perfTimings) 527 c.Assert(err, IsNil) 528 529 // now run the change and check the result 530 // use the expected kind otherwise settle with start another one 531 chg := st.NewChange("seed", "run the populate from seed changes") 532 for _, ts := range tsAll { 533 chg.AddAll(ts) 534 } 535 c.Assert(st.Changes(), HasLen, 1) 536 537 checkOrder(c, tsAll, "core", "pc-kernel", "pc", "foo", "local") 538 checkTasks(c, tsAll) 539 540 // avoid device reg 541 chg1 := st.NewChange("become-operational", "init device") 542 chg1.SetStatus(state.DoingStatus) 543 544 return chg, model 545 } 546 547 func (s *firstBoot16Suite) TestPopulateFromSeedHappy(c *C) { 548 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 549 bootloader.Force(bloader) 550 defer bootloader.Force(nil) 551 bloader.SetBootKernel("pc-kernel_1.snap") 552 bloader.SetBootBase("core_1.snap") 553 554 st := s.overlord.State() 555 chg, model := s.makeSeedChange(c, st, nil, checkSeedTasks, checkOrder) 556 err := s.overlord.Settle(settleTimeout) 557 c.Assert(err, IsNil) 558 559 st.Lock() 560 defer st.Unlock() 561 562 c.Assert(chg.Err(), IsNil) 563 564 // and check the snap got correctly installed 565 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) 566 567 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "local", "x1", "meta", "snap.yaml")), Equals, true) 568 569 // verify 570 r, err := os.Open(dirs.SnapStateFile) 571 c.Assert(err, IsNil) 572 state, err := state.ReadState(nil, r) 573 c.Assert(err, IsNil) 574 575 state.Lock() 576 defer state.Unlock() 577 // check core, kernel, gadget 578 _, err = snapstate.CurrentInfo(state, "core") 579 c.Assert(err, IsNil) 580 _, err = snapstate.CurrentInfo(state, "pc-kernel") 581 c.Assert(err, IsNil) 582 _, err = snapstate.CurrentInfo(state, "pc") 583 c.Assert(err, IsNil) 584 585 // ensure required flag is set on all essential snaps 586 var snapst snapstate.SnapState 587 for _, reqName := range []string{"core", "pc-kernel", "pc"} { 588 err = snapstate.Get(state, reqName, &snapst) 589 c.Assert(err, IsNil) 590 c.Assert(snapst.Required, Equals, true, Commentf("required not set for %v", reqName)) 591 } 592 593 // check foo 594 info, err := snapstate.CurrentInfo(state, "foo") 595 c.Assert(err, IsNil) 596 c.Assert(info.SnapID, Equals, "foodidididididididididididididid") 597 c.Assert(info.Revision, Equals, snap.R(128)) 598 c.Assert(info.Contact(), Equals, "mailto:some.guy@example.com") 599 pubAcct, err := assertstate.Publisher(st, info.SnapID) 600 c.Assert(err, IsNil) 601 c.Check(pubAcct.AccountID(), Equals, "developerid") 602 603 err = snapstate.Get(state, "foo", &snapst) 604 c.Assert(err, IsNil) 605 c.Assert(snapst.DevMode, Equals, true) 606 c.Assert(snapst.Required, Equals, true) 607 608 // check local 609 info, err = snapstate.CurrentInfo(state, "local") 610 c.Assert(err, IsNil) 611 c.Assert(info.SnapID, Equals, "") 612 c.Assert(info.Revision, Equals, snap.R("x1")) 613 614 var snapst2 snapstate.SnapState 615 err = snapstate.Get(state, "local", &snapst2) 616 c.Assert(err, IsNil) 617 c.Assert(snapst2.Required, Equals, false) 618 619 // and ensure state is now considered seeded 620 var seeded bool 621 err = state.Get("seeded", &seeded) 622 c.Assert(err, IsNil) 623 c.Check(seeded, Equals, true) 624 625 // check we set seed-time 626 var seedTime time.Time 627 err = state.Get("seed-time", &seedTime) 628 c.Assert(err, IsNil) 629 c.Check(seedTime.IsZero(), Equals, false) 630 631 var whatseeded []devicestate.SeededSystem 632 err = state.Get("seeded-systems", &whatseeded) 633 c.Assert(err, IsNil) 634 c.Assert(whatseeded, DeepEquals, []devicestate.SeededSystem{{ 635 System: "", 636 Model: "my-model", 637 BrandID: "my-brand", 638 Revision: model.Revision(), 639 Timestamp: model.Timestamp(), 640 SeedTime: seedTime, 641 }}) 642 } 643 644 func (s *firstBoot16Suite) TestPopulateFromSeedMissingBootloader(c *C) { 645 st0 := s.overlord.State() 646 st0.Lock() 647 db := assertstate.DB(st0) 648 st0.Unlock() 649 650 // we run only with the relevant managers to produce the error 651 // situation 652 o := overlord.Mock() 653 st := o.State() 654 snapmgr, err := snapstate.Manager(st, o.TaskRunner()) 655 c.Assert(err, IsNil) 656 o.AddManager(snapmgr) 657 658 ifacemgr, err := ifacestate.Manager(st, nil, o.TaskRunner(), nil, nil) 659 c.Assert(err, IsNil) 660 o.AddManager(ifacemgr) 661 c.Assert(o.StartUp(), IsNil) 662 663 hookMgr, err := hookstate.Manager(st, o.TaskRunner()) 664 c.Assert(err, IsNil) 665 _, err = devicestate.Manager(st, hookMgr, o.TaskRunner(), nil) 666 c.Assert(err, IsNil) 667 668 st.Lock() 669 assertstate.ReplaceDB(st, db.(*asserts.Database)) 670 st.Unlock() 671 672 o.AddManager(o.TaskRunner()) 673 674 chg, _ := s.makeSeedChange(c, st, nil, checkSeedTasks, checkOrder) 675 676 se := o.StateEngine() 677 // we cannot use Settle because the Change will not become Clean 678 // under the subset of managers 679 for i := 0; i < 25 && !chg.IsReady(); i++ { 680 se.Ensure() 681 se.Wait() 682 } 683 684 st.Lock() 685 defer st.Unlock() 686 c.Assert(chg.Err(), ErrorMatches, `(?s).* cannot determine bootloader.*`) 687 } 688 689 func (s *firstBoot16Suite) TestPopulateFromSeedHappyMultiAssertsFiles(c *C) { 690 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 691 bootloader.Force(bloader) 692 defer bootloader.Force(nil) 693 bloader.SetBootKernel("pc-kernel_1.snap") 694 bloader.SetBootBase("core_1.snap") 695 696 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "") 697 698 // put a firstboot snap into the SnapBlobDir 699 snapYaml := `name: foo 700 version: 1.0` 701 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 702 s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl) 703 704 // put a 2nd firstboot snap into the SnapBlobDir 705 snapYaml = `name: bar 706 version: 1.0` 707 barFname, barDecl, barRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(65), "developerid") 708 s.WriteAssertions("bar.asserts", s.devAcct, barDecl, barRev) 709 710 // add a model assertion and its chain 711 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 712 s.WriteAssertions("model.asserts", assertsChain...) 713 714 // create a seed.yaml 715 content := []byte(fmt.Sprintf(` 716 snaps: 717 - name: core 718 file: %s 719 - name: pc-kernel 720 file: %s 721 - name: pc 722 file: %s 723 - name: foo 724 file: %s 725 - name: bar 726 file: %s 727 `, coreFname, kernelFname, gadgetFname, fooFname, barFname)) 728 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 729 c.Assert(err, IsNil) 730 731 // run the firstboot stuff 732 st := s.overlord.State() 733 st.Lock() 734 defer st.Unlock() 735 736 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 737 c.Assert(err, IsNil) 738 // use the expected kind otherwise settle with start another one 739 chg := st.NewChange("seed", "run the populate from seed changes") 740 for _, ts := range tsAll { 741 chg.AddAll(ts) 742 } 743 c.Assert(st.Changes(), HasLen, 1) 744 745 // avoid device reg 746 chg1 := st.NewChange("become-operational", "init device") 747 chg1.SetStatus(state.DoingStatus) 748 749 st.Unlock() 750 err = s.overlord.Settle(settleTimeout) 751 st.Lock() 752 c.Assert(chg.Err(), IsNil) 753 c.Assert(err, IsNil) 754 755 // and check the snap got correctly installed 756 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) 757 758 // and check the snap got correctly installed 759 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "bar", "65", "meta", "snap.yaml")), Equals, true) 760 761 // verify 762 r, err := os.Open(dirs.SnapStateFile) 763 c.Assert(err, IsNil) 764 state, err := state.ReadState(nil, r) 765 c.Assert(err, IsNil) 766 767 state.Lock() 768 defer state.Unlock() 769 // check foo 770 info, err := snapstate.CurrentInfo(state, "foo") 771 c.Assert(err, IsNil) 772 c.Check(info.SnapID, Equals, "foodidididididididididididididid") 773 c.Check(info.Revision, Equals, snap.R(128)) 774 pubAcct, err := assertstate.Publisher(st, info.SnapID) 775 c.Assert(err, IsNil) 776 c.Check(pubAcct.AccountID(), Equals, "developerid") 777 778 // check bar 779 info, err = snapstate.CurrentInfo(state, "bar") 780 c.Assert(err, IsNil) 781 c.Check(info.SnapID, Equals, "bardidididididididididididididid") 782 c.Check(info.Revision, Equals, snap.R(65)) 783 pubAcct, err = assertstate.Publisher(st, info.SnapID) 784 c.Assert(err, IsNil) 785 c.Check(pubAcct.AccountID(), Equals, "developerid") 786 } 787 788 func (s *firstBoot16Suite) TestPopulateFromSeedConfigureHappy(c *C) { 789 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 790 bootloader.Force(bloader) 791 defer bootloader.Force(nil) 792 bloader.SetBootKernel("pc-kernel_1.snap") 793 bloader.SetBootBase("core_1.snap") 794 795 const defaultsYaml = ` 796 defaults: 797 foodidididididididididididididid: 798 foo-cfg: foo. 799 99T7MUlRhtI3U0QFgl5mXXESAiSwt776: # core 800 core-cfg: core_cfg_defl 801 pckernelidididididididididididid: 802 pc-kernel-cfg: pc-kernel_cfg_defl 803 pcididididididididididididididid: 804 pc-cfg: pc_cfg_defl 805 ` 806 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, defaultsYaml) 807 808 s.WriteAssertions("developer.account", s.devAcct) 809 810 // put a firstboot snap into the SnapBlobDir 811 files := [][]string{{"meta/hooks/configure", ""}} 812 snapYaml := `name: foo 813 version: 1.0` 814 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, files, snap.R(128), "developerid") 815 s.WriteAssertions("foo.asserts", fooDecl, fooRev) 816 817 // add a model assertion and its chain 818 assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo") 819 s.WriteAssertions("model.asserts", assertsChain...) 820 821 // create a seed.yaml 822 content := []byte(fmt.Sprintf(` 823 snaps: 824 - name: core 825 file: %s 826 - name: pc-kernel 827 file: %s 828 - name: pc 829 file: %s 830 - name: foo 831 file: %s 832 `, coreFname, kernelFname, gadgetFname, fooFname)) 833 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 834 c.Assert(err, IsNil) 835 836 // run the firstboot stuff 837 st := s.overlord.State() 838 st.Lock() 839 defer st.Unlock() 840 841 dev, gi, err := devicestate.PreloadGadget(s.overlord.DeviceManager()) 842 c.Assert(err, IsNil) 843 c.Check(gi.Defaults, HasLen, 4) 844 c.Check(dev.RunMode(), Equals, true) 845 c.Check(dev.Classic(), Equals, false) 846 c.Check(dev.HasModeenv(), Equals, false) 847 c.Check(dev.Kernel(), Equals, "pc-kernel") 848 849 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 850 c.Assert(err, IsNil) 851 852 checkSeedTasks(c, tsAll) 853 854 // now run the change and check the result 855 // use the expected kind otherwise settle with start another one 856 chg := st.NewChange("seed", "run the populate from seed changes") 857 for _, ts := range tsAll { 858 chg.AddAll(ts) 859 } 860 c.Assert(st.Changes(), HasLen, 1) 861 862 var configured []string 863 hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) { 864 ctx.Lock() 865 defer ctx.Unlock() 866 // we have a gadget at this point(s) 867 ok, err := snapstate.HasSnapOfType(st, snap.TypeGadget) 868 c.Check(err, IsNil) 869 c.Check(ok, Equals, true) 870 configured = append(configured, ctx.InstanceName()) 871 return nil, nil 872 } 873 874 rhk := hookstate.MockRunHook(hookInvoke) 875 defer rhk() 876 877 // ensure we have something that captures the core config 878 restore := configstate.MockConfigcoreRun(func(sysconfig.Device, config.Conf) error { 879 configured = append(configured, "configcore") 880 return nil 881 }) 882 defer restore() 883 884 // avoid device reg 885 chg1 := st.NewChange("become-operational", "init device") 886 chg1.SetStatus(state.DoingStatus) 887 888 st.Unlock() 889 err = s.overlord.Settle(settleTimeout) 890 st.Lock() 891 c.Assert(chg.Err(), IsNil) 892 c.Assert(err, IsNil) 893 894 // and check the snap got correctly installed 895 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) 896 897 // verify 898 r, err := os.Open(dirs.SnapStateFile) 899 c.Assert(err, IsNil) 900 state, err := state.ReadState(nil, r) 901 c.Assert(err, IsNil) 902 903 state.Lock() 904 defer state.Unlock() 905 tr := config.NewTransaction(state) 906 var val string 907 908 // check core, kernel, gadget 909 _, err = snapstate.CurrentInfo(state, "core") 910 c.Assert(err, IsNil) 911 err = tr.Get("core", "core-cfg", &val) 912 c.Assert(err, IsNil) 913 c.Check(val, Equals, "core_cfg_defl") 914 915 _, err = snapstate.CurrentInfo(state, "pc-kernel") 916 c.Assert(err, IsNil) 917 err = tr.Get("pc-kernel", "pc-kernel-cfg", &val) 918 c.Assert(err, IsNil) 919 c.Check(val, Equals, "pc-kernel_cfg_defl") 920 921 _, err = snapstate.CurrentInfo(state, "pc") 922 c.Assert(err, IsNil) 923 err = tr.Get("pc", "pc-cfg", &val) 924 c.Assert(err, IsNil) 925 c.Check(val, Equals, "pc_cfg_defl") 926 927 // check foo 928 info, err := snapstate.CurrentInfo(state, "foo") 929 c.Assert(err, IsNil) 930 c.Assert(info.SnapID, Equals, "foodidididididididididididididid") 931 c.Assert(info.Revision, Equals, snap.R(128)) 932 pubAcct, err := assertstate.Publisher(st, info.SnapID) 933 c.Assert(err, IsNil) 934 c.Check(pubAcct.AccountID(), Equals, "developerid") 935 936 // check foo config 937 err = tr.Get("foo", "foo-cfg", &val) 938 c.Assert(err, IsNil) 939 c.Check(val, Equals, "foo.") 940 941 c.Check(configured, DeepEquals, []string{"configcore", "pc-kernel", "pc", "foo"}) 942 943 // and ensure state is now considered seeded 944 var seeded bool 945 err = state.Get("seeded", &seeded) 946 c.Assert(err, IsNil) 947 c.Check(seeded, Equals, true) 948 } 949 950 func (s *firstBoot16Suite) TestPopulateFromSeedGadgetConnectHappy(c *C) { 951 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 952 bootloader.Force(bloader) 953 defer bootloader.Force(nil) 954 bloader.SetBootKernel("pc-kernel_1.snap") 955 bloader.SetBootBase("core_1.snap") 956 957 const connectionsYaml = ` 958 connections: 959 - plug: foodidididididididididididididid:network-control 960 ` 961 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, connectionsYaml) 962 963 s.WriteAssertions("developer.account", s.devAcct) 964 965 snapYaml := `name: foo 966 version: 1.0 967 plugs: 968 network-control: 969 ` 970 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 971 s.WriteAssertions("foo.asserts", fooDecl, fooRev) 972 973 // add a model assertion and its chain 974 assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo") 975 s.WriteAssertions("model.asserts", assertsChain...) 976 977 // create a seed.yaml 978 content := []byte(fmt.Sprintf(` 979 snaps: 980 - name: core 981 file: %s 982 - name: pc-kernel 983 file: %s 984 - name: pc 985 file: %s 986 - name: foo 987 file: %s 988 `, coreFname, kernelFname, gadgetFname, fooFname)) 989 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 990 c.Assert(err, IsNil) 991 992 // run the firstboot stuff 993 st := s.overlord.State() 994 st.Lock() 995 defer st.Unlock() 996 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 997 c.Assert(err, IsNil) 998 999 checkSeedTasks(c, tsAll) 1000 1001 // now run the change and check the result 1002 // use the expected kind otherwise settle with start another one 1003 chg := st.NewChange("seed", "run the populate from seed changes") 1004 for _, ts := range tsAll { 1005 chg.AddAll(ts) 1006 } 1007 c.Assert(st.Changes(), HasLen, 1) 1008 1009 // avoid device reg 1010 chg1 := st.NewChange("become-operational", "init device") 1011 chg1.SetStatus(state.DoingStatus) 1012 1013 st.Unlock() 1014 err = s.overlord.Settle(settleTimeout) 1015 st.Lock() 1016 c.Assert(chg.Err(), IsNil) 1017 c.Assert(err, IsNil) 1018 1019 // and check the snap got correctly installed 1020 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) 1021 1022 // verify 1023 r, err := os.Open(dirs.SnapStateFile) 1024 c.Assert(err, IsNil) 1025 state, err := state.ReadState(nil, r) 1026 c.Assert(err, IsNil) 1027 1028 state.Lock() 1029 defer state.Unlock() 1030 1031 // check foo 1032 info, err := snapstate.CurrentInfo(state, "foo") 1033 c.Assert(err, IsNil) 1034 c.Assert(info.SnapID, Equals, "foodidididididididididididididid") 1035 c.Assert(info.Revision, Equals, snap.R(128)) 1036 pubAcct, err := assertstate.Publisher(st, info.SnapID) 1037 c.Assert(err, IsNil) 1038 c.Check(pubAcct.AccountID(), Equals, "developerid") 1039 1040 // check connection 1041 var conns map[string]interface{} 1042 err = state.Get("conns", &conns) 1043 c.Assert(err, IsNil) 1044 c.Check(conns, HasLen, 1) 1045 c.Check(conns, DeepEquals, map[string]interface{}{ 1046 "foo:network-control core:network-control": map[string]interface{}{ 1047 "interface": "network-control", "auto": true, "by-gadget": true, 1048 }, 1049 }) 1050 1051 // and ensure state is now considered seeded 1052 var seeded bool 1053 err = state.Get("seeded", &seeded) 1054 c.Assert(err, IsNil) 1055 c.Check(seeded, Equals, true) 1056 } 1057 1058 func (s *firstBoot16Suite) TestImportAssertionsFromSeedClassicModelMismatch(c *C) { 1059 restore := release.MockOnClassic(true) 1060 defer restore() 1061 1062 ovld, err := overlord.New(nil) 1063 c.Assert(err, IsNil) 1064 st := ovld.State() 1065 1066 // add the odel assertion and its chain 1067 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1068 s.WriteAssertions("model.asserts", assertsChain...) 1069 1070 // import them 1071 st.Lock() 1072 defer st.Unlock() 1073 1074 _, err = devicestate.ImportAssertionsFromSeed(st, "") 1075 c.Assert(err, ErrorMatches, "cannot seed a classic system with an all-snaps model") 1076 } 1077 1078 func (s *firstBoot16Suite) TestImportAssertionsFromSeedAllSnapsModelMismatch(c *C) { 1079 ovld, err := overlord.New(nil) 1080 c.Assert(err, IsNil) 1081 st := ovld.State() 1082 1083 // add the model assertion and its chain 1084 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil) 1085 s.WriteAssertions("model.asserts", assertsChain...) 1086 1087 // import them 1088 st.Lock() 1089 defer st.Unlock() 1090 1091 _, err = devicestate.ImportAssertionsFromSeed(st, "") 1092 c.Assert(err, ErrorMatches, "cannot seed an all-snaps system with a classic model") 1093 } 1094 1095 func (s *firstBoot16Suite) TestLoadDeviceSeed(c *C) { 1096 ovld, err := overlord.New(nil) 1097 c.Assert(err, IsNil) 1098 st := ovld.State() 1099 1100 // add a bunch of assertions (model assertion and its chain) 1101 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1102 for i, as := range assertsChain { 1103 fname := strconv.Itoa(i) 1104 if as.Type() == asserts.ModelType { 1105 fname = "model" 1106 } 1107 s.WriteAssertions(fname, as) 1108 } 1109 1110 // load them 1111 st.Lock() 1112 defer st.Unlock() 1113 1114 deviceSeed, err := devicestate.LoadDeviceSeed(st, "") 1115 c.Assert(err, IsNil) 1116 1117 c.Check(deviceSeed.Model().BrandID(), Equals, "my-brand") 1118 c.Check(deviceSeed.Model().Model(), Equals, "my-model") 1119 1120 // verify that the model was added 1121 db := assertstate.DB(st) 1122 as, err := db.Find(asserts.ModelType, map[string]string{ 1123 "series": "16", 1124 "brand-id": "my-brand", 1125 "model": "my-model", 1126 }) 1127 c.Assert(err, IsNil) 1128 c.Check(as, DeepEquals, deviceSeed.Model()) 1129 } 1130 1131 func (s *firstBoot16Suite) TestLoadDeviceSeedCaching(c *C) { 1132 ovld, err := overlord.New(nil) 1133 c.Assert(err, IsNil) 1134 st := ovld.State() 1135 1136 // add a bunch of assertions (model assertion and its chain) 1137 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1138 for i, as := range assertsChain { 1139 fname := strconv.Itoa(i) 1140 if as.Type() == asserts.ModelType { 1141 fname = "model" 1142 } 1143 s.WriteAssertions(fname, as) 1144 } 1145 1146 // load them 1147 st.Lock() 1148 defer st.Unlock() 1149 1150 deviceSeed, err := devicestate.LoadDeviceSeed(st, "") 1151 c.Assert(err, IsNil) 1152 1153 c.Check(deviceSeed.Model().Model(), Equals, "my-model") 1154 1155 // break the seed 1156 modelAway := filepath.Join(c.MkDir(), "model") 1157 modelFn := filepath.Join(s.AssertsDir(), "model") 1158 err = os.Rename(modelFn, modelAway) 1159 c.Assert(err, IsNil) 1160 1161 // result is still cached 1162 deviceSeed, err = devicestate.LoadDeviceSeed(st, "") 1163 c.Assert(err, IsNil) 1164 1165 c.Check(deviceSeed.Model().Model(), Equals, "my-model") 1166 1167 // unload cached result 1168 devicestate.UnloadDeviceSeed(st) 1169 1170 // error now 1171 _, err1 := devicestate.LoadDeviceSeed(st, "") 1172 c.Assert(err1, ErrorMatches, "seed must have a model assertion") 1173 1174 // refix the seed 1175 err = os.Rename(modelAway, modelFn) 1176 c.Assert(err, IsNil) 1177 1178 // error is also cached 1179 _, err = devicestate.LoadDeviceSeed(st, "") 1180 c.Assert(err, Equals, err1) 1181 1182 // unload cached error 1183 devicestate.UnloadDeviceSeed(st) 1184 1185 // soundness check: loading works again 1186 _, err = devicestate.LoadDeviceSeed(st, "") 1187 c.Assert(err, IsNil) 1188 } 1189 1190 func (s *firstBoot16Suite) TestImportAssertionsFromSeedHappy(c *C) { 1191 ovld, err := overlord.New(nil) 1192 c.Assert(err, IsNil) 1193 st := ovld.State() 1194 1195 // add a bunch of assertions (model assertion and its chain) 1196 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1197 for i, as := range assertsChain { 1198 fname := strconv.Itoa(i) 1199 if as.Type() == asserts.ModelType { 1200 fname = "model" 1201 } 1202 s.WriteAssertions(fname, as) 1203 } 1204 1205 // import them 1206 st.Lock() 1207 defer st.Unlock() 1208 1209 deviceSeed, err := devicestate.ImportAssertionsFromSeed(st, "") 1210 c.Assert(err, IsNil) 1211 c.Assert(deviceSeed, NotNil) 1212 1213 model := deviceSeed.Model() 1214 1215 // verify that the model was added 1216 db := assertstate.DB(st) 1217 as, err := db.Find(asserts.ModelType, map[string]string{ 1218 "series": "16", 1219 "brand-id": "my-brand", 1220 "model": "my-model", 1221 }) 1222 c.Assert(err, IsNil) 1223 _, ok := as.(*asserts.Model) 1224 c.Check(ok, Equals, true) 1225 1226 ds, err := devicestatetest.Device(st) 1227 c.Assert(err, IsNil) 1228 c.Check(ds.Brand, Equals, "my-brand") 1229 c.Check(ds.Model, Equals, "my-model") 1230 1231 c.Check(model.BrandID(), Equals, "my-brand") 1232 c.Check(model.Model(), Equals, "my-model") 1233 } 1234 1235 func (s *firstBoot16Suite) TestImportAssertionsFromSeedMissingSig(c *C) { 1236 st := s.overlord.State() 1237 st.Lock() 1238 defer st.Unlock() 1239 1240 // write out only the model assertion 1241 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1242 for _, as := range assertsChain { 1243 if as.Type() == asserts.ModelType { 1244 s.WriteAssertions("model", as) 1245 break 1246 } 1247 } 1248 1249 // try import and verify that its rejects because other assertions are 1250 // missing 1251 _, err := devicestate.ImportAssertionsFromSeed(st, "") 1252 c.Assert(err, ErrorMatches, "cannot resolve prerequisite assertion: account-key .*") 1253 } 1254 1255 func (s *firstBoot16Suite) TestImportAssertionsFromSeedTwoModelAsserts(c *C) { 1256 st := s.overlord.State() 1257 st.Lock() 1258 defer st.Unlock() 1259 1260 // write out two model assertions 1261 model := s.Brands.Model("my-brand", "my-model", modelHeaders("my-model")) 1262 s.WriteAssertions("model", model) 1263 1264 model2 := s.Brands.Model("my-brand", "my-second-model", modelHeaders("my-second-model")) 1265 s.WriteAssertions("model2", model2) 1266 1267 // try import and verify that its rejects because other assertions are 1268 // missing 1269 _, err := devicestate.ImportAssertionsFromSeed(st, "") 1270 c.Assert(err, ErrorMatches, "cannot have multiple model assertions in seed") 1271 } 1272 1273 func (s *firstBoot16Suite) TestImportAssertionsFromSeedNoModelAsserts(c *C) { 1274 st := s.overlord.State() 1275 st.Lock() 1276 defer st.Unlock() 1277 1278 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1279 for i, as := range assertsChain { 1280 if as.Type() != asserts.ModelType { 1281 s.WriteAssertions(strconv.Itoa(i), as) 1282 } 1283 } 1284 1285 // try import and verify that its rejects because other assertions are 1286 // missing 1287 _, err := devicestate.ImportAssertionsFromSeed(st, "") 1288 c.Assert(err, ErrorMatches, "seed must have a model assertion") 1289 } 1290 1291 type core18SnapsOpts struct { 1292 classic bool 1293 gadget bool 1294 } 1295 1296 func (s *firstBoot16BaseTest) makeCore18Snaps(c *C, opts *core18SnapsOpts) (core18Fn, snapdFn, kernelFn, gadgetFn string) { 1297 if opts == nil { 1298 opts = &core18SnapsOpts{} 1299 } 1300 1301 files := [][]string{} 1302 1303 core18Yaml := `name: core18 1304 version: 1.0 1305 type: base` 1306 core18Fname, core18Decl, core18Rev := s.MakeAssertedSnap(c, core18Yaml, files, snap.R(1), "canonical") 1307 s.WriteAssertions("core18.asserts", core18Rev, core18Decl) 1308 1309 snapdYaml := `name: snapd 1310 version: 1.0 1311 ` 1312 snapdFname, snapdDecl, snapdRev := s.MakeAssertedSnap(c, snapdYaml, nil, snap.R(2), "canonical") 1313 s.WriteAssertions("snapd.asserts", snapdRev, snapdDecl) 1314 1315 var kernelFname string 1316 if !opts.classic { 1317 kernelYaml := `name: pc-kernel 1318 version: 1.0 1319 type: kernel` 1320 fname, kernelDecl, kernelRev := s.MakeAssertedSnap(c, kernelYaml, files, snap.R(1), "canonical") 1321 s.WriteAssertions("kernel.asserts", kernelRev, kernelDecl) 1322 kernelFname = fname 1323 } 1324 1325 if !opts.classic { 1326 gadgetYaml := ` 1327 volumes: 1328 volume-id: 1329 bootloader: grub 1330 ` 1331 files = append(files, []string{"meta/gadget.yaml", gadgetYaml}) 1332 } 1333 1334 var gadgetFname string 1335 if !opts.classic || opts.gadget { 1336 gaYaml := `name: pc 1337 version: 1.0 1338 type: gadget 1339 base: core18 1340 ` 1341 fname, gadgetDecl, gadgetRev := s.MakeAssertedSnap(c, gaYaml, files, snap.R(1), "canonical") 1342 s.WriteAssertions("gadget.asserts", gadgetRev, gadgetDecl) 1343 gadgetFname = fname 1344 } 1345 1346 return core18Fname, snapdFname, kernelFname, gadgetFname 1347 } 1348 1349 func (s *firstBoot16Suite) TestPopulateFromSeedWithBaseHappy(c *C) { 1350 var sysdLog [][]string 1351 systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1352 sysdLog = append(sysdLog, cmd) 1353 return []byte("ActiveState=inactive\n"), nil 1354 }) 1355 defer systemctlRestorer() 1356 1357 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 1358 bootloader.Force(bloader) 1359 defer bootloader.Force(nil) 1360 bloader.SetBootKernel("pc-kernel_1.snap") 1361 bloader.SetBootBase("core18_1.snap") 1362 1363 core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil) 1364 1365 s.WriteAssertions("developer.account", s.devAcct) 1366 1367 // add a model assertion and its chain 1368 assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"}) 1369 s.WriteAssertions("model.asserts", assertsChain...) 1370 1371 // create a seed.yaml 1372 content := []byte(fmt.Sprintf(` 1373 snaps: 1374 - name: snapd 1375 file: %s 1376 - name: core18 1377 file: %s 1378 - name: pc-kernel 1379 file: %s 1380 - name: pc 1381 file: %s 1382 `, snapdFname, core18Fname, kernelFname, gadgetFname)) 1383 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1384 c.Assert(err, IsNil) 1385 1386 // run the firstboot stuff 1387 st := s.overlord.State() 1388 st.Lock() 1389 defer st.Unlock() 1390 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1391 c.Assert(err, IsNil) 1392 1393 checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc") 1394 1395 // now run the change and check the result 1396 // use the expected kind otherwise settle with start another one 1397 chg := st.NewChange("seed", "run the populate from seed changes") 1398 for _, ts := range tsAll { 1399 chg.AddAll(ts) 1400 } 1401 c.Assert(st.Changes(), HasLen, 1) 1402 1403 c.Assert(chg.Err(), IsNil) 1404 1405 // avoid device reg 1406 chg1 := st.NewChange("become-operational", "init device") 1407 chg1.SetStatus(state.DoingStatus) 1408 1409 // run change until it wants to restart 1410 st.Unlock() 1411 err = s.overlord.Settle(settleTimeout) 1412 st.Lock() 1413 c.Assert(err, IsNil) 1414 1415 // at this point the system is "restarting", pretend the restart has 1416 // happened 1417 c.Assert(chg.Status(), Equals, state.DoingStatus) 1418 state.MockRestarting(st, state.RestartUnset) 1419 st.Unlock() 1420 err = s.overlord.Settle(settleTimeout) 1421 st.Lock() 1422 c.Assert(err, IsNil) 1423 c.Assert(chg.Status(), Equals, state.DoneStatus) 1424 1425 // verify 1426 r, err := os.Open(dirs.SnapStateFile) 1427 c.Assert(err, IsNil) 1428 state, err := state.ReadState(nil, r) 1429 c.Assert(err, IsNil) 1430 1431 state.Lock() 1432 defer state.Unlock() 1433 // check snapd, core18, kernel, gadget 1434 _, err = snapstate.CurrentInfo(state, "snapd") 1435 c.Check(err, IsNil) 1436 _, err = snapstate.CurrentInfo(state, "core18") 1437 c.Check(err, IsNil) 1438 _, err = snapstate.CurrentInfo(state, "pc-kernel") 1439 c.Check(err, IsNil) 1440 _, err = snapstate.CurrentInfo(state, "pc") 1441 c.Check(err, IsNil) 1442 1443 // ensure required flag is set on all essential snaps 1444 var snapst snapstate.SnapState 1445 for _, reqName := range []string{"snapd", "core18", "pc-kernel", "pc"} { 1446 err = snapstate.Get(state, reqName, &snapst) 1447 c.Assert(err, IsNil) 1448 c.Assert(snapst.Required, Equals, true, Commentf("required not set for %v", reqName)) 1449 } 1450 1451 // the right systemd commands were run 1452 c.Check(sysdLog, testutil.DeepContains, []string{"start", "usr-lib-snapd.mount"}) 1453 1454 // and ensure state is now considered seeded 1455 var seeded bool 1456 err = state.Get("seeded", &seeded) 1457 c.Assert(err, IsNil) 1458 c.Check(seeded, Equals, true) 1459 1460 // check we set seed-time 1461 var seedTime time.Time 1462 err = state.Get("seed-time", &seedTime) 1463 c.Assert(err, IsNil) 1464 c.Check(seedTime.IsZero(), Equals, false) 1465 } 1466 1467 func (s *firstBoot16Suite) TestPopulateFromSeedOrdering(c *C) { 1468 s.WriteAssertions("developer.account", s.devAcct) 1469 1470 // add a model assertion and its chain 1471 assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"}) 1472 s.WriteAssertions("model.asserts", assertsChain...) 1473 1474 core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil) 1475 1476 snapYaml := `name: snap-req-other-base 1477 version: 1.0 1478 base: other-base 1479 ` 1480 snapFname, snapDecl, snapRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1481 s.WriteAssertions("snap-req-other-base.asserts", s.devAcct, snapRev, snapDecl) 1482 baseYaml := `name: other-base 1483 version: 1.0 1484 type: base 1485 ` 1486 baseFname, baseDecl, baseRev := s.MakeAssertedSnap(c, baseYaml, nil, snap.R(127), "developerid") 1487 s.WriteAssertions("other-base.asserts", s.devAcct, baseRev, baseDecl) 1488 1489 // create a seed.yaml 1490 content := []byte(fmt.Sprintf(` 1491 snaps: 1492 - name: snapd 1493 file: %s 1494 - name: core18 1495 file: %s 1496 - name: pc-kernel 1497 file: %s 1498 - name: pc 1499 file: %s 1500 - name: snap-req-other-base 1501 file: %s 1502 - name: other-base 1503 file: %s 1504 `, snapdFname, core18Fname, kernelFname, gadgetFname, snapFname, baseFname)) 1505 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1506 c.Assert(err, IsNil) 1507 1508 // run the firstboot stuff 1509 st := s.overlord.State() 1510 st.Lock() 1511 defer st.Unlock() 1512 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1513 c.Assert(err, IsNil) 1514 1515 checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc", "other-base", "snap-req-other-base") 1516 } 1517 1518 func (s *firstBoot16Suite) TestFirstbootGadgetBaseModelBaseMismatch(c *C) { 1519 s.WriteAssertions("developer.account", s.devAcct) 1520 1521 // add a model assertion and its chain 1522 assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"}) 1523 s.WriteAssertions("model.asserts", assertsChain...) 1524 1525 core18Fname, snapdFname, kernelFname, _ := s.makeCore18Snaps(c, nil) 1526 // take the gadget without "base: core18" 1527 _, _, gadgetFname := s.makeCoreSnaps(c, "") 1528 1529 // create a seed.yaml 1530 content := []byte(fmt.Sprintf(` 1531 snaps: 1532 - name: snapd 1533 file: %s 1534 - name: core18 1535 file: %s 1536 - name: pc-kernel 1537 file: %s 1538 - name: pc 1539 file: %s 1540 `, snapdFname, core18Fname, kernelFname, gadgetFname)) 1541 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1542 c.Assert(err, IsNil) 1543 1544 // run the firstboot stuff 1545 st := s.overlord.State() 1546 st.Lock() 1547 defer st.Unlock() 1548 1549 _, err = devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1550 c.Assert(err, ErrorMatches, `cannot use gadget snap because its base "core" is different from model base "core18"`) 1551 // note, cannot use st.Tasks() here as it filters out tasks with no change 1552 c.Check(st.TaskCount(), Equals, 0) 1553 } 1554 1555 func (s *firstBoot16Suite) TestPopulateFromSeedWrongContentProviderOrder(c *C) { 1556 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 1557 bootloader.Force(bloader) 1558 defer bootloader.Force(nil) 1559 bloader.SetBootKernel("pc-kernel_1.snap") 1560 bloader.SetBootBase("core_1.snap") 1561 1562 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "") 1563 1564 // a snap that uses content providers 1565 snapYaml := `name: gnome-calculator 1566 version: 1.0 1567 plugs: 1568 gtk-3-themes: 1569 interface: content 1570 default-provider: gtk-common-themes 1571 target: $SNAP/data-dir/themes 1572 ` 1573 calcFname, calcDecl, calcRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1574 s.WriteAssertions("calc.asserts", s.devAcct, calcRev, calcDecl) 1575 1576 // put a 2nd firstboot snap into the SnapBlobDir 1577 snapYaml = `name: gtk-common-themes 1578 version: 1.0 1579 slots: 1580 gtk-3-themes: 1581 interface: content 1582 source: 1583 read: 1584 - $SNAP/share/themes/Adawaita 1585 ` 1586 themesFname, themesDecl, themesRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(65), "developerid") 1587 s.WriteAssertions("themes.asserts", s.devAcct, themesDecl, themesRev) 1588 1589 // add a model assertion and its chain 1590 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1591 s.WriteAssertions("model.asserts", assertsChain...) 1592 1593 // create a seed.yaml 1594 content := []byte(fmt.Sprintf(` 1595 snaps: 1596 - name: core 1597 file: %s 1598 - name: pc-kernel 1599 file: %s 1600 - name: pc 1601 file: %s 1602 - name: gnome-calculator 1603 file: %s 1604 - name: gtk-common-themes 1605 file: %s 1606 `, coreFname, kernelFname, gadgetFname, calcFname, themesFname)) 1607 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1608 c.Assert(err, IsNil) 1609 1610 // run the firstboot stuff 1611 st := s.overlord.State() 1612 st.Lock() 1613 defer st.Unlock() 1614 1615 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1616 c.Assert(err, IsNil) 1617 // use the expected kind otherwise settle with start another one 1618 chg := st.NewChange("seed", "run the populate from seed changes") 1619 for _, ts := range tsAll { 1620 chg.AddAll(ts) 1621 } 1622 c.Assert(st.Changes(), HasLen, 1) 1623 1624 // avoid device reg 1625 chg1 := st.NewChange("become-operational", "init device") 1626 chg1.SetStatus(state.DoingStatus) 1627 1628 st.Unlock() 1629 err = s.overlord.Settle(settleTimeout) 1630 st.Lock() 1631 1632 c.Assert(chg.Err(), IsNil) 1633 c.Assert(err, IsNil) 1634 1635 // verify the result 1636 var conns map[string]interface{} 1637 err = st.Get("conns", &conns) 1638 c.Assert(err, IsNil) 1639 c.Check(conns, HasLen, 1) 1640 conn, hasConn := conns["gnome-calculator:gtk-3-themes gtk-common-themes:gtk-3-themes"] 1641 c.Check(hasConn, Equals, true) 1642 c.Check(conn.(map[string]interface{})["auto"], Equals, true) 1643 c.Check(conn.(map[string]interface{})["interface"], Equals, "content") 1644 } 1645 1646 func (s *firstBoot16Suite) TestPopulateFromSeedMissingBase(c *C) { 1647 s.WriteAssertions("developer.account", s.devAcct) 1648 1649 // add a model assertion and its chain 1650 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1651 s.WriteAssertions("model.asserts", assertsChain...) 1652 1653 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "") 1654 1655 // TODO: this test doesn't particularly need to use a local snap 1656 // local snap with unknown base 1657 snapYaml = `name: local 1658 base: foo 1659 version: 1.0` 1660 mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml, nil) 1661 localFname := filepath.Base(mockSnapFile) 1662 targetSnapFile2 := filepath.Join(dirs.SnapSeedDir, "snaps", localFname) 1663 c.Assert(os.Rename(mockSnapFile, targetSnapFile2), IsNil) 1664 1665 // create a seed.yaml 1666 content := []byte(fmt.Sprintf(` 1667 snaps: 1668 - name: core 1669 file: %s 1670 - name: pc-kernel 1671 file: %s 1672 - name: pc 1673 file: %s 1674 - name: local 1675 unasserted: true 1676 file: %s 1677 `, coreFname, kernelFname, gadgetFname, localFname)) 1678 1679 c.Assert(ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644), IsNil) 1680 1681 // run the firstboot stuff 1682 st := s.overlord.State() 1683 st.Lock() 1684 defer st.Unlock() 1685 _, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1686 c.Assert(err, ErrorMatches, `cannot use snap "local": base "foo" is missing`) 1687 } 1688 1689 func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicWithSnapdOnlyHappy(c *C) { 1690 restore := release.MockOnClassic(true) 1691 defer restore() 1692 1693 var sysdLog [][]string 1694 systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1695 sysdLog = append(sysdLog, cmd) 1696 return []byte("ActiveState=inactive\n"), nil 1697 }) 1698 defer systemctlRestorer() 1699 1700 core18Fname, snapdFname, _, _ := s.makeCore18Snaps(c, &core18SnapsOpts{ 1701 classic: true, 1702 }) 1703 1704 // put a firstboot snap into the SnapBlobDir 1705 snapYaml := `name: foo 1706 version: 1.0 1707 base: core18 1708 ` 1709 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1710 s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl) 1711 1712 // add a model assertion and its chain 1713 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil) 1714 s.WriteAssertions("model.asserts", assertsChain...) 1715 1716 // create a seed.yaml 1717 content := []byte(fmt.Sprintf(` 1718 snaps: 1719 - name: snapd 1720 file: %s 1721 - name: foo 1722 file: %s 1723 - name: core18 1724 file: %s 1725 `, snapdFname, fooFname, core18Fname)) 1726 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1727 c.Assert(err, IsNil) 1728 1729 // run the firstboot stuff 1730 st := s.overlord.State() 1731 st.Lock() 1732 defer st.Unlock() 1733 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1734 c.Assert(err, IsNil) 1735 1736 checkOrder(c, tsAll, "snapd", "core18", "foo") 1737 1738 // now run the change and check the result 1739 // use the expected kind otherwise settle with start another one 1740 chg := st.NewChange("seed", "run the populate from seed changes") 1741 for _, ts := range tsAll { 1742 chg.AddAll(ts) 1743 } 1744 c.Assert(st.Changes(), HasLen, 1) 1745 1746 c.Assert(chg.Err(), IsNil) 1747 1748 // avoid device reg 1749 chg1 := st.NewChange("become-operational", "init device") 1750 chg1.SetStatus(state.DoingStatus) 1751 1752 // run change until it wants to restart 1753 st.Unlock() 1754 err = s.overlord.Settle(settleTimeout) 1755 st.Lock() 1756 c.Assert(err, IsNil) 1757 1758 // at this point the system is "restarting", pretend the restart has 1759 // happened 1760 c.Assert(chg.Status(), Equals, state.DoingStatus) 1761 state.MockRestarting(st, state.RestartUnset) 1762 st.Unlock() 1763 err = s.overlord.Settle(settleTimeout) 1764 st.Lock() 1765 c.Assert(err, IsNil) 1766 c.Assert(chg.Status(), Equals, state.DoneStatus) 1767 1768 // verify 1769 r, err := os.Open(dirs.SnapStateFile) 1770 c.Assert(err, IsNil) 1771 state, err := state.ReadState(nil, r) 1772 c.Assert(err, IsNil) 1773 1774 state.Lock() 1775 defer state.Unlock() 1776 // check snapd, core18, kernel, gadget 1777 _, err = snapstate.CurrentInfo(state, "snapd") 1778 c.Check(err, IsNil) 1779 _, err = snapstate.CurrentInfo(state, "core18") 1780 c.Check(err, IsNil) 1781 _, err = snapstate.CurrentInfo(state, "foo") 1782 c.Check(err, IsNil) 1783 1784 // and ensure state is now considered seeded 1785 var seeded bool 1786 err = state.Get("seeded", &seeded) 1787 c.Assert(err, IsNil) 1788 c.Check(seeded, Equals, true) 1789 1790 // check we set seed-time 1791 var seedTime time.Time 1792 err = state.Get("seed-time", &seedTime) 1793 c.Assert(err, IsNil) 1794 c.Check(seedTime.IsZero(), Equals, false) 1795 } 1796 1797 func (s *firstBoot16Suite) TestPopulateFromSeedMissingAssertions(c *C) { 1798 restore := release.MockOnClassic(true) 1799 defer restore() 1800 1801 core18Fname, snapdFname, _, _ := s.makeCore18Snaps(c, &core18SnapsOpts{}) 1802 1803 // create a seed.yaml 1804 content := []byte(fmt.Sprintf(` 1805 snaps: 1806 - name: snapd 1807 file: %s 1808 - name: core18 1809 file: %s 1810 `, snapdFname, core18Fname)) 1811 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1812 c.Assert(err, IsNil) 1813 1814 // run the firstboot stuff 1815 st := s.overlord.State() 1816 st.Lock() 1817 defer st.Unlock() 1818 _, err = devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1819 c.Assert(err, NotNil) 1820 // note, cannot use st.Tasks() here as it filters out tasks with no change 1821 c.Check(st.TaskCount(), Equals, 0) 1822 } 1823 1824 func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicWithSnapdOnlyAndGadgetHappy(c *C) { 1825 restore := release.MockOnClassic(true) 1826 defer restore() 1827 1828 var sysdLog [][]string 1829 systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1830 sysdLog = append(sysdLog, cmd) 1831 return []byte("ActiveState=inactive\n"), nil 1832 }) 1833 defer systemctlRestorer() 1834 1835 core18Fname, snapdFname, _, gadgetFname := s.makeCore18Snaps(c, &core18SnapsOpts{ 1836 classic: true, 1837 gadget: true, 1838 }) 1839 1840 // put a firstboot snap into the SnapBlobDir 1841 snapYaml := `name: foo 1842 version: 1.0 1843 base: core18 1844 ` 1845 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1846 1847 s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl) 1848 1849 // add a model assertion and its chain 1850 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", map[string]interface{}{"gadget": "pc"}) 1851 s.WriteAssertions("model.asserts", assertsChain...) 1852 1853 // create a seed.yaml 1854 content := []byte(fmt.Sprintf(` 1855 snaps: 1856 - name: snapd 1857 file: %s 1858 - name: foo 1859 file: %s 1860 - name: core18 1861 file: %s 1862 - name: pc 1863 file: %s 1864 `, snapdFname, fooFname, core18Fname, gadgetFname)) 1865 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1866 c.Assert(err, IsNil) 1867 1868 // run the firstboot stuff 1869 st := s.overlord.State() 1870 st.Lock() 1871 defer st.Unlock() 1872 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1873 c.Assert(err, IsNil) 1874 1875 checkOrder(c, tsAll, "snapd", "core18", "pc", "foo") 1876 1877 // now run the change and check the result 1878 // use the expected kind otherwise settle with start another one 1879 chg := st.NewChange("seed", "run the populate from seed changes") 1880 for _, ts := range tsAll { 1881 chg.AddAll(ts) 1882 } 1883 c.Assert(st.Changes(), HasLen, 1) 1884 1885 c.Assert(chg.Err(), IsNil) 1886 1887 // avoid device reg 1888 chg1 := st.NewChange("become-operational", "init device") 1889 chg1.SetStatus(state.DoingStatus) 1890 1891 // run change until it wants to restart 1892 st.Unlock() 1893 err = s.overlord.Settle(settleTimeout) 1894 st.Lock() 1895 c.Assert(err, IsNil) 1896 1897 // at this point the system is "restarting", pretend the restart has 1898 // happened 1899 c.Assert(chg.Status(), Equals, state.DoingStatus) 1900 state.MockRestarting(st, state.RestartUnset) 1901 st.Unlock() 1902 err = s.overlord.Settle(settleTimeout) 1903 st.Lock() 1904 c.Assert(err, IsNil) 1905 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%s", chg.Err())) 1906 1907 // verify 1908 r, err := os.Open(dirs.SnapStateFile) 1909 c.Assert(err, IsNil) 1910 state, err := state.ReadState(nil, r) 1911 c.Assert(err, IsNil) 1912 1913 state.Lock() 1914 defer state.Unlock() 1915 // check snapd, core18, kernel, gadget 1916 _, err = snapstate.CurrentInfo(state, "snapd") 1917 c.Check(err, IsNil) 1918 _, err = snapstate.CurrentInfo(state, "core18") 1919 c.Check(err, IsNil) 1920 _, err = snapstate.CurrentInfo(state, "pc") 1921 c.Check(err, IsNil) 1922 _, err = snapstate.CurrentInfo(state, "foo") 1923 c.Check(err, IsNil) 1924 1925 // and ensure state is now considered seeded 1926 var seeded bool 1927 err = state.Get("seeded", &seeded) 1928 c.Assert(err, IsNil) 1929 c.Check(seeded, Equals, true) 1930 1931 // check we set seed-time 1932 var seedTime time.Time 1933 err = state.Get("seed-time", &seedTime) 1934 c.Assert(err, IsNil) 1935 c.Check(seedTime.IsZero(), Equals, false) 1936 } 1937 1938 func (s *firstBoot16Suite) TestCriticalTaskEdgesForPreseed(c *C) { 1939 st := s.overlord.State() 1940 st.Lock() 1941 defer st.Unlock() 1942 1943 t1 := st.NewTask("task1", "") 1944 t2 := st.NewTask("task2", "") 1945 t3 := st.NewTask("task2", "") 1946 1947 ts := state.NewTaskSet(t1, t2, t3) 1948 ts.MarkEdge(t1, snapstate.BeginEdge) 1949 ts.MarkEdge(t2, snapstate.BeforeHooksEdge) 1950 ts.MarkEdge(t3, snapstate.HooksEdge) 1951 1952 beginEdge, beforeHooksEdge, hooksEdge, err := devicestate.CriticalTaskEdges(ts) 1953 c.Assert(err, IsNil) 1954 c.Assert(beginEdge, NotNil) 1955 c.Assert(beforeHooksEdge, NotNil) 1956 c.Assert(hooksEdge, NotNil) 1957 1958 c.Check(beginEdge.Kind(), Equals, "task1") 1959 c.Check(beforeHooksEdge.Kind(), Equals, "task2") 1960 c.Check(hooksEdge.Kind(), Equals, "task2") 1961 } 1962 1963 func (s *firstBoot16Suite) TestCriticalTaskEdgesForPreseedMissing(c *C) { 1964 st := s.overlord.State() 1965 st.Lock() 1966 defer st.Unlock() 1967 1968 t1 := st.NewTask("task1", "") 1969 t2 := st.NewTask("task2", "") 1970 t3 := st.NewTask("task2", "") 1971 1972 ts := state.NewTaskSet(t1, t2, t3) 1973 ts.MarkEdge(t1, snapstate.BeginEdge) 1974 1975 _, _, _, err := devicestate.CriticalTaskEdges(ts) 1976 c.Assert(err, NotNil) 1977 1978 ts = state.NewTaskSet(t1, t2, t3) 1979 ts.MarkEdge(t1, snapstate.BeginEdge) 1980 ts.MarkEdge(t2, snapstate.BeforeHooksEdge) 1981 _, _, _, err = devicestate.CriticalTaskEdges(ts) 1982 c.Assert(err, NotNil) 1983 1984 ts = state.NewTaskSet(t1, t2, t3) 1985 ts.MarkEdge(t1, snapstate.BeginEdge) 1986 ts.MarkEdge(t3, snapstate.HooksEdge) 1987 _, _, _, err = devicestate.CriticalTaskEdges(ts) 1988 c.Assert(err, NotNil) 1989 }