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