github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/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 // now run the change and check the result 518 // use the expected kind otherwise settle with start another one 519 chg := st.NewChange("seed", "run the populate from seed changes") 520 for _, ts := range tsAll { 521 chg.AddAll(ts) 522 } 523 c.Assert(st.Changes(), HasLen, 1) 524 525 checkOrder(c, tsAll, "core", "pc-kernel", "pc", "foo", "local") 526 checkTasks(c, tsAll) 527 528 // avoid device reg 529 chg1 := st.NewChange("become-operational", "init device") 530 chg1.SetStatus(state.DoingStatus) 531 532 return chg, model 533 } 534 535 func (s *firstBoot16Suite) TestPopulateFromSeedHappy(c *C) { 536 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 537 bootloader.Force(bloader) 538 defer bootloader.Force(nil) 539 bloader.SetBootKernel("pc-kernel_1.snap") 540 bloader.SetBootBase("core_1.snap") 541 542 st := s.overlord.State() 543 chg, model := s.makeSeedChange(c, st, nil, checkSeedTasks, checkOrder) 544 err := s.overlord.Settle(settleTimeout) 545 c.Assert(err, IsNil) 546 547 st.Lock() 548 defer st.Unlock() 549 550 c.Assert(chg.Err(), IsNil) 551 552 // and check the snap got correctly installed 553 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) 554 555 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "local", "x1", "meta", "snap.yaml")), Equals, true) 556 557 // verify 558 r, err := os.Open(dirs.SnapStateFile) 559 c.Assert(err, IsNil) 560 state, err := state.ReadState(nil, r) 561 c.Assert(err, IsNil) 562 563 state.Lock() 564 defer state.Unlock() 565 // check core, kernel, gadget 566 _, err = snapstate.CurrentInfo(state, "core") 567 c.Assert(err, IsNil) 568 _, err = snapstate.CurrentInfo(state, "pc-kernel") 569 c.Assert(err, IsNil) 570 _, err = snapstate.CurrentInfo(state, "pc") 571 c.Assert(err, IsNil) 572 573 // ensure required flag is set on all essential snaps 574 var snapst snapstate.SnapState 575 for _, reqName := range []string{"core", "pc-kernel", "pc"} { 576 err = snapstate.Get(state, reqName, &snapst) 577 c.Assert(err, IsNil) 578 c.Assert(snapst.Required, Equals, true, Commentf("required not set for %v", reqName)) 579 } 580 581 // check foo 582 info, err := snapstate.CurrentInfo(state, "foo") 583 c.Assert(err, IsNil) 584 c.Assert(info.SnapID, Equals, "foodidididididididididididididid") 585 c.Assert(info.Revision, Equals, snap.R(128)) 586 c.Assert(info.Contact, Equals, "mailto:some.guy@example.com") 587 pubAcct, err := assertstate.Publisher(st, info.SnapID) 588 c.Assert(err, IsNil) 589 c.Check(pubAcct.AccountID(), Equals, "developerid") 590 591 err = snapstate.Get(state, "foo", &snapst) 592 c.Assert(err, IsNil) 593 c.Assert(snapst.DevMode, Equals, true) 594 c.Assert(snapst.Required, Equals, true) 595 596 // check local 597 info, err = snapstate.CurrentInfo(state, "local") 598 c.Assert(err, IsNil) 599 c.Assert(info.SnapID, Equals, "") 600 c.Assert(info.Revision, Equals, snap.R("x1")) 601 602 var snapst2 snapstate.SnapState 603 err = snapstate.Get(state, "local", &snapst2) 604 c.Assert(err, IsNil) 605 c.Assert(snapst2.Required, Equals, false) 606 607 // and ensure state is now considered seeded 608 var seeded bool 609 err = state.Get("seeded", &seeded) 610 c.Assert(err, IsNil) 611 c.Check(seeded, Equals, true) 612 613 // check we set seed-time 614 var seedTime time.Time 615 err = state.Get("seed-time", &seedTime) 616 c.Assert(err, IsNil) 617 c.Check(seedTime.IsZero(), Equals, false) 618 619 var whatseeded []devicestate.SeededSystem 620 err = state.Get("seeded-systems", &whatseeded) 621 c.Assert(err, IsNil) 622 c.Assert(whatseeded, DeepEquals, []devicestate.SeededSystem{{ 623 System: "", 624 Model: "my-model", 625 BrandID: "my-brand", 626 Revision: model.Revision(), 627 Timestamp: model.Timestamp(), 628 SeedTime: seedTime, 629 }}) 630 } 631 632 func (s *firstBoot16Suite) TestPopulateFromSeedMissingBootloader(c *C) { 633 st0 := s.overlord.State() 634 st0.Lock() 635 db := assertstate.DB(st0) 636 st0.Unlock() 637 638 // we run only with the relevant managers to produce the error 639 // situation 640 o := overlord.Mock() 641 st := o.State() 642 snapmgr, err := snapstate.Manager(st, o.TaskRunner()) 643 c.Assert(err, IsNil) 644 o.AddManager(snapmgr) 645 646 ifacemgr, err := ifacestate.Manager(st, nil, o.TaskRunner(), nil, nil) 647 c.Assert(err, IsNil) 648 o.AddManager(ifacemgr) 649 c.Assert(o.StartUp(), IsNil) 650 651 hookMgr, err := hookstate.Manager(st, o.TaskRunner()) 652 c.Assert(err, IsNil) 653 _, err = devicestate.Manager(st, hookMgr, o.TaskRunner(), nil) 654 c.Assert(err, IsNil) 655 656 st.Lock() 657 assertstate.ReplaceDB(st, db.(*asserts.Database)) 658 st.Unlock() 659 660 o.AddManager(o.TaskRunner()) 661 662 chg, _ := s.makeSeedChange(c, st, nil, checkSeedTasks, checkOrder) 663 664 se := o.StateEngine() 665 // we cannot use Settle because the Change will not become Clean 666 // under the subset of managers 667 for i := 0; i < 25 && !chg.IsReady(); i++ { 668 se.Ensure() 669 se.Wait() 670 } 671 672 st.Lock() 673 defer st.Unlock() 674 c.Assert(chg.Err(), ErrorMatches, `(?s).* cannot determine bootloader.*`) 675 } 676 677 func (s *firstBoot16Suite) TestPopulateFromSeedHappyMultiAssertsFiles(c *C) { 678 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 679 bootloader.Force(bloader) 680 defer bootloader.Force(nil) 681 bloader.SetBootKernel("pc-kernel_1.snap") 682 bloader.SetBootBase("core_1.snap") 683 684 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "") 685 686 // put a firstboot snap into the SnapBlobDir 687 snapYaml := `name: foo 688 version: 1.0` 689 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 690 s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl) 691 692 // put a 2nd firstboot snap into the SnapBlobDir 693 snapYaml = `name: bar 694 version: 1.0` 695 barFname, barDecl, barRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(65), "developerid") 696 s.WriteAssertions("bar.asserts", s.devAcct, barDecl, barRev) 697 698 // add a model assertion and its chain 699 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 700 s.WriteAssertions("model.asserts", assertsChain...) 701 702 // create a seed.yaml 703 content := []byte(fmt.Sprintf(` 704 snaps: 705 - name: core 706 file: %s 707 - name: pc-kernel 708 file: %s 709 - name: pc 710 file: %s 711 - name: foo 712 file: %s 713 - name: bar 714 file: %s 715 `, coreFname, kernelFname, gadgetFname, fooFname, barFname)) 716 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 717 c.Assert(err, IsNil) 718 719 // run the firstboot stuff 720 st := s.overlord.State() 721 st.Lock() 722 defer st.Unlock() 723 724 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 725 c.Assert(err, IsNil) 726 // use the expected kind otherwise settle with start another one 727 chg := st.NewChange("seed", "run the populate from seed changes") 728 for _, ts := range tsAll { 729 chg.AddAll(ts) 730 } 731 c.Assert(st.Changes(), HasLen, 1) 732 733 // avoid device reg 734 chg1 := st.NewChange("become-operational", "init device") 735 chg1.SetStatus(state.DoingStatus) 736 737 st.Unlock() 738 err = s.overlord.Settle(settleTimeout) 739 st.Lock() 740 c.Assert(chg.Err(), IsNil) 741 c.Assert(err, IsNil) 742 743 // and check the snap got correctly installed 744 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) 745 746 // and check the snap got correctly installed 747 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "bar", "65", "meta", "snap.yaml")), Equals, true) 748 749 // verify 750 r, err := os.Open(dirs.SnapStateFile) 751 c.Assert(err, IsNil) 752 state, err := state.ReadState(nil, r) 753 c.Assert(err, IsNil) 754 755 state.Lock() 756 defer state.Unlock() 757 // check foo 758 info, err := snapstate.CurrentInfo(state, "foo") 759 c.Assert(err, IsNil) 760 c.Check(info.SnapID, Equals, "foodidididididididididididididid") 761 c.Check(info.Revision, Equals, snap.R(128)) 762 pubAcct, err := assertstate.Publisher(st, info.SnapID) 763 c.Assert(err, IsNil) 764 c.Check(pubAcct.AccountID(), Equals, "developerid") 765 766 // check bar 767 info, err = snapstate.CurrentInfo(state, "bar") 768 c.Assert(err, IsNil) 769 c.Check(info.SnapID, Equals, "bardidididididididididididididid") 770 c.Check(info.Revision, Equals, snap.R(65)) 771 pubAcct, err = assertstate.Publisher(st, info.SnapID) 772 c.Assert(err, IsNil) 773 c.Check(pubAcct.AccountID(), Equals, "developerid") 774 } 775 776 func (s *firstBoot16Suite) TestPopulateFromSeedConfigureHappy(c *C) { 777 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 778 bootloader.Force(bloader) 779 defer bootloader.Force(nil) 780 bloader.SetBootKernel("pc-kernel_1.snap") 781 bloader.SetBootBase("core_1.snap") 782 783 const defaultsYaml = ` 784 defaults: 785 foodidididididididididididididid: 786 foo-cfg: foo. 787 99T7MUlRhtI3U0QFgl5mXXESAiSwt776: # core 788 core-cfg: core_cfg_defl 789 pckernelidididididididididididid: 790 pc-kernel-cfg: pc-kernel_cfg_defl 791 pcididididididididididididididid: 792 pc-cfg: pc_cfg_defl 793 ` 794 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, defaultsYaml) 795 796 s.WriteAssertions("developer.account", s.devAcct) 797 798 // put a firstboot snap into the SnapBlobDir 799 files := [][]string{{"meta/hooks/configure", ""}} 800 snapYaml := `name: foo 801 version: 1.0` 802 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, files, snap.R(128), "developerid") 803 s.WriteAssertions("foo.asserts", fooDecl, fooRev) 804 805 // add a model assertion and its chain 806 assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo") 807 s.WriteAssertions("model.asserts", assertsChain...) 808 809 // create a seed.yaml 810 content := []byte(fmt.Sprintf(` 811 snaps: 812 - name: core 813 file: %s 814 - name: pc-kernel 815 file: %s 816 - name: pc 817 file: %s 818 - name: foo 819 file: %s 820 `, coreFname, kernelFname, gadgetFname, fooFname)) 821 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 822 c.Assert(err, IsNil) 823 824 // run the firstboot stuff 825 st := s.overlord.State() 826 st.Lock() 827 defer st.Unlock() 828 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 829 c.Assert(err, IsNil) 830 831 checkSeedTasks(c, tsAll) 832 833 // now run the change and check the result 834 // use the expected kind otherwise settle with start another one 835 chg := st.NewChange("seed", "run the populate from seed changes") 836 for _, ts := range tsAll { 837 chg.AddAll(ts) 838 } 839 c.Assert(st.Changes(), HasLen, 1) 840 841 var configured []string 842 hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) { 843 ctx.Lock() 844 defer ctx.Unlock() 845 // we have a gadget at this point(s) 846 ok, err := snapstate.HasSnapOfType(st, snap.TypeGadget) 847 c.Check(err, IsNil) 848 c.Check(ok, Equals, true) 849 configured = append(configured, ctx.InstanceName()) 850 return nil, nil 851 } 852 853 rhk := hookstate.MockRunHook(hookInvoke) 854 defer rhk() 855 856 // ensure we have something that captures the core config 857 restore := configstate.MockConfigcoreRun(func(config.Conf) error { 858 configured = append(configured, "configcore") 859 return nil 860 }) 861 defer restore() 862 863 // avoid device reg 864 chg1 := st.NewChange("become-operational", "init device") 865 chg1.SetStatus(state.DoingStatus) 866 867 st.Unlock() 868 err = s.overlord.Settle(settleTimeout) 869 st.Lock() 870 c.Assert(chg.Err(), IsNil) 871 c.Assert(err, IsNil) 872 873 // and check the snap got correctly installed 874 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) 875 876 // verify 877 r, err := os.Open(dirs.SnapStateFile) 878 c.Assert(err, IsNil) 879 state, err := state.ReadState(nil, r) 880 c.Assert(err, IsNil) 881 882 state.Lock() 883 defer state.Unlock() 884 tr := config.NewTransaction(state) 885 var val string 886 887 // check core, kernel, gadget 888 _, err = snapstate.CurrentInfo(state, "core") 889 c.Assert(err, IsNil) 890 err = tr.Get("core", "core-cfg", &val) 891 c.Assert(err, IsNil) 892 c.Check(val, Equals, "core_cfg_defl") 893 894 _, err = snapstate.CurrentInfo(state, "pc-kernel") 895 c.Assert(err, IsNil) 896 err = tr.Get("pc-kernel", "pc-kernel-cfg", &val) 897 c.Assert(err, IsNil) 898 c.Check(val, Equals, "pc-kernel_cfg_defl") 899 900 _, err = snapstate.CurrentInfo(state, "pc") 901 c.Assert(err, IsNil) 902 err = tr.Get("pc", "pc-cfg", &val) 903 c.Assert(err, IsNil) 904 c.Check(val, Equals, "pc_cfg_defl") 905 906 // check foo 907 info, err := snapstate.CurrentInfo(state, "foo") 908 c.Assert(err, IsNil) 909 c.Assert(info.SnapID, Equals, "foodidididididididididididididid") 910 c.Assert(info.Revision, Equals, snap.R(128)) 911 pubAcct, err := assertstate.Publisher(st, info.SnapID) 912 c.Assert(err, IsNil) 913 c.Check(pubAcct.AccountID(), Equals, "developerid") 914 915 // check foo config 916 err = tr.Get("foo", "foo-cfg", &val) 917 c.Assert(err, IsNil) 918 c.Check(val, Equals, "foo.") 919 920 c.Check(configured, DeepEquals, []string{"configcore", "pc-kernel", "pc", "foo"}) 921 922 // and ensure state is now considered seeded 923 var seeded bool 924 err = state.Get("seeded", &seeded) 925 c.Assert(err, IsNil) 926 c.Check(seeded, Equals, true) 927 } 928 929 func (s *firstBoot16Suite) TestPopulateFromSeedGadgetConnectHappy(c *C) { 930 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 931 bootloader.Force(bloader) 932 defer bootloader.Force(nil) 933 bloader.SetBootKernel("pc-kernel_1.snap") 934 bloader.SetBootBase("core_1.snap") 935 936 const connectionsYaml = ` 937 connections: 938 - plug: foodidididididididididididididid:network-control 939 ` 940 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, connectionsYaml) 941 942 s.WriteAssertions("developer.account", s.devAcct) 943 944 snapYaml := `name: foo 945 version: 1.0 946 plugs: 947 network-control: 948 ` 949 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 950 s.WriteAssertions("foo.asserts", fooDecl, fooRev) 951 952 // add a model assertion and its chain 953 assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo") 954 s.WriteAssertions("model.asserts", assertsChain...) 955 956 // create a seed.yaml 957 content := []byte(fmt.Sprintf(` 958 snaps: 959 - name: core 960 file: %s 961 - name: pc-kernel 962 file: %s 963 - name: pc 964 file: %s 965 - name: foo 966 file: %s 967 `, coreFname, kernelFname, gadgetFname, fooFname)) 968 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 969 c.Assert(err, IsNil) 970 971 // run the firstboot stuff 972 st := s.overlord.State() 973 st.Lock() 974 defer st.Unlock() 975 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 976 c.Assert(err, IsNil) 977 978 checkSeedTasks(c, tsAll) 979 980 // now run the change and check the result 981 // use the expected kind otherwise settle with start another one 982 chg := st.NewChange("seed", "run the populate from seed changes") 983 for _, ts := range tsAll { 984 chg.AddAll(ts) 985 } 986 c.Assert(st.Changes(), HasLen, 1) 987 988 // avoid device reg 989 chg1 := st.NewChange("become-operational", "init device") 990 chg1.SetStatus(state.DoingStatus) 991 992 st.Unlock() 993 err = s.overlord.Settle(settleTimeout) 994 st.Lock() 995 c.Assert(chg.Err(), IsNil) 996 c.Assert(err, IsNil) 997 998 // and check the snap got correctly installed 999 c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true) 1000 1001 // verify 1002 r, err := os.Open(dirs.SnapStateFile) 1003 c.Assert(err, IsNil) 1004 state, err := state.ReadState(nil, r) 1005 c.Assert(err, IsNil) 1006 1007 state.Lock() 1008 defer state.Unlock() 1009 1010 // check foo 1011 info, err := snapstate.CurrentInfo(state, "foo") 1012 c.Assert(err, IsNil) 1013 c.Assert(info.SnapID, Equals, "foodidididididididididididididid") 1014 c.Assert(info.Revision, Equals, snap.R(128)) 1015 pubAcct, err := assertstate.Publisher(st, info.SnapID) 1016 c.Assert(err, IsNil) 1017 c.Check(pubAcct.AccountID(), Equals, "developerid") 1018 1019 // check connection 1020 var conns map[string]interface{} 1021 err = state.Get("conns", &conns) 1022 c.Assert(err, IsNil) 1023 c.Check(conns, HasLen, 1) 1024 c.Check(conns, DeepEquals, map[string]interface{}{ 1025 "foo:network-control core:network-control": map[string]interface{}{ 1026 "interface": "network-control", "auto": true, "by-gadget": true, 1027 }, 1028 }) 1029 1030 // and ensure state is now considered seeded 1031 var seeded bool 1032 err = state.Get("seeded", &seeded) 1033 c.Assert(err, IsNil) 1034 c.Check(seeded, Equals, true) 1035 } 1036 1037 func (s *firstBoot16Suite) TestImportAssertionsFromSeedClassicModelMismatch(c *C) { 1038 restore := release.MockOnClassic(true) 1039 defer restore() 1040 1041 ovld, err := overlord.New(nil) 1042 c.Assert(err, IsNil) 1043 st := ovld.State() 1044 1045 // add the odel assertion and its chain 1046 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1047 s.WriteAssertions("model.asserts", assertsChain...) 1048 1049 // import them 1050 st.Lock() 1051 defer st.Unlock() 1052 1053 deviceSeed, err := seed.Open(dirs.SnapSeedDir, "") 1054 c.Assert(err, IsNil) 1055 1056 _, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed) 1057 c.Assert(err, ErrorMatches, "cannot seed a classic system with an all-snaps model") 1058 } 1059 1060 func (s *firstBoot16Suite) TestImportAssertionsFromSeedAllSnapsModelMismatch(c *C) { 1061 ovld, err := overlord.New(nil) 1062 c.Assert(err, IsNil) 1063 st := ovld.State() 1064 1065 // add the model assertion and its chain 1066 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil) 1067 s.WriteAssertions("model.asserts", assertsChain...) 1068 1069 // import them 1070 st.Lock() 1071 defer st.Unlock() 1072 1073 deviceSeed, err := seed.Open(dirs.SnapSeedDir, "") 1074 c.Assert(err, IsNil) 1075 1076 _, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed) 1077 c.Assert(err, ErrorMatches, "cannot seed an all-snaps system with a classic model") 1078 } 1079 1080 func (s *firstBoot16Suite) TestImportAssertionsFromSeedHappy(c *C) { 1081 ovld, err := overlord.New(nil) 1082 c.Assert(err, IsNil) 1083 st := ovld.State() 1084 1085 // add a bunch of assertions (model assertion and its chain) 1086 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1087 for i, as := range assertsChain { 1088 fname := strconv.Itoa(i) 1089 if as.Type() == asserts.ModelType { 1090 fname = "model" 1091 } 1092 s.WriteAssertions(fname, as) 1093 } 1094 1095 // import them 1096 st.Lock() 1097 defer st.Unlock() 1098 1099 deviceSeed, err := seed.Open(dirs.SnapSeedDir, "") 1100 c.Assert(err, IsNil) 1101 1102 model, err := devicestate.ImportAssertionsFromSeed(st, deviceSeed) 1103 c.Assert(err, IsNil) 1104 c.Assert(model, NotNil) 1105 1106 // verify that the model was added 1107 db := assertstate.DB(st) 1108 as, err := db.Find(asserts.ModelType, map[string]string{ 1109 "series": "16", 1110 "brand-id": "my-brand", 1111 "model": "my-model", 1112 }) 1113 c.Assert(err, IsNil) 1114 _, ok := as.(*asserts.Model) 1115 c.Check(ok, Equals, true) 1116 1117 ds, err := devicestatetest.Device(st) 1118 c.Assert(err, IsNil) 1119 c.Check(ds.Brand, Equals, "my-brand") 1120 c.Check(ds.Model, Equals, "my-model") 1121 1122 c.Check(model.BrandID(), Equals, "my-brand") 1123 c.Check(model.Model(), Equals, "my-model") 1124 } 1125 1126 func (s *firstBoot16Suite) TestImportAssertionsFromSeedMissingSig(c *C) { 1127 st := s.overlord.State() 1128 st.Lock() 1129 defer st.Unlock() 1130 1131 // write out only the model assertion 1132 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1133 for _, as := range assertsChain { 1134 if as.Type() == asserts.ModelType { 1135 s.WriteAssertions("model", as) 1136 break 1137 } 1138 } 1139 1140 deviceSeed, err := seed.Open(dirs.SnapSeedDir, "") 1141 c.Assert(err, IsNil) 1142 1143 // try import and verify that its rejects because other assertions are 1144 // missing 1145 _, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed) 1146 c.Assert(err, ErrorMatches, "cannot resolve prerequisite assertion: account-key .*") 1147 } 1148 1149 func (s *firstBoot16Suite) TestImportAssertionsFromSeedTwoModelAsserts(c *C) { 1150 st := s.overlord.State() 1151 st.Lock() 1152 defer st.Unlock() 1153 1154 // write out two model assertions 1155 model := s.Brands.Model("my-brand", "my-model", modelHeaders("my-model")) 1156 s.WriteAssertions("model", model) 1157 1158 model2 := s.Brands.Model("my-brand", "my-second-model", modelHeaders("my-second-model")) 1159 s.WriteAssertions("model2", model2) 1160 1161 deviceSeed, err := seed.Open(dirs.SnapSeedDir, "") 1162 c.Assert(err, IsNil) 1163 1164 // try import and verify that its rejects because other assertions are 1165 // missing 1166 _, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed) 1167 c.Assert(err, ErrorMatches, "cannot have multiple model assertions in seed") 1168 } 1169 1170 func (s *firstBoot16Suite) TestImportAssertionsFromSeedNoModelAsserts(c *C) { 1171 st := s.overlord.State() 1172 st.Lock() 1173 defer st.Unlock() 1174 1175 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1176 for i, as := range assertsChain { 1177 if as.Type() != asserts.ModelType { 1178 s.WriteAssertions(strconv.Itoa(i), as) 1179 } 1180 } 1181 1182 deviceSeed, err := seed.Open(dirs.SnapSeedDir, "") 1183 c.Assert(err, IsNil) 1184 1185 // try import and verify that its rejects because other assertions are 1186 // missing 1187 _, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed) 1188 c.Assert(err, ErrorMatches, "seed must have a model assertion") 1189 } 1190 1191 type core18SnapsOpts struct { 1192 classic bool 1193 gadget bool 1194 } 1195 1196 func (s *firstBoot16BaseTest) makeCore18Snaps(c *C, opts *core18SnapsOpts) (core18Fn, snapdFn, kernelFn, gadgetFn string) { 1197 if opts == nil { 1198 opts = &core18SnapsOpts{} 1199 } 1200 1201 files := [][]string{} 1202 1203 core18Yaml := `name: core18 1204 version: 1.0 1205 type: base` 1206 core18Fname, core18Decl, core18Rev := s.MakeAssertedSnap(c, core18Yaml, files, snap.R(1), "canonical") 1207 s.WriteAssertions("core18.asserts", core18Rev, core18Decl) 1208 1209 snapdYaml := `name: snapd 1210 version: 1.0 1211 ` 1212 snapdFname, snapdDecl, snapdRev := s.MakeAssertedSnap(c, snapdYaml, nil, snap.R(2), "canonical") 1213 s.WriteAssertions("snapd.asserts", snapdRev, snapdDecl) 1214 1215 var kernelFname string 1216 if !opts.classic { 1217 kernelYaml := `name: pc-kernel 1218 version: 1.0 1219 type: kernel` 1220 fname, kernelDecl, kernelRev := s.MakeAssertedSnap(c, kernelYaml, files, snap.R(1), "canonical") 1221 s.WriteAssertions("kernel.asserts", kernelRev, kernelDecl) 1222 kernelFname = fname 1223 } 1224 1225 if !opts.classic { 1226 gadgetYaml := ` 1227 volumes: 1228 volume-id: 1229 bootloader: grub 1230 ` 1231 files = append(files, []string{"meta/gadget.yaml", gadgetYaml}) 1232 } 1233 1234 var gadgetFname string 1235 if !opts.classic || opts.gadget { 1236 gaYaml := `name: pc 1237 version: 1.0 1238 type: gadget 1239 base: core18 1240 ` 1241 fname, gadgetDecl, gadgetRev := s.MakeAssertedSnap(c, gaYaml, files, snap.R(1), "canonical") 1242 s.WriteAssertions("gadget.asserts", gadgetRev, gadgetDecl) 1243 gadgetFname = fname 1244 } 1245 1246 return core18Fname, snapdFname, kernelFname, gadgetFname 1247 } 1248 1249 func (s *firstBoot16Suite) TestPopulateFromSeedWithBaseHappy(c *C) { 1250 var sysdLog [][]string 1251 systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1252 sysdLog = append(sysdLog, cmd) 1253 return []byte("ActiveState=inactive\n"), nil 1254 }) 1255 defer systemctlRestorer() 1256 1257 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 1258 bootloader.Force(bloader) 1259 defer bootloader.Force(nil) 1260 bloader.SetBootKernel("pc-kernel_1.snap") 1261 bloader.SetBootBase("core18_1.snap") 1262 1263 core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil) 1264 1265 s.WriteAssertions("developer.account", s.devAcct) 1266 1267 // add a model assertion and its chain 1268 assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"}) 1269 s.WriteAssertions("model.asserts", assertsChain...) 1270 1271 // create a seed.yaml 1272 content := []byte(fmt.Sprintf(` 1273 snaps: 1274 - name: snapd 1275 file: %s 1276 - name: core18 1277 file: %s 1278 - name: pc-kernel 1279 file: %s 1280 - name: pc 1281 file: %s 1282 `, snapdFname, core18Fname, kernelFname, gadgetFname)) 1283 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1284 c.Assert(err, IsNil) 1285 1286 // run the firstboot stuff 1287 st := s.overlord.State() 1288 st.Lock() 1289 defer st.Unlock() 1290 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1291 c.Assert(err, IsNil) 1292 1293 checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc") 1294 1295 // now run the change and check the result 1296 // use the expected kind otherwise settle with start another one 1297 chg := st.NewChange("seed", "run the populate from seed changes") 1298 for _, ts := range tsAll { 1299 chg.AddAll(ts) 1300 } 1301 c.Assert(st.Changes(), HasLen, 1) 1302 1303 c.Assert(chg.Err(), IsNil) 1304 1305 // avoid device reg 1306 chg1 := st.NewChange("become-operational", "init device") 1307 chg1.SetStatus(state.DoingStatus) 1308 1309 // run change until it wants to restart 1310 st.Unlock() 1311 err = s.overlord.Settle(settleTimeout) 1312 st.Lock() 1313 c.Assert(err, IsNil) 1314 1315 // at this point the system is "restarting", pretend the restart has 1316 // happened 1317 c.Assert(chg.Status(), Equals, state.DoingStatus) 1318 state.MockRestarting(st, state.RestartUnset) 1319 st.Unlock() 1320 err = s.overlord.Settle(settleTimeout) 1321 st.Lock() 1322 c.Assert(err, IsNil) 1323 c.Assert(chg.Status(), Equals, state.DoneStatus) 1324 1325 // verify 1326 r, err := os.Open(dirs.SnapStateFile) 1327 c.Assert(err, IsNil) 1328 state, err := state.ReadState(nil, r) 1329 c.Assert(err, IsNil) 1330 1331 state.Lock() 1332 defer state.Unlock() 1333 // check snapd, core18, kernel, gadget 1334 _, err = snapstate.CurrentInfo(state, "snapd") 1335 c.Check(err, IsNil) 1336 _, err = snapstate.CurrentInfo(state, "core18") 1337 c.Check(err, IsNil) 1338 _, err = snapstate.CurrentInfo(state, "pc-kernel") 1339 c.Check(err, IsNil) 1340 _, err = snapstate.CurrentInfo(state, "pc") 1341 c.Check(err, IsNil) 1342 1343 // ensure required flag is set on all essential snaps 1344 var snapst snapstate.SnapState 1345 for _, reqName := range []string{"snapd", "core18", "pc-kernel", "pc"} { 1346 err = snapstate.Get(state, reqName, &snapst) 1347 c.Assert(err, IsNil) 1348 c.Assert(snapst.Required, Equals, true, Commentf("required not set for %v", reqName)) 1349 } 1350 1351 // the right systemd commands were run 1352 c.Check(sysdLog, testutil.DeepContains, []string{"start", "usr-lib-snapd.mount"}) 1353 1354 // and ensure state is now considered seeded 1355 var seeded bool 1356 err = state.Get("seeded", &seeded) 1357 c.Assert(err, IsNil) 1358 c.Check(seeded, Equals, true) 1359 1360 // check we set seed-time 1361 var seedTime time.Time 1362 err = state.Get("seed-time", &seedTime) 1363 c.Assert(err, IsNil) 1364 c.Check(seedTime.IsZero(), Equals, false) 1365 } 1366 1367 func (s *firstBoot16Suite) TestPopulateFromSeedOrdering(c *C) { 1368 s.WriteAssertions("developer.account", s.devAcct) 1369 1370 // add a model assertion and its chain 1371 assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"}) 1372 s.WriteAssertions("model.asserts", assertsChain...) 1373 1374 core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil) 1375 1376 snapYaml := `name: snap-req-other-base 1377 version: 1.0 1378 base: other-base 1379 ` 1380 snapFname, snapDecl, snapRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1381 s.WriteAssertions("snap-req-other-base.asserts", s.devAcct, snapRev, snapDecl) 1382 baseYaml := `name: other-base 1383 version: 1.0 1384 type: base 1385 ` 1386 baseFname, baseDecl, baseRev := s.MakeAssertedSnap(c, baseYaml, nil, snap.R(127), "developerid") 1387 s.WriteAssertions("other-base.asserts", s.devAcct, baseRev, baseDecl) 1388 1389 // create a seed.yaml 1390 content := []byte(fmt.Sprintf(` 1391 snaps: 1392 - name: snapd 1393 file: %s 1394 - name: core18 1395 file: %s 1396 - name: pc-kernel 1397 file: %s 1398 - name: pc 1399 file: %s 1400 - name: snap-req-other-base 1401 file: %s 1402 - name: other-base 1403 file: %s 1404 `, snapdFname, core18Fname, kernelFname, gadgetFname, snapFname, baseFname)) 1405 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1406 c.Assert(err, IsNil) 1407 1408 // run the firstboot stuff 1409 st := s.overlord.State() 1410 st.Lock() 1411 defer st.Unlock() 1412 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1413 c.Assert(err, IsNil) 1414 1415 checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc", "other-base", "snap-req-other-base") 1416 } 1417 1418 func (s *firstBoot16Suite) TestFirstbootGadgetBaseModelBaseMismatch(c *C) { 1419 s.WriteAssertions("developer.account", s.devAcct) 1420 1421 // add a model assertion and its chain 1422 assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"}) 1423 s.WriteAssertions("model.asserts", assertsChain...) 1424 1425 core18Fname, snapdFname, kernelFname, _ := s.makeCore18Snaps(c, nil) 1426 // take the gadget without "base: core18" 1427 _, _, gadgetFname := s.makeCoreSnaps(c, "") 1428 1429 // create a seed.yaml 1430 content := []byte(fmt.Sprintf(` 1431 snaps: 1432 - name: snapd 1433 file: %s 1434 - name: core18 1435 file: %s 1436 - name: pc-kernel 1437 file: %s 1438 - name: pc 1439 file: %s 1440 `, snapdFname, core18Fname, kernelFname, gadgetFname)) 1441 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1442 c.Assert(err, IsNil) 1443 1444 // run the firstboot stuff 1445 st := s.overlord.State() 1446 st.Lock() 1447 defer st.Unlock() 1448 1449 _, err = devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1450 c.Assert(err, ErrorMatches, `cannot use gadget snap because its base "core" is different from model base "core18"`) 1451 // note, cannot use st.Tasks() here as it filters out tasks with no change 1452 c.Check(st.TaskCount(), Equals, 0) 1453 } 1454 1455 func (s *firstBoot16Suite) TestPopulateFromSeedWrongContentProviderOrder(c *C) { 1456 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 1457 bootloader.Force(bloader) 1458 defer bootloader.Force(nil) 1459 bloader.SetBootKernel("pc-kernel_1.snap") 1460 bloader.SetBootBase("core_1.snap") 1461 1462 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "") 1463 1464 // a snap that uses content providers 1465 snapYaml := `name: gnome-calculator 1466 version: 1.0 1467 plugs: 1468 gtk-3-themes: 1469 interface: content 1470 default-provider: gtk-common-themes 1471 target: $SNAP/data-dir/themes 1472 ` 1473 calcFname, calcDecl, calcRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1474 s.WriteAssertions("calc.asserts", s.devAcct, calcRev, calcDecl) 1475 1476 // put a 2nd firstboot snap into the SnapBlobDir 1477 snapYaml = `name: gtk-common-themes 1478 version: 1.0 1479 slots: 1480 gtk-3-themes: 1481 interface: content 1482 source: 1483 read: 1484 - $SNAP/share/themes/Adawaita 1485 ` 1486 themesFname, themesDecl, themesRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(65), "developerid") 1487 s.WriteAssertions("themes.asserts", s.devAcct, themesDecl, themesRev) 1488 1489 // add a model assertion and its chain 1490 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1491 s.WriteAssertions("model.asserts", assertsChain...) 1492 1493 // create a seed.yaml 1494 content := []byte(fmt.Sprintf(` 1495 snaps: 1496 - name: core 1497 file: %s 1498 - name: pc-kernel 1499 file: %s 1500 - name: pc 1501 file: %s 1502 - name: gnome-calculator 1503 file: %s 1504 - name: gtk-common-themes 1505 file: %s 1506 `, coreFname, kernelFname, gadgetFname, calcFname, themesFname)) 1507 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1508 c.Assert(err, IsNil) 1509 1510 // run the firstboot stuff 1511 st := s.overlord.State() 1512 st.Lock() 1513 defer st.Unlock() 1514 1515 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1516 c.Assert(err, IsNil) 1517 // use the expected kind otherwise settle with start another one 1518 chg := st.NewChange("seed", "run the populate from seed changes") 1519 for _, ts := range tsAll { 1520 chg.AddAll(ts) 1521 } 1522 c.Assert(st.Changes(), HasLen, 1) 1523 1524 // avoid device reg 1525 chg1 := st.NewChange("become-operational", "init device") 1526 chg1.SetStatus(state.DoingStatus) 1527 1528 st.Unlock() 1529 err = s.overlord.Settle(settleTimeout) 1530 st.Lock() 1531 1532 c.Assert(chg.Err(), IsNil) 1533 c.Assert(err, IsNil) 1534 1535 // verify the result 1536 var conns map[string]interface{} 1537 err = st.Get("conns", &conns) 1538 c.Assert(err, IsNil) 1539 c.Check(conns, HasLen, 1) 1540 conn, hasConn := conns["gnome-calculator:gtk-3-themes gtk-common-themes:gtk-3-themes"] 1541 c.Check(hasConn, Equals, true) 1542 c.Check(conn.(map[string]interface{})["auto"], Equals, true) 1543 c.Check(conn.(map[string]interface{})["interface"], Equals, "content") 1544 } 1545 1546 func (s *firstBoot16Suite) TestPopulateFromSeedMissingBase(c *C) { 1547 s.WriteAssertions("developer.account", s.devAcct) 1548 1549 // add a model assertion and its chain 1550 assertsChain := s.makeModelAssertionChain(c, "my-model", nil) 1551 s.WriteAssertions("model.asserts", assertsChain...) 1552 1553 coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "") 1554 1555 // TODO: this test doesn't particularly need to use a local snap 1556 // local snap with unknown base 1557 snapYaml = `name: local 1558 base: foo 1559 version: 1.0` 1560 mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml, nil) 1561 localFname := filepath.Base(mockSnapFile) 1562 targetSnapFile2 := filepath.Join(dirs.SnapSeedDir, "snaps", localFname) 1563 c.Assert(os.Rename(mockSnapFile, targetSnapFile2), IsNil) 1564 1565 // create a seed.yaml 1566 content := []byte(fmt.Sprintf(` 1567 snaps: 1568 - name: core 1569 file: %s 1570 - name: pc-kernel 1571 file: %s 1572 - name: pc 1573 file: %s 1574 - name: local 1575 unasserted: true 1576 file: %s 1577 `, coreFname, kernelFname, gadgetFname, localFname)) 1578 1579 c.Assert(ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644), IsNil) 1580 1581 // run the firstboot stuff 1582 st := s.overlord.State() 1583 st.Lock() 1584 defer st.Unlock() 1585 _, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1586 c.Assert(err, ErrorMatches, `cannot use snap "local": base "foo" is missing`) 1587 } 1588 1589 func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicWithSnapdOnlyHappy(c *C) { 1590 restore := release.MockOnClassic(true) 1591 defer restore() 1592 1593 var sysdLog [][]string 1594 systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1595 sysdLog = append(sysdLog, cmd) 1596 return []byte("ActiveState=inactive\n"), nil 1597 }) 1598 defer systemctlRestorer() 1599 1600 core18Fname, snapdFname, _, _ := s.makeCore18Snaps(c, &core18SnapsOpts{ 1601 classic: true, 1602 }) 1603 1604 // put a firstboot snap into the SnapBlobDir 1605 snapYaml := `name: foo 1606 version: 1.0 1607 base: core18 1608 ` 1609 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1610 s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl) 1611 1612 // add a model assertion and its chain 1613 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil) 1614 s.WriteAssertions("model.asserts", assertsChain...) 1615 1616 // create a seed.yaml 1617 content := []byte(fmt.Sprintf(` 1618 snaps: 1619 - name: snapd 1620 file: %s 1621 - name: foo 1622 file: %s 1623 - name: core18 1624 file: %s 1625 `, snapdFname, fooFname, core18Fname)) 1626 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1627 c.Assert(err, IsNil) 1628 1629 // run the firstboot stuff 1630 st := s.overlord.State() 1631 st.Lock() 1632 defer st.Unlock() 1633 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1634 c.Assert(err, IsNil) 1635 1636 checkOrder(c, tsAll, "snapd", "core18", "foo") 1637 1638 // now run the change and check the result 1639 // use the expected kind otherwise settle with start another one 1640 chg := st.NewChange("seed", "run the populate from seed changes") 1641 for _, ts := range tsAll { 1642 chg.AddAll(ts) 1643 } 1644 c.Assert(st.Changes(), HasLen, 1) 1645 1646 c.Assert(chg.Err(), IsNil) 1647 1648 // avoid device reg 1649 chg1 := st.NewChange("become-operational", "init device") 1650 chg1.SetStatus(state.DoingStatus) 1651 1652 // run change until it wants to restart 1653 st.Unlock() 1654 err = s.overlord.Settle(settleTimeout) 1655 st.Lock() 1656 c.Assert(err, IsNil) 1657 1658 // at this point the system is "restarting", pretend the restart has 1659 // happened 1660 c.Assert(chg.Status(), Equals, state.DoingStatus) 1661 state.MockRestarting(st, state.RestartUnset) 1662 st.Unlock() 1663 err = s.overlord.Settle(settleTimeout) 1664 st.Lock() 1665 c.Assert(err, IsNil) 1666 c.Assert(chg.Status(), Equals, state.DoneStatus) 1667 1668 // verify 1669 r, err := os.Open(dirs.SnapStateFile) 1670 c.Assert(err, IsNil) 1671 state, err := state.ReadState(nil, r) 1672 c.Assert(err, IsNil) 1673 1674 state.Lock() 1675 defer state.Unlock() 1676 // check snapd, core18, kernel, gadget 1677 _, err = snapstate.CurrentInfo(state, "snapd") 1678 c.Check(err, IsNil) 1679 _, err = snapstate.CurrentInfo(state, "core18") 1680 c.Check(err, IsNil) 1681 _, err = snapstate.CurrentInfo(state, "foo") 1682 c.Check(err, IsNil) 1683 1684 // and ensure state is now considered seeded 1685 var seeded bool 1686 err = state.Get("seeded", &seeded) 1687 c.Assert(err, IsNil) 1688 c.Check(seeded, Equals, true) 1689 1690 // check we set seed-time 1691 var seedTime time.Time 1692 err = state.Get("seed-time", &seedTime) 1693 c.Assert(err, IsNil) 1694 c.Check(seedTime.IsZero(), Equals, false) 1695 } 1696 1697 func (s *firstBoot16Suite) TestPopulateFromSeedMissingAssertions(c *C) { 1698 restore := release.MockOnClassic(true) 1699 defer restore() 1700 1701 core18Fname, snapdFname, _, _ := s.makeCore18Snaps(c, &core18SnapsOpts{}) 1702 1703 // create a seed.yaml 1704 content := []byte(fmt.Sprintf(` 1705 snaps: 1706 - name: snapd 1707 file: %s 1708 - name: core18 1709 file: %s 1710 `, snapdFname, core18Fname)) 1711 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1712 c.Assert(err, IsNil) 1713 1714 // run the firstboot stuff 1715 st := s.overlord.State() 1716 st.Lock() 1717 defer st.Unlock() 1718 _, err = devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1719 c.Assert(err, NotNil) 1720 // note, cannot use st.Tasks() here as it filters out tasks with no change 1721 c.Check(st.TaskCount(), Equals, 0) 1722 } 1723 1724 func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicWithSnapdOnlyAndGadgetHappy(c *C) { 1725 restore := release.MockOnClassic(true) 1726 defer restore() 1727 1728 var sysdLog [][]string 1729 systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1730 sysdLog = append(sysdLog, cmd) 1731 return []byte("ActiveState=inactive\n"), nil 1732 }) 1733 defer systemctlRestorer() 1734 1735 core18Fname, snapdFname, _, gadgetFname := s.makeCore18Snaps(c, &core18SnapsOpts{ 1736 classic: true, 1737 gadget: true, 1738 }) 1739 1740 // put a firstboot snap into the SnapBlobDir 1741 snapYaml := `name: foo 1742 version: 1.0 1743 base: core18 1744 ` 1745 fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid") 1746 1747 s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl) 1748 1749 // add a model assertion and its chain 1750 assertsChain := s.makeModelAssertionChain(c, "my-model-classic", map[string]interface{}{"gadget": "pc"}) 1751 s.WriteAssertions("model.asserts", assertsChain...) 1752 1753 // create a seed.yaml 1754 content := []byte(fmt.Sprintf(` 1755 snaps: 1756 - name: snapd 1757 file: %s 1758 - name: foo 1759 file: %s 1760 - name: core18 1761 file: %s 1762 - name: pc 1763 file: %s 1764 `, snapdFname, fooFname, core18Fname, gadgetFname)) 1765 err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644) 1766 c.Assert(err, IsNil) 1767 1768 // run the firstboot stuff 1769 st := s.overlord.State() 1770 st.Lock() 1771 defer st.Unlock() 1772 tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings) 1773 c.Assert(err, IsNil) 1774 1775 checkOrder(c, tsAll, "snapd", "core18", "pc", "foo") 1776 1777 // now run the change and check the result 1778 // use the expected kind otherwise settle with start another one 1779 chg := st.NewChange("seed", "run the populate from seed changes") 1780 for _, ts := range tsAll { 1781 chg.AddAll(ts) 1782 } 1783 c.Assert(st.Changes(), HasLen, 1) 1784 1785 c.Assert(chg.Err(), IsNil) 1786 1787 // avoid device reg 1788 chg1 := st.NewChange("become-operational", "init device") 1789 chg1.SetStatus(state.DoingStatus) 1790 1791 // run change until it wants to restart 1792 st.Unlock() 1793 err = s.overlord.Settle(settleTimeout) 1794 st.Lock() 1795 c.Assert(err, IsNil) 1796 1797 // at this point the system is "restarting", pretend the restart has 1798 // happened 1799 c.Assert(chg.Status(), Equals, state.DoingStatus) 1800 state.MockRestarting(st, state.RestartUnset) 1801 st.Unlock() 1802 err = s.overlord.Settle(settleTimeout) 1803 st.Lock() 1804 c.Assert(err, IsNil) 1805 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%s", chg.Err())) 1806 1807 // verify 1808 r, err := os.Open(dirs.SnapStateFile) 1809 c.Assert(err, IsNil) 1810 state, err := state.ReadState(nil, r) 1811 c.Assert(err, IsNil) 1812 1813 state.Lock() 1814 defer state.Unlock() 1815 // check snapd, core18, kernel, gadget 1816 _, err = snapstate.CurrentInfo(state, "snapd") 1817 c.Check(err, IsNil) 1818 _, err = snapstate.CurrentInfo(state, "core18") 1819 c.Check(err, IsNil) 1820 _, err = snapstate.CurrentInfo(state, "pc") 1821 c.Check(err, IsNil) 1822 _, err = snapstate.CurrentInfo(state, "foo") 1823 c.Check(err, IsNil) 1824 1825 // and ensure state is now considered seeded 1826 var seeded bool 1827 err = state.Get("seeded", &seeded) 1828 c.Assert(err, IsNil) 1829 c.Check(seeded, Equals, true) 1830 1831 // check we set seed-time 1832 var seedTime time.Time 1833 err = state.Get("seed-time", &seedTime) 1834 c.Assert(err, IsNil) 1835 c.Check(seedTime.IsZero(), Equals, false) 1836 } 1837 1838 func (s *firstBoot16Suite) TestCriticalTaskEdgesForPreseed(c *C) { 1839 st := s.overlord.State() 1840 st.Lock() 1841 defer st.Unlock() 1842 1843 t1 := st.NewTask("task1", "") 1844 t2 := st.NewTask("task2", "") 1845 t3 := st.NewTask("task2", "") 1846 1847 ts := state.NewTaskSet(t1, t2, t3) 1848 ts.MarkEdge(t1, snapstate.BeginEdge) 1849 ts.MarkEdge(t2, snapstate.BeforeHooksEdge) 1850 ts.MarkEdge(t3, snapstate.HooksEdge) 1851 1852 beginEdge, beforeHooksEdge, hooksEdge, err := devicestate.CriticalTaskEdges(ts) 1853 c.Assert(err, IsNil) 1854 c.Assert(beginEdge, NotNil) 1855 c.Assert(beforeHooksEdge, NotNil) 1856 c.Assert(hooksEdge, NotNil) 1857 1858 c.Check(beginEdge.Kind(), Equals, "task1") 1859 c.Check(beforeHooksEdge.Kind(), Equals, "task2") 1860 c.Check(hooksEdge.Kind(), Equals, "task2") 1861 } 1862 1863 func (s *firstBoot16Suite) TestCriticalTaskEdgesForPreseedMissing(c *C) { 1864 st := s.overlord.State() 1865 st.Lock() 1866 defer st.Unlock() 1867 1868 t1 := st.NewTask("task1", "") 1869 t2 := st.NewTask("task2", "") 1870 t3 := st.NewTask("task2", "") 1871 1872 ts := state.NewTaskSet(t1, t2, t3) 1873 ts.MarkEdge(t1, snapstate.BeginEdge) 1874 1875 _, _, _, err := devicestate.CriticalTaskEdges(ts) 1876 c.Assert(err, NotNil) 1877 1878 ts = state.NewTaskSet(t1, t2, t3) 1879 ts.MarkEdge(t1, snapstate.BeginEdge) 1880 ts.MarkEdge(t2, snapstate.BeforeHooksEdge) 1881 _, _, _, err = devicestate.CriticalTaskEdges(ts) 1882 c.Assert(err, NotNil) 1883 1884 ts = state.NewTaskSet(t1, t2, t3) 1885 ts.MarkEdge(t1, snapstate.BeginEdge) 1886 ts.MarkEdge(t3, snapstate.HooksEdge) 1887 _, _, _, err = devicestate.CriticalTaskEdges(ts) 1888 c.Assert(err, NotNil) 1889 }