github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/seed/seed16_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019-2020 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 seed_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "testing" 28 29 . "gopkg.in/check.v1" 30 "gopkg.in/yaml.v2" 31 32 "github.com/snapcore/snapd/asserts" 33 "github.com/snapcore/snapd/asserts/assertstest" 34 "github.com/snapcore/snapd/seed" 35 "github.com/snapcore/snapd/seed/seedtest" 36 "github.com/snapcore/snapd/snap" 37 "github.com/snapcore/snapd/snap/snaptest" 38 "github.com/snapcore/snapd/testutil" 39 "github.com/snapcore/snapd/timings" 40 ) 41 42 func Test(t *testing.T) { TestingT(t) } 43 44 type seed16Suite struct { 45 testutil.BaseTest 46 47 *seedtest.TestingSeed16 48 devAcct *asserts.Account 49 50 seedDir string 51 52 seed16 seed.Seed 53 54 db *asserts.Database 55 56 perfTimings timings.Measurer 57 } 58 59 var _ = Suite(&seed16Suite{}) 60 61 var ( 62 brandPrivKey, _ = assertstest.GenerateKey(752) 63 ) 64 65 func (s *seed16Suite) SetUpTest(c *C) { 66 s.BaseTest.SetUpTest(c) 67 s.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 68 69 s.TestingSeed16 = &seedtest.TestingSeed16{} 70 s.SetupAssertSigning("canonical") 71 s.Brands.Register("my-brand", brandPrivKey, map[string]interface{}{ 72 "verification": "verified", 73 }) 74 75 s.SeedDir = c.MkDir() 76 77 s.devAcct = assertstest.NewAccount(s.StoreSigning, "developer", map[string]interface{}{ 78 "account-id": "developerid", 79 }, "") 80 assertstest.AddMany(s.StoreSigning, s.devAcct) 81 82 seed16, err := seed.Open(s.SeedDir, "") 83 c.Assert(err, IsNil) 84 s.seed16 = seed16 85 86 db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 87 Backstore: asserts.NewMemoryBackstore(), 88 Trusted: s.StoreSigning.Trusted, 89 }) 90 c.Assert(err, IsNil) 91 s.db = db 92 93 s.perfTimings = timings.New(nil) 94 } 95 96 func (s *seed16Suite) commitTo(b *asserts.Batch) error { 97 return b.CommitTo(s.db, nil) 98 } 99 100 func (s *seed16Suite) TestLoadAssertionsNoAssertions(c *C) { 101 c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), Equals, seed.ErrNoAssertions) 102 } 103 104 func (s *seed16Suite) TestLoadAssertionsNoModelAssertion(c *C) { 105 err := os.Mkdir(s.AssertsDir(), 0755) 106 c.Assert(err, IsNil) 107 108 c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), ErrorMatches, "seed must have a model assertion") 109 } 110 111 func (s *seed16Suite) TestLoadAssertionsTwoModelAssertionsError(c *C) { 112 err := os.Mkdir(s.AssertsDir(), 0755) 113 c.Assert(err, IsNil) 114 115 headers := map[string]interface{}{ 116 "architecture": "amd64", 117 "kernel": "pc-kernel", 118 "gadget": "pc", 119 } 120 modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers) 121 s.WriteAssertions("model.asserts", modelChain...) 122 modelChain = s.MakeModelAssertionChain("my-brand", "my-model-2", headers) 123 s.WriteAssertions("model2.asserts", modelChain...) 124 125 c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), ErrorMatches, "cannot have multiple model assertions in seed") 126 } 127 128 func (s *seed16Suite) TestLoadAssertionsConsistencyError(c *C) { 129 err := os.Mkdir(s.AssertsDir(), 0755) 130 c.Assert(err, IsNil) 131 132 // write out only the model assertion 133 headers := map[string]interface{}{ 134 "architecture": "amd64", 135 "kernel": "pc-kernel", 136 "gadget": "pc", 137 } 138 model := s.Brands.Model("my-brand", "my-model", headers) 139 s.WriteAssertions("model.asserts", model) 140 141 c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), ErrorMatches, "cannot resolve prerequisite assertion: account-key .*") 142 } 143 144 func (s *seed16Suite) TestLoadAssertionsModelHappy(c *C) { 145 err := os.Mkdir(s.AssertsDir(), 0755) 146 c.Assert(err, IsNil) 147 148 headers := map[string]interface{}{ 149 "architecture": "amd64", 150 "kernel": "pc-kernel", 151 "gadget": "pc", 152 } 153 modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers) 154 s.WriteAssertions("model.asserts", modelChain...) 155 156 err = s.seed16.LoadAssertions(s.db, s.commitTo) 157 c.Assert(err, IsNil) 158 159 model := s.seed16.Model() 160 c.Check(model.Model(), Equals, "my-model") 161 162 _, err = s.db.Find(asserts.ModelType, map[string]string{ 163 "series": "16", 164 "brand-id": "my-brand", 165 "model": "my-model", 166 }) 167 c.Assert(err, IsNil) 168 } 169 170 func (s *seed16Suite) TestLoadAssertionsModelTempDBHappy(c *C) { 171 r := seed.MockTrusted(s.StoreSigning.Trusted) 172 defer r() 173 174 err := os.Mkdir(s.AssertsDir(), 0755) 175 c.Assert(err, IsNil) 176 177 headers := map[string]interface{}{ 178 "architecture": "amd64", 179 "kernel": "pc-kernel", 180 "gadget": "pc", 181 } 182 modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers) 183 s.WriteAssertions("model.asserts", modelChain...) 184 185 err = s.seed16.LoadAssertions(nil, nil) 186 c.Assert(err, IsNil) 187 188 model := s.seed16.Model() 189 c.Check(model.Model(), Equals, "my-model") 190 191 brand, err := s.seed16.Brand() 192 c.Assert(err, IsNil) 193 c.Check(brand.AccountID(), Equals, "my-brand") 194 c.Check(brand.DisplayName(), Equals, "My-brand") 195 } 196 197 func (s *seed16Suite) TestLoadMetaNoMeta(c *C) { 198 err := os.Mkdir(s.AssertsDir(), 0755) 199 c.Assert(err, IsNil) 200 201 headers := map[string]interface{}{ 202 "architecture": "amd64", 203 "kernel": "pc-kernel", 204 "gadget": "pc", 205 } 206 modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers) 207 s.WriteAssertions("model.asserts", modelChain...) 208 209 err = s.seed16.LoadAssertions(s.db, s.commitTo) 210 c.Assert(err, IsNil) 211 212 err = s.seed16.LoadMeta(s.perfTimings) 213 c.Check(err, Equals, seed.ErrNoMeta) 214 } 215 216 func (s *seed16Suite) TestLoadMetaInvalidSeedYaml(c *C) { 217 err := os.Mkdir(s.AssertsDir(), 0755) 218 c.Assert(err, IsNil) 219 220 headers := map[string]interface{}{ 221 "architecture": "amd64", 222 "kernel": "pc-kernel", 223 "gadget": "pc", 224 } 225 modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers) 226 s.WriteAssertions("model.asserts", modelChain...) 227 228 err = s.seed16.LoadAssertions(s.db, s.commitTo) 229 c.Assert(err, IsNil) 230 231 // create a seed.yaml 232 content, err := yaml.Marshal(map[string]interface{}{ 233 "snaps": []*seed.InternalSnap16{{ 234 Name: "core", 235 Channel: "track/not-a-risk", 236 }}, 237 }) 238 c.Assert(err, IsNil) 239 err = ioutil.WriteFile(filepath.Join(s.SeedDir, "seed.yaml"), content, 0644) 240 c.Assert(err, IsNil) 241 242 err = s.seed16.LoadMeta(s.perfTimings) 243 c.Check(err, ErrorMatches, `cannot read seed yaml: invalid risk in channel name: track/not-a-risk`) 244 } 245 246 var snapYaml = seedtest.MergeSampleSnapYaml(seedtest.SampleSnapYaml, map[string]string{ 247 "private-snap": `name: private-snap 248 base: core18 249 version: 1.0 250 `, 251 "contactable-snap": `name: contactable-snap 252 base: core18 253 version: 1.0 254 `, 255 "cont-producer": `name: cont-producer 256 type: app 257 base: core18 258 version: 1.1 259 slots: 260 cont: 261 interface: content 262 content: cont 263 `, 264 "cont-consumer": `name: cont-consumer 265 base: core18 266 version: 1.0 267 plugs: 268 cont: 269 interface: content 270 content: cont 271 default-provider: cont-producer 272 `, 273 }) 274 275 const pcGadgetYaml = ` 276 volumes: 277 pc: 278 bootloader: grub 279 ` 280 281 var pcGadgetFiles = [][]string{ 282 {"meta/gadget.yaml", pcGadgetYaml}, 283 } 284 285 var snapFiles = map[string][][]string{ 286 "pc": pcGadgetFiles, 287 "pc=18": pcGadgetFiles, 288 } 289 290 var snapPublishers = map[string]string{ 291 "required": "developerid", 292 } 293 294 var ( 295 coreSeed = &seed.InternalSnap16{ 296 Name: "core", 297 Channel: "stable", 298 } 299 kernelSeed = &seed.InternalSnap16{ 300 Name: "pc-kernel", 301 Channel: "stable", 302 } 303 gadgetSeed = &seed.InternalSnap16{ 304 Name: "pc", 305 Channel: "stable", 306 } 307 requiredSeed = &seed.InternalSnap16{ 308 Name: "required", 309 Channel: "stable", 310 } 311 // Core 18 312 snapdSeed = &seed.InternalSnap16{ 313 Name: "snapd", 314 Channel: "stable", 315 } 316 core18Seed = &seed.InternalSnap16{ 317 Name: "core18", 318 Channel: "stable", 319 } 320 kernel18Seed = &seed.InternalSnap16{ 321 Name: "pc-kernel", 322 Channel: "18", 323 } 324 gadget18Seed = &seed.InternalSnap16{ 325 Name: "pc", 326 Channel: "18", 327 } 328 required18Seed = &seed.InternalSnap16{ 329 Name: "required18", 330 Channel: "stable", 331 } 332 classicSnapSeed = &seed.InternalSnap16{ 333 Name: "classic-snap", 334 Channel: "stable", 335 Classic: true, 336 } 337 classicGadgetSeed = &seed.InternalSnap16{ 338 Name: "classic-gadget", 339 Channel: "stable", 340 } 341 classicGadget18Seed = &seed.InternalSnap16{ 342 Name: "classic-gadget18", 343 Channel: "stable", 344 } 345 privateSnapSeed = &seed.InternalSnap16{ 346 Name: "private-snap", 347 Channel: "stable", 348 Private: true, 349 } 350 contactableSnapSeed = &seed.InternalSnap16{ 351 Name: "contactable-snap", 352 Channel: "stable", 353 Contact: "author@example.com", 354 } 355 ) 356 357 func (s *seed16Suite) makeSeed(c *C, modelHeaders map[string]interface{}, seedSnaps ...*seed.InternalSnap16) []*seed.InternalSnap16 { 358 coreHeaders := map[string]interface{}{ 359 "architecture": "amd64", 360 } 361 362 if _, ok := modelHeaders["classic"]; !ok { 363 coreHeaders["kernel"] = "pc-kernel" 364 coreHeaders["gadget"] = "pc" 365 } 366 367 err := os.Mkdir(s.AssertsDir(), 0755) 368 c.Assert(err, IsNil) 369 370 modelChain := s.MakeModelAssertionChain("my-brand", "my-model", coreHeaders, modelHeaders) 371 s.WriteAssertions("model.asserts", modelChain...) 372 373 err = os.Mkdir(s.SnapsDir(), 0755) 374 c.Assert(err, IsNil) 375 376 var completeSeedSnaps []*seed.InternalSnap16 377 for _, seedSnap := range seedSnaps { 378 completeSeedSnap := *seedSnap 379 var snapFname string 380 if seedSnap.Unasserted { 381 mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml[seedSnap.Name], snapFiles[seedSnap.Name]) 382 snapFname = filepath.Base(mockSnapFile) 383 err := os.Rename(mockSnapFile, filepath.Join(s.SeedDir, "snaps", snapFname)) 384 c.Assert(err, IsNil) 385 } else { 386 publisher := snapPublishers[seedSnap.Name] 387 if publisher == "" { 388 publisher = "canonical" 389 } 390 whichYaml := seedSnap.Name 391 if seedSnap.Channel != "stable" { 392 whichYaml = whichYaml + "=" + seedSnap.Channel 393 } 394 fname, decl, rev := s.MakeAssertedSnap(c, snapYaml[whichYaml], snapFiles[whichYaml], snap.R(1), publisher) 395 acct, err := s.StoreSigning.Find(asserts.AccountType, map[string]string{"account-id": publisher}) 396 c.Assert(err, IsNil) 397 s.WriteAssertions(fmt.Sprintf("%s.asserts", seedSnap.Name), rev, decl, acct) 398 snapFname = fname 399 } 400 completeSeedSnap.File = snapFname 401 completeSeedSnaps = append(completeSeedSnaps, &completeSeedSnap) 402 } 403 404 s.writeSeed(c, completeSeedSnaps) 405 406 return completeSeedSnaps 407 } 408 409 func (s *seed16Suite) writeSeed(c *C, seedSnaps []*seed.InternalSnap16) { 410 // create a seed.yaml 411 content, err := yaml.Marshal(map[string]interface{}{ 412 "snaps": seedSnaps, 413 }) 414 c.Assert(err, IsNil) 415 err = ioutil.WriteFile(filepath.Join(s.SeedDir, "seed.yaml"), content, 0644) 416 c.Assert(err, IsNil) 417 } 418 419 func (s *seed16Suite) expectedPath(snapName string) string { 420 return filepath.Join(s.SeedDir, "snaps", filepath.Base(s.AssertedSnap(snapName))) 421 } 422 423 func (s *seed16Suite) TestLoadMetaCore16Minimal(c *C) { 424 s.makeSeed(c, nil, coreSeed, kernelSeed, gadgetSeed) 425 426 err := s.seed16.LoadAssertions(s.db, s.commitTo) 427 c.Assert(err, IsNil) 428 429 err = s.seed16.LoadMeta(s.perfTimings) 430 c.Assert(err, IsNil) 431 432 c.Check(s.seed16.UsesSnapdSnap(), Equals, false) 433 434 essSnaps := s.seed16.EssentialSnaps() 435 c.Check(essSnaps, HasLen, 3) 436 437 c.Check(essSnaps, DeepEquals, []*seed.Snap{ 438 { 439 Path: s.expectedPath("core"), 440 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 441 EssentialType: snap.TypeOS, 442 Essential: true, 443 Required: true, 444 Channel: "stable", 445 }, { 446 Path: s.expectedPath("pc-kernel"), 447 SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo, 448 EssentialType: snap.TypeKernel, 449 Essential: true, 450 Required: true, 451 Channel: "stable", 452 }, { 453 Path: s.expectedPath("pc"), 454 SideInfo: &s.AssertedSnapInfo("pc").SideInfo, 455 EssentialType: snap.TypeGadget, 456 Essential: true, 457 Required: true, 458 Channel: "stable", 459 }, 460 }) 461 462 runSnaps, err := s.seed16.ModeSnaps("run") 463 c.Assert(err, IsNil) 464 c.Check(runSnaps, HasLen, 0) 465 } 466 467 func (s *seed16Suite) TestLoadMetaCore16(c *C) { 468 s.makeSeed(c, map[string]interface{}{ 469 "required-snaps": []interface{}{"required"}, 470 }, coreSeed, kernelSeed, gadgetSeed, requiredSeed) 471 472 err := s.seed16.LoadAssertions(s.db, s.commitTo) 473 c.Assert(err, IsNil) 474 475 err = s.seed16.LoadMeta(s.perfTimings) 476 c.Assert(err, IsNil) 477 478 essSnaps := s.seed16.EssentialSnaps() 479 c.Check(essSnaps, HasLen, 3) 480 481 runSnaps, err := s.seed16.ModeSnaps("run") 482 c.Assert(err, IsNil) 483 c.Check(runSnaps, HasLen, 1) 484 485 // check that PlaceInfo method works 486 pi := essSnaps[0].PlaceInfo() 487 c.Check(pi.Filename(), Equals, "core_1.snap") 488 pi = essSnaps[1].PlaceInfo() 489 c.Check(pi.Filename(), Equals, "pc-kernel_1.snap") 490 pi = essSnaps[2].PlaceInfo() 491 c.Check(pi.Filename(), Equals, "pc_1.snap") 492 493 c.Check(runSnaps, DeepEquals, []*seed.Snap{ 494 { 495 Path: s.expectedPath("required"), 496 SideInfo: &s.AssertedSnapInfo("required").SideInfo, 497 Required: true, 498 Channel: "stable", 499 }, 500 }) 501 } 502 503 func (s *seed16Suite) TestLoadMetaCore18Minimal(c *C) { 504 s.makeSeed(c, map[string]interface{}{ 505 "base": "core18", 506 "kernel": "pc-kernel=18", 507 "gadget": "pc=18", 508 }, snapdSeed, core18Seed, kernel18Seed, gadget18Seed) 509 510 err := s.seed16.LoadAssertions(s.db, s.commitTo) 511 c.Assert(err, IsNil) 512 513 err = s.seed16.LoadMeta(s.perfTimings) 514 c.Assert(err, IsNil) 515 516 c.Check(s.seed16.UsesSnapdSnap(), Equals, true) 517 518 essSnaps := s.seed16.EssentialSnaps() 519 c.Check(essSnaps, HasLen, 4) 520 521 c.Check(essSnaps, DeepEquals, []*seed.Snap{ 522 { 523 Path: s.expectedPath("snapd"), 524 SideInfo: &s.AssertedSnapInfo("snapd").SideInfo, 525 EssentialType: snap.TypeSnapd, 526 Essential: true, 527 Required: true, 528 Channel: "stable", 529 }, { 530 Path: s.expectedPath("core18"), 531 SideInfo: &s.AssertedSnapInfo("core18").SideInfo, 532 EssentialType: snap.TypeBase, 533 Essential: true, 534 Required: true, 535 Channel: "stable", 536 }, { 537 Path: s.expectedPath("pc-kernel"), 538 SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo, 539 EssentialType: snap.TypeKernel, 540 Essential: true, 541 Required: true, 542 Channel: "18", 543 }, { 544 Path: s.expectedPath("pc"), 545 SideInfo: &s.AssertedSnapInfo("pc").SideInfo, 546 EssentialType: snap.TypeGadget, 547 Essential: true, 548 Required: true, 549 Channel: "18", 550 }, 551 }) 552 553 runSnaps, err := s.seed16.ModeSnaps("run") 554 c.Assert(err, IsNil) 555 c.Check(runSnaps, HasLen, 0) 556 } 557 558 func (s *seed16Suite) TestLoadMetaCore18(c *C) { 559 s.makeSeed(c, map[string]interface{}{ 560 "base": "core18", 561 "kernel": "pc-kernel=18", 562 "gadget": "pc=18", 563 "required-snaps": []interface{}{"core", "required", "required18"}, 564 }, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, requiredSeed, coreSeed, required18Seed) 565 566 err := s.seed16.LoadAssertions(s.db, s.commitTo) 567 c.Assert(err, IsNil) 568 569 err = s.seed16.LoadMeta(s.perfTimings) 570 c.Assert(err, IsNil) 571 572 essSnaps := s.seed16.EssentialSnaps() 573 c.Check(essSnaps, HasLen, 4) 574 575 c.Check(essSnaps, DeepEquals, []*seed.Snap{ 576 { 577 Path: s.expectedPath("snapd"), 578 SideInfo: &s.AssertedSnapInfo("snapd").SideInfo, 579 EssentialType: snap.TypeSnapd, 580 Essential: true, 581 Required: true, 582 Channel: "stable", 583 }, { 584 Path: s.expectedPath("core18"), 585 SideInfo: &s.AssertedSnapInfo("core18").SideInfo, 586 EssentialType: snap.TypeBase, 587 Essential: true, 588 Required: true, 589 Channel: "stable", 590 }, { 591 Path: s.expectedPath("pc-kernel"), 592 SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo, 593 EssentialType: snap.TypeKernel, 594 Essential: true, 595 Required: true, 596 Channel: "18", 597 }, { 598 Path: s.expectedPath("pc"), 599 SideInfo: &s.AssertedSnapInfo("pc").SideInfo, 600 EssentialType: snap.TypeGadget, 601 Essential: true, 602 Required: true, 603 Channel: "18", 604 }, 605 }) 606 607 runSnaps, err := s.seed16.ModeSnaps("run") 608 c.Assert(err, IsNil) 609 c.Check(runSnaps, HasLen, 3) 610 611 // these are not sorted by type, firstboot will do that 612 c.Check(runSnaps, DeepEquals, []*seed.Snap{ 613 { 614 Path: s.expectedPath("required"), 615 SideInfo: &s.AssertedSnapInfo("required").SideInfo, 616 Required: true, 617 Channel: "stable", 618 }, { 619 Path: s.expectedPath("core"), 620 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 621 Required: true, 622 Channel: "stable", 623 }, { 624 Path: s.expectedPath("required18"), 625 SideInfo: &s.AssertedSnapInfo("required18").SideInfo, 626 Required: true, 627 Channel: "stable", 628 }, 629 }) 630 } 631 632 func (s *seed16Suite) TestLoadMetaClassicNothing(c *C) { 633 s.makeSeed(c, map[string]interface{}{ 634 "classic": "true", 635 }) 636 637 err := s.seed16.LoadAssertions(s.db, s.commitTo) 638 c.Assert(err, IsNil) 639 640 err = s.seed16.LoadMeta(s.perfTimings) 641 c.Assert(err, IsNil) 642 643 c.Check(s.seed16.UsesSnapdSnap(), Equals, false) 644 645 essSnaps := s.seed16.EssentialSnaps() 646 c.Check(essSnaps, HasLen, 0) 647 648 runSnaps, err := s.seed16.ModeSnaps("run") 649 c.Assert(err, IsNil) 650 c.Check(runSnaps, HasLen, 0) 651 } 652 653 func (s *seed16Suite) TestLoadMetaClassicCore(c *C) { 654 s.makeSeed(c, map[string]interface{}{ 655 "classic": "true", 656 }, coreSeed, classicSnapSeed) 657 658 err := s.seed16.LoadAssertions(s.db, s.commitTo) 659 c.Assert(err, IsNil) 660 661 err = s.seed16.LoadMeta(s.perfTimings) 662 c.Assert(err, IsNil) 663 664 c.Check(s.seed16.UsesSnapdSnap(), Equals, false) 665 666 essSnaps := s.seed16.EssentialSnaps() 667 c.Check(essSnaps, HasLen, 1) 668 c.Check(essSnaps, DeepEquals, []*seed.Snap{ 669 { 670 Path: s.expectedPath("core"), 671 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 672 EssentialType: snap.TypeOS, 673 Essential: true, 674 Required: true, 675 Channel: "stable", 676 }, 677 }) 678 679 // classic-snap is not required, just an extra snap 680 runSnaps, err := s.seed16.ModeSnaps("run") 681 c.Assert(err, IsNil) 682 c.Check(runSnaps, HasLen, 1) 683 c.Check(runSnaps, DeepEquals, []*seed.Snap{ 684 { 685 Path: s.expectedPath("classic-snap"), 686 SideInfo: &s.AssertedSnapInfo("classic-snap").SideInfo, 687 Channel: "stable", 688 Classic: true, 689 }, 690 }) 691 } 692 693 func (s *seed16Suite) TestLoadMetaClassicCoreWithGadget(c *C) { 694 s.makeSeed(c, map[string]interface{}{ 695 "classic": "true", 696 "gadget": "classic-gadget", 697 }, coreSeed, classicGadgetSeed) 698 699 err := s.seed16.LoadAssertions(s.db, s.commitTo) 700 c.Assert(err, IsNil) 701 702 err = s.seed16.LoadMeta(s.perfTimings) 703 c.Assert(err, IsNil) 704 705 c.Check(s.seed16.UsesSnapdSnap(), Equals, false) 706 707 essSnaps := s.seed16.EssentialSnaps() 708 c.Check(essSnaps, HasLen, 2) 709 c.Check(essSnaps, DeepEquals, []*seed.Snap{ 710 { 711 Path: s.expectedPath("core"), 712 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 713 EssentialType: snap.TypeOS, 714 Essential: true, 715 Required: true, 716 Channel: "stable", 717 }, 718 { 719 Path: s.expectedPath("classic-gadget"), 720 SideInfo: &s.AssertedSnapInfo("classic-gadget").SideInfo, 721 EssentialType: snap.TypeGadget, 722 Essential: true, 723 Required: true, 724 Channel: "stable", 725 }, 726 }) 727 728 runSnaps, err := s.seed16.ModeSnaps("run") 729 c.Assert(err, IsNil) 730 c.Check(runSnaps, HasLen, 0) 731 } 732 733 func (s *seed16Suite) TestLoadMetaClassicSnapd(c *C) { 734 s.makeSeed(c, map[string]interface{}{ 735 "classic": "true", 736 "required-snaps": []interface{}{"core18", "required18"}, 737 }, snapdSeed, core18Seed, required18Seed) 738 739 err := s.seed16.LoadAssertions(s.db, s.commitTo) 740 c.Assert(err, IsNil) 741 742 err = s.seed16.LoadMeta(s.perfTimings) 743 c.Assert(err, IsNil) 744 745 c.Check(s.seed16.UsesSnapdSnap(), Equals, true) 746 747 essSnaps := s.seed16.EssentialSnaps() 748 c.Check(essSnaps, HasLen, 1) 749 c.Check(essSnaps, DeepEquals, []*seed.Snap{ 750 { 751 Path: s.expectedPath("snapd"), 752 SideInfo: &s.AssertedSnapInfo("snapd").SideInfo, 753 EssentialType: snap.TypeSnapd, 754 Essential: true, 755 Required: true, 756 Channel: "stable", 757 }, 758 }) 759 760 runSnaps, err := s.seed16.ModeSnaps("run") 761 c.Assert(err, IsNil) 762 c.Check(runSnaps, HasLen, 2) 763 c.Check(runSnaps, DeepEquals, []*seed.Snap{ 764 { 765 Path: s.expectedPath("core18"), 766 SideInfo: &s.AssertedSnapInfo("core18").SideInfo, 767 Required: true, 768 Channel: "stable", 769 }, { 770 Path: s.expectedPath("required18"), 771 SideInfo: &s.AssertedSnapInfo("required18").SideInfo, 772 Required: true, 773 Channel: "stable", 774 }, 775 }) 776 } 777 778 func (s *seed16Suite) TestLoadMetaClassicSnapdWithGadget(c *C) { 779 s.makeSeed(c, map[string]interface{}{ 780 "classic": "true", 781 "gadget": "classic-gadget", 782 }, snapdSeed, classicGadgetSeed, coreSeed) 783 784 err := s.seed16.LoadAssertions(s.db, s.commitTo) 785 c.Assert(err, IsNil) 786 787 err = s.seed16.LoadMeta(s.perfTimings) 788 c.Assert(err, IsNil) 789 790 c.Check(s.seed16.UsesSnapdSnap(), Equals, true) 791 792 essSnaps := s.seed16.EssentialSnaps() 793 c.Check(essSnaps, HasLen, 3) 794 c.Check(essSnaps, DeepEquals, []*seed.Snap{ 795 { 796 Path: s.expectedPath("snapd"), 797 SideInfo: &s.AssertedSnapInfo("snapd").SideInfo, 798 EssentialType: snap.TypeSnapd, 799 800 Essential: true, 801 Required: true, 802 Channel: "stable", 803 }, { 804 Path: s.expectedPath("classic-gadget"), 805 SideInfo: &s.AssertedSnapInfo("classic-gadget").SideInfo, 806 EssentialType: snap.TypeGadget, 807 808 Essential: true, 809 Required: true, 810 Channel: "stable", 811 }, { 812 Path: s.expectedPath("core"), 813 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 814 EssentialType: snap.TypeOS, 815 816 Essential: true, 817 Required: true, 818 Channel: "stable", 819 }, 820 }) 821 822 runSnaps, err := s.seed16.ModeSnaps("run") 823 c.Assert(err, IsNil) 824 c.Check(runSnaps, HasLen, 0) 825 } 826 827 func (s *seed16Suite) TestLoadMetaClassicSnapdWithGadget18(c *C) { 828 s.makeSeed(c, map[string]interface{}{ 829 "classic": "true", 830 "gadget": "classic-gadget18", 831 "required-snaps": []interface{}{"core", "required"}, 832 }, snapdSeed, coreSeed, requiredSeed, classicGadget18Seed, core18Seed) 833 834 err := s.seed16.LoadAssertions(s.db, s.commitTo) 835 c.Assert(err, IsNil) 836 837 err = s.seed16.LoadMeta(s.perfTimings) 838 c.Assert(err, IsNil) 839 840 c.Check(s.seed16.UsesSnapdSnap(), Equals, true) 841 842 essSnaps := s.seed16.EssentialSnaps() 843 c.Check(essSnaps, HasLen, 3) 844 c.Check(essSnaps, DeepEquals, []*seed.Snap{ 845 { 846 Path: s.expectedPath("snapd"), 847 SideInfo: &s.AssertedSnapInfo("snapd").SideInfo, 848 EssentialType: snap.TypeSnapd, 849 850 Essential: true, 851 Required: true, 852 Channel: "stable", 853 }, { 854 Path: s.expectedPath("classic-gadget18"), 855 SideInfo: &s.AssertedSnapInfo("classic-gadget18").SideInfo, 856 EssentialType: snap.TypeGadget, 857 858 Essential: true, 859 Required: true, 860 Channel: "stable", 861 }, { 862 Path: s.expectedPath("core18"), 863 SideInfo: &s.AssertedSnapInfo("core18").SideInfo, 864 EssentialType: snap.TypeBase, 865 866 Essential: true, 867 Required: true, 868 Channel: "stable", 869 }, 870 }) 871 872 runSnaps, err := s.seed16.ModeSnaps("run") 873 c.Assert(err, IsNil) 874 c.Check(runSnaps, HasLen, 2) 875 c.Check(runSnaps, DeepEquals, []*seed.Snap{ 876 { 877 Path: s.expectedPath("core"), 878 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 879 Required: true, 880 Channel: "stable", 881 }, { 882 Path: s.expectedPath("required"), 883 SideInfo: &s.AssertedSnapInfo("required").SideInfo, 884 Required: true, 885 Channel: "stable", 886 }, 887 }) 888 } 889 890 func (s *seed16Suite) TestLoadMetaCore18Local(c *C) { 891 localRequired18Seed := &seed.InternalSnap16{ 892 Name: "required18", 893 Unasserted: true, 894 DevMode: true, 895 } 896 s.makeSeed(c, map[string]interface{}{ 897 "base": "core18", 898 "kernel": "pc-kernel=18", 899 "gadget": "pc=18", 900 "required-snaps": []interface{}{"core", "required18"}, 901 }, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, localRequired18Seed) 902 903 err := s.seed16.LoadAssertions(s.db, s.commitTo) 904 c.Assert(err, IsNil) 905 906 err = s.seed16.LoadMeta(s.perfTimings) 907 c.Assert(err, IsNil) 908 909 essSnaps := s.seed16.EssentialSnaps() 910 c.Check(essSnaps, HasLen, 4) 911 912 c.Check(essSnaps, DeepEquals, []*seed.Snap{ 913 { 914 Path: s.expectedPath("snapd"), 915 SideInfo: &s.AssertedSnapInfo("snapd").SideInfo, 916 EssentialType: snap.TypeSnapd, 917 Essential: true, 918 Required: true, 919 Channel: "stable", 920 }, { 921 Path: s.expectedPath("core18"), 922 SideInfo: &s.AssertedSnapInfo("core18").SideInfo, 923 EssentialType: snap.TypeBase, 924 Essential: true, 925 Required: true, 926 Channel: "stable", 927 }, { 928 Path: s.expectedPath("pc-kernel"), 929 SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo, 930 EssentialType: snap.TypeKernel, 931 Essential: true, 932 Required: true, 933 Channel: "18", 934 }, { 935 Path: s.expectedPath("pc"), 936 SideInfo: &s.AssertedSnapInfo("pc").SideInfo, 937 EssentialType: snap.TypeGadget, 938 Essential: true, 939 Required: true, 940 Channel: "18", 941 }, 942 }) 943 944 runSnaps, err := s.seed16.ModeSnaps("run") 945 c.Assert(err, IsNil) 946 c.Check(runSnaps, HasLen, 1) 947 948 c.Check(runSnaps, DeepEquals, []*seed.Snap{ 949 { 950 Path: filepath.Join(s.SeedDir, "snaps", "required18_1.0_all.snap"), 951 SideInfo: &snap.SideInfo{RealName: "required18"}, 952 Required: true, 953 DevMode: true, 954 }, 955 }) 956 } 957 958 func (s *seed16Suite) TestLoadMetaCore18StoreInfo(c *C) { 959 s.makeSeed(c, map[string]interface{}{ 960 "base": "core18", 961 "kernel": "pc-kernel=18", 962 "gadget": "pc=18", 963 }, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, privateSnapSeed, contactableSnapSeed) 964 965 err := s.seed16.LoadAssertions(s.db, s.commitTo) 966 c.Assert(err, IsNil) 967 968 err = s.seed16.LoadMeta(s.perfTimings) 969 c.Assert(err, IsNil) 970 971 essSnaps := s.seed16.EssentialSnaps() 972 c.Check(essSnaps, HasLen, 4) 973 974 runSnaps, err := s.seed16.ModeSnaps("run") 975 c.Assert(err, IsNil) 976 c.Check(runSnaps, HasLen, 2) 977 978 privateSnapSideInfo := s.AssertedSnapInfo("private-snap").SideInfo 979 privateSnapSideInfo.Private = true 980 contactableSnapSideInfo := s.AssertedSnapInfo("contactable-snap").SideInfo 981 contactableSnapSideInfo.Contact = "author@example.com" 982 983 // these are not sorted by type, firstboot will do that 984 c.Check(runSnaps, DeepEquals, []*seed.Snap{ 985 { 986 Path: s.expectedPath("private-snap"), 987 SideInfo: &privateSnapSideInfo, 988 Channel: "stable", 989 }, { 990 Path: s.expectedPath("contactable-snap"), 991 SideInfo: &contactableSnapSideInfo, 992 Channel: "stable", 993 }, 994 }) 995 } 996 997 func (s *seed16Suite) TestLoadMetaCore18EnforcePinnedTracks(c *C) { 998 seedSnaps := s.makeSeed(c, map[string]interface{}{ 999 "base": "core18", 1000 "kernel": "pc-kernel=18", 1001 "gadget": "pc=18", 1002 }, snapdSeed, core18Seed, kernel18Seed, gadget18Seed) 1003 1004 // tweak channels 1005 for _, sn := range seedSnaps { 1006 switch sn.Name { 1007 case "pc": 1008 sn.Channel = "edge" 1009 case "pc-kernel": 1010 sn.Channel = "latest/candidate" 1011 } 1012 } 1013 s.writeSeed(c, seedSnaps) 1014 1015 err := s.seed16.LoadAssertions(s.db, s.commitTo) 1016 c.Assert(err, IsNil) 1017 1018 err = s.seed16.LoadMeta(s.perfTimings) 1019 c.Assert(err, IsNil) 1020 1021 c.Check(s.seed16.UsesSnapdSnap(), Equals, true) 1022 1023 essSnaps := s.seed16.EssentialSnaps() 1024 c.Check(essSnaps, HasLen, 4) 1025 1026 c.Check(essSnaps, DeepEquals, []*seed.Snap{ 1027 { 1028 Path: s.expectedPath("snapd"), 1029 SideInfo: &s.AssertedSnapInfo("snapd").SideInfo, 1030 EssentialType: snap.TypeSnapd, 1031 Essential: true, 1032 Required: true, 1033 Channel: "stable", 1034 }, { 1035 Path: s.expectedPath("core18"), 1036 SideInfo: &s.AssertedSnapInfo("core18").SideInfo, 1037 EssentialType: snap.TypeBase, 1038 Essential: true, 1039 Required: true, 1040 Channel: "stable", 1041 }, { 1042 Path: s.expectedPath("pc-kernel"), 1043 SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo, 1044 EssentialType: snap.TypeKernel, 1045 Essential: true, 1046 Required: true, 1047 Channel: "18", 1048 }, { 1049 Path: s.expectedPath("pc"), 1050 SideInfo: &s.AssertedSnapInfo("pc").SideInfo, 1051 EssentialType: snap.TypeGadget, 1052 Essential: true, 1053 Required: true, 1054 Channel: "18/edge", 1055 }, 1056 }) 1057 1058 runSnaps, err := s.seed16.ModeSnaps("run") 1059 c.Assert(err, IsNil) 1060 c.Check(runSnaps, HasLen, 0) 1061 } 1062 1063 func (s *seed16Suite) TestLoadMetaBrokenSeed(c *C) { 1064 seedSnap16s := s.makeSeed(c, map[string]interface{}{ 1065 "base": "core18", 1066 "kernel": "pc-kernel=18", 1067 "gadget": "pc=18", 1068 "required-snaps": []interface{}{"required18"}, 1069 }, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, required18Seed) 1070 1071 otherSnapFile := snaptest.MakeTestSnapWithFiles(c, `name: other 1072 version: other`, nil) 1073 otherFname := filepath.Base(otherSnapFile) 1074 err := os.Rename(otherSnapFile, filepath.Join(s.SeedDir, "snaps", otherFname)) 1075 c.Assert(err, IsNil) 1076 1077 const otherBaseGadget = `name: pc 1078 type: gadget 1079 base: other-base 1080 version: other-base 1081 ` 1082 otherBaseGadgetFname, obgDecl, obgRev := s.MakeAssertedSnap(c, otherBaseGadget, snapFiles["pc"], snap.R(3), "canonical") 1083 s.WriteAssertions("other-gadget.asserts", obgDecl, obgRev) 1084 1085 err = s.seed16.LoadAssertions(s.db, s.commitTo) 1086 c.Assert(err, IsNil) 1087 1088 omit := func(which int) func([]*seed.InternalSnap16) []*seed.InternalSnap16 { 1089 return func(snaps []*seed.InternalSnap16) []*seed.InternalSnap16 { 1090 broken := make([]*seed.InternalSnap16, 0, len(snaps)-1) 1091 for i, sn := range snaps { 1092 if i == which { 1093 continue 1094 } 1095 broken = append(broken, sn) 1096 } 1097 return broken 1098 } 1099 } 1100 replaceFile := func(snapName, fname string) func([]*seed.InternalSnap16) []*seed.InternalSnap16 { 1101 return func(snaps []*seed.InternalSnap16) []*seed.InternalSnap16 { 1102 for i := range snaps { 1103 if snaps[i].Name != snapName { 1104 continue 1105 } 1106 sn := *snaps[i] 1107 sn.File = fname 1108 snaps[i] = &sn 1109 } 1110 return snaps 1111 } 1112 } 1113 1114 tests := []struct { 1115 breakSeed func([]*seed.InternalSnap16) []*seed.InternalSnap16 1116 err string 1117 }{ 1118 {omit(0), `essential snap "snapd" required by the model is missing in the seed`}, 1119 {omit(1), `essential snap "core18" required by the model is missing in the seed`}, 1120 {omit(2), `essential snap "pc-kernel" required by the model is missing in the seed`}, 1121 {omit(3), `essential snap "pc" required by the model is missing in the seed`}, 1122 // omitting "required18" currently doesn't error in any way 1123 {replaceFile("core18", otherFname), `cannot find signatures with metadata for snap "core18".*`}, 1124 {replaceFile("required18", otherFname), `cannot find signatures with metadata for snap "required18".*`}, 1125 {replaceFile("core18", "not-existent"), `cannot compute snap .* digest: .*`}, 1126 {replaceFile("pc", otherBaseGadgetFname), `cannot use gadget snap because its base "other-base" is different from model base "core18"`}, 1127 } 1128 1129 for _, t := range tests { 1130 testSeedSnap16s := make([]*seed.InternalSnap16, 5) 1131 copy(testSeedSnap16s, seedSnap16s) 1132 1133 testSeedSnap16s = t.breakSeed(testSeedSnap16s) 1134 s.writeSeed(c, testSeedSnap16s) 1135 1136 c.Check(s.seed16.LoadMeta(s.perfTimings), ErrorMatches, t.err) 1137 } 1138 }