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