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