github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/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").Contact = "foo@example.com"
   563  
   564  	s.MakeAssertedSnap(c, requiredSnap18, nil, snap.R(6), "other")
   565  	s.AssertedSnapInfo("required-snap18").Contact = "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) TestSetupSeedWithBaseLegacySnap(c *C) {
  1135  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1136  	defer restore()
  1137  
  1138  	// replace model with a model that uses core18
  1139  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1140  		"architecture":   "amd64",
  1141  		"gadget":         "pc18",
  1142  		"kernel":         "pc-kernel",
  1143  		"base":           "core18",
  1144  		"required-snaps": []interface{}{"required-snap1"},
  1145  	})
  1146  
  1147  	// required-snap1 needs core, for backward compatibility
  1148  	// we will add it implicitly but warn about this
  1149  
  1150  	rootdir := filepath.Join(c.MkDir(), "image")
  1151  	s.setupSnaps(c, map[string]string{
  1152  		"core18":    "canonical",
  1153  		"pc18":      "canonical",
  1154  		"pc-kernel": "canonical",
  1155  		"snapd":     "canonical",
  1156  	}, "")
  1157  
  1158  	opts := &image.Options{
  1159  		PrepareDir: filepath.Dir(rootdir),
  1160  	}
  1161  
  1162  	err := image.SetupSeed(s.tsto, model, opts)
  1163  	c.Assert(err, IsNil)
  1164  
  1165  	// check seed
  1166  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  1167  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  1168  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  1169  	c.Check(essSnaps, HasLen, 4)
  1170  	c.Check(runSnaps, HasLen, 2)
  1171  
  1172  	// check the files are in place
  1173  	for i, name := range []string{"snapd", "core18_18.snap", "pc-kernel", "pc18"} {
  1174  		info := s.AssertedSnapInfo(name)
  1175  		if info == nil {
  1176  			switch name {
  1177  			case "core18_18.snap":
  1178  				info = &snap.Info{
  1179  					SideInfo: snap.SideInfo{
  1180  						SnapID:   s.AssertedSnapID("core18"),
  1181  						RealName: "core18",
  1182  						Revision: snap.R("18"),
  1183  					},
  1184  					SnapType: snap.TypeBase,
  1185  				}
  1186  			}
  1187  		}
  1188  
  1189  		fn := info.Filename()
  1190  		p := filepath.Join(seedsnapsdir, fn)
  1191  		c.Check(p, testutil.FilePresent)
  1192  		c.Check(essSnaps[i], DeepEquals, &seed.Snap{
  1193  			Path:          p,
  1194  			SideInfo:      &info.SideInfo,
  1195  			EssentialType: info.Type(),
  1196  			Essential:     true,
  1197  			Required:      true,
  1198  			Channel:       stableChannel,
  1199  		})
  1200  	}
  1201  	c.Check(runSnaps[0], DeepEquals, &seed.Snap{
  1202  		Path:     filepath.Join(seedsnapsdir, s.AssertedSnapInfo("core").Filename()),
  1203  		SideInfo: &s.AssertedSnapInfo("core").SideInfo,
  1204  		Required: false, // strange but expected
  1205  		Channel:  stableChannel,
  1206  	})
  1207  	c.Check(runSnaps[0].Path, testutil.FilePresent)
  1208  	c.Check(runSnaps[1], DeepEquals, &seed.Snap{
  1209  		Path:     filepath.Join(seedsnapsdir, s.AssertedSnapInfo("required-snap1").Filename()),
  1210  		SideInfo: &s.AssertedSnapInfo("required-snap1").SideInfo,
  1211  		Required: true,
  1212  		Channel:  stableChannel,
  1213  	})
  1214  	c.Check(runSnaps[1].Path, testutil.FilePresent)
  1215  
  1216  	l, err := ioutil.ReadDir(seedsnapsdir)
  1217  	c.Assert(err, IsNil)
  1218  	c.Check(l, HasLen, 6)
  1219  
  1220  	// check the bootloader config
  1221  	m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core")
  1222  	c.Assert(err, IsNil)
  1223  	c.Check(m["snap_kernel"], Equals, "pc-kernel_2.snap")
  1224  	c.Assert(err, IsNil)
  1225  	c.Check(m["snap_core"], Equals, "core18_18.snap")
  1226  
  1227  	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")
  1228  }
  1229  
  1230  func (s *imageSuite) TestSetupSeedWithBaseDefaultTrackSnap(c *C) {
  1231  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1232  	defer restore()
  1233  
  1234  	// replace model with a model that uses core18
  1235  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1236  		"architecture":   "amd64",
  1237  		"gadget":         "pc18",
  1238  		"kernel":         "pc-kernel",
  1239  		"base":           "core18",
  1240  		"required-snaps": []interface{}{"default-track-snap18"},
  1241  	})
  1242  
  1243  	// default-track-snap18 has a default-track
  1244  
  1245  	rootdir := filepath.Join(c.MkDir(), "image")
  1246  	s.setupSnaps(c, map[string]string{
  1247  		"core18":    "canonical",
  1248  		"pc18":      "canonical",
  1249  		"pc-kernel": "canonical",
  1250  		"snapd":     "canonical",
  1251  	}, "")
  1252  
  1253  	opts := &image.Options{
  1254  		PrepareDir: filepath.Dir(rootdir),
  1255  	}
  1256  
  1257  	err := image.SetupSeed(s.tsto, model, opts)
  1258  	c.Assert(err, IsNil)
  1259  
  1260  	// check seed
  1261  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  1262  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  1263  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  1264  	c.Check(essSnaps, HasLen, 4)
  1265  	c.Check(runSnaps, HasLen, 1)
  1266  
  1267  	// check the files are in place
  1268  	for i, name := range []string{"snapd", "core18_18.snap", "pc-kernel", "pc18"} {
  1269  		info := s.AssertedSnapInfo(name)
  1270  		if info == nil {
  1271  			switch name {
  1272  			case "core18_18.snap":
  1273  				info = &snap.Info{
  1274  					SideInfo: snap.SideInfo{
  1275  						SnapID:   s.AssertedSnapID("core18"),
  1276  						RealName: "core18",
  1277  						Revision: snap.R("18"),
  1278  					},
  1279  					SnapType: snap.TypeBase,
  1280  				}
  1281  			}
  1282  		}
  1283  
  1284  		fn := info.Filename()
  1285  		p := filepath.Join(seedsnapsdir, fn)
  1286  		c.Check(p, testutil.FilePresent)
  1287  		c.Check(essSnaps[i], DeepEquals, &seed.Snap{
  1288  			Path:          p,
  1289  			SideInfo:      &info.SideInfo,
  1290  			EssentialType: info.Type(),
  1291  			Essential:     true,
  1292  			Required:      true,
  1293  			Channel:       stableChannel,
  1294  		})
  1295  	}
  1296  	c.Check(runSnaps[0], DeepEquals, &seed.Snap{
  1297  		Path:     filepath.Join(seedsnapsdir, s.AssertedSnapInfo("default-track-snap18").Filename()),
  1298  		SideInfo: &s.AssertedSnapInfo("default-track-snap18").SideInfo,
  1299  		Required: true,
  1300  		Channel:  "default-track/stable",
  1301  	})
  1302  	c.Check(runSnaps[0].Path, testutil.FilePresent)
  1303  
  1304  	l, err := ioutil.ReadDir(seedsnapsdir)
  1305  	c.Assert(err, IsNil)
  1306  	c.Check(l, HasLen, 5)
  1307  
  1308  	c.Check(s.stderr.String(), Equals, "")
  1309  }
  1310  
  1311  func (s *imageSuite) TestSetupSeedKernelPublisherMismatch(c *C) {
  1312  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1313  	defer restore()
  1314  
  1315  	rootdir := filepath.Join(c.MkDir(), "image")
  1316  	s.setupSnaps(c, map[string]string{
  1317  		"pc":        "canonical",
  1318  		"pc-kernel": "other",
  1319  	}, "")
  1320  
  1321  	opts := &image.Options{
  1322  		PrepareDir: filepath.Dir(rootdir),
  1323  	}
  1324  
  1325  	err := image.SetupSeed(s.tsto, s.model, opts)
  1326  	c.Assert(err, ErrorMatches, `cannot use kernel "pc-kernel" published by "other" for model by "my-brand"`)
  1327  }
  1328  
  1329  func (s *imageSuite) TestInstallCloudConfigNoConfig(c *C) {
  1330  	targetDir := c.MkDir()
  1331  	emptyGadgetDir := c.MkDir()
  1332  
  1333  	err := image.InstallCloudConfig(targetDir, emptyGadgetDir)
  1334  	c.Assert(err, IsNil)
  1335  	c.Check(osutil.FileExists(filepath.Join(targetDir, "etc/cloud")), Equals, false)
  1336  }
  1337  
  1338  func (s *imageSuite) TestInstallCloudConfigWithCloudConfig(c *C) {
  1339  	canary := []byte("ni! ni! ni!")
  1340  
  1341  	targetDir := c.MkDir()
  1342  	gadgetDir := c.MkDir()
  1343  	err := ioutil.WriteFile(filepath.Join(gadgetDir, "cloud.conf"), canary, 0644)
  1344  	c.Assert(err, IsNil)
  1345  
  1346  	err = image.InstallCloudConfig(targetDir, gadgetDir)
  1347  	c.Assert(err, IsNil)
  1348  	c.Check(filepath.Join(targetDir, "etc/cloud/cloud.cfg"), testutil.FileEquals, canary)
  1349  }
  1350  
  1351  func (s *imageSuite) TestNewToolingStoreWithAuth(c *C) {
  1352  	tmpdir := c.MkDir()
  1353  	authFn := filepath.Join(tmpdir, "auth.json")
  1354  	err := ioutil.WriteFile(authFn, []byte(`{
  1355  "macaroon": "MACAROON",
  1356  "discharges": ["DISCHARGE"]
  1357  }`), 0600)
  1358  	c.Assert(err, IsNil)
  1359  
  1360  	os.Setenv("UBUNTU_STORE_AUTH_DATA_FILENAME", authFn)
  1361  	defer os.Unsetenv("UBUNTU_STORE_AUTH_DATA_FILENAME")
  1362  
  1363  	tsto, err := image.NewToolingStore()
  1364  	c.Assert(err, IsNil)
  1365  	user := tsto.User()
  1366  	c.Check(user.StoreMacaroon, Equals, "MACAROON")
  1367  	c.Check(user.StoreDischarges, DeepEquals, []string{"DISCHARGE"})
  1368  }
  1369  
  1370  func (s *imageSuite) TestNewToolingStoreWithAuthFromSnapcraftLoginFile(c *C) {
  1371  	tmpdir := c.MkDir()
  1372  	authFn := filepath.Join(tmpdir, "auth.json")
  1373  	err := ioutil.WriteFile(authFn, []byte(`[login.ubuntu.com]
  1374  macaroon = MACAROON
  1375  unbound_discharge = DISCHARGE
  1376  
  1377  `), 0600)
  1378  	c.Assert(err, IsNil)
  1379  
  1380  	os.Setenv("UBUNTU_STORE_AUTH_DATA_FILENAME", authFn)
  1381  	defer os.Unsetenv("UBUNTU_STORE_AUTH_DATA_FILENAME")
  1382  
  1383  	tsto, err := image.NewToolingStore()
  1384  	c.Assert(err, IsNil)
  1385  	user := tsto.User()
  1386  	c.Check(user.StoreMacaroon, Equals, "MACAROON")
  1387  	c.Check(user.StoreDischarges, DeepEquals, []string{"DISCHARGE"})
  1388  }
  1389  
  1390  func (s *imageSuite) TestSetupSeedLocalSnapsWithStoreAsserts(c *C) {
  1391  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1392  	defer restore()
  1393  
  1394  	rootdir := filepath.Join(c.MkDir(), "image")
  1395  	s.setupSnaps(c, map[string]string{
  1396  		"pc":        "canonical",
  1397  		"pc-kernel": "my-brand",
  1398  	}, "")
  1399  
  1400  	opts := &image.Options{
  1401  		Snaps: []string{
  1402  			s.AssertedSnap("core"),
  1403  			s.AssertedSnap("required-snap1"),
  1404  		},
  1405  		PrepareDir: filepath.Dir(rootdir),
  1406  	}
  1407  
  1408  	err := image.SetupSeed(s.tsto, s.model, opts)
  1409  	c.Assert(err, IsNil)
  1410  
  1411  	// check seed
  1412  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  1413  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  1414  	essSnaps, runSnaps, roDB := s.loadSeed(c, seeddir)
  1415  	c.Check(essSnaps, HasLen, 3)
  1416  	c.Check(runSnaps, HasLen, 1)
  1417  
  1418  	// check the files are in place
  1419  	for i, name := range []string{"core_3.snap", "pc-kernel", "pc"} {
  1420  		info := s.AssertedSnapInfo(name)
  1421  		if info == nil {
  1422  			switch name {
  1423  			case "core_3.snap":
  1424  				info = &snap.Info{
  1425  					SideInfo: snap.SideInfo{
  1426  						RealName: "core",
  1427  						SnapID:   s.AssertedSnapID("core"),
  1428  						Revision: snap.R(3),
  1429  					},
  1430  					SnapType: snap.TypeOS,
  1431  				}
  1432  			default:
  1433  				c.Errorf("cannot have %s", name)
  1434  			}
  1435  		}
  1436  
  1437  		fn := info.Filename()
  1438  		p := filepath.Join(seedsnapsdir, fn)
  1439  		c.Check(p, testutil.FilePresent)
  1440  		c.Check(essSnaps[i], DeepEquals, &seed.Snap{
  1441  			Path:          p,
  1442  			SideInfo:      &info.SideInfo,
  1443  			EssentialType: info.Type(),
  1444  			Essential:     true,
  1445  			Required:      true,
  1446  			Channel:       stableChannel,
  1447  		})
  1448  	}
  1449  	c.Check(runSnaps[0], DeepEquals, &seed.Snap{
  1450  		Path:     filepath.Join(seedsnapsdir, "required-snap1_3.snap"),
  1451  		Required: true,
  1452  		SideInfo: &snap.SideInfo{
  1453  			RealName: "required-snap1",
  1454  			SnapID:   s.AssertedSnapID("required-snap1"),
  1455  			Revision: snap.R(3),
  1456  		},
  1457  		Channel: stableChannel,
  1458  	})
  1459  	c.Check(runSnaps[0].Path, testutil.FilePresent)
  1460  
  1461  	l, err := ioutil.ReadDir(seedsnapsdir)
  1462  	c.Assert(err, IsNil)
  1463  	c.Check(l, HasLen, 4)
  1464  
  1465  	// check assertions
  1466  	decls, err := roDB.FindMany(asserts.SnapDeclarationType, nil)
  1467  	c.Assert(err, IsNil)
  1468  	c.Check(decls, HasLen, 4)
  1469  
  1470  	// check the bootloader config
  1471  	m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core")
  1472  	c.Assert(err, IsNil)
  1473  	c.Check(m["snap_kernel"], Equals, "pc-kernel_2.snap")
  1474  	c.Assert(err, IsNil)
  1475  	c.Check(m["snap_core"], Equals, "core_3.snap")
  1476  
  1477  	c.Check(s.stderr.String(), Equals, "")
  1478  }
  1479  
  1480  func (s *imageSuite) TestSetupSeedLocalSnapsWithChannels(c *C) {
  1481  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1482  	defer restore()
  1483  
  1484  	rootdir := filepath.Join(c.MkDir(), "image")
  1485  	s.setupSnaps(c, map[string]string{
  1486  		"pc":        "canonical",
  1487  		"pc-kernel": "my-brand",
  1488  	}, "")
  1489  
  1490  	opts := &image.Options{
  1491  		Snaps: []string{
  1492  			"core",
  1493  			s.AssertedSnap("required-snap1"),
  1494  		},
  1495  		PrepareDir: filepath.Dir(rootdir),
  1496  		SnapChannels: map[string]string{
  1497  			"core": "candidate",
  1498  			// keep this comment for gofmt 1.9
  1499  			s.AssertedSnap("required-snap1"): "edge",
  1500  		},
  1501  	}
  1502  
  1503  	err := image.SetupSeed(s.tsto, s.model, opts)
  1504  	c.Assert(err, IsNil)
  1505  
  1506  	// check seed
  1507  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  1508  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  1509  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  1510  	c.Check(essSnaps, HasLen, 3)
  1511  	c.Check(runSnaps, HasLen, 1)
  1512  
  1513  	// check the files are in place
  1514  	for i, name := range []string{"core_3.snap", "pc-kernel", "pc"} {
  1515  		info := s.AssertedSnapInfo(name)
  1516  		channel := stableChannel
  1517  		if info == nil {
  1518  			switch name {
  1519  			case "core_3.snap":
  1520  				info = &snap.Info{
  1521  					SideInfo: snap.SideInfo{
  1522  						RealName: "core",
  1523  						SnapID:   s.AssertedSnapID("core"),
  1524  						Revision: snap.R(3),
  1525  					},
  1526  					SnapType: snap.TypeOS,
  1527  				}
  1528  				channel = "candidate"
  1529  			default:
  1530  				c.Errorf("cannot have %s", name)
  1531  			}
  1532  		}
  1533  
  1534  		fn := info.Filename()
  1535  		p := filepath.Join(seedsnapsdir, fn)
  1536  		c.Check(p, testutil.FilePresent)
  1537  		c.Check(essSnaps[i], DeepEquals, &seed.Snap{
  1538  			Path:          p,
  1539  			SideInfo:      &info.SideInfo,
  1540  			EssentialType: info.Type(),
  1541  			Essential:     true,
  1542  			Required:      true,
  1543  			Channel:       channel,
  1544  		})
  1545  	}
  1546  	c.Check(runSnaps[0], DeepEquals, &seed.Snap{
  1547  		Path:     filepath.Join(seedsnapsdir, "required-snap1_3.snap"),
  1548  		Required: true,
  1549  		SideInfo: &snap.SideInfo{
  1550  			RealName: "required-snap1",
  1551  			SnapID:   s.AssertedSnapID("required-snap1"),
  1552  			Revision: snap.R(3),
  1553  		},
  1554  		Channel: "edge",
  1555  	})
  1556  	c.Check(runSnaps[0].Path, testutil.FilePresent)
  1557  }
  1558  
  1559  func (s *imageSuite) TestCannotCreateGadgetUnpackDir(c *C) {
  1560  	fn := filepath.Join(c.MkDir(), "model.assertion")
  1561  	err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644)
  1562  	c.Assert(err, IsNil)
  1563  
  1564  	err = image.Prepare(&image.Options{
  1565  		ModelFile:  fn,
  1566  		Channel:    "stable",
  1567  		PrepareDir: "/no-where",
  1568  	})
  1569  	c.Assert(err, ErrorMatches, `cannot create unpack dir "/no-where/gadget": mkdir .*`)
  1570  }
  1571  
  1572  func (s *imageSuite) TestNoLocalParallelSnapInstances(c *C) {
  1573  	fn := filepath.Join(c.MkDir(), "model.assertion")
  1574  	err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644)
  1575  	c.Assert(err, IsNil)
  1576  
  1577  	err = image.Prepare(&image.Options{
  1578  		ModelFile: fn,
  1579  		Snaps:     []string{"foo_instance"},
  1580  	})
  1581  	c.Assert(err, ErrorMatches, `cannot use snap "foo_instance", parallel snap instances are unsupported`)
  1582  }
  1583  
  1584  func (s *imageSuite) TestNoInvalidSnapNames(c *C) {
  1585  	fn := filepath.Join(c.MkDir(), "model.assertion")
  1586  	err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644)
  1587  	c.Assert(err, IsNil)
  1588  
  1589  	err = image.Prepare(&image.Options{
  1590  		ModelFile: fn,
  1591  		Snaps:     []string{"foo.invalid.name"},
  1592  	})
  1593  	c.Assert(err, ErrorMatches, `invalid snap name: "foo.invalid.name"`)
  1594  }
  1595  
  1596  func (s *imageSuite) TestPrepareInvalidChannel(c *C) {
  1597  	fn := filepath.Join(c.MkDir(), "model.assertion")
  1598  	err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644)
  1599  	c.Assert(err, IsNil)
  1600  
  1601  	err = image.Prepare(&image.Options{
  1602  		ModelFile: fn,
  1603  		Channel:   "x/x/x/x",
  1604  	})
  1605  	c.Assert(err, ErrorMatches, `cannot use global default option channel: channel name has too many components: x/x/x/x`)
  1606  }
  1607  
  1608  func (s *imageSuite) TestPrepareClassicModeNoClassicModel(c *C) {
  1609  	fn := filepath.Join(c.MkDir(), "model.assertion")
  1610  	err := ioutil.WriteFile(fn, asserts.Encode(s.model), 0644)
  1611  	c.Assert(err, IsNil)
  1612  
  1613  	err = image.Prepare(&image.Options{
  1614  		Classic:   true,
  1615  		ModelFile: fn,
  1616  	})
  1617  	c.Assert(err, ErrorMatches, "cannot prepare the image for a core model with --classic mode specified")
  1618  }
  1619  
  1620  func (s *imageSuite) TestPrepareClassicModelNoClassicMode(c *C) {
  1621  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1622  	defer restore()
  1623  
  1624  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1625  		"classic": "true",
  1626  	})
  1627  
  1628  	fn := filepath.Join(c.MkDir(), "model.assertion")
  1629  	err := ioutil.WriteFile(fn, asserts.Encode(model), 0644)
  1630  	c.Assert(err, IsNil)
  1631  
  1632  	err = image.Prepare(&image.Options{
  1633  		ModelFile: fn,
  1634  	})
  1635  	c.Assert(err, ErrorMatches, "--classic mode is required to prepare the image for a classic model")
  1636  }
  1637  
  1638  func (s *imageSuite) TestPrepareClassicModelArchOverrideFails(c *C) {
  1639  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1640  	defer restore()
  1641  
  1642  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1643  		"classic":      "true",
  1644  		"architecture": "amd64",
  1645  	})
  1646  
  1647  	fn := filepath.Join(c.MkDir(), "model.assertion")
  1648  	err := ioutil.WriteFile(fn, asserts.Encode(model), 0644)
  1649  	c.Assert(err, IsNil)
  1650  
  1651  	err = image.Prepare(&image.Options{
  1652  		Classic:      true,
  1653  		ModelFile:    fn,
  1654  		Architecture: "i386",
  1655  	})
  1656  	c.Assert(err, ErrorMatches, "cannot override model architecture: amd64")
  1657  }
  1658  
  1659  func (s *imageSuite) TestPrepareClassicModelSnapsButNoArchFails(c *C) {
  1660  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1661  	defer restore()
  1662  
  1663  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1664  		"classic": "true",
  1665  		"gadget":  "classic-gadget",
  1666  	})
  1667  
  1668  	fn := filepath.Join(c.MkDir(), "model.assertion")
  1669  	err := ioutil.WriteFile(fn, asserts.Encode(model), 0644)
  1670  	c.Assert(err, IsNil)
  1671  
  1672  	err = image.Prepare(&image.Options{
  1673  		Classic:   true,
  1674  		ModelFile: fn,
  1675  	})
  1676  	c.Assert(err, ErrorMatches, "cannot have snaps for a classic image without an architecture in the model or from --arch")
  1677  }
  1678  
  1679  func (s *imageSuite) TestSetupSeedWithKernelAndGadgetTrack(c *C) {
  1680  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1681  	defer restore()
  1682  
  1683  	// replace model with a model that uses core18
  1684  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1685  		"architecture": "amd64",
  1686  		"gadget":       "pc=18",
  1687  		"kernel":       "pc-kernel=18",
  1688  	})
  1689  
  1690  	rootdir := filepath.Join(c.MkDir(), "image")
  1691  	s.setupSnaps(c, map[string]string{
  1692  		"core":      "canonical",
  1693  		"pc":        "canonical",
  1694  		"pc-kernel": "canonical",
  1695  	}, "")
  1696  
  1697  	opts := &image.Options{
  1698  		PrepareDir: filepath.Dir(rootdir),
  1699  		Channel:    "stable",
  1700  	}
  1701  
  1702  	err := image.SetupSeed(s.tsto, model, opts)
  1703  	c.Assert(err, IsNil)
  1704  
  1705  	// check seed
  1706  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  1707  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  1708  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  1709  	c.Check(essSnaps, HasLen, 3)
  1710  	c.Check(runSnaps, HasLen, 0)
  1711  
  1712  	c.Check(essSnaps[0], DeepEquals, &seed.Snap{
  1713  		Path:          filepath.Join(seedsnapsdir, "core_3.snap"),
  1714  		SideInfo:      &s.AssertedSnapInfo("core").SideInfo,
  1715  		EssentialType: snap.TypeOS,
  1716  		Essential:     true,
  1717  		Required:      true,
  1718  		Channel:       "stable",
  1719  	})
  1720  	c.Check(essSnaps[1], DeepEquals, &seed.Snap{
  1721  		Path:          filepath.Join(seedsnapsdir, "pc-kernel_2.snap"),
  1722  		SideInfo:      &s.AssertedSnapInfo("pc-kernel").SideInfo,
  1723  		EssentialType: snap.TypeKernel,
  1724  		Essential:     true,
  1725  		Required:      true,
  1726  		Channel:       "18/stable",
  1727  	})
  1728  	c.Check(essSnaps[2], DeepEquals, &seed.Snap{
  1729  		Path:          filepath.Join(seedsnapsdir, "pc_1.snap"),
  1730  		SideInfo:      &s.AssertedSnapInfo("pc").SideInfo,
  1731  		EssentialType: snap.TypeGadget,
  1732  		Essential:     true,
  1733  		Required:      true,
  1734  		Channel:       "18/stable",
  1735  	})
  1736  
  1737  	// check the downloads
  1738  	c.Check(s.storeActions, HasLen, 3)
  1739  	c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{
  1740  		Action:       "download",
  1741  		InstanceName: "core",
  1742  		Channel:      "stable",
  1743  	})
  1744  	c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{
  1745  		Action:       "download",
  1746  		InstanceName: "pc-kernel",
  1747  		Channel:      "18/stable",
  1748  	})
  1749  	c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{
  1750  		Action:       "download",
  1751  		InstanceName: "pc",
  1752  		Channel:      "18/stable",
  1753  	})
  1754  }
  1755  
  1756  func (s *imageSuite) TestSetupSeedWithKernelTrackWithDefaultChannel(c *C) {
  1757  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1758  	defer restore()
  1759  
  1760  	// replace model with a model that uses core18
  1761  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1762  		"architecture": "amd64",
  1763  		"gadget":       "pc",
  1764  		"kernel":       "pc-kernel=18",
  1765  	})
  1766  
  1767  	s.setupSnaps(c, map[string]string{
  1768  		"core":      "canonical",
  1769  		"pc":        "canonical",
  1770  		"pc-kernel": "canonical",
  1771  	}, "")
  1772  
  1773  	rootdir := filepath.Join(c.MkDir(), "image")
  1774  	opts := &image.Options{
  1775  		PrepareDir: filepath.Dir(rootdir),
  1776  		Channel:    "edge",
  1777  	}
  1778  
  1779  	err := image.SetupSeed(s.tsto, model, opts)
  1780  	c.Assert(err, IsNil)
  1781  
  1782  	// check seed
  1783  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  1784  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  1785  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  1786  	c.Check(essSnaps, HasLen, 3)
  1787  	c.Check(runSnaps, HasLen, 0)
  1788  
  1789  	c.Check(essSnaps[0], DeepEquals, &seed.Snap{
  1790  		Path:          filepath.Join(seedsnapsdir, "core_3.snap"),
  1791  		SideInfo:      &s.AssertedSnapInfo("core").SideInfo,
  1792  		EssentialType: snap.TypeOS,
  1793  		Essential:     true,
  1794  		Required:      true,
  1795  		Channel:       "edge",
  1796  	})
  1797  	c.Check(essSnaps[1], DeepEquals, &seed.Snap{
  1798  		Path:          filepath.Join(seedsnapsdir, "pc-kernel_2.snap"),
  1799  		SideInfo:      &s.AssertedSnapInfo("pc-kernel").SideInfo,
  1800  		EssentialType: snap.TypeKernel,
  1801  		Essential:     true,
  1802  		Required:      true,
  1803  		Channel:       "18/edge",
  1804  	})
  1805  	c.Check(essSnaps[2], DeepEquals, &seed.Snap{
  1806  		Path:          filepath.Join(seedsnapsdir, "pc_1.snap"),
  1807  		SideInfo:      &s.AssertedSnapInfo("pc").SideInfo,
  1808  		EssentialType: snap.TypeGadget,
  1809  		Essential:     true,
  1810  		Required:      true,
  1811  		Channel:       "edge",
  1812  	})
  1813  }
  1814  
  1815  func (s *imageSuite) TestSetupSeedWithKernelTrackOnLocalSnap(c *C) {
  1816  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1817  	defer restore()
  1818  
  1819  	// replace model with a model that uses core18
  1820  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1821  		"architecture": "amd64",
  1822  		"gadget":       "pc",
  1823  		"kernel":       "pc-kernel=18",
  1824  	})
  1825  
  1826  	rootdir := filepath.Join(c.MkDir(), "image")
  1827  	s.setupSnaps(c, map[string]string{
  1828  		"core":      "canonical",
  1829  		"pc":        "canonical",
  1830  		"pc-kernel": "canonical",
  1831  	}, "")
  1832  
  1833  	// pretend we downloaded the core,kernel already
  1834  	cfn := s.AssertedSnap("core")
  1835  	kfn := s.AssertedSnap("pc-kernel")
  1836  	opts := &image.Options{
  1837  		PrepareDir: filepath.Dir(rootdir),
  1838  		Snaps:      []string{kfn, cfn},
  1839  		Channel:    "beta",
  1840  	}
  1841  
  1842  	err := image.SetupSeed(s.tsto, model, opts)
  1843  	c.Assert(err, IsNil)
  1844  
  1845  	// check seed
  1846  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  1847  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  1848  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  1849  	c.Check(essSnaps, HasLen, 3)
  1850  	c.Check(runSnaps, HasLen, 0)
  1851  
  1852  	c.Check(essSnaps[0], DeepEquals, &seed.Snap{
  1853  		Path:          filepath.Join(seedsnapsdir, "core_3.snap"),
  1854  		SideInfo:      &s.AssertedSnapInfo("core").SideInfo,
  1855  		EssentialType: snap.TypeOS,
  1856  		Essential:     true,
  1857  		Required:      true,
  1858  		Channel:       "beta",
  1859  	})
  1860  	c.Check(essSnaps[1], DeepEquals, &seed.Snap{
  1861  		Path:          filepath.Join(seedsnapsdir, "pc-kernel_2.snap"),
  1862  		SideInfo:      &s.AssertedSnapInfo("pc-kernel").SideInfo,
  1863  		EssentialType: snap.TypeKernel,
  1864  		Essential:     true,
  1865  		Required:      true,
  1866  		Channel:       "18/beta",
  1867  	})
  1868  }
  1869  
  1870  func (s *imageSuite) TestSetupSeedWithBaseAndLocalLegacyCoreOrdering(c *C) {
  1871  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1872  	defer restore()
  1873  
  1874  	// replace model with a model that uses core18
  1875  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1876  		"architecture":   "amd64",
  1877  		"base":           "core18",
  1878  		"gadget":         "pc18",
  1879  		"kernel":         "pc-kernel",
  1880  		"required-snaps": []interface{}{"required-snap1"},
  1881  	})
  1882  
  1883  	rootdir := filepath.Join(c.MkDir(), "image")
  1884  	s.setupSnaps(c, map[string]string{
  1885  		"core18":    "canonical",
  1886  		"pc18":      "canonical",
  1887  		"pc-kernel": "canonical",
  1888  	}, "")
  1889  
  1890  	coreFn := snaptest.MakeTestSnapWithFiles(c, packageCore, [][]string{{"local", ""}})
  1891  
  1892  	opts := &image.Options{
  1893  		PrepareDir: filepath.Dir(rootdir),
  1894  		Snaps: []string{
  1895  			coreFn,
  1896  		},
  1897  	}
  1898  
  1899  	err := image.SetupSeed(s.tsto, model, opts)
  1900  	c.Assert(err, IsNil)
  1901  
  1902  	// check seed
  1903  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  1904  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  1905  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  1906  	c.Check(essSnaps, HasLen, 4)
  1907  	c.Check(runSnaps, HasLen, 2)
  1908  
  1909  	c.Check(essSnaps[0].Path, Equals, filepath.Join(seedsnapsdir, "snapd_18.snap"))
  1910  	c.Check(essSnaps[1].Path, Equals, filepath.Join(seedsnapsdir, "core18_18.snap"))
  1911  	c.Check(essSnaps[2].Path, Equals, filepath.Join(seedsnapsdir, "pc-kernel_2.snap"))
  1912  	c.Check(essSnaps[3].Path, Equals, filepath.Join(seedsnapsdir, "pc18_4.snap"))
  1913  
  1914  	c.Check(runSnaps[0].Path, Equals, filepath.Join(seedsnapsdir, "core_x1.snap"))
  1915  	c.Check(runSnaps[1].Path, Equals, filepath.Join(seedsnapsdir, "required-snap1_3.snap"))
  1916  }
  1917  
  1918  func (s *imageSuite) TestSetupSeedWithBaseAndLegacyCoreOrdering(c *C) {
  1919  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1920  	defer restore()
  1921  
  1922  	// replace model with a model that uses core18
  1923  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1924  		"architecture":   "amd64",
  1925  		"base":           "core18",
  1926  		"gadget":         "pc18",
  1927  		"kernel":         "pc-kernel",
  1928  		"required-snaps": []interface{}{"required-snap1", "core"},
  1929  	})
  1930  
  1931  	rootdir := filepath.Join(c.MkDir(), "image")
  1932  	s.setupSnaps(c, map[string]string{
  1933  		"core18":    "canonical",
  1934  		"core":      "canonical",
  1935  		"pc18":      "canonical",
  1936  		"pc-kernel": "canonical",
  1937  	}, "")
  1938  
  1939  	opts := &image.Options{
  1940  		PrepareDir: filepath.Dir(rootdir),
  1941  	}
  1942  
  1943  	err := image.SetupSeed(s.tsto, model, opts)
  1944  	c.Assert(err, IsNil)
  1945  
  1946  	// check seed
  1947  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  1948  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  1949  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  1950  	c.Check(essSnaps, HasLen, 4)
  1951  	c.Check(runSnaps, HasLen, 2)
  1952  
  1953  	c.Check(essSnaps[0].Path, Equals, filepath.Join(seedsnapsdir, "snapd_18.snap"))
  1954  	c.Check(essSnaps[1].Path, Equals, filepath.Join(seedsnapsdir, "core18_18.snap"))
  1955  	c.Check(essSnaps[2].Path, Equals, filepath.Join(seedsnapsdir, "pc-kernel_2.snap"))
  1956  	c.Check(essSnaps[3].Path, Equals, filepath.Join(seedsnapsdir, "pc18_4.snap"))
  1957  
  1958  	c.Check(runSnaps[0].Path, Equals, filepath.Join(seedsnapsdir, "core_3.snap"))
  1959  	c.Check(runSnaps[1].Path, Equals, filepath.Join(seedsnapsdir, "required-snap1_3.snap"))
  1960  }
  1961  
  1962  func (s *imageSuite) TestSetupSeedGadgetBaseModelBaseMismatch(c *C) {
  1963  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1964  	defer restore()
  1965  	// replace model with a model that uses core18 and a gadget
  1966  	// without a base
  1967  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1968  		"architecture":   "amd64",
  1969  		"base":           "core18",
  1970  		"gadget":         "pc",
  1971  		"kernel":         "pc-kernel",
  1972  		"required-snaps": []interface{}{"required-snap1"},
  1973  	})
  1974  
  1975  	rootdir := filepath.Join(c.MkDir(), "image")
  1976  	s.setupSnaps(c, map[string]string{
  1977  		"core18":    "canonical",
  1978  		"pc":        "canonical",
  1979  		"pc-kernel": "canonical",
  1980  	}, "")
  1981  	opts := &image.Options{
  1982  		PrepareDir: filepath.Dir(rootdir),
  1983  	}
  1984  
  1985  	err := image.SetupSeed(s.tsto, model, opts)
  1986  	c.Assert(err, ErrorMatches, `cannot use gadget snap because its base "" is different from model base "core18"`)
  1987  }
  1988  
  1989  func (s *imageSuite) TestSetupSeedSnapReqBase(c *C) {
  1990  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  1991  	defer restore()
  1992  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  1993  		"architecture":   "amd64",
  1994  		"gadget":         "pc",
  1995  		"kernel":         "pc-kernel",
  1996  		"required-snaps": []interface{}{"snap-req-other-base"},
  1997  	})
  1998  
  1999  	rootdir := filepath.Join(c.MkDir(), "image")
  2000  	s.setupSnaps(c, map[string]string{
  2001  		"core":                "canonical",
  2002  		"pc":                  "canonical",
  2003  		"pc-kernel":           "canonical",
  2004  		"snap-req-other-base": "canonical",
  2005  	}, "")
  2006  	opts := &image.Options{
  2007  		PrepareDir: filepath.Dir(rootdir),
  2008  	}
  2009  
  2010  	err := image.SetupSeed(s.tsto, model, opts)
  2011  	c.Assert(err, ErrorMatches, `cannot add snap "snap-req-other-base" without also adding its base "other-base" explicitly`)
  2012  }
  2013  
  2014  func (s *imageSuite) TestSetupSeedBaseNone(c *C) {
  2015  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2016  	defer restore()
  2017  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2018  		"architecture":   "amd64",
  2019  		"gadget":         "pc",
  2020  		"kernel":         "pc-kernel",
  2021  		"required-snaps": []interface{}{"snap-base-none"},
  2022  	})
  2023  
  2024  	rootdir := filepath.Join(c.MkDir(), "image")
  2025  	s.setupSnaps(c, map[string]string{
  2026  		"core":           "canonical",
  2027  		"pc":             "canonical",
  2028  		"pc-kernel":      "canonical",
  2029  		"snap-base-none": "canonical",
  2030  	}, "")
  2031  	opts := &image.Options{
  2032  		PrepareDir: filepath.Dir(rootdir),
  2033  	}
  2034  
  2035  	c.Assert(image.SetupSeed(s.tsto, model, opts), IsNil)
  2036  }
  2037  
  2038  func (s *imageSuite) TestSetupSeedCore18GadgetDefaults(c *C) {
  2039  	systemctlMock := testutil.MockCommand(c, "systemctl", "")
  2040  	defer systemctlMock.Restore()
  2041  
  2042  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2043  	defer restore()
  2044  
  2045  	// replace model with a model that uses core18
  2046  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2047  		"architecture": "amd64",
  2048  		"gadget":       "pc18",
  2049  		"kernel":       "pc-kernel",
  2050  		"base":         "core18",
  2051  	})
  2052  
  2053  	defaults := `defaults:
  2054    system:
  2055         service:
  2056           ssh:
  2057             disable: true
  2058  `
  2059  
  2060  	rootdir := filepath.Join(c.MkDir(), "image")
  2061  	s.setupSnaps(c, map[string]string{
  2062  		"pc18":      "canonical",
  2063  		"pc-kernel": "canonical",
  2064  	}, defaults)
  2065  
  2066  	snapdFn := snaptest.MakeTestSnapWithFiles(c, snapdSnap, [][]string{{"local", ""}})
  2067  	core18Fn := snaptest.MakeTestSnapWithFiles(c, packageCore18, [][]string{{"local", ""}})
  2068  
  2069  	opts := &image.Options{
  2070  		Snaps: []string{
  2071  			snapdFn,
  2072  			core18Fn,
  2073  		},
  2074  
  2075  		PrepareDir: filepath.Dir(rootdir),
  2076  	}
  2077  
  2078  	err := image.SetupSeed(s.tsto, model, opts)
  2079  	c.Assert(err, IsNil)
  2080  	c.Check(osutil.FileExists(filepath.Join(rootdir, "_writable_defaults/etc/ssh/sshd_not_to_be_run")), Equals, true)
  2081  }
  2082  
  2083  func (s *imageSuite) TestSetupSeedSnapCoreSatisfiesCore16(c *C) {
  2084  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2085  	defer restore()
  2086  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2087  		"architecture":   "amd64",
  2088  		"gadget":         "pc",
  2089  		"kernel":         "pc-kernel",
  2090  		"required-snaps": []interface{}{"snap-req-core16-base"},
  2091  	})
  2092  
  2093  	rootdir := filepath.Join(c.MkDir(), "image")
  2094  	s.setupSnaps(c, map[string]string{
  2095  		"core":                "canonical",
  2096  		"pc":                  "canonical",
  2097  		"pc-kernel":           "canonical",
  2098  		"snap-req-other-base": "canonical",
  2099  	}, "")
  2100  	opts := &image.Options{
  2101  		PrepareDir: filepath.Dir(rootdir),
  2102  	}
  2103  
  2104  	err := image.SetupSeed(s.tsto, model, opts)
  2105  	c.Assert(err, IsNil)
  2106  }
  2107  
  2108  func (s *imageSuite) TestSetupSeedStoreAssertionMissing(c *C) {
  2109  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2110  	defer restore()
  2111  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2112  		"architecture": "amd64",
  2113  		"gadget":       "pc",
  2114  		"kernel":       "pc-kernel",
  2115  		"store":        "my-store",
  2116  	})
  2117  
  2118  	rootdir := filepath.Join(c.MkDir(), "image")
  2119  	s.setupSnaps(c, map[string]string{
  2120  		"core":      "canonical",
  2121  		"pc":        "canonical",
  2122  		"pc-kernel": "canonical",
  2123  	}, "")
  2124  	opts := &image.Options{
  2125  		PrepareDir: filepath.Dir(rootdir),
  2126  	}
  2127  
  2128  	err := image.SetupSeed(s.tsto, model, opts)
  2129  	c.Assert(err, IsNil)
  2130  }
  2131  
  2132  func (s *imageSuite) TestSetupSeedStoreAssertionFetched(c *C) {
  2133  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2134  	defer restore()
  2135  
  2136  	// add store assertion
  2137  	storeAs, err := s.StoreSigning.Sign(asserts.StoreType, map[string]interface{}{
  2138  		"store":       "my-store",
  2139  		"operator-id": "canonical",
  2140  		"timestamp":   time.Now().UTC().Format(time.RFC3339),
  2141  	}, nil, "")
  2142  	c.Assert(err, IsNil)
  2143  	err = s.StoreSigning.Add(storeAs)
  2144  	c.Assert(err, IsNil)
  2145  
  2146  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2147  		"architecture": "amd64",
  2148  		"gadget":       "pc",
  2149  		"kernel":       "pc-kernel",
  2150  		"store":        "my-store",
  2151  	})
  2152  
  2153  	rootdir := filepath.Join(c.MkDir(), "image")
  2154  	s.setupSnaps(c, map[string]string{
  2155  		"core":      "canonical",
  2156  		"pc":        "canonical",
  2157  		"pc-kernel": "canonical",
  2158  	}, "")
  2159  	opts := &image.Options{
  2160  		PrepareDir: filepath.Dir(rootdir),
  2161  	}
  2162  
  2163  	err = image.SetupSeed(s.tsto, model, opts)
  2164  	c.Assert(err, IsNil)
  2165  
  2166  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  2167  	essSnaps, runSnaps, roDB := s.loadSeed(c, seeddir)
  2168  	c.Check(essSnaps, HasLen, 3)
  2169  	c.Check(runSnaps, HasLen, 0)
  2170  
  2171  	// check the store assertion was fetched
  2172  	_, err = roDB.Find(asserts.StoreType, map[string]string{
  2173  		"store": "my-store",
  2174  	})
  2175  	c.Check(err, IsNil)
  2176  }
  2177  
  2178  func (s *imageSuite) TestSetupSeedSnapReqBaseFromLocal(c *C) {
  2179  	// As originally written it let an extra snap fullfil
  2180  	// the prereq of a required one, this does not work anymore!
  2181  	// See TestSetupSeedSnapReqBaseFromExtraFails.
  2182  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2183  	defer restore()
  2184  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2185  		"architecture":   "amd64",
  2186  		"gadget":         "pc",
  2187  		"kernel":         "pc-kernel",
  2188  		"required-snaps": []interface{}{"other-base", "snap-req-other-base"},
  2189  	})
  2190  
  2191  	rootdir := filepath.Join(c.MkDir(), "image")
  2192  	s.setupSnaps(c, map[string]string{
  2193  		"core":                "canonical",
  2194  		"pc":                  "canonical",
  2195  		"pc-kernel":           "canonical",
  2196  		"snap-req-other-base": "canonical",
  2197  		"other-base":          "canonical",
  2198  	}, "")
  2199  	bfn := s.AssertedSnap("other-base")
  2200  	opts := &image.Options{
  2201  		PrepareDir: filepath.Dir(rootdir),
  2202  		Snaps:      []string{bfn},
  2203  	}
  2204  
  2205  	err := image.SetupSeed(s.tsto, model, opts)
  2206  	c.Assert(err, IsNil)
  2207  }
  2208  
  2209  func (s *imageSuite) TestSetupSeedSnapReqBaseFromExtraFails(c *C) {
  2210  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2211  	defer restore()
  2212  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2213  		"architecture":   "amd64",
  2214  		"gadget":         "pc",
  2215  		"kernel":         "pc-kernel",
  2216  		"required-snaps": []interface{}{"snap-req-other-base"},
  2217  	})
  2218  
  2219  	rootdir := filepath.Join(c.MkDir(), "image")
  2220  	s.setupSnaps(c, map[string]string{
  2221  		"core":                "canonical",
  2222  		"pc":                  "canonical",
  2223  		"pc-kernel":           "canonical",
  2224  		"snap-req-other-base": "canonical",
  2225  		"other-base":          "canonical",
  2226  	}, "")
  2227  	bfn := s.AssertedSnap("other-base")
  2228  	opts := &image.Options{
  2229  		PrepareDir: filepath.Dir(rootdir),
  2230  		Snaps:      []string{bfn},
  2231  	}
  2232  
  2233  	err := image.SetupSeed(s.tsto, model, opts)
  2234  	c.Check(err, ErrorMatches, `cannot add snap "snap-req-other-base" without also adding its base "other-base" explicitly`)
  2235  }
  2236  
  2237  func (s *imageSuite) TestSetupSeedMissingContentProvider(c *C) {
  2238  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2239  	defer restore()
  2240  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2241  		"architecture":   "amd64",
  2242  		"gadget":         "pc",
  2243  		"kernel":         "pc-kernel",
  2244  		"required-snaps": []interface{}{"snap-req-content-provider"},
  2245  	})
  2246  
  2247  	rootdir := filepath.Join(c.MkDir(), "image")
  2248  	s.setupSnaps(c, map[string]string{
  2249  		"core":                  "canonical",
  2250  		"pc":                    "canonical",
  2251  		"pc-kernel":             "canonical",
  2252  		"snap-req-content-snap": "canonical",
  2253  	}, "")
  2254  	opts := &image.Options{
  2255  		PrepareDir: filepath.Dir(rootdir),
  2256  	}
  2257  
  2258  	err := image.SetupSeed(s.tsto, model, opts)
  2259  	c.Check(err, ErrorMatches, `cannot use snap "snap-req-content-provider" without its default content provider "gtk-common-themes" being added explicitly`)
  2260  }
  2261  
  2262  func (s *imageSuite) TestSetupSeedClassic(c *C) {
  2263  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2264  	defer restore()
  2265  
  2266  	// classic model with gadget etc
  2267  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2268  		"classic":        "true",
  2269  		"architecture":   "amd64",
  2270  		"gadget":         "classic-gadget",
  2271  		"required-snaps": []interface{}{"required-snap1"},
  2272  	})
  2273  
  2274  	rootdir := c.MkDir()
  2275  	s.setupSnaps(c, map[string]string{
  2276  		"classic-gadget": "my-brand",
  2277  	}, "")
  2278  
  2279  	opts := &image.Options{
  2280  		Classic:    true,
  2281  		PrepareDir: rootdir,
  2282  	}
  2283  
  2284  	err := image.SetupSeed(s.tsto, model, opts)
  2285  	c.Assert(err, IsNil)
  2286  
  2287  	// check seed
  2288  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  2289  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  2290  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  2291  	c.Check(essSnaps, HasLen, 2)
  2292  	c.Check(runSnaps, HasLen, 1)
  2293  
  2294  	// check the files are in place
  2295  	c.Check(essSnaps[0], DeepEquals, &seed.Snap{
  2296  		Path:          filepath.Join(seedsnapsdir, "core_3.snap"),
  2297  		SideInfo:      &s.AssertedSnapInfo("core").SideInfo,
  2298  		EssentialType: snap.TypeOS,
  2299  		Essential:     true,
  2300  		Required:      true,
  2301  		Channel:       stableChannel,
  2302  	})
  2303  	c.Check(essSnaps[0].Path, testutil.FilePresent)
  2304  	c.Check(essSnaps[1], DeepEquals, &seed.Snap{
  2305  		Path:          filepath.Join(seedsnapsdir, "classic-gadget_5.snap"),
  2306  		SideInfo:      &s.AssertedSnapInfo("classic-gadget").SideInfo,
  2307  		EssentialType: snap.TypeGadget,
  2308  		Essential:     true,
  2309  		Required:      true,
  2310  		Channel:       stableChannel,
  2311  	})
  2312  	c.Check(essSnaps[1].Path, testutil.FilePresent)
  2313  	c.Check(runSnaps[0], DeepEquals, &seed.Snap{
  2314  		Path:     filepath.Join(seedsnapsdir, "required-snap1_3.snap"),
  2315  		SideInfo: &s.AssertedSnapInfo("required-snap1").SideInfo,
  2316  		Required: true,
  2317  		Channel:  stableChannel,
  2318  	})
  2319  	c.Check(runSnaps[0].Path, testutil.FilePresent)
  2320  
  2321  	l, err := ioutil.ReadDir(seedsnapsdir)
  2322  	c.Assert(err, IsNil)
  2323  	c.Check(l, HasLen, 3)
  2324  
  2325  	// check that the  bootloader is unset
  2326  	m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core")
  2327  	c.Assert(err, IsNil)
  2328  	c.Check(m, DeepEquals, map[string]string{
  2329  		"snap_core":   "",
  2330  		"snap_kernel": "",
  2331  	})
  2332  
  2333  	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`)
  2334  
  2335  	// no blob dir created
  2336  	blobdir := filepath.Join(rootdir, "var/lib/snapd/snaps")
  2337  	c.Check(osutil.FileExists(blobdir), Equals, false)
  2338  }
  2339  
  2340  func (s *imageSuite) TestSetupSeedClassicWithLocalClassicSnap(c *C) {
  2341  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2342  	defer restore()
  2343  
  2344  	// classic model
  2345  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2346  		"classic":      "true",
  2347  		"architecture": "amd64",
  2348  	})
  2349  
  2350  	rootdir := c.MkDir()
  2351  	s.setupSnaps(c, nil, "")
  2352  
  2353  	snapFile := snaptest.MakeTestSnapWithFiles(c, classicSnap, nil)
  2354  
  2355  	opts := &image.Options{
  2356  		Classic:    true,
  2357  		Snaps:      []string{snapFile},
  2358  		PrepareDir: rootdir,
  2359  	}
  2360  
  2361  	err := image.SetupSeed(s.tsto, model, opts)
  2362  	c.Assert(err, IsNil)
  2363  
  2364  	// check seed
  2365  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  2366  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  2367  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  2368  	c.Check(essSnaps, HasLen, 1)
  2369  	c.Check(runSnaps, HasLen, 1)
  2370  
  2371  	c.Check(essSnaps[0], DeepEquals, &seed.Snap{
  2372  		Path:          filepath.Join(seedsnapsdir, "core_3.snap"),
  2373  		SideInfo:      &s.AssertedSnapInfo("core").SideInfo,
  2374  		EssentialType: snap.TypeOS,
  2375  		Essential:     true,
  2376  		Required:      true,
  2377  		Channel:       stableChannel,
  2378  	})
  2379  	c.Check(essSnaps[0].Path, testutil.FilePresent)
  2380  
  2381  	c.Check(runSnaps[0], DeepEquals, &seed.Snap{
  2382  		Path: filepath.Join(seedsnapsdir, "classic-snap_x1.snap"),
  2383  		SideInfo: &snap.SideInfo{
  2384  			RealName: "classic-snap",
  2385  		},
  2386  		Classic: true,
  2387  	})
  2388  	c.Check(runSnaps[0].Path, testutil.FilePresent)
  2389  
  2390  	l, err := ioutil.ReadDir(seedsnapsdir)
  2391  	c.Assert(err, IsNil)
  2392  	c.Check(l, HasLen, 2)
  2393  
  2394  	// check that the  bootloader is unset
  2395  	m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core")
  2396  	c.Assert(err, IsNil)
  2397  	c.Check(m, DeepEquals, map[string]string{
  2398  		"snap_core":   "",
  2399  		"snap_kernel": "",
  2400  	})
  2401  }
  2402  
  2403  func (s *imageSuite) TestSetupSeedClassicSnapdOnly(c *C) {
  2404  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2405  	defer restore()
  2406  
  2407  	// classic model with gadget etc
  2408  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2409  		"classic":        "true",
  2410  		"architecture":   "amd64",
  2411  		"gadget":         "classic-gadget18",
  2412  		"required-snaps": []interface{}{"core18", "required-snap18"},
  2413  	})
  2414  
  2415  	rootdir := c.MkDir()
  2416  	s.setupSnaps(c, map[string]string{
  2417  		"classic-gadget18": "my-brand",
  2418  	}, "")
  2419  
  2420  	opts := &image.Options{
  2421  		Classic:    true,
  2422  		PrepareDir: rootdir,
  2423  	}
  2424  
  2425  	err := image.SetupSeed(s.tsto, model, opts)
  2426  	c.Assert(err, IsNil)
  2427  
  2428  	// check seed
  2429  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  2430  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  2431  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  2432  	c.Check(essSnaps, HasLen, 3)
  2433  	c.Check(runSnaps, HasLen, 1)
  2434  
  2435  	// check the files are in place
  2436  	for i, name := range []string{"snapd", "classic-gadget18", "core18"} {
  2437  		info := s.AssertedSnapInfo(name)
  2438  
  2439  		fn := info.Filename()
  2440  		p := filepath.Join(seedsnapsdir, fn)
  2441  		c.Check(p, testutil.FilePresent)
  2442  		c.Check(essSnaps[i], DeepEquals, &seed.Snap{
  2443  			Path:          p,
  2444  			SideInfo:      &info.SideInfo,
  2445  			EssentialType: info.Type(),
  2446  			Essential:     true,
  2447  			Required:      true,
  2448  			Channel:       stableChannel,
  2449  		})
  2450  	}
  2451  	c.Check(runSnaps[0], DeepEquals, &seed.Snap{
  2452  		Path:     filepath.Join(seedsnapsdir, "required-snap18_6.snap"),
  2453  		SideInfo: &s.AssertedSnapInfo("required-snap18").SideInfo,
  2454  		Required: true,
  2455  		Channel:  stableChannel,
  2456  	})
  2457  	c.Check(runSnaps[0].Path, testutil.FilePresent)
  2458  
  2459  	l, err := ioutil.ReadDir(seedsnapsdir)
  2460  	c.Assert(err, IsNil)
  2461  	c.Check(l, HasLen, 4)
  2462  
  2463  	// check that the  bootloader is unset
  2464  	m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core")
  2465  	c.Assert(err, IsNil)
  2466  	c.Check(m, DeepEquals, map[string]string{
  2467  		"snap_core":   "",
  2468  		"snap_kernel": "",
  2469  	})
  2470  
  2471  	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`)
  2472  
  2473  	// no blob dir created
  2474  	blobdir := filepath.Join(rootdir, "var/lib/snapd/snaps")
  2475  	c.Check(osutil.FileExists(blobdir), Equals, false)
  2476  }
  2477  
  2478  func (s *imageSuite) TestSetupSeedClassicNoSnaps(c *C) {
  2479  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2480  	defer restore()
  2481  
  2482  	// classic model with gadget etc
  2483  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2484  		"classic": "true",
  2485  	})
  2486  
  2487  	rootdir := c.MkDir()
  2488  
  2489  	opts := &image.Options{
  2490  		Classic:    true,
  2491  		PrepareDir: rootdir,
  2492  	}
  2493  
  2494  	err := image.SetupSeed(s.tsto, model, opts)
  2495  	c.Assert(err, IsNil)
  2496  
  2497  	// check seed
  2498  	seeddir := filepath.Join(rootdir, "var/lib/snapd/seed")
  2499  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  2500  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  2501  	c.Check(essSnaps, HasLen, 0)
  2502  	c.Check(runSnaps, HasLen, 0)
  2503  
  2504  	l, err := ioutil.ReadDir(seedsnapsdir)
  2505  	c.Assert(err, IsNil)
  2506  	c.Check(l, HasLen, 0)
  2507  
  2508  	// check that the  bootloader is unset
  2509  	m, err := s.bootloader.GetBootVars("snap_kernel", "snap_core")
  2510  	c.Assert(err, IsNil)
  2511  	c.Check(m, DeepEquals, map[string]string{
  2512  		"snap_core":   "",
  2513  		"snap_kernel": "",
  2514  	})
  2515  
  2516  	// no blob dir created
  2517  	blobdir := filepath.Join(rootdir, "var/lib/snapd/snaps")
  2518  	c.Check(osutil.FileExists(blobdir), Equals, false)
  2519  }
  2520  
  2521  func (s *imageSuite) TestSetupSeedClassicSnapdOnlyMissingCore16(c *C) {
  2522  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2523  	defer restore()
  2524  
  2525  	// classic model with gadget etc
  2526  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2527  		"classic":        "true",
  2528  		"architecture":   "amd64",
  2529  		"gadget":         "classic-gadget18",
  2530  		"required-snaps": []interface{}{"core18", "snap-req-core16-base"},
  2531  	})
  2532  
  2533  	rootdir := c.MkDir()
  2534  	s.setupSnaps(c, map[string]string{
  2535  		"classic-gadget18": "my-brand",
  2536  	}, "")
  2537  
  2538  	opts := &image.Options{
  2539  		Classic:    true,
  2540  		PrepareDir: rootdir,
  2541  	}
  2542  
  2543  	err := image.SetupSeed(s.tsto, model, opts)
  2544  	c.Assert(err, ErrorMatches, `cannot use "snap-req-core16-base" requiring base "core16" without adding "core16" \(or "core"\) explicitly`)
  2545  }
  2546  
  2547  func (s *imageSuite) TestSetupSeedLocalSnapd(c *C) {
  2548  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2549  	defer restore()
  2550  
  2551  	// replace model with a model that uses core18
  2552  	model := s.Brands.Model("my-brand", "my-model", map[string]interface{}{
  2553  		"architecture": "amd64",
  2554  		"gadget":       "pc18",
  2555  		"kernel":       "pc-kernel",
  2556  		"base":         "core18",
  2557  	})
  2558  
  2559  	rootdir := filepath.Join(c.MkDir(), "image")
  2560  	s.setupSnaps(c, map[string]string{
  2561  		"pc18":      "canonical",
  2562  		"pc-kernel": "canonical",
  2563  	}, "")
  2564  
  2565  	snapdFn := snaptest.MakeTestSnapWithFiles(c, snapdSnap, [][]string{{"local", ""}})
  2566  	core18Fn := snaptest.MakeTestSnapWithFiles(c, packageCore18, [][]string{{"local", ""}})
  2567  
  2568  	opts := &image.Options{
  2569  		Snaps: []string{
  2570  			snapdFn,
  2571  			core18Fn,
  2572  		},
  2573  
  2574  		PrepareDir: filepath.Dir(rootdir),
  2575  	}
  2576  
  2577  	err := image.SetupSeed(s.tsto, model, opts)
  2578  	c.Assert(err, IsNil)
  2579  	c.Assert(s.stdout.String(), Matches, `(?ms).*Copying ".*/snapd_3.14_all.snap" \(snapd\)`)
  2580  }
  2581  
  2582  func (s *imageSuite) TestCore20MakeLabel(c *C) {
  2583  	c.Check(image.MakeLabel(time.Date(2019, 10, 30, 0, 0, 0, 0, time.UTC)), Equals, "20191030")
  2584  }
  2585  
  2586  func (s *imageSuite) makeSnap(c *C, yamlKey string, files [][]string, revno snap.Revision, publisher string) {
  2587  	if publisher == "" {
  2588  		publisher = "canonical"
  2589  	}
  2590  	s.MakeAssertedSnap(c, seedtest.SampleSnapYaml[yamlKey], files, revno, publisher)
  2591  }
  2592  
  2593  func (s *imageSuite) makeUC20Model(extraHeaders map[string]interface{}) *asserts.Model {
  2594  	headers := map[string]interface{}{
  2595  		"display-name": "my model",
  2596  		"architecture": "amd64",
  2597  		"base":         "core20",
  2598  		"snaps": []interface{}{
  2599  			map[string]interface{}{
  2600  				"name":            "pc-kernel",
  2601  				"id":              s.AssertedSnapID("pc-kernel"),
  2602  				"type":            "kernel",
  2603  				"default-channel": "20",
  2604  			},
  2605  			map[string]interface{}{
  2606  				"name":            "pc",
  2607  				"id":              s.AssertedSnapID("pc"),
  2608  				"type":            "gadget",
  2609  				"default-channel": "20",
  2610  			},
  2611  			map[string]interface{}{
  2612  				"name": "required20",
  2613  				"id":   s.AssertedSnapID("required20"),
  2614  			}},
  2615  	}
  2616  	for k, v := range extraHeaders {
  2617  		headers[k] = v
  2618  	}
  2619  
  2620  	return s.Brands.Model("my-brand", "my-model", headers)
  2621  }
  2622  
  2623  func (s *imageSuite) TestSetupSeedCore20Grub(c *C) {
  2624  	bootloader.Force(nil)
  2625  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2626  	defer restore()
  2627  
  2628  	// a model that uses core20
  2629  	model := s.makeUC20Model(nil)
  2630  
  2631  	prepareDir := c.MkDir()
  2632  
  2633  	s.makeSnap(c, "snapd", nil, snap.R(1), "")
  2634  	s.makeSnap(c, "core20", nil, snap.R(20), "")
  2635  	s.makeSnap(c, "pc-kernel=20", nil, snap.R(1), "")
  2636  	gadgetContent := [][]string{
  2637  		{"grub-recovery.conf", "# recovery grub.cfg"},
  2638  		{"grub.conf", "# boot grub.cfg"},
  2639  		{"meta/gadget.yaml", pcUC20GadgetYaml},
  2640  	}
  2641  	s.makeSnap(c, "pc=20", gadgetContent, snap.R(22), "")
  2642  	s.makeSnap(c, "required20", nil, snap.R(21), "other")
  2643  
  2644  	opts := &image.Options{
  2645  		PrepareDir: prepareDir,
  2646  	}
  2647  
  2648  	err := image.SetupSeed(s.tsto, model, opts)
  2649  	c.Assert(err, IsNil)
  2650  
  2651  	// check seed
  2652  	seeddir := filepath.Join(prepareDir, "system-seed")
  2653  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  2654  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  2655  	c.Check(essSnaps, HasLen, 4)
  2656  	c.Check(runSnaps, HasLen, 1)
  2657  
  2658  	stableChannel := "latest/stable"
  2659  
  2660  	// check the files are in place
  2661  	for i, name := range []string{"snapd", "pc-kernel", "core20", "pc"} {
  2662  		info := s.AssertedSnapInfo(name)
  2663  
  2664  		channel := stableChannel
  2665  		switch name {
  2666  		case "pc", "pc-kernel":
  2667  			channel = "20"
  2668  		}
  2669  
  2670  		fn := info.Filename()
  2671  		p := filepath.Join(seedsnapsdir, fn)
  2672  		c.Check(p, testutil.FilePresent)
  2673  		c.Check(essSnaps[i], DeepEquals, &seed.Snap{
  2674  			Path:          p,
  2675  			SideInfo:      &info.SideInfo,
  2676  			EssentialType: info.Type(),
  2677  			Essential:     true,
  2678  			Required:      true,
  2679  			Channel:       channel,
  2680  		})
  2681  	}
  2682  	c.Check(runSnaps[0], DeepEquals, &seed.Snap{
  2683  		Path:     filepath.Join(seedsnapsdir, "required20_21.snap"),
  2684  		SideInfo: &s.AssertedSnapInfo("required20").SideInfo,
  2685  		Required: true,
  2686  		Channel:  stableChannel,
  2687  	})
  2688  	c.Check(runSnaps[0].Path, testutil.FilePresent)
  2689  
  2690  	l, err := ioutil.ReadDir(seedsnapsdir)
  2691  	c.Assert(err, IsNil)
  2692  	c.Check(l, HasLen, 5)
  2693  
  2694  	// check boot config
  2695  	grubCfg := filepath.Join(prepareDir, "system-seed", "EFI/ubuntu/grub.cfg")
  2696  	seedGrubenv := filepath.Join(prepareDir, "system-seed", "EFI/ubuntu/grubenv")
  2697  	grubRecoveryCfgAsset := assets.Internal("grub-recovery.cfg")
  2698  	c.Assert(grubRecoveryCfgAsset, NotNil)
  2699  	c.Check(grubCfg, testutil.FileEquals, string(grubRecoveryCfgAsset))
  2700  	// make sure that grub.cfg and grubenv are the only files present inside
  2701  	// the directory
  2702  	gl, err := filepath.Glob(filepath.Join(prepareDir, "system-seed/EFI/ubuntu/*"))
  2703  	c.Assert(err, IsNil)
  2704  	c.Check(gl, DeepEquals, []string{
  2705  		grubCfg,
  2706  		seedGrubenv,
  2707  	})
  2708  
  2709  	// check recovery system specific config
  2710  	systems, err := filepath.Glob(filepath.Join(seeddir, "systems", "*"))
  2711  	c.Assert(err, IsNil)
  2712  	c.Assert(systems, HasLen, 1)
  2713  
  2714  	seedGenv := grubenv.NewEnv(seedGrubenv)
  2715  	c.Assert(seedGenv.Load(), IsNil)
  2716  	c.Check(seedGenv.Get("snapd_recovery_system"), Equals, filepath.Base(systems[0]))
  2717  	c.Check(seedGenv.Get("snapd_recovery_mode"), Equals, "install")
  2718  
  2719  	c.Check(s.stderr.String(), Equals, "")
  2720  
  2721  	systemGenv := grubenv.NewEnv(filepath.Join(systems[0], "grubenv"))
  2722  	c.Assert(systemGenv.Load(), IsNil)
  2723  	c.Check(systemGenv.Get("snapd_recovery_kernel"), Equals, "/snaps/pc-kernel_1.snap")
  2724  
  2725  	// check the downloads
  2726  	c.Check(s.storeActions, HasLen, 5)
  2727  	c.Check(s.storeActions[0], DeepEquals, &store.SnapAction{
  2728  		Action:       "download",
  2729  		InstanceName: "snapd",
  2730  		Channel:      stableChannel,
  2731  	})
  2732  	c.Check(s.storeActions[1], DeepEquals, &store.SnapAction{
  2733  		Action:       "download",
  2734  		InstanceName: "pc-kernel",
  2735  		Channel:      "20",
  2736  	})
  2737  	c.Check(s.storeActions[2], DeepEquals, &store.SnapAction{
  2738  		Action:       "download",
  2739  		InstanceName: "core20",
  2740  		Channel:      stableChannel,
  2741  	})
  2742  	c.Check(s.storeActions[3], DeepEquals, &store.SnapAction{
  2743  		Action:       "download",
  2744  		InstanceName: "pc",
  2745  		Channel:      "20",
  2746  	})
  2747  	c.Check(s.storeActions[4], DeepEquals, &store.SnapAction{
  2748  		Action:       "download",
  2749  		InstanceName: "required20",
  2750  		Channel:      stableChannel,
  2751  	})
  2752  }
  2753  
  2754  func (s *imageSuite) TestSetupSeedCore20UBoot(c *C) {
  2755  	bootloader.Force(nil)
  2756  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2757  	defer restore()
  2758  
  2759  	// a model that uses core20 and our gadget
  2760  	headers := map[string]interface{}{
  2761  		"display-name": "my model",
  2762  		"architecture": "arm64",
  2763  		"base":         "core20",
  2764  		"snaps": []interface{}{
  2765  			map[string]interface{}{
  2766  				"name":            "arm-kernel",
  2767  				"id":              s.AssertedSnapID("arm-kernel"),
  2768  				"type":            "kernel",
  2769  				"default-channel": "20",
  2770  			},
  2771  			map[string]interface{}{
  2772  				"name":            "uboot-gadget",
  2773  				"id":              s.AssertedSnapID("uboot-gadget"),
  2774  				"type":            "gadget",
  2775  				"default-channel": "20",
  2776  			},
  2777  		},
  2778  	}
  2779  	model := s.Brands.Model("my-brand", "my-model", headers)
  2780  
  2781  	prepareDir := c.MkDir()
  2782  
  2783  	s.makeSnap(c, "snapd", nil, snap.R(1), "")
  2784  	s.makeSnap(c, "core20", nil, snap.R(20), "")
  2785  	kernelContent := [][]string{
  2786  		{"kernel.img", "some kernel"},
  2787  		{"initrd.img", "some initrd"},
  2788  		{"dtbs/foo.dtb", "some dtb"},
  2789  	}
  2790  	s.makeSnap(c, "arm-kernel=20", kernelContent, snap.R(1), "")
  2791  	gadgetContent := [][]string{
  2792  		// this file must be empty
  2793  		// TODO:UC20: write this test with non-empty uboot.env when we support
  2794  		//            that
  2795  		{"uboot.conf", ""},
  2796  		{"meta/gadget.yaml", piUC20GadgetYaml},
  2797  	}
  2798  	s.makeSnap(c, "uboot-gadget=20", gadgetContent, snap.R(22), "")
  2799  
  2800  	opts := &image.Options{
  2801  		PrepareDir: prepareDir,
  2802  	}
  2803  
  2804  	err := image.SetupSeed(s.tsto, model, opts)
  2805  	c.Assert(err, IsNil)
  2806  
  2807  	// sanity checks
  2808  	seeddir := filepath.Join(prepareDir, "system-seed")
  2809  	seedsnapsdir := filepath.Join(seeddir, "snaps")
  2810  	essSnaps, runSnaps, _ := s.loadSeed(c, seeddir)
  2811  	c.Check(essSnaps, HasLen, 4)
  2812  	c.Check(runSnaps, HasLen, 0)
  2813  	l, err := ioutil.ReadDir(seedsnapsdir)
  2814  	c.Assert(err, IsNil)
  2815  	c.Check(l, HasLen, 4)
  2816  
  2817  	// check boot config
  2818  
  2819  	// uboot.env will be missing
  2820  	ubootEnv := filepath.Join(prepareDir, "system-seed", "uboot.env")
  2821  	c.Check(ubootEnv, testutil.FileAbsent)
  2822  
  2823  	// boot.sel will be present and have snapd_recovery_system set
  2824  	expectedLabel := image.MakeLabel(time.Now())
  2825  	bootSel := filepath.Join(prepareDir, "system-seed", "uboot", "ubuntu", "boot.sel")
  2826  
  2827  	env, err := ubootenv.Open(bootSel)
  2828  	c.Assert(err, IsNil)
  2829  	c.Assert(env.Get("snapd_recovery_system"), Equals, expectedLabel)
  2830  	c.Assert(env.Get("snapd_recovery_mode"), Equals, "install")
  2831  
  2832  	// check recovery system specific config
  2833  	systems, err := filepath.Glob(filepath.Join(seeddir, "systems", "*"))
  2834  	c.Assert(err, IsNil)
  2835  	c.Assert(systems, HasLen, 1)
  2836  	c.Check(filepath.Base(systems[0]), Equals, expectedLabel)
  2837  
  2838  	// check we extracted the kernel assets
  2839  	for _, fileAndContent := range kernelContent {
  2840  		file := fileAndContent[0]
  2841  		content := fileAndContent[1]
  2842  		c.Assert(filepath.Join(systems[0], "kernel", file), testutil.FileEquals, content)
  2843  	}
  2844  }
  2845  
  2846  func (s *imageSuite) TestSetupSeedCore20NoKernelRefsConsumed(c *C) {
  2847  	bootloader.Force(nil)
  2848  	restore := image.MockTrusted(s.StoreSigning.Trusted)
  2849  	defer restore()
  2850  
  2851  	// a model that uses core20 and our gadget
  2852  	headers := map[string]interface{}{
  2853  		"display-name": "my model",
  2854  		"architecture": "arm64",
  2855  		"base":         "core20",
  2856  		"snaps": []interface{}{
  2857  			map[string]interface{}{
  2858  				"name":            "arm-kernel",
  2859  				"id":              s.AssertedSnapID("arm-kernel"),
  2860  				"type":            "kernel",
  2861  				"default-channel": "20",
  2862  			},
  2863  			map[string]interface{}{
  2864  				"name":            "uboot-gadget",
  2865  				"id":              s.AssertedSnapID("uboot-gadget"),
  2866  				"type":            "gadget",
  2867  				"default-channel": "20",
  2868  			},
  2869  		},
  2870  	}
  2871  	model := s.Brands.Model("my-brand", "my-model", headers)
  2872  
  2873  	prepareDir := c.MkDir()
  2874  
  2875  	s.makeSnap(c, "snapd", nil, snap.R(1), "")
  2876  	s.makeSnap(c, "core20", nil, snap.R(20), "")
  2877  	kernelYaml := `
  2878  assets:
  2879   ref:
  2880    update: true
  2881    content:
  2882     - dtbs/`
  2883  	kernelContent := [][]string{
  2884  		{"meta/kernel.yaml", kernelYaml},
  2885  		{"kernel.img", "some kernel"},
  2886  		{"initrd.img", "some initrd"},
  2887  		{"dtbs/foo.dtb", "some dtb"},
  2888  	}
  2889  	s.makeSnap(c, "arm-kernel=20", kernelContent, snap.R(1), "")
  2890  	gadgetContent := [][]string{
  2891  		// this file must be empty
  2892  		// TODO:UC20: write this test with non-empty uboot.env when we support
  2893  		//            that
  2894  		{"uboot.conf", ""},
  2895  		{"meta/gadget.yaml", piUC20GadgetYaml},
  2896  	}
  2897  	s.makeSnap(c, "uboot-gadget=20", gadgetContent, snap.R(22), "")
  2898  
  2899  	opts := &image.Options{
  2900  		PrepareDir: prepareDir,
  2901  	}
  2902  
  2903  	err := image.SetupSeed(s.tsto, model, opts)
  2904  	c.Assert(err, ErrorMatches, `no asset from the kernel.yaml needing synced update is consumed by the gadget at "/.*"`)
  2905  }
  2906  
  2907  type toolingStoreContextSuite struct {
  2908  	sc store.DeviceAndAuthContext
  2909  }
  2910  
  2911  var _ = Suite(&toolingStoreContextSuite{})
  2912  
  2913  func (s *toolingStoreContextSuite) SetUpTest(c *C) {
  2914  	s.sc = image.ToolingStoreContext()
  2915  }
  2916  
  2917  func (s *toolingStoreContextSuite) TestNopBits(c *C) {
  2918  	info, err := s.sc.CloudInfo()
  2919  	c.Assert(err, IsNil)
  2920  	c.Check(info, IsNil)
  2921  
  2922  	device, err := s.sc.Device()
  2923  	c.Assert(err, IsNil)
  2924  	c.Check(device, DeepEquals, &auth.DeviceState{})
  2925  
  2926  	p, err := s.sc.DeviceSessionRequestParams("")
  2927  	c.Assert(err, Equals, store.ErrNoSerial)
  2928  	c.Check(p, IsNil)
  2929  
  2930  	defURL, err := url.Parse("http://store")
  2931  	c.Assert(err, IsNil)
  2932  	proxyStoreID, proxyStoreURL, err := s.sc.ProxyStoreParams(defURL)
  2933  	c.Assert(err, IsNil)
  2934  	c.Check(proxyStoreID, Equals, "")
  2935  	c.Check(proxyStoreURL, Equals, defURL)
  2936  
  2937  	storeID, err := s.sc.StoreID("")
  2938  	c.Assert(err, IsNil)
  2939  	c.Check(storeID, Equals, "")
  2940  
  2941  	storeID, err = s.sc.StoreID("my-store")
  2942  	c.Assert(err, IsNil)
  2943  	c.Check(storeID, Equals, "my-store")
  2944  
  2945  	_, err = s.sc.UpdateDeviceAuth(nil, "")
  2946  	c.Assert(err, NotNil)
  2947  }
  2948  
  2949  func (s *toolingStoreContextSuite) TestUpdateUserAuth(c *C) {
  2950  	u := &auth.UserState{
  2951  		StoreMacaroon:   "macaroon",
  2952  		StoreDischarges: []string{"discharge1"},
  2953  	}
  2954  
  2955  	u1, err := s.sc.UpdateUserAuth(u, []string{"discharge2"})
  2956  	c.Assert(err, IsNil)
  2957  	c.Check(u1, Equals, u)
  2958  	c.Check(u1.StoreDischarges, DeepEquals, []string{"discharge2"})
  2959  }