github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/image/image_test.go (about)

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