gitee.com/mysnapcore/mysnapd@v0.1.0/image/image_test.go (about)

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