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