github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/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() (*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 gi, err := devicestate.PreloadGadget(s.overlord.DeviceManager()) 842 c.Assert(err, IsNil) 843 c.Check(gi.Defaults, HasLen, 4) 844 845 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 846 c.Assert(err, IsNil) 847 848 checkSeedTasks(c, tsAll) 849 850 // now run the change and check the result 851 // use the expected kind otherwise settle with start another one 852 chg := st.NewChange("seed", "run the populate from seed changes") 853 for _, ts := range tsAll { 854 chg.AddAll(ts) 855 } 856 c.Assert(st.Changes(), HasLen, 1) 857 858 var configured []string 859 hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) { 860 ctx.Lock() 861 defer ctx.Unlock() 862 // we have a gadget at this point(s) 863 ok, err := snapstate.HasSnapOfType(st, snap.TypeGadget) 864 c.Check(err, IsNil) 865 c.Check(ok, Equals, true) 866 configured = append(configured, ctx.InstanceName()) 867 return nil, nil 868 } 869 870 rhk := hookstate.MockRunHook(hookInvoke) 871 defer rhk() 872 873 // ensure we have something that captures the core config 874 restore := configstate.MockConfigcoreRun(func(config.Conf) error { 875 configured = append(configured, "configcore") 876 return nil 877 }) 878 defer restore() 879 880 // avoid device reg 881 chg1 := st.NewChange("become-operational", "init device") 882 chg1.SetStatus(state.DoingStatus) 883 884 st.Unlock() 885 err = s.overlord.Settle(settleTimeout) 886 st.Lock() 887 c.Assert(chg.Err(), IsNil) 888 c.Assert(err, IsNil) 889 890 // and check the snap got correctly installed 891 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) 892 893 // verify 894 r, err := os.Open(dirs.SnapStateFile) 895 c.Assert(err, IsNil) 896 state, err := state.ReadState(nil, r) 897 c.Assert(err, IsNil) 898 899 state.Lock() 900 defer state.Unlock() 901 tr := config.NewTransaction(state) 902 var val string 903 904 // check core, kernel, gadget 905 _, err = snapstate.CurrentInfo(state, "core") 906 c.Assert(err, IsNil) 907 err = tr.Get("core", "core-cfg", &val) 908 c.Assert(err, IsNil) 909 c.Check(val, Equals, "core_cfg_defl") 910 911 _, err = snapstate.CurrentInfo(state, "pc-kernel") 912 c.Assert(err, IsNil) 913 err = tr.Get("pc-kernel", "pc-kernel-cfg", &val) 914 c.Assert(err, IsNil) 915 c.Check(val, Equals, "pc-kernel_cfg_defl") 916 917 _, err = snapstate.CurrentInfo(state, "pc") 918 c.Assert(err, IsNil) 919 err = tr.Get("pc", "pc-cfg", &val) 920 c.Assert(err, IsNil) 921 c.Check(val, Equals, "pc_cfg_defl") 922 923 // check foo 924 info, err := snapstate.CurrentInfo(state, "foo") 925 c.Assert(err, IsNil) 926 c.Assert(info.SnapID, Equals, "foodidididididididididididididid") 927 c.Assert(info.Revision, Equals, snap.R(128)) 928 pubAcct, err := assertstate.Publisher(st, info.SnapID) 929 c.Assert(err, IsNil) 930 c.Check(pubAcct.AccountID(), Equals, "developerid") 931 932 // check foo config 933 err = tr.Get("foo", "foo-cfg", &val) 934 c.Assert(err, IsNil) 935 c.Check(val, Equals, "foo.") 936 937 c.Check(configured, DeepEquals, []string{"configcore", "pc-kernel", "pc", "foo"}) 938 939 // and ensure state is now considered seeded 940 var seeded bool 941 err = state.Get("seeded", &seeded) 942 c.Assert(err, IsNil) 943 c.Check(seeded, Equals, true) 944 } 945 946 func (s *firstBoot16Suite) TestPopulateFromSeedGadgetConnectHappy(c *C) { 947 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 948 bootloader.Force(bloader) 949 defer bootloader.Force(nil) 950 bloader.SetBootKernel("pc-kernel_1.snap") 951 bloader.SetBootBase("core_1.snap") 952 953 const connectionsYaml = ` 954 connections: 955 - plug: foodidididididididididididididid:network-control 956 ` 957 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, connectionsYaml) 958 959 s.WriteAssertions("developer.account", s.devAcct) 960 961 snapYaml := `name: foo 962 version: 1.0 963 plugs: 964 network-control: 965 ` 966 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 967 s.WriteAssertions("foo.asserts", fooDecl, fooRev) 968 969 // add a model assertion and its chain 970 assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo") 971 s.WriteAssertions("model.asserts", assertsChain...) 972 973 // create a seed.yaml 974 content := []byte(fmt.Sprintf(` 975 snaps: 976 - name: core 977 file: %s 978 - name: pc-kernel 979 file: %s 980 - name: pc 981 file: %s 982 - name: foo 983 file: %s 984 `, coreFname, kernelFname, gadgetFname, fooFname)) 985 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 986 c.Assert(err, IsNil) 987 988 // run the firstboot stuff 989 st := s.overlord.State() 990 st.Lock() 991 defer st.Unlock() 992 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 993 c.Assert(err, IsNil) 994 995 checkSeedTasks(c, tsAll) 996 997 // now run the change and check the result 998 // use the expected kind otherwise settle with start another one 999 chg := st.NewChange("seed", "run the populate from seed changes") 1000 for _, ts := range tsAll { 1001 chg.AddAll(ts) 1002 } 1003 c.Assert(st.Changes(), HasLen, 1) 1004 1005 // avoid device reg 1006 chg1 := st.NewChange("become-operational", "init device") 1007 chg1.SetStatus(state.DoingStatus) 1008 1009 st.Unlock() 1010 err = s.overlord.Settle(settleTimeout) 1011 st.Lock() 1012 c.Assert(chg.Err(), IsNil) 1013 c.Assert(err, IsNil) 1014 1015 // and check the snap got correctly installed 1016 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) 1017 1018 // verify 1019 r, err := os.Open(dirs.SnapStateFile) 1020 c.Assert(err, IsNil) 1021 state, err := state.ReadState(nil, r) 1022 c.Assert(err, IsNil) 1023 1024 state.Lock() 1025 defer state.Unlock() 1026 1027 // check foo 1028 info, err := snapstate.CurrentInfo(state, "foo") 1029 c.Assert(err, IsNil) 1030 c.Assert(info.SnapID, Equals, "foodidididididididididididididid") 1031 c.Assert(info.Revision, Equals, snap.R(128)) 1032 pubAcct, err := assertstate.Publisher(st, info.SnapID) 1033 c.Assert(err, IsNil) 1034 c.Check(pubAcct.AccountID(), Equals, "developerid") 1035 1036 // check connection 1037 var conns map[string]interface{} 1038 err = state.Get("conns", &conns) 1039 c.Assert(err, IsNil) 1040 c.Check(conns, HasLen, 1) 1041 c.Check(conns, DeepEquals, map[string]interface{}{ 1042 "foo:network-control core:network-control": map[string]interface{}{ 1043 "interface": "network-control", "auto": true, "by-gadget": true, 1044 }, 1045 }) 1046 1047 // and ensure state is now considered seeded 1048 var seeded bool 1049 err = state.Get("seeded", &seeded) 1050 c.Assert(err, IsNil) 1051 c.Check(seeded, Equals, true) 1052 } 1053 1054 func (s *firstBoot16Suite) TestImportAssertionsFromSeedClassicModelMismatch(c *C) { 1055 restore := release.MockOnClassic(true) 1056 defer restore() 1057 1058 ovld, err := overlord.New(nil) 1059 c.Assert(err, IsNil) 1060 st := ovld.State() 1061 1062 // add the odel assertion and its chain 1063 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1064 s.WriteAssertions("model.asserts", assertsChain...) 1065 1066 // import them 1067 st.Lock() 1068 defer st.Unlock() 1069 1070 _, err = devicestate.ImportAssertionsFromSeed(st, "") 1071 c.Assert(err, ErrorMatches, "cannot seed a classic system with an all-snaps model") 1072 } 1073 1074 func (s *firstBoot16Suite) TestImportAssertionsFromSeedAllSnapsModelMismatch(c *C) { 1075 ovld, err := overlord.New(nil) 1076 c.Assert(err, IsNil) 1077 st := ovld.State() 1078 1079 // add the model assertion and its chain 1080 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil) 1081 s.WriteAssertions("model.asserts", assertsChain...) 1082 1083 // import them 1084 st.Lock() 1085 defer st.Unlock() 1086 1087 _, err = devicestate.ImportAssertionsFromSeed(st, "") 1088 c.Assert(err, ErrorMatches, "cannot seed an all-snaps system with a classic model") 1089 } 1090 1091 func (s *firstBoot16Suite) TestLoadDeviceSeed(c *C) { 1092 ovld, err := overlord.New(nil) 1093 c.Assert(err, IsNil) 1094 st := ovld.State() 1095 1096 // add a bunch of assertions (model assertion and its chain) 1097 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1098 for i, as := range assertsChain { 1099 fname := strconv.Itoa(i) 1100 if as.Type() == asserts.ModelType { 1101 fname = "model" 1102 } 1103 s.WriteAssertions(fname, as) 1104 } 1105 1106 // load them 1107 st.Lock() 1108 defer st.Unlock() 1109 1110 deviceSeed, err := devicestate.LoadDeviceSeed(st, "") 1111 c.Assert(err, IsNil) 1112 1113 c.Check(deviceSeed.Model().BrandID(), Equals, "my-brand") 1114 c.Check(deviceSeed.Model().Model(), Equals, "my-model") 1115 1116 // verify that the model was added 1117 db := assertstate.DB(st) 1118 as, err := db.Find(asserts.ModelType, map[string]string{ 1119 "series": "16", 1120 "brand-id": "my-brand", 1121 "model": "my-model", 1122 }) 1123 c.Assert(err, IsNil) 1124 c.Check(as, DeepEquals, deviceSeed.Model()) 1125 } 1126 1127 func (s *firstBoot16Suite) TestLoadDeviceSeedCaching(c *C) { 1128 ovld, err := overlord.New(nil) 1129 c.Assert(err, IsNil) 1130 st := ovld.State() 1131 1132 // add a bunch of assertions (model assertion and its chain) 1133 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1134 for i, as := range assertsChain { 1135 fname := strconv.Itoa(i) 1136 if as.Type() == asserts.ModelType { 1137 fname = "model" 1138 } 1139 s.WriteAssertions(fname, as) 1140 } 1141 1142 // load them 1143 st.Lock() 1144 defer st.Unlock() 1145 1146 deviceSeed, err := devicestate.LoadDeviceSeed(st, "") 1147 c.Assert(err, IsNil) 1148 1149 c.Check(deviceSeed.Model().Model(), Equals, "my-model") 1150 1151 // break the seed 1152 modelAway := filepath.Join(c.MkDir(), "model") 1153 modelFn := filepath.Join(s.AssertsDir(), "model") 1154 err = os.Rename(modelFn, modelAway) 1155 c.Assert(err, IsNil) 1156 1157 // result is still cached 1158 deviceSeed, err = devicestate.LoadDeviceSeed(st, "") 1159 c.Assert(err, IsNil) 1160 1161 c.Check(deviceSeed.Model().Model(), Equals, "my-model") 1162 1163 // unload cached result 1164 devicestate.UnloadDeviceSeed(st) 1165 1166 // error now 1167 _, err1 := devicestate.LoadDeviceSeed(st, "") 1168 c.Assert(err1, ErrorMatches, "seed must have a model assertion") 1169 1170 // refix the seed 1171 err = os.Rename(modelAway, modelFn) 1172 c.Assert(err, IsNil) 1173 1174 // error is also cached 1175 _, err = devicestate.LoadDeviceSeed(st, "") 1176 c.Assert(err, Equals, err1) 1177 1178 // unload cached error 1179 devicestate.UnloadDeviceSeed(st) 1180 1181 // soundness check: loading works again 1182 _, err = devicestate.LoadDeviceSeed(st, "") 1183 c.Assert(err, IsNil) 1184 } 1185 1186 func (s *firstBoot16Suite) TestImportAssertionsFromSeedHappy(c *C) { 1187 ovld, err := overlord.New(nil) 1188 c.Assert(err, IsNil) 1189 st := ovld.State() 1190 1191 // add a bunch of assertions (model assertion and its chain) 1192 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1193 for i, as := range assertsChain { 1194 fname := strconv.Itoa(i) 1195 if as.Type() == asserts.ModelType { 1196 fname = "model" 1197 } 1198 s.WriteAssertions(fname, as) 1199 } 1200 1201 // import them 1202 st.Lock() 1203 defer st.Unlock() 1204 1205 deviceSeed, err := devicestate.ImportAssertionsFromSeed(st, "") 1206 c.Assert(err, IsNil) 1207 c.Assert(deviceSeed, NotNil) 1208 1209 model := deviceSeed.Model() 1210 1211 // verify that the model was added 1212 db := assertstate.DB(st) 1213 as, err := db.Find(asserts.ModelType, map[string]string{ 1214 "series": "16", 1215 "brand-id": "my-brand", 1216 "model": "my-model", 1217 }) 1218 c.Assert(err, IsNil) 1219 _, ok := as.(*asserts.Model) 1220 c.Check(ok, Equals, true) 1221 1222 ds, err := devicestatetest.Device(st) 1223 c.Assert(err, IsNil) 1224 c.Check(ds.Brand, Equals, "my-brand") 1225 c.Check(ds.Model, Equals, "my-model") 1226 1227 c.Check(model.BrandID(), Equals, "my-brand") 1228 c.Check(model.Model(), Equals, "my-model") 1229 } 1230 1231 func (s *firstBoot16Suite) TestImportAssertionsFromSeedMissingSig(c *C) { 1232 st := s.overlord.State() 1233 st.Lock() 1234 defer st.Unlock() 1235 1236 // write out only the model assertion 1237 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1238 for _, as := range assertsChain { 1239 if as.Type() == asserts.ModelType { 1240 s.WriteAssertions("model", as) 1241 break 1242 } 1243 } 1244 1245 // try import and verify that its rejects because other assertions are 1246 // missing 1247 _, err := devicestate.ImportAssertionsFromSeed(st, "") 1248 c.Assert(err, ErrorMatches, "cannot resolve prerequisite assertion: account-key .*") 1249 } 1250 1251 func (s *firstBoot16Suite) TestImportAssertionsFromSeedTwoModelAsserts(c *C) { 1252 st := s.overlord.State() 1253 st.Lock() 1254 defer st.Unlock() 1255 1256 // write out two model assertions 1257 model := s.Brands.Model("my-brand", "my-model", modelHeaders("my-model")) 1258 s.WriteAssertions("model", model) 1259 1260 model2 := s.Brands.Model("my-brand", "my-second-model", modelHeaders("my-second-model")) 1261 s.WriteAssertions("model2", model2) 1262 1263 // try import and verify that its rejects because other assertions are 1264 // missing 1265 _, err := devicestate.ImportAssertionsFromSeed(st, "") 1266 c.Assert(err, ErrorMatches, "cannot have multiple model assertions in seed") 1267 } 1268 1269 func (s *firstBoot16Suite) TestImportAssertionsFromSeedNoModelAsserts(c *C) { 1270 st := s.overlord.State() 1271 st.Lock() 1272 defer st.Unlock() 1273 1274 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1275 for i, as := range assertsChain { 1276 if as.Type() != asserts.ModelType { 1277 s.WriteAssertions(strconv.Itoa(i), as) 1278 } 1279 } 1280 1281 // try import and verify that its rejects because other assertions are 1282 // missing 1283 _, err := devicestate.ImportAssertionsFromSeed(st, "") 1284 c.Assert(err, ErrorMatches, "seed must have a model assertion") 1285 } 1286 1287 type core18SnapsOpts struct { 1288 classic bool 1289 gadget bool 1290 } 1291 1292 func (s *firstBoot16BaseTest) makeCore18Snaps(c *C, opts *core18SnapsOpts) (core18Fn, snapdFn, kernelFn, gadgetFn string) { 1293 if opts == nil { 1294 opts = &core18SnapsOpts{} 1295 } 1296 1297 files := [][]string{} 1298 1299 core18Yaml := `name: core18 1300 version: 1.0 1301 type: base` 1302 core18Fname, core18Decl, core18Rev := s.MakeAssertedSnap(c, core18Yaml, files, snap.R(1), "canonical") 1303 s.WriteAssertions("core18.asserts", core18Rev, core18Decl) 1304 1305 snapdYaml := `name: snapd 1306 version: 1.0 1307 ` 1308 snapdFname, snapdDecl, snapdRev := s.MakeAssertedSnap(c, snapdYaml, nil, snap.R(2), "canonical") 1309 s.WriteAssertions("snapd.asserts", snapdRev, snapdDecl) 1310 1311 var kernelFname string 1312 if !opts.classic { 1313 kernelYaml := `name: pc-kernel 1314 version: 1.0 1315 type: kernel` 1316 fname, kernelDecl, kernelRev := s.MakeAssertedSnap(c, kernelYaml, files, snap.R(1), "canonical") 1317 s.WriteAssertions("kernel.asserts", kernelRev, kernelDecl) 1318 kernelFname = fname 1319 } 1320 1321 if !opts.classic { 1322 gadgetYaml := ` 1323 volumes: 1324 volume-id: 1325 bootloader: grub 1326 ` 1327 files = append(files, []string{"meta/gadget.yaml", gadgetYaml}) 1328 } 1329 1330 var gadgetFname string 1331 if !opts.classic || opts.gadget { 1332 gaYaml := `name: pc 1333 version: 1.0 1334 type: gadget 1335 base: core18 1336 ` 1337 fname, gadgetDecl, gadgetRev := s.MakeAssertedSnap(c, gaYaml, files, snap.R(1), "canonical") 1338 s.WriteAssertions("gadget.asserts", gadgetRev, gadgetDecl) 1339 gadgetFname = fname 1340 } 1341 1342 return core18Fname, snapdFname, kernelFname, gadgetFname 1343 } 1344 1345 func (s *firstBoot16Suite) TestPopulateFromSeedWithBaseHappy(c *C) { 1346 var sysdLog [][]string 1347 systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1348 sysdLog = append(sysdLog, cmd) 1349 return []byte("ActiveState=inactive\n"), nil 1350 }) 1351 defer systemctlRestorer() 1352 1353 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 1354 bootloader.Force(bloader) 1355 defer bootloader.Force(nil) 1356 bloader.SetBootKernel("pc-kernel_1.snap") 1357 bloader.SetBootBase("core18_1.snap") 1358 1359 core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil) 1360 1361 s.WriteAssertions("developer.account", s.devAcct) 1362 1363 // add a model assertion and its chain 1364 assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"}) 1365 s.WriteAssertions("model.asserts", assertsChain...) 1366 1367 // create a seed.yaml 1368 content := []byte(fmt.Sprintf(` 1369 snaps: 1370 - name: snapd 1371 file: %s 1372 - name: core18 1373 file: %s 1374 - name: pc-kernel 1375 file: %s 1376 - name: pc 1377 file: %s 1378 `, snapdFname, core18Fname, kernelFname, gadgetFname)) 1379 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1380 c.Assert(err, IsNil) 1381 1382 // run the firstboot stuff 1383 st := s.overlord.State() 1384 st.Lock() 1385 defer st.Unlock() 1386 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1387 c.Assert(err, IsNil) 1388 1389 checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc") 1390 1391 // now run the change and check the result 1392 // use the expected kind otherwise settle with start another one 1393 chg := st.NewChange("seed", "run the populate from seed changes") 1394 for _, ts := range tsAll { 1395 chg.AddAll(ts) 1396 } 1397 c.Assert(st.Changes(), HasLen, 1) 1398 1399 c.Assert(chg.Err(), IsNil) 1400 1401 // avoid device reg 1402 chg1 := st.NewChange("become-operational", "init device") 1403 chg1.SetStatus(state.DoingStatus) 1404 1405 // run change until it wants to restart 1406 st.Unlock() 1407 err = s.overlord.Settle(settleTimeout) 1408 st.Lock() 1409 c.Assert(err, IsNil) 1410 1411 // at this point the system is "restarting", pretend the restart has 1412 // happened 1413 c.Assert(chg.Status(), Equals, state.DoingStatus) 1414 state.MockRestarting(st, state.RestartUnset) 1415 st.Unlock() 1416 err = s.overlord.Settle(settleTimeout) 1417 st.Lock() 1418 c.Assert(err, IsNil) 1419 c.Assert(chg.Status(), Equals, state.DoneStatus) 1420 1421 // verify 1422 r, err := os.Open(dirs.SnapStateFile) 1423 c.Assert(err, IsNil) 1424 state, err := state.ReadState(nil, r) 1425 c.Assert(err, IsNil) 1426 1427 state.Lock() 1428 defer state.Unlock() 1429 // check snapd, core18, kernel, gadget 1430 _, err = snapstate.CurrentInfo(state, "snapd") 1431 c.Check(err, IsNil) 1432 _, err = snapstate.CurrentInfo(state, "core18") 1433 c.Check(err, IsNil) 1434 _, err = snapstate.CurrentInfo(state, "pc-kernel") 1435 c.Check(err, IsNil) 1436 _, err = snapstate.CurrentInfo(state, "pc") 1437 c.Check(err, IsNil) 1438 1439 // ensure required flag is set on all essential snaps 1440 var snapst snapstate.SnapState 1441 for _, reqName := range []string{"snapd", "core18", "pc-kernel", "pc"} { 1442 err = snapstate.Get(state, reqName, &snapst) 1443 c.Assert(err, IsNil) 1444 c.Assert(snapst.Required, Equals, true, Commentf("required not set for %v", reqName)) 1445 } 1446 1447 // the right systemd commands were run 1448 c.Check(sysdLog, testutil.DeepContains, []string{"start", "usr-lib-snapd.mount"}) 1449 1450 // and ensure state is now considered seeded 1451 var seeded bool 1452 err = state.Get("seeded", &seeded) 1453 c.Assert(err, IsNil) 1454 c.Check(seeded, Equals, true) 1455 1456 // check we set seed-time 1457 var seedTime time.Time 1458 err = state.Get("seed-time", &seedTime) 1459 c.Assert(err, IsNil) 1460 c.Check(seedTime.IsZero(), Equals, false) 1461 } 1462 1463 func (s *firstBoot16Suite) TestPopulateFromSeedOrdering(c *C) { 1464 s.WriteAssertions("developer.account", s.devAcct) 1465 1466 // add a model assertion and its chain 1467 assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"}) 1468 s.WriteAssertions("model.asserts", assertsChain...) 1469 1470 core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil) 1471 1472 snapYaml := `name: snap-req-other-base 1473 version: 1.0 1474 base: other-base 1475 ` 1476 snapFname, snapDecl, snapRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1477 s.WriteAssertions("snap-req-other-base.asserts", s.devAcct, snapRev, snapDecl) 1478 baseYaml := `name: other-base 1479 version: 1.0 1480 type: base 1481 ` 1482 baseFname, baseDecl, baseRev := s.MakeAssertedSnap(c, baseYaml, nil, snap.R(127), "developerid") 1483 s.WriteAssertions("other-base.asserts", s.devAcct, baseRev, baseDecl) 1484 1485 // create a seed.yaml 1486 content := []byte(fmt.Sprintf(` 1487 snaps: 1488 - name: snapd 1489 file: %s 1490 - name: core18 1491 file: %s 1492 - name: pc-kernel 1493 file: %s 1494 - name: pc 1495 file: %s 1496 - name: snap-req-other-base 1497 file: %s 1498 - name: other-base 1499 file: %s 1500 `, snapdFname, core18Fname, kernelFname, gadgetFname, snapFname, baseFname)) 1501 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1502 c.Assert(err, IsNil) 1503 1504 // run the firstboot stuff 1505 st := s.overlord.State() 1506 st.Lock() 1507 defer st.Unlock() 1508 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1509 c.Assert(err, IsNil) 1510 1511 checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc", "other-base", "snap-req-other-base") 1512 } 1513 1514 func (s *firstBoot16Suite) TestFirstbootGadgetBaseModelBaseMismatch(c *C) { 1515 s.WriteAssertions("developer.account", s.devAcct) 1516 1517 // add a model assertion and its chain 1518 assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"}) 1519 s.WriteAssertions("model.asserts", assertsChain...) 1520 1521 core18Fname, snapdFname, kernelFname, _ := s.makeCore18Snaps(c, nil) 1522 // take the gadget without "base: core18" 1523 _, _, gadgetFname := s.makeCoreSnaps(c, "") 1524 1525 // create a seed.yaml 1526 content := []byte(fmt.Sprintf(` 1527 snaps: 1528 - name: snapd 1529 file: %s 1530 - name: core18 1531 file: %s 1532 - name: pc-kernel 1533 file: %s 1534 - name: pc 1535 file: %s 1536 `, snapdFname, core18Fname, kernelFname, gadgetFname)) 1537 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1538 c.Assert(err, IsNil) 1539 1540 // run the firstboot stuff 1541 st := s.overlord.State() 1542 st.Lock() 1543 defer st.Unlock() 1544 1545 _, err = devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1546 c.Assert(err, ErrorMatches, `cannot use gadget snap because its base "core" is different from model base "core18"`) 1547 // note, cannot use st.Tasks() here as it filters out tasks with no change 1548 c.Check(st.TaskCount(), Equals, 0) 1549 } 1550 1551 func (s *firstBoot16Suite) TestPopulateFromSeedWrongContentProviderOrder(c *C) { 1552 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 1553 bootloader.Force(bloader) 1554 defer bootloader.Force(nil) 1555 bloader.SetBootKernel("pc-kernel_1.snap") 1556 bloader.SetBootBase("core_1.snap") 1557 1558 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "") 1559 1560 // a snap that uses content providers 1561 snapYaml := `name: gnome-calculator 1562 version: 1.0 1563 plugs: 1564 gtk-3-themes: 1565 interface: content 1566 default-provider: gtk-common-themes 1567 target: $SNAP/data-dir/themes 1568 ` 1569 calcFname, calcDecl, calcRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1570 s.WriteAssertions("calc.asserts", s.devAcct, calcRev, calcDecl) 1571 1572 // put a 2nd firstboot snap into the SnapBlobDir 1573 snapYaml = `name: gtk-common-themes 1574 version: 1.0 1575 slots: 1576 gtk-3-themes: 1577 interface: content 1578 source: 1579 read: 1580 - $SNAP/share/themes/Adawaita 1581 ` 1582 themesFname, themesDecl, themesRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(65), "developerid") 1583 s.WriteAssertions("themes.asserts", s.devAcct, themesDecl, themesRev) 1584 1585 // add a model assertion and its chain 1586 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1587 s.WriteAssertions("model.asserts", assertsChain...) 1588 1589 // create a seed.yaml 1590 content := []byte(fmt.Sprintf(` 1591 snaps: 1592 - name: core 1593 file: %s 1594 - name: pc-kernel 1595 file: %s 1596 - name: pc 1597 file: %s 1598 - name: gnome-calculator 1599 file: %s 1600 - name: gtk-common-themes 1601 file: %s 1602 `, coreFname, kernelFname, gadgetFname, calcFname, themesFname)) 1603 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1604 c.Assert(err, IsNil) 1605 1606 // run the firstboot stuff 1607 st := s.overlord.State() 1608 st.Lock() 1609 defer st.Unlock() 1610 1611 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1612 c.Assert(err, IsNil) 1613 // use the expected kind otherwise settle with start another one 1614 chg := st.NewChange("seed", "run the populate from seed changes") 1615 for _, ts := range tsAll { 1616 chg.AddAll(ts) 1617 } 1618 c.Assert(st.Changes(), HasLen, 1) 1619 1620 // avoid device reg 1621 chg1 := st.NewChange("become-operational", "init device") 1622 chg1.SetStatus(state.DoingStatus) 1623 1624 st.Unlock() 1625 err = s.overlord.Settle(settleTimeout) 1626 st.Lock() 1627 1628 c.Assert(chg.Err(), IsNil) 1629 c.Assert(err, IsNil) 1630 1631 // verify the result 1632 var conns map[string]interface{} 1633 err = st.Get("conns", &conns) 1634 c.Assert(err, IsNil) 1635 c.Check(conns, HasLen, 1) 1636 conn, hasConn := conns["gnome-calculator:gtk-3-themes gtk-common-themes:gtk-3-themes"] 1637 c.Check(hasConn, Equals, true) 1638 c.Check(conn.(map[string]interface{})["auto"], Equals, true) 1639 c.Check(conn.(map[string]interface{})["interface"], Equals, "content") 1640 } 1641 1642 func (s *firstBoot16Suite) TestPopulateFromSeedMissingBase(c *C) { 1643 s.WriteAssertions("developer.account", s.devAcct) 1644 1645 // add a model assertion and its chain 1646 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1647 s.WriteAssertions("model.asserts", assertsChain...) 1648 1649 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "") 1650 1651 // TODO: this test doesn't particularly need to use a local snap 1652 // local snap with unknown base 1653 snapYaml = `name: local 1654 base: foo 1655 version: 1.0` 1656 mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml, nil) 1657 localFname := filepath.Base(mockSnapFile) 1658 targetSnapFile2 := filepath.Join(dirs.SnapSeedDir, "snaps", localFname) 1659 c.Assert(os.Rename(mockSnapFile, targetSnapFile2), IsNil) 1660 1661 // create a seed.yaml 1662 content := []byte(fmt.Sprintf(` 1663 snaps: 1664 - name: core 1665 file: %s 1666 - name: pc-kernel 1667 file: %s 1668 - name: pc 1669 file: %s 1670 - name: local 1671 unasserted: true 1672 file: %s 1673 `, coreFname, kernelFname, gadgetFname, localFname)) 1674 1675 c.Assert(ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644), IsNil) 1676 1677 // run the firstboot stuff 1678 st := s.overlord.State() 1679 st.Lock() 1680 defer st.Unlock() 1681 _, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1682 c.Assert(err, ErrorMatches, `cannot use snap "local": base "foo" is missing`) 1683 } 1684 1685 func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicWithSnapdOnlyHappy(c *C) { 1686 restore := release.MockOnClassic(true) 1687 defer restore() 1688 1689 var sysdLog [][]string 1690 systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1691 sysdLog = append(sysdLog, cmd) 1692 return []byte("ActiveState=inactive\n"), nil 1693 }) 1694 defer systemctlRestorer() 1695 1696 core18Fname, snapdFname, _, _ := s.makeCore18Snaps(c, &core18SnapsOpts{ 1697 classic: true, 1698 }) 1699 1700 // put a firstboot snap into the SnapBlobDir 1701 snapYaml := `name: foo 1702 version: 1.0 1703 base: core18 1704 ` 1705 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1706 s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl) 1707 1708 // add a model assertion and its chain 1709 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil) 1710 s.WriteAssertions("model.asserts", assertsChain...) 1711 1712 // create a seed.yaml 1713 content := []byte(fmt.Sprintf(` 1714 snaps: 1715 - name: snapd 1716 file: %s 1717 - name: foo 1718 file: %s 1719 - name: core18 1720 file: %s 1721 `, snapdFname, fooFname, core18Fname)) 1722 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1723 c.Assert(err, IsNil) 1724 1725 // run the firstboot stuff 1726 st := s.overlord.State() 1727 st.Lock() 1728 defer st.Unlock() 1729 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1730 c.Assert(err, IsNil) 1731 1732 checkOrder(c, tsAll, "snapd", "core18", "foo") 1733 1734 // now run the change and check the result 1735 // use the expected kind otherwise settle with start another one 1736 chg := st.NewChange("seed", "run the populate from seed changes") 1737 for _, ts := range tsAll { 1738 chg.AddAll(ts) 1739 } 1740 c.Assert(st.Changes(), HasLen, 1) 1741 1742 c.Assert(chg.Err(), IsNil) 1743 1744 // avoid device reg 1745 chg1 := st.NewChange("become-operational", "init device") 1746 chg1.SetStatus(state.DoingStatus) 1747 1748 // run change until it wants to restart 1749 st.Unlock() 1750 err = s.overlord.Settle(settleTimeout) 1751 st.Lock() 1752 c.Assert(err, IsNil) 1753 1754 // at this point the system is "restarting", pretend the restart has 1755 // happened 1756 c.Assert(chg.Status(), Equals, state.DoingStatus) 1757 state.MockRestarting(st, state.RestartUnset) 1758 st.Unlock() 1759 err = s.overlord.Settle(settleTimeout) 1760 st.Lock() 1761 c.Assert(err, IsNil) 1762 c.Assert(chg.Status(), Equals, state.DoneStatus) 1763 1764 // verify 1765 r, err := os.Open(dirs.SnapStateFile) 1766 c.Assert(err, IsNil) 1767 state, err := state.ReadState(nil, r) 1768 c.Assert(err, IsNil) 1769 1770 state.Lock() 1771 defer state.Unlock() 1772 // check snapd, core18, kernel, gadget 1773 _, err = snapstate.CurrentInfo(state, "snapd") 1774 c.Check(err, IsNil) 1775 _, err = snapstate.CurrentInfo(state, "core18") 1776 c.Check(err, IsNil) 1777 _, err = snapstate.CurrentInfo(state, "foo") 1778 c.Check(err, IsNil) 1779 1780 // and ensure state is now considered seeded 1781 var seeded bool 1782 err = state.Get("seeded", &seeded) 1783 c.Assert(err, IsNil) 1784 c.Check(seeded, Equals, true) 1785 1786 // check we set seed-time 1787 var seedTime time.Time 1788 err = state.Get("seed-time", &seedTime) 1789 c.Assert(err, IsNil) 1790 c.Check(seedTime.IsZero(), Equals, false) 1791 } 1792 1793 func (s *firstBoot16Suite) TestPopulateFromSeedMissingAssertions(c *C) { 1794 restore := release.MockOnClassic(true) 1795 defer restore() 1796 1797 core18Fname, snapdFname, _, _ := s.makeCore18Snaps(c, &core18SnapsOpts{}) 1798 1799 // create a seed.yaml 1800 content := []byte(fmt.Sprintf(` 1801 snaps: 1802 - name: snapd 1803 file: %s 1804 - name: core18 1805 file: %s 1806 `, snapdFname, core18Fname)) 1807 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1808 c.Assert(err, IsNil) 1809 1810 // run the firstboot stuff 1811 st := s.overlord.State() 1812 st.Lock() 1813 defer st.Unlock() 1814 _, err = devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1815 c.Assert(err, NotNil) 1816 // note, cannot use st.Tasks() here as it filters out tasks with no change 1817 c.Check(st.TaskCount(), Equals, 0) 1818 } 1819 1820 func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicWithSnapdOnlyAndGadgetHappy(c *C) { 1821 restore := release.MockOnClassic(true) 1822 defer restore() 1823 1824 var sysdLog [][]string 1825 systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1826 sysdLog = append(sysdLog, cmd) 1827 return []byte("ActiveState=inactive\n"), nil 1828 }) 1829 defer systemctlRestorer() 1830 1831 core18Fname, snapdFname, _, gadgetFname := s.makeCore18Snaps(c, &core18SnapsOpts{ 1832 classic: true, 1833 gadget: true, 1834 }) 1835 1836 // put a firstboot snap into the SnapBlobDir 1837 snapYaml := `name: foo 1838 version: 1.0 1839 base: core18 1840 ` 1841 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1842 1843 s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl) 1844 1845 // add a model assertion and its chain 1846 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", map[string]interface{}{"gadget": "pc"}) 1847 s.WriteAssertions("model.asserts", assertsChain...) 1848 1849 // create a seed.yaml 1850 content := []byte(fmt.Sprintf(` 1851 snaps: 1852 - name: snapd 1853 file: %s 1854 - name: foo 1855 file: %s 1856 - name: core18 1857 file: %s 1858 - name: pc 1859 file: %s 1860 `, snapdFname, fooFname, core18Fname, gadgetFname)) 1861 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1862 c.Assert(err, IsNil) 1863 1864 // run the firstboot stuff 1865 st := s.overlord.State() 1866 st.Lock() 1867 defer st.Unlock() 1868 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1869 c.Assert(err, IsNil) 1870 1871 checkOrder(c, tsAll, "snapd", "core18", "pc", "foo") 1872 1873 // now run the change and check the result 1874 // use the expected kind otherwise settle with start another one 1875 chg := st.NewChange("seed", "run the populate from seed changes") 1876 for _, ts := range tsAll { 1877 chg.AddAll(ts) 1878 } 1879 c.Assert(st.Changes(), HasLen, 1) 1880 1881 c.Assert(chg.Err(), IsNil) 1882 1883 // avoid device reg 1884 chg1 := st.NewChange("become-operational", "init device") 1885 chg1.SetStatus(state.DoingStatus) 1886 1887 // run change until it wants to restart 1888 st.Unlock() 1889 err = s.overlord.Settle(settleTimeout) 1890 st.Lock() 1891 c.Assert(err, IsNil) 1892 1893 // at this point the system is "restarting", pretend the restart has 1894 // happened 1895 c.Assert(chg.Status(), Equals, state.DoingStatus) 1896 state.MockRestarting(st, state.RestartUnset) 1897 st.Unlock() 1898 err = s.overlord.Settle(settleTimeout) 1899 st.Lock() 1900 c.Assert(err, IsNil) 1901 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%s", chg.Err())) 1902 1903 // verify 1904 r, err := os.Open(dirs.SnapStateFile) 1905 c.Assert(err, IsNil) 1906 state, err := state.ReadState(nil, r) 1907 c.Assert(err, IsNil) 1908 1909 state.Lock() 1910 defer state.Unlock() 1911 // check snapd, core18, kernel, gadget 1912 _, err = snapstate.CurrentInfo(state, "snapd") 1913 c.Check(err, IsNil) 1914 _, err = snapstate.CurrentInfo(state, "core18") 1915 c.Check(err, IsNil) 1916 _, err = snapstate.CurrentInfo(state, "pc") 1917 c.Check(err, IsNil) 1918 _, err = snapstate.CurrentInfo(state, "foo") 1919 c.Check(err, IsNil) 1920 1921 // and ensure state is now considered seeded 1922 var seeded bool 1923 err = state.Get("seeded", &seeded) 1924 c.Assert(err, IsNil) 1925 c.Check(seeded, Equals, true) 1926 1927 // check we set seed-time 1928 var seedTime time.Time 1929 err = state.Get("seed-time", &seedTime) 1930 c.Assert(err, IsNil) 1931 c.Check(seedTime.IsZero(), Equals, false) 1932 } 1933 1934 func (s *firstBoot16Suite) TestCriticalTaskEdgesForPreseed(c *C) { 1935 st := s.overlord.State() 1936 st.Lock() 1937 defer st.Unlock() 1938 1939 t1 := st.NewTask("task1", "") 1940 t2 := st.NewTask("task2", "") 1941 t3 := st.NewTask("task2", "") 1942 1943 ts := state.NewTaskSet(t1, t2, t3) 1944 ts.MarkEdge(t1, snapstate.BeginEdge) 1945 ts.MarkEdge(t2, snapstate.BeforeHooksEdge) 1946 ts.MarkEdge(t3, snapstate.HooksEdge) 1947 1948 beginEdge, beforeHooksEdge, hooksEdge, err := devicestate.CriticalTaskEdges(ts) 1949 c.Assert(err, IsNil) 1950 c.Assert(beginEdge, NotNil) 1951 c.Assert(beforeHooksEdge, NotNil) 1952 c.Assert(hooksEdge, NotNil) 1953 1954 c.Check(beginEdge.Kind(), Equals, "task1") 1955 c.Check(beforeHooksEdge.Kind(), Equals, "task2") 1956 c.Check(hooksEdge.Kind(), Equals, "task2") 1957 } 1958 1959 func (s *firstBoot16Suite) TestCriticalTaskEdgesForPreseedMissing(c *C) { 1960 st := s.overlord.State() 1961 st.Lock() 1962 defer st.Unlock() 1963 1964 t1 := st.NewTask("task1", "") 1965 t2 := st.NewTask("task2", "") 1966 t3 := st.NewTask("task2", "") 1967 1968 ts := state.NewTaskSet(t1, t2, t3) 1969 ts.MarkEdge(t1, snapstate.BeginEdge) 1970 1971 _, _, _, err := devicestate.CriticalTaskEdges(ts) 1972 c.Assert(err, NotNil) 1973 1974 ts = state.NewTaskSet(t1, t2, t3) 1975 ts.MarkEdge(t1, snapstate.BeginEdge) 1976 ts.MarkEdge(t2, snapstate.BeforeHooksEdge) 1977 _, _, _, err = devicestate.CriticalTaskEdges(ts) 1978 c.Assert(err, NotNil) 1979 1980 ts = state.NewTaskSet(t1, t2, t3) 1981 ts.MarkEdge(t1, snapstate.BeginEdge) 1982 ts.MarkEdge(t3, snapstate.HooksEdge) 1983 _, _, _, err = devicestate.CriticalTaskEdges(ts) 1984 c.Assert(err, NotNil) 1985 }