github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/image/image_test.go (about)

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