github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/image/image_test.go (about)

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