github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/image/image_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-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 image_test 21 22 import ( 23 "bytes" 24 "context" 25 "fmt" 26 "io/ioutil" 27 "net/url" 28 "os" 29 "path/filepath" 30 "strings" 31 "testing" 32 "time" 33 34 . "gopkg.in/check.v1" 35 36 "github.com/snapcore/snapd/asserts" 37 "github.com/snapcore/snapd/asserts/assertstest" 38 "github.com/snapcore/snapd/bootloader" 39 "github.com/snapcore/snapd/bootloader/assets" 40 "github.com/snapcore/snapd/bootloader/bootloadertest" 41 "github.com/snapcore/snapd/bootloader/ubootenv" 42 "github.com/snapcore/snapd/image" 43 "github.com/snapcore/snapd/osutil" 44 "github.com/snapcore/snapd/overlord/auth" 45 "github.com/snapcore/snapd/progress" 46 "github.com/snapcore/snapd/seed" 47 "github.com/snapcore/snapd/seed/seedtest" 48 "github.com/snapcore/snapd/snap" 49 "github.com/snapcore/snapd/snap/snaptest" 50 "github.com/snapcore/snapd/store" 51 "github.com/snapcore/snapd/testutil" 52 "github.com/snapcore/snapd/timings" 53 ) 54 55 func Test(t *testing.T) { TestingT(t) } 56 57 type imageSuite struct { 58 testutil.BaseTest 59 root string 60 bootloader *bootloadertest.MockBootloader 61 62 stdout *bytes.Buffer 63 stderr *bytes.Buffer 64 65 storeActions []*store.SnapAction 66 tsto *image.ToolingStore 67 68 // SeedSnaps helps creating and making available seed snaps 69 // (it provides MakeAssertedSnap etc.) for the tests. 70 *seedtest.SeedSnaps 71 72 model *asserts.Model 73 } 74 75 var _ = Suite(&imageSuite{}) 76 77 var ( 78 brandPrivKey, _ = assertstest.GenerateKey(752) 79 ) 80 81 func (s *imageSuite) SetUpTest(c *C) { 82 s.root = c.MkDir() 83 s.bootloader = bootloadertest.Mock("grub", c.MkDir()) 84 bootloader.Force(s.bootloader) 85 86 s.BaseTest.SetUpTest(c) 87 s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 88 89 s.stdout = &bytes.Buffer{} 90 image.Stdout = s.stdout 91 s.stderr = &bytes.Buffer{} 92 image.Stderr = s.stderr 93 s.tsto = image.MockToolingStore(s) 94 95 s.SeedSnaps = &seedtest.SeedSnaps{} 96 s.SetupAssertSigning("canonical") 97 s.Brands.Register("my-brand", brandPrivKey, map[string]interface{}{ 98 "verification": "verified", 99 }) 100 assertstest.AddMany(s.StoreSigning, s.Brands.AccountsAndKeys("my-brand")...) 101 102 s.model = s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 103 "display-name": "my display name", 104 "architecture": "amd64", 105 "gadget": "pc", 106 "kernel": "pc-kernel", 107 "required-snaps": []interface{}{"required-snap1"}, 108 }) 109 110 otherAcct := assertstest.NewAccount(s.StoreSigning, "other", map[string]interface{}{ 111 "account-id": "other", 112 }, "") 113 s.StoreSigning.Add(otherAcct) 114 115 // mock the mount cmds (for the extract kernel assets stuff) 116 c1 := testutil.MockCommand(c, "mount", "") 117 s.AddCleanup(c1.Restore) 118 c2 := testutil.MockCommand(c, "umount", "") 119 s.AddCleanup(c2.Restore) 120 } 121 122 func (s *imageSuite) TearDownTest(c *C) { 123 s.BaseTest.TearDownTest(c) 124 bootloader.Force(nil) 125 image.Stdout = os.Stdout 126 image.Stderr = os.Stderr 127 s.storeActions = nil 128 } 129 130 // interface for the store 131 func (s *imageSuite) SnapAction(_ context.Context, _ []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, _ *auth.UserState, _ *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) { 132 if assertQuery != nil { 133 return nil, nil, fmt.Errorf("unexpected assertion query") 134 } 135 136 if len(actions) != 1 { 137 return nil, nil, fmt.Errorf("expected 1 action, got %d", len(actions)) 138 } 139 140 if actions[0].Action != "download" { 141 return nil, nil, fmt.Errorf("unexpected action %q", actions[0].Action) 142 } 143 144 if _, instanceKey := snap.SplitInstanceName(actions[0].InstanceName); instanceKey != "" { 145 return nil, nil, fmt.Errorf("unexpected instance key in %q", actions[0].InstanceName) 146 } 147 // record 148 s.storeActions = append(s.storeActions, actions[0]) 149 150 if info := s.AssertedSnapInfo(actions[0].InstanceName); info != nil { 151 info1 := *info 152 channel := actions[0].Channel 153 redirectChannel := "" 154 if strings.HasPrefix(actions[0].InstanceName, "default-track-") { 155 channel = "default-track/stable" 156 redirectChannel = channel 157 } 158 info1.Channel = channel 159 return []store.SnapActionResult{{Info: &info1, RedirectChannel: redirectChannel}}, nil, nil 160 } 161 return nil, nil, fmt.Errorf("no %q in the fake store", actions[0].InstanceName) 162 } 163 164 func (s *imageSuite) Download(ctx context.Context, name, targetFn string, downloadInfo *snap.DownloadInfo, pbar progress.Meter, user *auth.UserState, dlOpts *store.DownloadOptions) error { 165 return osutil.CopyFile(s.AssertedSnap(name), targetFn, 0) 166 } 167 168 func (s *imageSuite) Assertion(assertType *asserts.AssertionType, primaryKey []string, user *auth.UserState) (asserts.Assertion, error) { 169 ref := &asserts.Ref{Type: assertType, PrimaryKey: primaryKey} 170 return ref.Resolve(s.StoreSigning.Find) 171 } 172 173 // TODO: use seedtest.SampleSnapYaml for some of these 174 const packageGadget = ` 175 name: pc 176 version: 1.0 177 type: gadget 178 ` 179 180 const packageGadgetWithBase = ` 181 name: pc18 182 version: 1.0 183 type: gadget 184 base: core18 185 ` 186 const packageClassicGadget = ` 187 name: classic-gadget 188 version: 1.0 189 type: gadget 190 ` 191 192 const packageClassicGadget18 = ` 193 name: classic-gadget18 194 version: 1.0 195 type: gadget 196 base: core18 197 ` 198 199 const packageKernel = ` 200 name: pc-kernel 201 version: 4.4-1 202 type: kernel 203 ` 204 205 const packageCore = ` 206 name: core 207 version: 16.04 208 type: os 209 ` 210 211 const packageCore18 = ` 212 name: core18 213 version: 18.04 214 type: base 215 ` 216 217 const snapdSnap = ` 218 name: snapd 219 version: 3.14 220 type: snapd 221 ` 222 223 const otherBase = ` 224 name: other-base 225 version: 2.5029 226 type: base 227 ` 228 229 const devmodeSnap = ` 230 name: devmode-snap 231 version: 1.0 232 type: app 233 confinement: devmode 234 ` 235 236 const classicSnap = ` 237 name: classic-snap 238 version: 1.0 239 type: app 240 confinement: classic 241 ` 242 243 const requiredSnap1 = ` 244 name: required-snap1 245 version: 1.0 246 ` 247 248 const requiredSnap18 = ` 249 name: required-snap18 250 version: 1.0 251 base: core18 252 ` 253 254 const defaultTrackSnap18 = ` 255 name: default-track-snap18 256 version: 1.0 257 base: core18 258 ` 259 260 const snapReqOtherBase = ` 261 name: snap-req-other-base 262 version: 1.0 263 base: other-base 264 ` 265 266 const snapReqCore16Base = ` 267 name: snap-req-core16-base 268 version: 1.0 269 base: core16 270 ` 271 272 const snapReqContentProvider = ` 273 name: snap-req-content-provider 274 version: 1.0 275 plugs: 276 gtk-3-themes: 277 interface: content 278 default-provider: gtk-common-themes 279 target: $SNAP/data-dir/themes 280 ` 281 282 const snapBaseNone = ` 283 name: snap-base-none 284 version: 1.0 285 base: none 286 ` 287 288 func (s *imageSuite) TestMissingModelAssertions(c *C) { 289 _, err := image.DecodeModelAssertion(&image.Options{}) 290 c.Assert(err, ErrorMatches, "cannot read model assertion: open : no such file or directory") 291 } 292 293 func (s *imageSuite) TestIncorrectModelAssertions(c *C) { 294 fn := filepath.Join(c.MkDir(), "broken-model.assertion") 295 err := ioutil.WriteFile(fn, nil, 0644) 296 c.Assert(err, IsNil) 297 _, err = image.DecodeModelAssertion(&image.Options{ 298 ModelFile: fn, 299 }) 300 c.Assert(err, ErrorMatches, fmt.Sprintf(`cannot decode model assertion "%s": assertion content/signature separator not found`, fn)) 301 } 302 303 func (s *imageSuite) TestValidButDifferentAssertion(c *C) { 304 var differentAssertion = []byte(`type: snap-declaration 305 authority-id: canonical 306 series: 16 307 snap-id: snap-id-1 308 snap-name: first 309 publisher-id: dev-id1 310 timestamp: 2016-01-02T10:00:00-05:00 311 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 312 313 AXNpZw== 314 `) 315 316 fn := filepath.Join(c.MkDir(), "different.assertion") 317 err := ioutil.WriteFile(fn, differentAssertion, 0644) 318 c.Assert(err, IsNil) 319 320 _, err = image.DecodeModelAssertion(&image.Options{ 321 ModelFile: fn, 322 }) 323 c.Assert(err, ErrorMatches, fmt.Sprintf(`assertion in "%s" is not a model assertion`, fn)) 324 } 325 326 func (s *imageSuite) TestModelAssertionReservedHeaders(c *C) { 327 const mod = `type: model 328 authority-id: brand 329 series: 16 330 brand-id: brand 331 model: baz-3000 332 architecture: armhf 333 gadget: brand-gadget 334 kernel: kernel 335 timestamp: 2016-01-02T10:00:00-05:00 336 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 337 338 AXNpZw== 339 ` 340 341 reserved := []string{ 342 "core", 343 "os", 344 "class", 345 "allowed-modes", 346 } 347 348 for _, rsvd := range reserved { 349 tweaked := strings.Replace(mod, "kernel: kernel\n", fmt.Sprintf("kernel: kernel\n%s: stuff\n", rsvd), 1) 350 fn := filepath.Join(c.MkDir(), "model.assertion") 351 err := ioutil.WriteFile(fn, []byte(tweaked), 0644) 352 c.Assert(err, IsNil) 353 _, err = image.DecodeModelAssertion(&image.Options{ 354 ModelFile: fn, 355 }) 356 c.Check(err, ErrorMatches, fmt.Sprintf("model assertion cannot have reserved/unsupported header %q set", rsvd)) 357 } 358 } 359 360 func (s *imageSuite) TestModelAssertionNoParallelInstancesOfSnaps(c *C) { 361 const mod = `type: model 362 authority-id: brand 363 series: 16 364 brand-id: brand 365 model: baz-3000 366 architecture: armhf 367 gadget: brand-gadget 368 kernel: kernel 369 required-snaps: 370 - foo_instance 371 timestamp: 2016-01-02T10:00:00-05:00 372 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 373 374 AXNpZw== 375 ` 376 377 fn := filepath.Join(c.MkDir(), "model.assertion") 378 err := ioutil.WriteFile(fn, []byte(mod), 0644) 379 c.Assert(err, IsNil) 380 _, err = image.DecodeModelAssertion(&image.Options{ 381 ModelFile: fn, 382 }) 383 c.Check(err, ErrorMatches, `.* assertion model: invalid snap name in "required-snaps" header: foo_instance`) 384 } 385 386 func (s *imageSuite) TestModelAssertionNoParallelInstancesOfKernel(c *C) { 387 const mod = `type: model 388 authority-id: brand 389 series: 16 390 brand-id: brand 391 model: baz-3000 392 architecture: armhf 393 gadget: brand-gadget 394 kernel: kernel_instance 395 timestamp: 2016-01-02T10:00:00-05:00 396 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 397 398 AXNpZw== 399 ` 400 401 fn := filepath.Join(c.MkDir(), "model.assertion") 402 err := ioutil.WriteFile(fn, []byte(mod), 0644) 403 c.Assert(err, IsNil) 404 _, err = image.DecodeModelAssertion(&image.Options{ 405 ModelFile: fn, 406 }) 407 c.Check(err, ErrorMatches, `.* assertion model: invalid snap name in "kernel" header: kernel_instance`) 408 } 409 410 func (s *imageSuite) TestModelAssertionNoParallelInstancesOfGadget(c *C) { 411 const mod = `type: model 412 authority-id: brand 413 series: 16 414 brand-id: brand 415 model: baz-3000 416 architecture: armhf 417 gadget: brand-gadget_instance 418 kernel: kernel 419 timestamp: 2016-01-02T10:00:00-05:00 420 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 421 422 AXNpZw== 423 ` 424 425 fn := filepath.Join(c.MkDir(), "model.assertion") 426 err := ioutil.WriteFile(fn, []byte(mod), 0644) 427 c.Assert(err, IsNil) 428 _, err = image.DecodeModelAssertion(&image.Options{ 429 ModelFile: fn, 430 }) 431 c.Check(err, ErrorMatches, `.* assertion model: invalid snap name in "gadget" header: brand-gadget_instance`) 432 } 433 434 func (s *imageSuite) TestModelAssertionNoParallelInstancesOfBase(c *C) { 435 const mod = `type: model 436 authority-id: brand 437 series: 16 438 brand-id: brand 439 model: baz-3000 440 architecture: armhf 441 gadget: brand-gadget 442 kernel: kernel 443 base: core18_instance 444 timestamp: 2016-01-02T10:00:00-05:00 445 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 446 447 AXNpZw== 448 ` 449 450 fn := filepath.Join(c.MkDir(), "model.assertion") 451 err := ioutil.WriteFile(fn, []byte(mod), 0644) 452 c.Assert(err, IsNil) 453 _, err = image.DecodeModelAssertion(&image.Options{ 454 ModelFile: fn, 455 }) 456 c.Check(err, ErrorMatches, `.* assertion model: invalid snap name in "base" header: core18_instance`) 457 } 458 459 func (s *imageSuite) TestHappyDecodeModelAssertion(c *C) { 460 fn := filepath.Join(c.MkDir(), "model.assertion") 461 err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644) 462 c.Assert(err, IsNil) 463 464 a, err := image.DecodeModelAssertion(&image.Options{ 465 ModelFile: fn, 466 }) 467 c.Assert(err, IsNil) 468 c.Check(a.Type(), Equals, asserts.ModelType) 469 } 470 471 func (s *imageSuite) MakeAssertedSnap(c *C, snapYaml string, files [][]string, revision snap.Revision, developerID string) { 472 s.SeedSnaps.MakeAssertedSnap(c, snapYaml, files, revision, developerID, s.StoreSigning.Database) 473 } 474 475 const stableChannel = "stable" 476 477 const pcGadgetYaml = ` 478 volumes: 479 pc: 480 bootloader: grub 481 ` 482 483 func (s *imageSuite) setupSnaps(c *C, publishers map[string]string, defaultsYaml string) { 484 gadgetYaml := pcGadgetYaml + defaultsYaml 485 if _, ok := publishers["pc"]; ok { 486 s.MakeAssertedSnap(c, packageGadget, [][]string{ 487 {"grub.conf", ""}, {"grub.cfg", "I'm a grub.cfg"}, 488 {"meta/gadget.yaml", gadgetYaml}, 489 }, snap.R(1), publishers["pc"]) 490 } 491 if _, ok := publishers["pc18"]; ok { 492 s.MakeAssertedSnap(c, packageGadgetWithBase, [][]string{ 493 {"grub.conf", ""}, {"grub.cfg", "I'm a grub.cfg"}, 494 {"meta/gadget.yaml", gadgetYaml}, 495 }, snap.R(4), publishers["pc18"]) 496 } 497 498 if _, ok := publishers["classic-gadget"]; ok { 499 s.MakeAssertedSnap(c, packageClassicGadget, [][]string{ 500 {"some-file", "Some file"}, 501 }, snap.R(5), publishers["classic-gadget"]) 502 } 503 504 if _, ok := publishers["classic-gadget18"]; ok { 505 s.MakeAssertedSnap(c, packageClassicGadget18, [][]string{ 506 {"some-file", "Some file"}, 507 }, snap.R(5), publishers["classic-gadget18"]) 508 } 509 510 if _, ok := publishers["pc-kernel"]; ok { 511 s.MakeAssertedSnap(c, packageKernel, nil, snap.R(2), publishers["pc-kernel"]) 512 } 513 514 s.MakeAssertedSnap(c, packageCore, nil, snap.R(3), "canonical") 515 516 s.MakeAssertedSnap(c, packageCore18, nil, snap.R(18), "canonical") 517 s.MakeAssertedSnap(c, snapdSnap, nil, snap.R(18), "canonical") 518 519 s.MakeAssertedSnap(c, otherBase, nil, snap.R(18), "other") 520 521 s.MakeAssertedSnap(c, snapReqCore16Base, nil, snap.R(16), "other") 522 523 s.MakeAssertedSnap(c, requiredSnap1, nil, snap.R(3), "other") 524 s.AssertedSnapInfo("required-snap1").Contact = "foo@example.com" 525 526 s.MakeAssertedSnap(c, requiredSnap18, nil, snap.R(6), "other") 527 s.AssertedSnapInfo("required-snap18").Contact = "foo@example.com" 528 529 s.MakeAssertedSnap(c, defaultTrackSnap18, nil, snap.R(5), "other") 530 531 s.MakeAssertedSnap(c, snapReqOtherBase, nil, snap.R(5), "other") 532 533 s.MakeAssertedSnap(c, snapReqContentProvider, nil, snap.R(5), "other") 534 535 s.MakeAssertedSnap(c, snapBaseNone, nil, snap.R(1), "other") 536 } 537 538 func (s *imageSuite) loadSeed(c *C, seeddir string) (essSnaps []*seed.Snap, runSnaps []*seed.Snap, roDB asserts.RODatabase) { 539 label := "" 540 systems, err := filepath.Glob(filepath.Join(seeddir, "systems", "*")) 541 c.Assert(err, IsNil) 542 if len(systems) > 1 { 543 c.Fatal("expected at most 1 Core 20 recovery system") 544 } else if len(systems) == 1 { 545 label = filepath.Base(systems[0]) 546 } 547 548 seed, err := seed.Open(seeddir, label) 549 c.Assert(err, IsNil) 550 551 db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 552 Backstore: asserts.NewMemoryBackstore(), 553 Trusted: s.StoreSigning.Trusted, 554 }) 555 c.Assert(err, IsNil) 556 557 commitTo := func(b *asserts.Batch) error { 558 return b.CommitTo(db, nil) 559 } 560 561 err = seed.LoadAssertions(db, commitTo) 562 c.Assert(err, IsNil) 563 564 err = seed.LoadMeta(timings.New(nil)) 565 c.Assert(err, IsNil) 566 567 essSnaps = seed.EssentialSnaps() 568 runSnaps, err = seed.ModeSnaps("run") 569 c.Assert(err, IsNil) 570 571 return essSnaps, runSnaps, db 572 } 573 574 func (s *imageSuite) TestSetupSeed(c *C) { 575 restore := image.MockTrusted(s.StoreSigning.Trusted) 576 defer restore() 577 578 rootdir := filepath.Join(c.MkDir(), "image") 579 blobdir := filepath.Join(rootdir, "var/lib/snapd/snaps") 580 s.setupSnaps(c, map[string]string{ 581 "pc": "canonical", 582 "pc-kernel": "canonical", 583 }, "") 584 585 opts := &image.Options{ 586 PrepareDir: filepath.Dir(rootdir), 587 } 588 589 err := image.SetupSeed(s.tsto, s.model, opts) 590 c.Assert(err, IsNil) 591 592 // check seed 593 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 594 seedsnapsdir := filepath.Join(seeddir, "snaps") 595 essSnaps, runSnaps, roDB := s.loadSeed(c, seeddir) 596 c.Check(essSnaps, HasLen, 3) 597 c.Check(runSnaps, HasLen, 1) 598 599 // check the files are in place 600 for i, name := range []string{"core", "pc-kernel", "pc"} { 601 info := s.AssertedSnapInfo(name) 602 fn := info.Filename() 603 p := filepath.Join(seedsnapsdir, fn) 604 c.Check(p, testutil.FilePresent) 605 c.Check(essSnaps[i], DeepEquals, &seed.Snap{ 606 Path: p, 607 608 SideInfo: &info.SideInfo, 609 610 EssentialType: info.Type(), 611 Essential: true, 612 Required: true, 613 614 Channel: stableChannel, 615 }) 616 // sanity 617 if name == "core" { 618 c.Check(essSnaps[i].SideInfo.SnapID, Equals, s.AssertedSnapID("core")) 619 } 620 } 621 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 622 Path: filepath.Join(seedsnapsdir, s.AssertedSnapInfo("required-snap1").Filename()), 623 624 SideInfo: &s.AssertedSnapInfo("required-snap1").SideInfo, 625 626 Required: true, 627 628 Channel: stableChannel, 629 }) 630 c.Check(runSnaps[0].Path, testutil.FilePresent) 631 632 l, err := ioutil.ReadDir(seedsnapsdir) 633 c.Assert(err, IsNil) 634 c.Check(l, HasLen, 4) 635 636 // check assertions 637 model1, err := s.model.Ref().Resolve(roDB.Find) 638 c.Assert(err, IsNil) 639 c.Check(model1, DeepEquals, s.model) 640 641 storeAccountKey := s.StoreSigning.StoreAccountKey("") 642 brandPubKey := s.Brands.PublicKey("my-brand") 643 _, err = roDB.Find(asserts.AccountKeyType, map[string]string{ 644 "public-key-sha3-384": storeAccountKey.PublicKeyID(), 645 }) 646 c.Check(err, IsNil) 647 _, err = roDB.Find(asserts.AccountKeyType, map[string]string{ 648 "public-key-sha3-384": brandPubKey.ID(), 649 }) 650 c.Check(err, IsNil) 651 652 // check the bootloader config 653 m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core", "snap_menuentry") 654 c.Assert(err, IsNil) 655 c.Check(m["snap_kernel"], Equals, "pc-kernel_2.snap") 656 c.Check(m["snap_core"], Equals, "core_3.snap") 657 c.Check(m["snap_menuentry"], Equals, "my display name") 658 659 // check symlinks from snap blob dir 660 kernelInfo := s.AssertedSnapInfo("pc-kernel") 661 coreInfo := s.AssertedSnapInfo("core") 662 kernelBlob := filepath.Join(blobdir, kernelInfo.Filename()) 663 dst, err := os.Readlink(kernelBlob) 664 c.Assert(err, IsNil) 665 c.Check(dst, Equals, "../seed/snaps/pc-kernel_2.snap") 666 c.Check(kernelBlob, testutil.FilePresent) 667 668 coreBlob := filepath.Join(blobdir, coreInfo.Filename()) 669 dst, err = os.Readlink(coreBlob) 670 c.Assert(err, IsNil) 671 c.Check(dst, Equals, "../seed/snaps/core_3.snap") 672 c.Check(coreBlob, testutil.FilePresent) 673 674 c.Check(s.stderr.String(), Equals, "") 675 676 // check the downloads 677 c.Check(s.storeActions, HasLen, 4) 678 c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{ 679 Action: "download", 680 InstanceName: "core", 681 Channel: stableChannel, 682 }) 683 c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{ 684 Action: "download", 685 InstanceName: "pc-kernel", 686 Channel: stableChannel, 687 }) 688 c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{ 689 Action: "download", 690 InstanceName: "pc", 691 Channel: stableChannel, 692 }) 693 } 694 695 func (s *imageSuite) TestSetupSeedLocalCoreBrandKernel(c *C) { 696 restore := image.MockTrusted(s.StoreSigning.Trusted) 697 defer restore() 698 699 rootdir := filepath.Join(c.MkDir(), "image") 700 s.setupSnaps(c, map[string]string{ 701 "pc": "canonical", 702 "pc-kernel": "my-brand", 703 }, "") 704 705 coreFn := snaptest.MakeTestSnapWithFiles(c, packageCore, [][]string{{"local", ""}}) 706 requiredSnap1Fn := snaptest.MakeTestSnapWithFiles(c, requiredSnap1, [][]string{{"local", ""}}) 707 708 opts := &image.Options{ 709 Snaps: []string{ 710 coreFn, 711 requiredSnap1Fn, 712 }, 713 PrepareDir: filepath.Dir(rootdir), 714 } 715 716 err := image.SetupSeed(s.tsto, s.model, opts) 717 c.Assert(err, IsNil) 718 719 // check seed 720 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 721 seedsnapsdir := filepath.Join(seeddir, "snaps") 722 essSnaps, runSnaps, roDB := s.loadSeed(c, seeddir) 723 c.Check(essSnaps, HasLen, 3) 724 c.Check(runSnaps, HasLen, 1) 725 726 // check the files are in place 727 for i, name := range []string{"core_x1.snap", "pc-kernel", "pc"} { 728 channel := stableChannel 729 info := s.AssertedSnapInfo(name) 730 var pinfo snap.PlaceInfo = info 731 var sideInfo *snap.SideInfo 732 var snapType snap.Type 733 if info == nil { 734 switch name { 735 case "core_x1.snap": 736 pinfo = snap.MinimalPlaceInfo("core", snap.R(-1)) 737 sideInfo = &snap.SideInfo{ 738 RealName: "core", 739 } 740 channel = "" 741 snapType = snap.TypeOS 742 } 743 } else { 744 sideInfo = &info.SideInfo 745 snapType = info.Type() 746 } 747 748 fn := pinfo.Filename() 749 p := filepath.Join(seedsnapsdir, fn) 750 c.Check(p, testutil.FilePresent) 751 c.Check(essSnaps[i], DeepEquals, &seed.Snap{ 752 Path: p, 753 754 SideInfo: sideInfo, 755 756 EssentialType: snapType, 757 Essential: true, 758 Required: true, 759 760 Channel: channel, 761 }) 762 } 763 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 764 Path: filepath.Join(seedsnapsdir, "required-snap1_x1.snap"), 765 766 SideInfo: &snap.SideInfo{ 767 RealName: "required-snap1", 768 }, 769 Required: true, 770 }) 771 c.Check(runSnaps[0].Path, testutil.FilePresent) 772 773 // check assertions 774 decls, err := roDB.FindMany(asserts.SnapDeclarationType, nil) 775 c.Assert(err, IsNil) 776 // nothing for core 777 c.Check(decls, HasLen, 2) 778 779 // check the bootloader config 780 m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core") 781 c.Assert(err, IsNil) 782 c.Check(m["snap_kernel"], Equals, "pc-kernel_2.snap") 783 c.Assert(err, IsNil) 784 c.Check(m["snap_core"], Equals, "core_x1.snap") 785 786 c.Check(s.stderr.String(), Equals, "WARNING: \"core\", \"required-snap1\" installed from local snaps disconnected from a store cannot be refreshed subsequently!\n") 787 } 788 789 func (s *imageSuite) TestSetupSeedWithWideCohort(c *C) { 790 restore := image.MockTrusted(s.StoreSigning.Trusted) 791 defer restore() 792 793 rootdir := filepath.Join(c.MkDir(), "image") 794 s.setupSnaps(c, map[string]string{ 795 "pc": "canonical", 796 "pc-kernel": "canonical", 797 }, "") 798 799 snapFile := snaptest.MakeTestSnapWithFiles(c, devmodeSnap, nil) 800 801 opts := &image.Options{ 802 Snaps: []string{snapFile}, 803 804 PrepareDir: filepath.Dir(rootdir), 805 WideCohortKey: "wide-cohort-key", 806 } 807 808 err := image.SetupSeed(s.tsto, s.model, opts) 809 c.Assert(err, IsNil) 810 811 // check the downloads 812 c.Check(s.storeActions, HasLen, 4) 813 c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{ 814 Action: "download", 815 InstanceName: "core", 816 Channel: stableChannel, 817 CohortKey: "wide-cohort-key", 818 }) 819 c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{ 820 Action: "download", 821 InstanceName: "pc-kernel", 822 Channel: stableChannel, 823 CohortKey: "wide-cohort-key", 824 }) 825 c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{ 826 Action: "download", 827 InstanceName: "pc", 828 Channel: stableChannel, 829 CohortKey: "wide-cohort-key", 830 }) 831 c.Check(s.storeActions[3], DeepEquals, &store.SnapAction{ 832 Action: "download", 833 InstanceName: "required-snap1", 834 Channel: stableChannel, 835 CohortKey: "wide-cohort-key", 836 }) 837 } 838 839 func (s *imageSuite) TestSetupSeedDevmodeSnap(c *C) { 840 restore := image.MockTrusted(s.StoreSigning.Trusted) 841 defer restore() 842 843 rootdir := filepath.Join(c.MkDir(), "image") 844 s.setupSnaps(c, map[string]string{ 845 "pc": "canonical", 846 "pc-kernel": "canonical", 847 }, "") 848 849 snapFile := snaptest.MakeTestSnapWithFiles(c, devmodeSnap, nil) 850 851 opts := &image.Options{ 852 Snaps: []string{snapFile}, 853 854 PrepareDir: filepath.Dir(rootdir), 855 Channel: "beta", 856 } 857 858 err := image.SetupSeed(s.tsto, s.model, opts) 859 c.Assert(err, IsNil) 860 861 // check seed 862 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 863 seedsnapsdir := filepath.Join(seeddir, "snaps") 864 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 865 c.Check(essSnaps, HasLen, 3) 866 c.Check(runSnaps, HasLen, 2) 867 868 for i, name := range []string{"core", "pc-kernel", "pc"} { 869 info := s.AssertedSnapInfo(name) 870 c.Check(essSnaps[i], DeepEquals, &seed.Snap{ 871 Path: filepath.Join(seedsnapsdir, info.Filename()), 872 SideInfo: &info.SideInfo, 873 EssentialType: info.Type(), 874 Essential: true, 875 Required: true, 876 Channel: "beta", 877 }) 878 } 879 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 880 Path: filepath.Join(seedsnapsdir, "required-snap1_3.snap"), 881 SideInfo: &s.AssertedSnapInfo("required-snap1").SideInfo, 882 Required: true, 883 Channel: "beta", 884 }) 885 // ensure local snaps are put last in seed.yaml 886 c.Check(runSnaps[1], DeepEquals, &seed.Snap{ 887 Path: filepath.Join(seedsnapsdir, "devmode-snap_x1.snap"), 888 SideInfo: &snap.SideInfo{ 889 RealName: "devmode-snap", 890 }, 891 DevMode: true, 892 // no channel for unasserted snaps 893 Channel: "", 894 }) 895 // check devmode-snap blob 896 c.Check(runSnaps[1].Path, testutil.FilePresent) 897 } 898 899 func (s *imageSuite) TestSetupSeedWithClassicSnapFails(c *C) { 900 restore := image.MockTrusted(s.StoreSigning.Trusted) 901 defer restore() 902 903 rootdir := filepath.Join(c.MkDir(), "image") 904 s.setupSnaps(c, map[string]string{ 905 "pc": "canonical", 906 "pc-kernel": "canonical", 907 }, "") 908 909 s.MakeAssertedSnap(c, classicSnap, nil, snap.R(1), "other") 910 911 opts := &image.Options{ 912 Snaps: []string{s.AssertedSnap("classic-snap")}, 913 914 PrepareDir: filepath.Dir(rootdir), 915 Channel: "beta", 916 } 917 918 err := image.SetupSeed(s.tsto, s.model, opts) 919 c.Assert(err, ErrorMatches, `cannot use classic snap "classic-snap" in a core system`) 920 } 921 922 func (s *imageSuite) TestSetupSeedWithBase(c *C) { 923 restore := image.MockTrusted(s.StoreSigning.Trusted) 924 defer restore() 925 926 // replace model with a model that uses core18 927 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 928 "architecture": "amd64", 929 "gadget": "pc18", 930 "kernel": "pc-kernel", 931 "base": "core18", 932 "required-snaps": []interface{}{"other-base"}, 933 }) 934 935 rootdir := filepath.Join(c.MkDir(), "image") 936 blobdir := filepath.Join(rootdir, "/var/lib/snapd/snaps") 937 s.setupSnaps(c, map[string]string{ 938 "core18": "canonical", 939 "pc18": "canonical", 940 "pc-kernel": "canonical", 941 "snapd": "canonical", 942 "other-base": "other", 943 }, "") 944 945 opts := &image.Options{ 946 PrepareDir: filepath.Dir(rootdir), 947 } 948 949 err := image.SetupSeed(s.tsto, model, opts) 950 c.Assert(err, IsNil) 951 952 // check seed 953 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 954 seedsnapsdir := filepath.Join(seeddir, "snaps") 955 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 956 c.Check(essSnaps, HasLen, 4) 957 c.Check(runSnaps, HasLen, 1) 958 959 // check the files are in place 960 for i, name := range []string{"snapd", "core18_18.snap", "pc-kernel", "pc18"} { 961 info := s.AssertedSnapInfo(name) 962 if info == nil { 963 switch name { 964 case "core18_18.snap": 965 info = &snap.Info{ 966 SideInfo: snap.SideInfo{ 967 SnapID: s.AssertedSnapID("core18"), 968 RealName: "core18", 969 Revision: snap.R("18"), 970 }, 971 SnapType: snap.TypeBase, 972 } 973 } 974 } 975 976 fn := info.Filename() 977 p := filepath.Join(seedsnapsdir, fn) 978 c.Check(p, testutil.FilePresent) 979 c.Check(essSnaps[i], DeepEquals, &seed.Snap{ 980 Path: p, 981 SideInfo: &info.SideInfo, 982 EssentialType: info.Type(), 983 Essential: true, 984 Required: true, 985 Channel: stableChannel, 986 }) 987 } 988 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 989 Path: filepath.Join(seedsnapsdir, "other-base_18.snap"), 990 SideInfo: &s.AssertedSnapInfo("other-base").SideInfo, 991 Required: true, 992 Channel: stableChannel, 993 }) 994 c.Check(runSnaps[0].Path, testutil.FilePresent) 995 996 l, err := ioutil.ReadDir(seedsnapsdir) 997 c.Assert(err, IsNil) 998 c.Check(l, HasLen, 5) 999 1000 // check the bootloader config 1001 m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core") 1002 c.Assert(err, IsNil) 1003 c.Check(m["snap_kernel"], Equals, "pc-kernel_2.snap") 1004 c.Assert(err, IsNil) 1005 c.Check(m["snap_core"], Equals, "core18_18.snap") 1006 1007 // check symlinks from snap blob dir 1008 kernelInfo := s.AssertedSnapInfo("pc-kernel") 1009 baseInfo := s.AssertedSnapInfo("core18") 1010 kernelBlob := filepath.Join(blobdir, kernelInfo.Filename()) 1011 dst, err := os.Readlink(kernelBlob) 1012 c.Assert(err, IsNil) 1013 c.Check(dst, Equals, "../seed/snaps/pc-kernel_2.snap") 1014 c.Check(kernelBlob, testutil.FilePresent) 1015 1016 baseBlob := filepath.Join(blobdir, baseInfo.Filename()) 1017 dst, err = os.Readlink(baseBlob) 1018 c.Assert(err, IsNil) 1019 c.Check(dst, Equals, "../seed/snaps/core18_18.snap") 1020 c.Check(baseBlob, testutil.FilePresent) 1021 1022 c.Check(s.stderr.String(), Equals, "") 1023 1024 // check the downloads 1025 c.Check(s.storeActions, HasLen, 5) 1026 c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{ 1027 Action: "download", 1028 InstanceName: "snapd", 1029 Channel: stableChannel, 1030 }) 1031 c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{ 1032 Action: "download", 1033 InstanceName: "pc-kernel", 1034 Channel: stableChannel, 1035 }) 1036 c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{ 1037 Action: "download", 1038 InstanceName: "core18", 1039 Channel: stableChannel, 1040 }) 1041 c.Check(s.storeActions[3], DeepEquals, &store.SnapAction{ 1042 Action: "download", 1043 InstanceName: "pc18", 1044 Channel: stableChannel, 1045 }) 1046 } 1047 1048 func (s *imageSuite) TestSetupSeedWithBaseWithCloudConf(c *C) { 1049 restore := image.MockTrusted(s.StoreSigning.Trusted) 1050 defer restore() 1051 1052 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1053 "architecture": "amd64", 1054 "gadget": "pc18", 1055 "kernel": "pc-kernel", 1056 "base": "core18", 1057 }) 1058 1059 rootdir := filepath.Join(c.MkDir(), "image") 1060 s.setupSnaps(c, map[string]string{ 1061 "core18": "canonical", 1062 "pc-kernel": "canonical", 1063 "snapd": "canonical", 1064 }, "") 1065 s.MakeAssertedSnap(c, packageGadgetWithBase, [][]string{ 1066 {"grub.conf", ""}, 1067 {"grub.cfg", "I'm a grub.cfg"}, 1068 {"cloud.conf", "# cloud config"}, 1069 {"meta/gadget.yaml", pcGadgetYaml}, 1070 }, snap.R(5), "canonical") 1071 1072 opts := &image.Options{ 1073 PrepareDir: filepath.Dir(rootdir), 1074 } 1075 1076 err := image.SetupSeed(s.tsto, model, opts) 1077 c.Assert(err, IsNil) 1078 1079 c.Check(filepath.Join(rootdir, "/etc/cloud/cloud.cfg"), testutil.FileEquals, "# cloud config") 1080 } 1081 1082 func (s *imageSuite) TestSetupSeedWithBaseLegacySnap(c *C) { 1083 restore := image.MockTrusted(s.StoreSigning.Trusted) 1084 defer restore() 1085 1086 // replace model with a model that uses core18 1087 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1088 "architecture": "amd64", 1089 "gadget": "pc18", 1090 "kernel": "pc-kernel", 1091 "base": "core18", 1092 "required-snaps": []interface{}{"required-snap1"}, 1093 }) 1094 1095 // required-snap1 needs core, for backward compatibility 1096 // we will add it implicitly but warn about this 1097 1098 rootdir := filepath.Join(c.MkDir(), "image") 1099 s.setupSnaps(c, map[string]string{ 1100 "core18": "canonical", 1101 "pc18": "canonical", 1102 "pc-kernel": "canonical", 1103 "snapd": "canonical", 1104 }, "") 1105 1106 opts := &image.Options{ 1107 PrepareDir: filepath.Dir(rootdir), 1108 } 1109 1110 err := image.SetupSeed(s.tsto, model, opts) 1111 c.Assert(err, IsNil) 1112 1113 // check seed 1114 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 1115 seedsnapsdir := filepath.Join(seeddir, "snaps") 1116 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 1117 c.Check(essSnaps, HasLen, 4) 1118 c.Check(runSnaps, HasLen, 2) 1119 1120 // check the files are in place 1121 for i, name := range []string{"snapd", "core18_18.snap", "pc-kernel", "pc18"} { 1122 info := s.AssertedSnapInfo(name) 1123 if info == nil { 1124 switch name { 1125 case "core18_18.snap": 1126 info = &snap.Info{ 1127 SideInfo: snap.SideInfo{ 1128 SnapID: s.AssertedSnapID("core18"), 1129 RealName: "core18", 1130 Revision: snap.R("18"), 1131 }, 1132 SnapType: snap.TypeBase, 1133 } 1134 } 1135 } 1136 1137 fn := info.Filename() 1138 p := filepath.Join(seedsnapsdir, fn) 1139 c.Check(p, testutil.FilePresent) 1140 c.Check(essSnaps[i], DeepEquals, &seed.Snap{ 1141 Path: p, 1142 SideInfo: &info.SideInfo, 1143 EssentialType: info.Type(), 1144 Essential: true, 1145 Required: true, 1146 Channel: stableChannel, 1147 }) 1148 } 1149 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 1150 Path: filepath.Join(seedsnapsdir, s.AssertedSnapInfo("core").Filename()), 1151 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 1152 Required: false, // strange but expected 1153 Channel: stableChannel, 1154 }) 1155 c.Check(runSnaps[0].Path, testutil.FilePresent) 1156 c.Check(runSnaps[1], DeepEquals, &seed.Snap{ 1157 Path: filepath.Join(seedsnapsdir, s.AssertedSnapInfo("required-snap1").Filename()), 1158 SideInfo: &s.AssertedSnapInfo("required-snap1").SideInfo, 1159 Required: true, 1160 Channel: stableChannel, 1161 }) 1162 c.Check(runSnaps[1].Path, testutil.FilePresent) 1163 1164 l, err := ioutil.ReadDir(seedsnapsdir) 1165 c.Assert(err, IsNil) 1166 c.Check(l, HasLen, 6) 1167 1168 // check the bootloader config 1169 m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core") 1170 c.Assert(err, IsNil) 1171 c.Check(m["snap_kernel"], Equals, "pc-kernel_2.snap") 1172 c.Assert(err, IsNil) 1173 c.Check(m["snap_core"], Equals, "core18_18.snap") 1174 1175 c.Check(s.stderr.String(), Equals, "WARNING: model has base \"core18\" but some snaps (\"required-snap1\") require \"core\" as base as well, for compatibility it was added implicitly, adding \"core\" explicitly is recommended\n") 1176 } 1177 1178 func (s *imageSuite) TestSetupSeedWithBaseDefaultTrackSnap(c *C) { 1179 restore := image.MockTrusted(s.StoreSigning.Trusted) 1180 defer restore() 1181 1182 // replace model with a model that uses core18 1183 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1184 "architecture": "amd64", 1185 "gadget": "pc18", 1186 "kernel": "pc-kernel", 1187 "base": "core18", 1188 "required-snaps": []interface{}{"default-track-snap18"}, 1189 }) 1190 1191 // default-track-snap18 has a default-track 1192 1193 rootdir := filepath.Join(c.MkDir(), "image") 1194 s.setupSnaps(c, map[string]string{ 1195 "core18": "canonical", 1196 "pc18": "canonical", 1197 "pc-kernel": "canonical", 1198 "snapd": "canonical", 1199 }, "") 1200 1201 opts := &image.Options{ 1202 PrepareDir: filepath.Dir(rootdir), 1203 } 1204 1205 err := image.SetupSeed(s.tsto, model, opts) 1206 c.Assert(err, IsNil) 1207 1208 // check seed 1209 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 1210 seedsnapsdir := filepath.Join(seeddir, "snaps") 1211 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 1212 c.Check(essSnaps, HasLen, 4) 1213 c.Check(runSnaps, HasLen, 1) 1214 1215 // check the files are in place 1216 for i, name := range []string{"snapd", "core18_18.snap", "pc-kernel", "pc18"} { 1217 info := s.AssertedSnapInfo(name) 1218 if info == nil { 1219 switch name { 1220 case "core18_18.snap": 1221 info = &snap.Info{ 1222 SideInfo: snap.SideInfo{ 1223 SnapID: s.AssertedSnapID("core18"), 1224 RealName: "core18", 1225 Revision: snap.R("18"), 1226 }, 1227 SnapType: snap.TypeBase, 1228 } 1229 } 1230 } 1231 1232 fn := info.Filename() 1233 p := filepath.Join(seedsnapsdir, fn) 1234 c.Check(p, testutil.FilePresent) 1235 c.Check(essSnaps[i], DeepEquals, &seed.Snap{ 1236 Path: p, 1237 SideInfo: &info.SideInfo, 1238 EssentialType: info.Type(), 1239 Essential: true, 1240 Required: true, 1241 Channel: stableChannel, 1242 }) 1243 } 1244 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 1245 Path: filepath.Join(seedsnapsdir, s.AssertedSnapInfo("default-track-snap18").Filename()), 1246 SideInfo: &s.AssertedSnapInfo("default-track-snap18").SideInfo, 1247 Required: true, 1248 Channel: "default-track/stable", 1249 }) 1250 c.Check(runSnaps[0].Path, testutil.FilePresent) 1251 1252 l, err := ioutil.ReadDir(seedsnapsdir) 1253 c.Assert(err, IsNil) 1254 c.Check(l, HasLen, 5) 1255 1256 c.Check(s.stderr.String(), Equals, "") 1257 } 1258 1259 func (s *imageSuite) TestSetupSeedKernelPublisherMismatch(c *C) { 1260 restore := image.MockTrusted(s.StoreSigning.Trusted) 1261 defer restore() 1262 1263 rootdir := filepath.Join(c.MkDir(), "image") 1264 s.setupSnaps(c, map[string]string{ 1265 "pc": "canonical", 1266 "pc-kernel": "other", 1267 }, "") 1268 1269 opts := &image.Options{ 1270 PrepareDir: filepath.Dir(rootdir), 1271 } 1272 1273 err := image.SetupSeed(s.tsto, s.model, opts) 1274 c.Assert(err, ErrorMatches, `cannot use kernel "pc-kernel" published by "other" for model by "my-brand"`) 1275 } 1276 1277 func (s *imageSuite) TestInstallCloudConfigNoConfig(c *C) { 1278 targetDir := c.MkDir() 1279 emptyGadgetDir := c.MkDir() 1280 1281 err := image.InstallCloudConfig(targetDir, emptyGadgetDir) 1282 c.Assert(err, IsNil) 1283 c.Check(osutil.FileExists(filepath.Join(targetDir, "etc/cloud")), Equals, false) 1284 } 1285 1286 func (s *imageSuite) TestInstallCloudConfigWithCloudConfig(c *C) { 1287 canary := []byte("ni! ni! ni!") 1288 1289 targetDir := c.MkDir() 1290 gadgetDir := c.MkDir() 1291 err := ioutil.WriteFile(filepath.Join(gadgetDir, "cloud.conf"), canary, 0644) 1292 c.Assert(err, IsNil) 1293 1294 err = image.InstallCloudConfig(targetDir, gadgetDir) 1295 c.Assert(err, IsNil) 1296 c.Check(filepath.Join(targetDir, "etc/cloud/cloud.cfg"), testutil.FileEquals, canary) 1297 } 1298 1299 func (s *imageSuite) TestNewToolingStoreWithAuth(c *C) { 1300 tmpdir := c.MkDir() 1301 authFn := filepath.Join(tmpdir, "auth.json") 1302 err := ioutil.WriteFile(authFn, []byte(`{ 1303 "macaroon": "MACAROON", 1304 "discharges": ["DISCHARGE"] 1305 }`), 0600) 1306 c.Assert(err, IsNil) 1307 1308 os.Setenv("UBUNTU_STORE_AUTH_DATA_FILENAME", authFn) 1309 defer os.Unsetenv("UBUNTU_STORE_AUTH_DATA_FILENAME") 1310 1311 tsto, err := image.NewToolingStore() 1312 c.Assert(err, IsNil) 1313 user := tsto.User() 1314 c.Check(user.StoreMacaroon, Equals, "MACAROON") 1315 c.Check(user.StoreDischarges, DeepEquals, []string{"DISCHARGE"}) 1316 } 1317 1318 func (s *imageSuite) TestNewToolingStoreWithAuthFromSnapcraftLoginFile(c *C) { 1319 tmpdir := c.MkDir() 1320 authFn := filepath.Join(tmpdir, "auth.json") 1321 err := ioutil.WriteFile(authFn, []byte(`[login.ubuntu.com] 1322 macaroon = MACAROON 1323 unbound_discharge = DISCHARGE 1324 1325 `), 0600) 1326 c.Assert(err, IsNil) 1327 1328 os.Setenv("UBUNTU_STORE_AUTH_DATA_FILENAME", authFn) 1329 defer os.Unsetenv("UBUNTU_STORE_AUTH_DATA_FILENAME") 1330 1331 tsto, err := image.NewToolingStore() 1332 c.Assert(err, IsNil) 1333 user := tsto.User() 1334 c.Check(user.StoreMacaroon, Equals, "MACAROON") 1335 c.Check(user.StoreDischarges, DeepEquals, []string{"DISCHARGE"}) 1336 } 1337 1338 func (s *imageSuite) TestSetupSeedLocalSnapsWithStoreAsserts(c *C) { 1339 restore := image.MockTrusted(s.StoreSigning.Trusted) 1340 defer restore() 1341 1342 rootdir := filepath.Join(c.MkDir(), "image") 1343 s.setupSnaps(c, map[string]string{ 1344 "pc": "canonical", 1345 "pc-kernel": "my-brand", 1346 }, "") 1347 1348 opts := &image.Options{ 1349 Snaps: []string{ 1350 s.AssertedSnap("core"), 1351 s.AssertedSnap("required-snap1"), 1352 }, 1353 PrepareDir: filepath.Dir(rootdir), 1354 } 1355 1356 err := image.SetupSeed(s.tsto, s.model, opts) 1357 c.Assert(err, IsNil) 1358 1359 // check seed 1360 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 1361 seedsnapsdir := filepath.Join(seeddir, "snaps") 1362 essSnaps, runSnaps, roDB := s.loadSeed(c, seeddir) 1363 c.Check(essSnaps, HasLen, 3) 1364 c.Check(runSnaps, HasLen, 1) 1365 1366 // check the files are in place 1367 for i, name := range []string{"core_3.snap", "pc-kernel", "pc"} { 1368 info := s.AssertedSnapInfo(name) 1369 if info == nil { 1370 switch name { 1371 case "core_3.snap": 1372 info = &snap.Info{ 1373 SideInfo: snap.SideInfo{ 1374 RealName: "core", 1375 SnapID: s.AssertedSnapID("core"), 1376 Revision: snap.R(3), 1377 }, 1378 SnapType: snap.TypeOS, 1379 } 1380 default: 1381 c.Errorf("cannot have %s", name) 1382 } 1383 } 1384 1385 fn := info.Filename() 1386 p := filepath.Join(seedsnapsdir, fn) 1387 c.Check(p, testutil.FilePresent) 1388 c.Check(essSnaps[i], DeepEquals, &seed.Snap{ 1389 Path: p, 1390 SideInfo: &info.SideInfo, 1391 EssentialType: info.Type(), 1392 Essential: true, 1393 Required: true, 1394 Channel: stableChannel, 1395 }) 1396 } 1397 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 1398 Path: filepath.Join(seedsnapsdir, "required-snap1_3.snap"), 1399 Required: true, 1400 SideInfo: &snap.SideInfo{ 1401 RealName: "required-snap1", 1402 SnapID: s.AssertedSnapID("required-snap1"), 1403 Revision: snap.R(3), 1404 }, 1405 Channel: stableChannel, 1406 }) 1407 c.Check(runSnaps[0].Path, testutil.FilePresent) 1408 1409 l, err := ioutil.ReadDir(seedsnapsdir) 1410 c.Assert(err, IsNil) 1411 c.Check(l, HasLen, 4) 1412 1413 // check assertions 1414 decls, err := roDB.FindMany(asserts.SnapDeclarationType, nil) 1415 c.Assert(err, IsNil) 1416 c.Check(decls, HasLen, 4) 1417 1418 // check the bootloader config 1419 m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core") 1420 c.Assert(err, IsNil) 1421 c.Check(m["snap_kernel"], Equals, "pc-kernel_2.snap") 1422 c.Assert(err, IsNil) 1423 c.Check(m["snap_core"], Equals, "core_3.snap") 1424 1425 c.Check(s.stderr.String(), Equals, "") 1426 } 1427 1428 func (s *imageSuite) TestSetupSeedLocalSnapsWithChannels(c *C) { 1429 restore := image.MockTrusted(s.StoreSigning.Trusted) 1430 defer restore() 1431 1432 rootdir := filepath.Join(c.MkDir(), "image") 1433 s.setupSnaps(c, map[string]string{ 1434 "pc": "canonical", 1435 "pc-kernel": "my-brand", 1436 }, "") 1437 1438 opts := &image.Options{ 1439 Snaps: []string{ 1440 "core", 1441 s.AssertedSnap("required-snap1"), 1442 }, 1443 PrepareDir: filepath.Dir(rootdir), 1444 SnapChannels: map[string]string{ 1445 "core": "candidate", 1446 // keep this comment for gofmt 1.9 1447 s.AssertedSnap("required-snap1"): "edge", 1448 }, 1449 } 1450 1451 err := image.SetupSeed(s.tsto, s.model, opts) 1452 c.Assert(err, IsNil) 1453 1454 // check seed 1455 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 1456 seedsnapsdir := filepath.Join(seeddir, "snaps") 1457 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 1458 c.Check(essSnaps, HasLen, 3) 1459 c.Check(runSnaps, HasLen, 1) 1460 1461 // check the files are in place 1462 for i, name := range []string{"core_3.snap", "pc-kernel", "pc"} { 1463 info := s.AssertedSnapInfo(name) 1464 channel := stableChannel 1465 if info == nil { 1466 switch name { 1467 case "core_3.snap": 1468 info = &snap.Info{ 1469 SideInfo: snap.SideInfo{ 1470 RealName: "core", 1471 SnapID: s.AssertedSnapID("core"), 1472 Revision: snap.R(3), 1473 }, 1474 SnapType: snap.TypeOS, 1475 } 1476 channel = "candidate" 1477 default: 1478 c.Errorf("cannot have %s", name) 1479 } 1480 } 1481 1482 fn := info.Filename() 1483 p := filepath.Join(seedsnapsdir, fn) 1484 c.Check(p, testutil.FilePresent) 1485 c.Check(essSnaps[i], DeepEquals, &seed.Snap{ 1486 Path: p, 1487 SideInfo: &info.SideInfo, 1488 EssentialType: info.Type(), 1489 Essential: true, 1490 Required: true, 1491 Channel: channel, 1492 }) 1493 } 1494 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 1495 Path: filepath.Join(seedsnapsdir, "required-snap1_3.snap"), 1496 Required: true, 1497 SideInfo: &snap.SideInfo{ 1498 RealName: "required-snap1", 1499 SnapID: s.AssertedSnapID("required-snap1"), 1500 Revision: snap.R(3), 1501 }, 1502 Channel: "edge", 1503 }) 1504 c.Check(runSnaps[0].Path, testutil.FilePresent) 1505 } 1506 1507 func (s *imageSuite) TestCannotCreateGadgetUnpackDir(c *C) { 1508 fn := filepath.Join(c.MkDir(), "model.assertion") 1509 err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644) 1510 c.Assert(err, IsNil) 1511 1512 err = image.Prepare(&image.Options{ 1513 ModelFile: fn, 1514 Channel: "stable", 1515 PrepareDir: "/no-where", 1516 }) 1517 c.Assert(err, ErrorMatches, `cannot create gadget unpack dir "/no-where/gadget": mkdir .*`) 1518 } 1519 1520 func (s *imageSuite) TestNoLocalParallelSnapInstances(c *C) { 1521 fn := filepath.Join(c.MkDir(), "model.assertion") 1522 err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644) 1523 c.Assert(err, IsNil) 1524 1525 err = image.Prepare(&image.Options{ 1526 ModelFile: fn, 1527 Snaps: []string{"foo_instance"}, 1528 }) 1529 c.Assert(err, ErrorMatches, `cannot use snap "foo_instance", parallel snap instances are unsupported`) 1530 } 1531 1532 func (s *imageSuite) TestNoInvalidSnapNames(c *C) { 1533 fn := filepath.Join(c.MkDir(), "model.assertion") 1534 err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644) 1535 c.Assert(err, IsNil) 1536 1537 err = image.Prepare(&image.Options{ 1538 ModelFile: fn, 1539 Snaps: []string{"foo.invalid.name"}, 1540 }) 1541 c.Assert(err, ErrorMatches, `invalid snap name: "foo.invalid.name"`) 1542 } 1543 1544 func (s *imageSuite) TestPrepareInvalidChannel(c *C) { 1545 fn := filepath.Join(c.MkDir(), "model.assertion") 1546 err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644) 1547 c.Assert(err, IsNil) 1548 1549 err = image.Prepare(&image.Options{ 1550 ModelFile: fn, 1551 Channel: "x/x/x/x", 1552 }) 1553 c.Assert(err, ErrorMatches, `cannot use global default option channel: channel name has too many components: x/x/x/x`) 1554 } 1555 1556 func (s *imageSuite) TestPrepareClassicModeNoClassicModel(c *C) { 1557 fn := filepath.Join(c.MkDir(), "model.assertion") 1558 err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644) 1559 c.Assert(err, IsNil) 1560 1561 err = image.Prepare(&image.Options{ 1562 Classic: true, 1563 ModelFile: fn, 1564 }) 1565 c.Assert(err, ErrorMatches, "cannot prepare the image for a core model with --classic mode specified") 1566 } 1567 1568 func (s *imageSuite) TestPrepareClassicModelNoClassicMode(c *C) { 1569 restore := image.MockTrusted(s.StoreSigning.Trusted) 1570 defer restore() 1571 1572 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1573 "classic": "true", 1574 }) 1575 1576 fn := filepath.Join(c.MkDir(), "model.assertion") 1577 err := ioutil.WriteFile(fn, asserts.Encode(model), 0644) 1578 c.Assert(err, IsNil) 1579 1580 err = image.Prepare(&image.Options{ 1581 ModelFile: fn, 1582 }) 1583 c.Assert(err, ErrorMatches, "--classic mode is required to prepare the image for a classic model") 1584 } 1585 1586 func (s *imageSuite) TestPrepareClassicModelArchOverrideFails(c *C) { 1587 restore := image.MockTrusted(s.StoreSigning.Trusted) 1588 defer restore() 1589 1590 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1591 "classic": "true", 1592 "architecture": "amd64", 1593 }) 1594 1595 fn := filepath.Join(c.MkDir(), "model.assertion") 1596 err := ioutil.WriteFile(fn, asserts.Encode(model), 0644) 1597 c.Assert(err, IsNil) 1598 1599 err = image.Prepare(&image.Options{ 1600 Classic: true, 1601 ModelFile: fn, 1602 Architecture: "i386", 1603 }) 1604 c.Assert(err, ErrorMatches, "cannot override model architecture: amd64") 1605 } 1606 1607 func (s *imageSuite) TestPrepareClassicModelSnapsButNoArchFails(c *C) { 1608 restore := image.MockTrusted(s.StoreSigning.Trusted) 1609 defer restore() 1610 1611 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1612 "classic": "true", 1613 "gadget": "classic-gadget", 1614 }) 1615 1616 fn := filepath.Join(c.MkDir(), "model.assertion") 1617 err := ioutil.WriteFile(fn, asserts.Encode(model), 0644) 1618 c.Assert(err, IsNil) 1619 1620 err = image.Prepare(&image.Options{ 1621 Classic: true, 1622 ModelFile: fn, 1623 }) 1624 c.Assert(err, ErrorMatches, "cannot have snaps for a classic image without an architecture in the model or from --arch") 1625 } 1626 1627 func (s *imageSuite) TestSetupSeedWithKernelAndGadgetTrack(c *C) { 1628 restore := image.MockTrusted(s.StoreSigning.Trusted) 1629 defer restore() 1630 1631 // replace model with a model that uses core18 1632 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1633 "architecture": "amd64", 1634 "gadget": "pc=18", 1635 "kernel": "pc-kernel=18", 1636 }) 1637 1638 rootdir := filepath.Join(c.MkDir(), "image") 1639 s.setupSnaps(c, map[string]string{ 1640 "core": "canonical", 1641 "pc": "canonical", 1642 "pc-kernel": "canonical", 1643 }, "") 1644 1645 opts := &image.Options{ 1646 PrepareDir: filepath.Dir(rootdir), 1647 Channel: "stable", 1648 } 1649 1650 err := image.SetupSeed(s.tsto, model, opts) 1651 c.Assert(err, IsNil) 1652 1653 // check seed 1654 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 1655 seedsnapsdir := filepath.Join(seeddir, "snaps") 1656 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 1657 c.Check(essSnaps, HasLen, 3) 1658 c.Check(runSnaps, HasLen, 0) 1659 1660 c.Check(essSnaps[0], DeepEquals, &seed.Snap{ 1661 Path: filepath.Join(seedsnapsdir, "core_3.snap"), 1662 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 1663 EssentialType: snap.TypeOS, 1664 Essential: true, 1665 Required: true, 1666 Channel: "stable", 1667 }) 1668 c.Check(essSnaps[1], DeepEquals, &seed.Snap{ 1669 Path: filepath.Join(seedsnapsdir, "pc-kernel_2.snap"), 1670 SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo, 1671 EssentialType: snap.TypeKernel, 1672 Essential: true, 1673 Required: true, 1674 Channel: "18/stable", 1675 }) 1676 c.Check(essSnaps[2], DeepEquals, &seed.Snap{ 1677 Path: filepath.Join(seedsnapsdir, "pc_1.snap"), 1678 SideInfo: &s.AssertedSnapInfo("pc").SideInfo, 1679 EssentialType: snap.TypeGadget, 1680 Essential: true, 1681 Required: true, 1682 Channel: "18/stable", 1683 }) 1684 1685 // check the downloads 1686 c.Check(s.storeActions, HasLen, 3) 1687 c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{ 1688 Action: "download", 1689 InstanceName: "core", 1690 Channel: "stable", 1691 }) 1692 c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{ 1693 Action: "download", 1694 InstanceName: "pc-kernel", 1695 Channel: "18/stable", 1696 }) 1697 c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{ 1698 Action: "download", 1699 InstanceName: "pc", 1700 Channel: "18/stable", 1701 }) 1702 } 1703 1704 func (s *imageSuite) TestSetupSeedWithKernelTrackWithDefaultChannel(c *C) { 1705 restore := image.MockTrusted(s.StoreSigning.Trusted) 1706 defer restore() 1707 1708 // replace model with a model that uses core18 1709 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1710 "architecture": "amd64", 1711 "gadget": "pc", 1712 "kernel": "pc-kernel=18", 1713 }) 1714 1715 s.setupSnaps(c, map[string]string{ 1716 "core": "canonical", 1717 "pc": "canonical", 1718 "pc-kernel": "canonical", 1719 }, "") 1720 1721 rootdir := filepath.Join(c.MkDir(), "image") 1722 opts := &image.Options{ 1723 PrepareDir: filepath.Dir(rootdir), 1724 Channel: "edge", 1725 } 1726 1727 err := image.SetupSeed(s.tsto, model, opts) 1728 c.Assert(err, IsNil) 1729 1730 // check seed 1731 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 1732 seedsnapsdir := filepath.Join(seeddir, "snaps") 1733 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 1734 c.Check(essSnaps, HasLen, 3) 1735 c.Check(runSnaps, HasLen, 0) 1736 1737 c.Check(essSnaps[0], DeepEquals, &seed.Snap{ 1738 Path: filepath.Join(seedsnapsdir, "core_3.snap"), 1739 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 1740 EssentialType: snap.TypeOS, 1741 Essential: true, 1742 Required: true, 1743 Channel: "edge", 1744 }) 1745 c.Check(essSnaps[1], DeepEquals, &seed.Snap{ 1746 Path: filepath.Join(seedsnapsdir, "pc-kernel_2.snap"), 1747 SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo, 1748 EssentialType: snap.TypeKernel, 1749 Essential: true, 1750 Required: true, 1751 Channel: "18/edge", 1752 }) 1753 c.Check(essSnaps[2], DeepEquals, &seed.Snap{ 1754 Path: filepath.Join(seedsnapsdir, "pc_1.snap"), 1755 SideInfo: &s.AssertedSnapInfo("pc").SideInfo, 1756 EssentialType: snap.TypeGadget, 1757 Essential: true, 1758 Required: true, 1759 Channel: "edge", 1760 }) 1761 } 1762 1763 func (s *imageSuite) TestSetupSeedWithKernelTrackOnLocalSnap(c *C) { 1764 restore := image.MockTrusted(s.StoreSigning.Trusted) 1765 defer restore() 1766 1767 // replace model with a model that uses core18 1768 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1769 "architecture": "amd64", 1770 "gadget": "pc", 1771 "kernel": "pc-kernel=18", 1772 }) 1773 1774 rootdir := filepath.Join(c.MkDir(), "image") 1775 s.setupSnaps(c, map[string]string{ 1776 "core": "canonical", 1777 "pc": "canonical", 1778 "pc-kernel": "canonical", 1779 }, "") 1780 1781 // pretend we downloaded the core,kernel already 1782 cfn := s.AssertedSnap("core") 1783 kfn := s.AssertedSnap("pc-kernel") 1784 opts := &image.Options{ 1785 PrepareDir: filepath.Dir(rootdir), 1786 Snaps: []string{kfn, cfn}, 1787 Channel: "beta", 1788 } 1789 1790 err := image.SetupSeed(s.tsto, model, opts) 1791 c.Assert(err, IsNil) 1792 1793 // check seed 1794 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 1795 seedsnapsdir := filepath.Join(seeddir, "snaps") 1796 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 1797 c.Check(essSnaps, HasLen, 3) 1798 c.Check(runSnaps, HasLen, 0) 1799 1800 c.Check(essSnaps[0], DeepEquals, &seed.Snap{ 1801 Path: filepath.Join(seedsnapsdir, "core_3.snap"), 1802 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 1803 EssentialType: snap.TypeOS, 1804 Essential: true, 1805 Required: true, 1806 Channel: "beta", 1807 }) 1808 c.Check(essSnaps[1], DeepEquals, &seed.Snap{ 1809 Path: filepath.Join(seedsnapsdir, "pc-kernel_2.snap"), 1810 SideInfo: &s.AssertedSnapInfo("pc-kernel").SideInfo, 1811 EssentialType: snap.TypeKernel, 1812 Essential: true, 1813 Required: true, 1814 Channel: "18/beta", 1815 }) 1816 } 1817 1818 func (s *imageSuite) TestSetupSeedWithBaseAndLocalLegacyCoreOrdering(c *C) { 1819 restore := image.MockTrusted(s.StoreSigning.Trusted) 1820 defer restore() 1821 1822 // replace model with a model that uses core18 1823 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1824 "architecture": "amd64", 1825 "base": "core18", 1826 "gadget": "pc18", 1827 "kernel": "pc-kernel", 1828 "required-snaps": []interface{}{"required-snap1"}, 1829 }) 1830 1831 rootdir := filepath.Join(c.MkDir(), "image") 1832 s.setupSnaps(c, map[string]string{ 1833 "core18": "canonical", 1834 "pc18": "canonical", 1835 "pc-kernel": "canonical", 1836 }, "") 1837 1838 coreFn := snaptest.MakeTestSnapWithFiles(c, packageCore, [][]string{{"local", ""}}) 1839 1840 opts := &image.Options{ 1841 PrepareDir: filepath.Dir(rootdir), 1842 Snaps: []string{ 1843 coreFn, 1844 }, 1845 } 1846 1847 err := image.SetupSeed(s.tsto, model, opts) 1848 c.Assert(err, IsNil) 1849 1850 // check seed 1851 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 1852 seedsnapsdir := filepath.Join(seeddir, "snaps") 1853 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 1854 c.Check(essSnaps, HasLen, 4) 1855 c.Check(runSnaps, HasLen, 2) 1856 1857 c.Check(essSnaps[0].Path, Equals, filepath.Join(seedsnapsdir, "snapd_18.snap")) 1858 c.Check(essSnaps[1].Path, Equals, filepath.Join(seedsnapsdir, "core18_18.snap")) 1859 c.Check(essSnaps[2].Path, Equals, filepath.Join(seedsnapsdir, "pc-kernel_2.snap")) 1860 c.Check(essSnaps[3].Path, Equals, filepath.Join(seedsnapsdir, "pc18_4.snap")) 1861 1862 c.Check(runSnaps[0].Path, Equals, filepath.Join(seedsnapsdir, "core_x1.snap")) 1863 c.Check(runSnaps[1].Path, Equals, filepath.Join(seedsnapsdir, "required-snap1_3.snap")) 1864 } 1865 1866 func (s *imageSuite) TestSetupSeedWithBaseAndLegacyCoreOrdering(c *C) { 1867 restore := image.MockTrusted(s.StoreSigning.Trusted) 1868 defer restore() 1869 1870 // replace model with a model that uses core18 1871 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1872 "architecture": "amd64", 1873 "base": "core18", 1874 "gadget": "pc18", 1875 "kernel": "pc-kernel", 1876 "required-snaps": []interface{}{"required-snap1", "core"}, 1877 }) 1878 1879 rootdir := filepath.Join(c.MkDir(), "image") 1880 s.setupSnaps(c, map[string]string{ 1881 "core18": "canonical", 1882 "core": "canonical", 1883 "pc18": "canonical", 1884 "pc-kernel": "canonical", 1885 }, "") 1886 1887 opts := &image.Options{ 1888 PrepareDir: filepath.Dir(rootdir), 1889 } 1890 1891 err := image.SetupSeed(s.tsto, model, opts) 1892 c.Assert(err, IsNil) 1893 1894 // check seed 1895 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 1896 seedsnapsdir := filepath.Join(seeddir, "snaps") 1897 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 1898 c.Check(essSnaps, HasLen, 4) 1899 c.Check(runSnaps, HasLen, 2) 1900 1901 c.Check(essSnaps[0].Path, Equals, filepath.Join(seedsnapsdir, "snapd_18.snap")) 1902 c.Check(essSnaps[1].Path, Equals, filepath.Join(seedsnapsdir, "core18_18.snap")) 1903 c.Check(essSnaps[2].Path, Equals, filepath.Join(seedsnapsdir, "pc-kernel_2.snap")) 1904 c.Check(essSnaps[3].Path, Equals, filepath.Join(seedsnapsdir, "pc18_4.snap")) 1905 1906 c.Check(runSnaps[0].Path, Equals, filepath.Join(seedsnapsdir, "core_3.snap")) 1907 c.Check(runSnaps[1].Path, Equals, filepath.Join(seedsnapsdir, "required-snap1_3.snap")) 1908 } 1909 1910 func (s *imageSuite) TestSetupSeedGadgetBaseModelBaseMismatch(c *C) { 1911 restore := image.MockTrusted(s.StoreSigning.Trusted) 1912 defer restore() 1913 // replace model with a model that uses core18 and a gadget 1914 // without a base 1915 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1916 "architecture": "amd64", 1917 "base": "core18", 1918 "gadget": "pc", 1919 "kernel": "pc-kernel", 1920 "required-snaps": []interface{}{"required-snap1"}, 1921 }) 1922 1923 rootdir := filepath.Join(c.MkDir(), "image") 1924 s.setupSnaps(c, map[string]string{ 1925 "core18": "canonical", 1926 "pc": "canonical", 1927 "pc-kernel": "canonical", 1928 }, "") 1929 opts := &image.Options{ 1930 PrepareDir: filepath.Dir(rootdir), 1931 } 1932 1933 err := image.SetupSeed(s.tsto, model, opts) 1934 c.Assert(err, ErrorMatches, `cannot use gadget snap because its base "" is different from model base "core18"`) 1935 } 1936 1937 func (s *imageSuite) TestSetupSeedSnapReqBase(c *C) { 1938 restore := image.MockTrusted(s.StoreSigning.Trusted) 1939 defer restore() 1940 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1941 "architecture": "amd64", 1942 "gadget": "pc", 1943 "kernel": "pc-kernel", 1944 "required-snaps": []interface{}{"snap-req-other-base"}, 1945 }) 1946 1947 rootdir := filepath.Join(c.MkDir(), "image") 1948 s.setupSnaps(c, map[string]string{ 1949 "core": "canonical", 1950 "pc": "canonical", 1951 "pc-kernel": "canonical", 1952 "snap-req-other-base": "canonical", 1953 }, "") 1954 opts := &image.Options{ 1955 PrepareDir: filepath.Dir(rootdir), 1956 } 1957 1958 err := image.SetupSeed(s.tsto, model, opts) 1959 c.Assert(err, ErrorMatches, `cannot add snap "snap-req-other-base" without also adding its base "other-base" explicitly`) 1960 } 1961 1962 func (s *imageSuite) TestSetupSeedBaseNone(c *C) { 1963 restore := image.MockTrusted(s.StoreSigning.Trusted) 1964 defer restore() 1965 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1966 "architecture": "amd64", 1967 "gadget": "pc", 1968 "kernel": "pc-kernel", 1969 "required-snaps": []interface{}{"snap-base-none"}, 1970 }) 1971 1972 rootdir := filepath.Join(c.MkDir(), "image") 1973 s.setupSnaps(c, map[string]string{ 1974 "core": "canonical", 1975 "pc": "canonical", 1976 "pc-kernel": "canonical", 1977 "snap-base-none": "canonical", 1978 }, "") 1979 opts := &image.Options{ 1980 PrepareDir: filepath.Dir(rootdir), 1981 } 1982 1983 c.Assert(image.SetupSeed(s.tsto, model, opts), IsNil) 1984 } 1985 1986 func (s *imageSuite) TestSetupSeedCore18GadgetDefaults(c *C) { 1987 systemctlMock := testutil.MockCommand(c, "systemctl", "") 1988 defer systemctlMock.Restore() 1989 1990 restore := image.MockTrusted(s.StoreSigning.Trusted) 1991 defer restore() 1992 1993 // replace model with a model that uses core18 1994 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 1995 "architecture": "amd64", 1996 "gadget": "pc18", 1997 "kernel": "pc-kernel", 1998 "base": "core18", 1999 }) 2000 2001 defaults := `defaults: 2002 system: 2003 service: 2004 ssh: 2005 disable: true 2006 ` 2007 2008 rootdir := filepath.Join(c.MkDir(), "image") 2009 s.setupSnaps(c, map[string]string{ 2010 "pc18": "canonical", 2011 "pc-kernel": "canonical", 2012 }, defaults) 2013 2014 snapdFn := snaptest.MakeTestSnapWithFiles(c, snapdSnap, [][]string{{"local", ""}}) 2015 core18Fn := snaptest.MakeTestSnapWithFiles(c, packageCore18, [][]string{{"local", ""}}) 2016 2017 opts := &image.Options{ 2018 Snaps: []string{ 2019 snapdFn, 2020 core18Fn, 2021 }, 2022 2023 PrepareDir: filepath.Dir(rootdir), 2024 } 2025 2026 err := image.SetupSeed(s.tsto, model, opts) 2027 c.Assert(err, IsNil) 2028 c.Check(osutil.FileExists(filepath.Join(rootdir, "_writable_defaults/etc/ssh/sshd_not_to_be_run")), Equals, true) 2029 } 2030 2031 func (s *imageSuite) TestSetupSeedSnapCoreSatisfiesCore16(c *C) { 2032 restore := image.MockTrusted(s.StoreSigning.Trusted) 2033 defer restore() 2034 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2035 "architecture": "amd64", 2036 "gadget": "pc", 2037 "kernel": "pc-kernel", 2038 "required-snaps": []interface{}{"snap-req-core16-base"}, 2039 }) 2040 2041 rootdir := filepath.Join(c.MkDir(), "image") 2042 s.setupSnaps(c, map[string]string{ 2043 "core": "canonical", 2044 "pc": "canonical", 2045 "pc-kernel": "canonical", 2046 "snap-req-other-base": "canonical", 2047 }, "") 2048 opts := &image.Options{ 2049 PrepareDir: filepath.Dir(rootdir), 2050 } 2051 2052 err := image.SetupSeed(s.tsto, model, opts) 2053 c.Assert(err, IsNil) 2054 } 2055 2056 func (s *imageSuite) TestSetupSeedStoreAssertionMissing(c *C) { 2057 restore := image.MockTrusted(s.StoreSigning.Trusted) 2058 defer restore() 2059 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2060 "architecture": "amd64", 2061 "gadget": "pc", 2062 "kernel": "pc-kernel", 2063 "store": "my-store", 2064 }) 2065 2066 rootdir := filepath.Join(c.MkDir(), "image") 2067 s.setupSnaps(c, map[string]string{ 2068 "core": "canonical", 2069 "pc": "canonical", 2070 "pc-kernel": "canonical", 2071 }, "") 2072 opts := &image.Options{ 2073 PrepareDir: filepath.Dir(rootdir), 2074 } 2075 2076 err := image.SetupSeed(s.tsto, model, opts) 2077 c.Assert(err, IsNil) 2078 } 2079 2080 func (s *imageSuite) TestSetupSeedStoreAssertionFetched(c *C) { 2081 restore := image.MockTrusted(s.StoreSigning.Trusted) 2082 defer restore() 2083 2084 // add store assertion 2085 storeAs, err := s.StoreSigning.Sign(asserts.StoreType, map[string]interface{}{ 2086 "store": "my-store", 2087 "operator-id": "canonical", 2088 "timestamp": time.Now().UTC().Format(time.RFC3339), 2089 }, nil, "") 2090 c.Assert(err, IsNil) 2091 err = s.StoreSigning.Add(storeAs) 2092 c.Assert(err, IsNil) 2093 2094 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2095 "architecture": "amd64", 2096 "gadget": "pc", 2097 "kernel": "pc-kernel", 2098 "store": "my-store", 2099 }) 2100 2101 rootdir := filepath.Join(c.MkDir(), "image") 2102 s.setupSnaps(c, map[string]string{ 2103 "core": "canonical", 2104 "pc": "canonical", 2105 "pc-kernel": "canonical", 2106 }, "") 2107 opts := &image.Options{ 2108 PrepareDir: filepath.Dir(rootdir), 2109 } 2110 2111 err = image.SetupSeed(s.tsto, model, opts) 2112 c.Assert(err, IsNil) 2113 2114 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 2115 essSnaps, runSnaps, roDB := s.loadSeed(c, seeddir) 2116 c.Check(essSnaps, HasLen, 3) 2117 c.Check(runSnaps, HasLen, 0) 2118 2119 // check the store assertion was fetched 2120 _, err = roDB.Find(asserts.StoreType, map[string]string{ 2121 "store": "my-store", 2122 }) 2123 c.Check(err, IsNil) 2124 } 2125 2126 func (s *imageSuite) TestSetupSeedSnapReqBaseFromLocal(c *C) { 2127 // As originally written it let an extra snap fullfil 2128 // the prereq of a required one, this does not work anymore! 2129 // See TestSetupSeedSnapReqBaseFromExtraFails. 2130 restore := image.MockTrusted(s.StoreSigning.Trusted) 2131 defer restore() 2132 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2133 "architecture": "amd64", 2134 "gadget": "pc", 2135 "kernel": "pc-kernel", 2136 "required-snaps": []interface{}{"other-base", "snap-req-other-base"}, 2137 }) 2138 2139 rootdir := filepath.Join(c.MkDir(), "image") 2140 s.setupSnaps(c, map[string]string{ 2141 "core": "canonical", 2142 "pc": "canonical", 2143 "pc-kernel": "canonical", 2144 "snap-req-other-base": "canonical", 2145 "other-base": "canonical", 2146 }, "") 2147 bfn := s.AssertedSnap("other-base") 2148 opts := &image.Options{ 2149 PrepareDir: filepath.Dir(rootdir), 2150 Snaps: []string{bfn}, 2151 } 2152 2153 err := image.SetupSeed(s.tsto, model, opts) 2154 c.Assert(err, IsNil) 2155 } 2156 2157 func (s *imageSuite) TestSetupSeedSnapReqBaseFromExtraFails(c *C) { 2158 restore := image.MockTrusted(s.StoreSigning.Trusted) 2159 defer restore() 2160 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2161 "architecture": "amd64", 2162 "gadget": "pc", 2163 "kernel": "pc-kernel", 2164 "required-snaps": []interface{}{"snap-req-other-base"}, 2165 }) 2166 2167 rootdir := filepath.Join(c.MkDir(), "image") 2168 s.setupSnaps(c, map[string]string{ 2169 "core": "canonical", 2170 "pc": "canonical", 2171 "pc-kernel": "canonical", 2172 "snap-req-other-base": "canonical", 2173 "other-base": "canonical", 2174 }, "") 2175 bfn := s.AssertedSnap("other-base") 2176 opts := &image.Options{ 2177 PrepareDir: filepath.Dir(rootdir), 2178 Snaps: []string{bfn}, 2179 } 2180 2181 err := image.SetupSeed(s.tsto, model, opts) 2182 c.Check(err, ErrorMatches, `cannot add snap "snap-req-other-base" without also adding its base "other-base" explicitly`) 2183 } 2184 2185 func (s *imageSuite) TestSetupSeedMissingContentProvider(c *C) { 2186 restore := image.MockTrusted(s.StoreSigning.Trusted) 2187 defer restore() 2188 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2189 "architecture": "amd64", 2190 "gadget": "pc", 2191 "kernel": "pc-kernel", 2192 "required-snaps": []interface{}{"snap-req-content-provider"}, 2193 }) 2194 2195 rootdir := filepath.Join(c.MkDir(), "image") 2196 s.setupSnaps(c, map[string]string{ 2197 "core": "canonical", 2198 "pc": "canonical", 2199 "pc-kernel": "canonical", 2200 "snap-req-content-snap": "canonical", 2201 }, "") 2202 opts := &image.Options{ 2203 PrepareDir: filepath.Dir(rootdir), 2204 } 2205 2206 err := image.SetupSeed(s.tsto, model, opts) 2207 c.Check(err, ErrorMatches, `cannot use snap "snap-req-content-provider" without its default content provider "gtk-common-themes" being added explicitly`) 2208 } 2209 2210 func (s *imageSuite) TestSetupSeedClassic(c *C) { 2211 restore := image.MockTrusted(s.StoreSigning.Trusted) 2212 defer restore() 2213 2214 // classic model with gadget etc 2215 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2216 "classic": "true", 2217 "architecture": "amd64", 2218 "gadget": "classic-gadget", 2219 "required-snaps": []interface{}{"required-snap1"}, 2220 }) 2221 2222 rootdir := c.MkDir() 2223 s.setupSnaps(c, map[string]string{ 2224 "classic-gadget": "my-brand", 2225 }, "") 2226 2227 opts := &image.Options{ 2228 Classic: true, 2229 PrepareDir: rootdir, 2230 } 2231 2232 err := image.SetupSeed(s.tsto, model, opts) 2233 c.Assert(err, IsNil) 2234 2235 // check seed 2236 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 2237 seedsnapsdir := filepath.Join(seeddir, "snaps") 2238 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 2239 c.Check(essSnaps, HasLen, 2) 2240 c.Check(runSnaps, HasLen, 1) 2241 2242 // check the files are in place 2243 c.Check(essSnaps[0], DeepEquals, &seed.Snap{ 2244 Path: filepath.Join(seedsnapsdir, "core_3.snap"), 2245 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 2246 EssentialType: snap.TypeOS, 2247 Essential: true, 2248 Required: true, 2249 Channel: stableChannel, 2250 }) 2251 c.Check(essSnaps[0].Path, testutil.FilePresent) 2252 c.Check(essSnaps[1], DeepEquals, &seed.Snap{ 2253 Path: filepath.Join(seedsnapsdir, "classic-gadget_5.snap"), 2254 SideInfo: &s.AssertedSnapInfo("classic-gadget").SideInfo, 2255 EssentialType: snap.TypeGadget, 2256 Essential: true, 2257 Required: true, 2258 Channel: stableChannel, 2259 }) 2260 c.Check(essSnaps[1].Path, testutil.FilePresent) 2261 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 2262 Path: filepath.Join(seedsnapsdir, "required-snap1_3.snap"), 2263 SideInfo: &s.AssertedSnapInfo("required-snap1").SideInfo, 2264 Required: true, 2265 Channel: stableChannel, 2266 }) 2267 c.Check(runSnaps[0].Path, testutil.FilePresent) 2268 2269 l, err := ioutil.ReadDir(seedsnapsdir) 2270 c.Assert(err, IsNil) 2271 c.Check(l, HasLen, 3) 2272 2273 // check that the bootloader is unset 2274 m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core") 2275 c.Assert(err, IsNil) 2276 c.Check(m, DeepEquals, map[string]string{ 2277 "snap_core": "", 2278 "snap_kernel": "", 2279 }) 2280 2281 c.Check(s.stderr.String(), Matches, `WARNING: ensure that the contents under .*/var/lib/snapd/seed are owned by root:root in the \(final\) image`) 2282 2283 // no blob dir created 2284 blobdir := filepath.Join(rootdir, "var/lib/snapd/snaps") 2285 c.Check(osutil.FileExists(blobdir), Equals, false) 2286 } 2287 2288 func (s *imageSuite) TestSetupSeedClassicWithLocalClassicSnap(c *C) { 2289 restore := image.MockTrusted(s.StoreSigning.Trusted) 2290 defer restore() 2291 2292 // classic model 2293 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2294 "classic": "true", 2295 "architecture": "amd64", 2296 }) 2297 2298 rootdir := c.MkDir() 2299 s.setupSnaps(c, nil, "") 2300 2301 snapFile := snaptest.MakeTestSnapWithFiles(c, classicSnap, nil) 2302 2303 opts := &image.Options{ 2304 Classic: true, 2305 Snaps: []string{snapFile}, 2306 PrepareDir: rootdir, 2307 } 2308 2309 err := image.SetupSeed(s.tsto, model, opts) 2310 c.Assert(err, IsNil) 2311 2312 // check seed 2313 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 2314 seedsnapsdir := filepath.Join(seeddir, "snaps") 2315 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 2316 c.Check(essSnaps, HasLen, 1) 2317 c.Check(runSnaps, HasLen, 1) 2318 2319 c.Check(essSnaps[0], DeepEquals, &seed.Snap{ 2320 Path: filepath.Join(seedsnapsdir, "core_3.snap"), 2321 SideInfo: &s.AssertedSnapInfo("core").SideInfo, 2322 EssentialType: snap.TypeOS, 2323 Essential: true, 2324 Required: true, 2325 Channel: stableChannel, 2326 }) 2327 c.Check(essSnaps[0].Path, testutil.FilePresent) 2328 2329 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 2330 Path: filepath.Join(seedsnapsdir, "classic-snap_x1.snap"), 2331 SideInfo: &snap.SideInfo{ 2332 RealName: "classic-snap", 2333 }, 2334 Classic: true, 2335 }) 2336 c.Check(runSnaps[0].Path, testutil.FilePresent) 2337 2338 l, err := ioutil.ReadDir(seedsnapsdir) 2339 c.Assert(err, IsNil) 2340 c.Check(l, HasLen, 2) 2341 2342 // check that the bootloader is unset 2343 m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core") 2344 c.Assert(err, IsNil) 2345 c.Check(m, DeepEquals, map[string]string{ 2346 "snap_core": "", 2347 "snap_kernel": "", 2348 }) 2349 } 2350 2351 func (s *imageSuite) TestSetupSeedClassicSnapdOnly(c *C) { 2352 restore := image.MockTrusted(s.StoreSigning.Trusted) 2353 defer restore() 2354 2355 // classic model with gadget etc 2356 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2357 "classic": "true", 2358 "architecture": "amd64", 2359 "gadget": "classic-gadget18", 2360 "required-snaps": []interface{}{"core18", "required-snap18"}, 2361 }) 2362 2363 rootdir := c.MkDir() 2364 s.setupSnaps(c, map[string]string{ 2365 "classic-gadget18": "my-brand", 2366 }, "") 2367 2368 opts := &image.Options{ 2369 Classic: true, 2370 PrepareDir: rootdir, 2371 } 2372 2373 err := image.SetupSeed(s.tsto, model, opts) 2374 c.Assert(err, IsNil) 2375 2376 // check seed 2377 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 2378 seedsnapsdir := filepath.Join(seeddir, "snaps") 2379 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 2380 c.Check(essSnaps, HasLen, 3) 2381 c.Check(runSnaps, HasLen, 1) 2382 2383 // check the files are in place 2384 for i, name := range []string{"snapd", "classic-gadget18", "core18"} { 2385 info := s.AssertedSnapInfo(name) 2386 2387 fn := info.Filename() 2388 p := filepath.Join(seedsnapsdir, fn) 2389 c.Check(p, testutil.FilePresent) 2390 c.Check(essSnaps[i], DeepEquals, &seed.Snap{ 2391 Path: p, 2392 SideInfo: &info.SideInfo, 2393 EssentialType: info.Type(), 2394 Essential: true, 2395 Required: true, 2396 Channel: stableChannel, 2397 }) 2398 } 2399 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 2400 Path: filepath.Join(seedsnapsdir, "required-snap18_6.snap"), 2401 SideInfo: &s.AssertedSnapInfo("required-snap18").SideInfo, 2402 Required: true, 2403 Channel: stableChannel, 2404 }) 2405 c.Check(runSnaps[0].Path, testutil.FilePresent) 2406 2407 l, err := ioutil.ReadDir(seedsnapsdir) 2408 c.Assert(err, IsNil) 2409 c.Check(l, HasLen, 4) 2410 2411 // check that the bootloader is unset 2412 m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core") 2413 c.Assert(err, IsNil) 2414 c.Check(m, DeepEquals, map[string]string{ 2415 "snap_core": "", 2416 "snap_kernel": "", 2417 }) 2418 2419 c.Check(s.stderr.String(), Matches, `WARNING: ensure that the contents under .*/var/lib/snapd/seed are owned by root:root in the \(final\) image`) 2420 2421 // no blob dir created 2422 blobdir := filepath.Join(rootdir, "var/lib/snapd/snaps") 2423 c.Check(osutil.FileExists(blobdir), Equals, false) 2424 } 2425 2426 func (s *imageSuite) TestSetupSeedClassicNoSnaps(c *C) { 2427 restore := image.MockTrusted(s.StoreSigning.Trusted) 2428 defer restore() 2429 2430 // classic model with gadget etc 2431 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2432 "classic": "true", 2433 }) 2434 2435 rootdir := c.MkDir() 2436 2437 opts := &image.Options{ 2438 Classic: true, 2439 PrepareDir: rootdir, 2440 } 2441 2442 err := image.SetupSeed(s.tsto, model, opts) 2443 c.Assert(err, IsNil) 2444 2445 // check seed 2446 seeddir := filepath.Join(rootdir, "var/lib/snapd/seed") 2447 seedsnapsdir := filepath.Join(seeddir, "snaps") 2448 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 2449 c.Check(essSnaps, HasLen, 0) 2450 c.Check(runSnaps, HasLen, 0) 2451 2452 l, err := ioutil.ReadDir(seedsnapsdir) 2453 c.Assert(err, IsNil) 2454 c.Check(l, HasLen, 0) 2455 2456 // check that the bootloader is unset 2457 m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core") 2458 c.Assert(err, IsNil) 2459 c.Check(m, DeepEquals, map[string]string{ 2460 "snap_core": "", 2461 "snap_kernel": "", 2462 }) 2463 2464 // no blob dir created 2465 blobdir := filepath.Join(rootdir, "var/lib/snapd/snaps") 2466 c.Check(osutil.FileExists(blobdir), Equals, false) 2467 } 2468 2469 func (s *imageSuite) TestSetupSeedClassicSnapdOnlyMissingCore16(c *C) { 2470 restore := image.MockTrusted(s.StoreSigning.Trusted) 2471 defer restore() 2472 2473 // classic model with gadget etc 2474 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2475 "classic": "true", 2476 "architecture": "amd64", 2477 "gadget": "classic-gadget18", 2478 "required-snaps": []interface{}{"core18", "snap-req-core16-base"}, 2479 }) 2480 2481 rootdir := c.MkDir() 2482 s.setupSnaps(c, map[string]string{ 2483 "classic-gadget18": "my-brand", 2484 }, "") 2485 2486 opts := &image.Options{ 2487 Classic: true, 2488 PrepareDir: rootdir, 2489 } 2490 2491 err := image.SetupSeed(s.tsto, model, opts) 2492 c.Assert(err, ErrorMatches, `cannot use "snap-req-core16-base" requiring base "core16" without adding "core16" \(or "core"\) explicitly`) 2493 } 2494 2495 func (s *imageSuite) TestSetupSeedLocalSnapd(c *C) { 2496 restore := image.MockTrusted(s.StoreSigning.Trusted) 2497 defer restore() 2498 2499 // replace model with a model that uses core18 2500 model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{ 2501 "architecture": "amd64", 2502 "gadget": "pc18", 2503 "kernel": "pc-kernel", 2504 "base": "core18", 2505 }) 2506 2507 rootdir := filepath.Join(c.MkDir(), "image") 2508 s.setupSnaps(c, map[string]string{ 2509 "pc18": "canonical", 2510 "pc-kernel": "canonical", 2511 }, "") 2512 2513 snapdFn := snaptest.MakeTestSnapWithFiles(c, snapdSnap, [][]string{{"local", ""}}) 2514 core18Fn := snaptest.MakeTestSnapWithFiles(c, packageCore18, [][]string{{"local", ""}}) 2515 2516 opts := &image.Options{ 2517 Snaps: []string{ 2518 snapdFn, 2519 core18Fn, 2520 }, 2521 2522 PrepareDir: filepath.Dir(rootdir), 2523 } 2524 2525 err := image.SetupSeed(s.tsto, model, opts) 2526 c.Assert(err, IsNil) 2527 c.Assert(s.stdout.String(), Matches, `(?ms).*Copying ".*/snapd_3.14_all.snap" \(snapd\)`) 2528 } 2529 2530 func (s *imageSuite) TestCore20MakeLabel(c *C) { 2531 c.Check(image.MakeLabel(time.Date(2019, 10, 30, 0, 0, 0, 0, time.UTC)), Equals, "20191030") 2532 } 2533 2534 func (s *imageSuite) makeSnap(c *C, yamlKey string, files [][]string, revno snap.Revision, publisher string) { 2535 if publisher == "" { 2536 publisher = "canonical" 2537 } 2538 s.MakeAssertedSnap(c, seedtest.SampleSnapYaml[yamlKey], files, revno, publisher) 2539 } 2540 2541 func (s *imageSuite) makeUC20Model(extraHeaders map[string]interface{}) *asserts.Model { 2542 headers := map[string]interface{}{ 2543 "display-name": "my model", 2544 "architecture": "amd64", 2545 "base": "core20", 2546 "snaps": []interface{}{ 2547 map[string]interface{}{ 2548 "name": "pc-kernel", 2549 "id": s.AssertedSnapID("pc-kernel"), 2550 "type": "kernel", 2551 "default-channel": "20", 2552 }, 2553 map[string]interface{}{ 2554 "name": "pc", 2555 "id": s.AssertedSnapID("pc"), 2556 "type": "gadget", 2557 "default-channel": "20", 2558 }, 2559 map[string]interface{}{ 2560 "name": "required20", 2561 "id": s.AssertedSnapID("required20"), 2562 }}, 2563 } 2564 for k, v := range extraHeaders { 2565 headers[k] = v 2566 } 2567 2568 return s.Brands.Model("my-brand", "my-model", headers) 2569 } 2570 2571 func (s *imageSuite) TestSetupSeedCore20(c *C) { 2572 bl := bootloadertest.Mock("grub", c.MkDir()).RecoveryAware() 2573 bootloader.Force(bl) 2574 2575 restore := image.MockTrusted(s.StoreSigning.Trusted) 2576 defer restore() 2577 2578 // a model that uses core20 2579 model := s.makeUC20Model(nil) 2580 2581 prepareDir := c.MkDir() 2582 2583 s.makeSnap(c, "snapd", nil, snap.R(1), "") 2584 s.makeSnap(c, "core20", nil, snap.R(20), "") 2585 s.makeSnap(c, "pc-kernel=20", nil, snap.R(1), "") 2586 gadgetContent := [][]string{ 2587 {"grub-recovery.conf", "# recovery grub.cfg"}, 2588 {"grub.conf", "# boot grub.cfg"}, 2589 } 2590 s.makeSnap(c, "pc=20", gadgetContent, snap.R(22), "") 2591 s.makeSnap(c, "required20", nil, snap.R(21), "other") 2592 2593 opts := &image.Options{ 2594 PrepareDir: prepareDir, 2595 } 2596 2597 err := image.SetupSeed(s.tsto, model, opts) 2598 c.Assert(err, IsNil) 2599 2600 // check seed 2601 seeddir := filepath.Join(prepareDir, "system-seed") 2602 seedsnapsdir := filepath.Join(seeddir, "snaps") 2603 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 2604 c.Check(essSnaps, HasLen, 4) 2605 c.Check(runSnaps, HasLen, 1) 2606 2607 stableChannel := "latest/stable" 2608 2609 // check the files are in place 2610 for i, name := range []string{"snapd", "pc-kernel", "core20", "pc"} { 2611 info := s.AssertedSnapInfo(name) 2612 2613 channel := stableChannel 2614 switch name { 2615 case "pc", "pc-kernel": 2616 channel = "20" 2617 } 2618 2619 fn := info.Filename() 2620 p := filepath.Join(seedsnapsdir, fn) 2621 c.Check(p, testutil.FilePresent) 2622 c.Check(essSnaps[i], DeepEquals, &seed.Snap{ 2623 Path: p, 2624 SideInfo: &info.SideInfo, 2625 EssentialType: info.Type(), 2626 Essential: true, 2627 Required: true, 2628 Channel: channel, 2629 }) 2630 } 2631 c.Check(runSnaps[0], DeepEquals, &seed.Snap{ 2632 Path: filepath.Join(seedsnapsdir, "required20_21.snap"), 2633 SideInfo: &s.AssertedSnapInfo("required20").SideInfo, 2634 Required: true, 2635 Channel: stableChannel, 2636 }) 2637 c.Check(runSnaps[0].Path, testutil.FilePresent) 2638 2639 l, err := ioutil.ReadDir(seedsnapsdir) 2640 c.Assert(err, IsNil) 2641 c.Check(l, HasLen, 5) 2642 2643 // check boot config 2644 grubCfg := filepath.Join(prepareDir, "system-seed", "EFI/ubuntu/grub.cfg") 2645 grubRecoveryCfgAsset := assets.Internal("grub-recovery.cfg") 2646 c.Assert(grubRecoveryCfgAsset, NotNil) 2647 c.Check(grubCfg, testutil.FileEquals, string(grubRecoveryCfgAsset)) 2648 // make sure that grub.cfg is the only file present inside the directory 2649 gl, err := filepath.Glob(filepath.Join(prepareDir, "system-seed/EFI/ubuntu/*")) 2650 c.Assert(err, IsNil) 2651 c.Check(gl, DeepEquals, []string{grubCfg}) 2652 2653 c.Check(s.stderr.String(), Equals, "") 2654 2655 // check recovery system specific config 2656 systems, err := filepath.Glob(filepath.Join(seeddir, "systems", "*")) 2657 c.Assert(err, IsNil) 2658 c.Assert(systems, HasLen, 1) 2659 2660 c.Check(bl.RecoverySystemDir, Equals, fmt.Sprintf("/systems/%s", filepath.Base(systems[0]))) 2661 c.Check(bl.RecoverySystemBootVars, DeepEquals, map[string]string{ 2662 "snapd_recovery_kernel": "/snaps/pc-kernel_1.snap", 2663 }) 2664 c.Check(bl.BootVars, DeepEquals, map[string]string{ 2665 "snapd_recovery_system": filepath.Base(systems[0]), 2666 }) 2667 2668 // check the downloads 2669 c.Check(s.storeActions, HasLen, 5) 2670 c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{ 2671 Action: "download", 2672 InstanceName: "snapd", 2673 Channel: stableChannel, 2674 }) 2675 c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{ 2676 Action: "download", 2677 InstanceName: "pc-kernel", 2678 Channel: "20", 2679 }) 2680 c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{ 2681 Action: "download", 2682 InstanceName: "core20", 2683 Channel: stableChannel, 2684 }) 2685 c.Check(s.storeActions[3], DeepEquals, &store.SnapAction{ 2686 Action: "download", 2687 InstanceName: "pc", 2688 Channel: "20", 2689 }) 2690 c.Check(s.storeActions[4], DeepEquals, &store.SnapAction{ 2691 Action: "download", 2692 InstanceName: "required20", 2693 Channel: stableChannel, 2694 }) 2695 } 2696 2697 func (s *imageSuite) TestSetupSeedCore20UBoot(c *C) { 2698 bootloader.Force(nil) 2699 restore := image.MockTrusted(s.StoreSigning.Trusted) 2700 defer restore() 2701 2702 // a model that uses core20 and our gadget 2703 headers := map[string]interface{}{ 2704 "display-name": "my model", 2705 "architecture": "arm64", 2706 "base": "core20", 2707 "snaps": []interface{}{ 2708 map[string]interface{}{ 2709 "name": "arm-kernel", 2710 "id": s.AssertedSnapID("arm-kernel"), 2711 "type": "kernel", 2712 "default-channel": "20", 2713 }, 2714 map[string]interface{}{ 2715 "name": "uboot-gadget", 2716 "id": s.AssertedSnapID("uboot-gadget"), 2717 "type": "gadget", 2718 "default-channel": "20", 2719 }, 2720 }, 2721 } 2722 model := s.Brands.Model("my-brand", "my-model", headers) 2723 2724 prepareDir := c.MkDir() 2725 2726 s.makeSnap(c, "snapd", nil, snap.R(1), "") 2727 s.makeSnap(c, "core20", nil, snap.R(20), "") 2728 kernelContent := [][]string{ 2729 {"kernel.img", "some kernel"}, 2730 {"initrd.img", "some initrd"}, 2731 {"dtbs/foo.dtb", "some dtb"}, 2732 } 2733 s.makeSnap(c, "arm-kernel=20", kernelContent, snap.R(1), "") 2734 gadgetContent := [][]string{ 2735 // this file must be empty 2736 // TODO:UC20: write this test with non-empty uboot.env when we support 2737 // that 2738 {"uboot.conf", ""}, 2739 } 2740 s.makeSnap(c, "uboot-gadget=20", gadgetContent, snap.R(22), "") 2741 2742 opts := &image.Options{ 2743 PrepareDir: prepareDir, 2744 } 2745 2746 err := image.SetupSeed(s.tsto, model, opts) 2747 c.Assert(err, IsNil) 2748 2749 // sanity checks 2750 seeddir := filepath.Join(prepareDir, "system-seed") 2751 seedsnapsdir := filepath.Join(seeddir, "snaps") 2752 essSnaps, runSnaps, _ := s.loadSeed(c, seeddir) 2753 c.Check(essSnaps, HasLen, 4) 2754 c.Check(runSnaps, HasLen, 0) 2755 l, err := ioutil.ReadDir(seedsnapsdir) 2756 c.Assert(err, IsNil) 2757 c.Check(l, HasLen, 4) 2758 2759 // check boot config 2760 2761 // uboot.env will be missing 2762 ubootEnv := filepath.Join(prepareDir, "system-seed", "uboot.env") 2763 c.Check(ubootEnv, testutil.FileAbsent) 2764 2765 // boot.sel will be present and have snapd_recovery_system set 2766 expectedLabel := image.MakeLabel(time.Now()) 2767 bootSel := filepath.Join(prepareDir, "system-seed", "uboot", "ubuntu", "boot.sel") 2768 2769 env, err := ubootenv.Open(bootSel) 2770 c.Assert(err, IsNil) 2771 c.Assert(env.Get("snapd_recovery_system"), Equals, expectedLabel) 2772 2773 // check recovery system specific config 2774 systems, err := filepath.Glob(filepath.Join(seeddir, "systems", "*")) 2775 c.Assert(err, IsNil) 2776 c.Assert(systems, HasLen, 1) 2777 c.Check(filepath.Base(systems[0]), Equals, expectedLabel) 2778 2779 // check we extracted the kernel assets 2780 for _, fileAndContent := range kernelContent { 2781 file := fileAndContent[0] 2782 content := fileAndContent[1] 2783 c.Assert(filepath.Join(systems[0], "kernel", file), testutil.FileEquals, content) 2784 } 2785 } 2786 2787 type toolingStoreContextSuite struct { 2788 sc store.DeviceAndAuthContext 2789 } 2790 2791 var _ = Suite(&toolingStoreContextSuite{}) 2792 2793 func (s *toolingStoreContextSuite) SetUpTest(c *C) { 2794 s.sc = image.ToolingStoreContext() 2795 } 2796 2797 func (s *toolingStoreContextSuite) TestNopBits(c *C) { 2798 info, err := s.sc.CloudInfo() 2799 c.Assert(err, IsNil) 2800 c.Check(info, IsNil) 2801 2802 device, err := s.sc.Device() 2803 c.Assert(err, IsNil) 2804 c.Check(device, DeepEquals, &auth.DeviceState{}) 2805 2806 p, err := s.sc.DeviceSessionRequestParams("") 2807 c.Assert(err, Equals, store.ErrNoSerial) 2808 c.Check(p, IsNil) 2809 2810 defURL, err := url.Parse("http://store") 2811 c.Assert(err, IsNil) 2812 proxyStoreID, proxyStoreURL, err := s.sc.ProxyStoreParams(defURL) 2813 c.Assert(err, IsNil) 2814 c.Check(proxyStoreID, Equals, "") 2815 c.Check(proxyStoreURL, Equals, defURL) 2816 2817 storeID, err := s.sc.StoreID("") 2818 c.Assert(err, IsNil) 2819 c.Check(storeID, Equals, "") 2820 2821 storeID, err = s.sc.StoreID("my-store") 2822 c.Assert(err, IsNil) 2823 c.Check(storeID, Equals, "my-store") 2824 2825 _, err = s.sc.UpdateDeviceAuth(nil, "") 2826 c.Assert(err, NotNil) 2827 } 2828 2829 func (s *toolingStoreContextSuite) TestUpdateUserAuth(c *C) { 2830 u := &auth.UserState{ 2831 StoreMacaroon: "macaroon", 2832 StoreDischarges: []string{"discharge1"}, 2833 } 2834 2835 u1, err := s.sc.UpdateUserAuth(u, []string{"discharge2"}) 2836 c.Assert(err, IsNil) 2837 c.Check(u1, Equals, u) 2838 c.Check(u1.StoreDischarges, DeepEquals, []string{"discharge2"}) 2839 }