github.com/rigado/snapd@v2.42.5-go-mod+incompatible/seed/seed16_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 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 seed_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  
    28  	. "gopkg.in/check.v1"
    29  	"gopkg.in/yaml.v2"
    30  
    31  	"github.com/snapcore/snapd/asserts"
    32  	"github.com/snapcore/snapd/asserts/assertstest"
    33  	"github.com/snapcore/snapd/seed"
    34  	"github.com/snapcore/snapd/seed/seedtest"
    35  	"github.com/snapcore/snapd/snap"
    36  	"github.com/snapcore/snapd/snap/snaptest"
    37  	"github.com/snapcore/snapd/testutil"
    38  	"github.com/snapcore/snapd/timings"
    39  )
    40  
    41  type seed16Suite struct {
    42  	testutil.BaseTest
    43  
    44  	*seedtest.TestingSeed
    45  	devAcct *asserts.Account
    46  
    47  	seedDir string
    48  
    49  	seed16 seed.Seed
    50  
    51  	db *asserts.Database
    52  
    53  	perfTimings timings.Measurer
    54  }
    55  
    56  var _ = Suite(&seed16Suite{})
    57  
    58  var (
    59  	brandPrivKey, _ = assertstest.GenerateKey(752)
    60  )
    61  
    62  func (s *seed16Suite) SetUpTest(c *C) {
    63  	s.BaseTest.SetUpTest(c)
    64  	s.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    65  
    66  	s.TestingSeed = &seedtest.TestingSeed{}
    67  	s.SetupAssertSigning("canonical", s)
    68  	s.Brands.Register("my-brand", brandPrivKey, map[string]interface{}{
    69  		"verification": "verified",
    70  	})
    71  
    72  	s.seedDir = c.MkDir()
    73  
    74  	s.SnapsDir = filepath.Join(s.seedDir, "snaps")
    75  	s.AssertsDir = filepath.Join(s.seedDir, "assertions")
    76  
    77  	s.devAcct = assertstest.NewAccount(s.StoreSigning, "developer", map[string]interface{}{
    78  		"account-id": "developerid",
    79  	}, "")
    80  	assertstest.AddMany(s.StoreSigning, s.devAcct)
    81  
    82  	seed16, err := seed.Open(s.seedDir)
    83  	c.Assert(err, IsNil)
    84  	s.seed16 = seed16
    85  
    86  	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
    87  		Backstore: asserts.NewMemoryBackstore(),
    88  		Trusted:   s.StoreSigning.Trusted,
    89  	})
    90  	c.Assert(err, IsNil)
    91  	s.db = db
    92  
    93  	s.perfTimings = timings.New(nil)
    94  }
    95  
    96  func (s *seed16Suite) commitTo(b *asserts.Batch) error {
    97  	return b.CommitTo(s.db, nil)
    98  }
    99  
   100  func (s *seed16Suite) TestLoadAssertionsNoAssertions(c *C) {
   101  	c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), Equals, seed.ErrNoAssertions)
   102  }
   103  
   104  func (s *seed16Suite) TestLoadAssertionsNoModelAssertion(c *C) {
   105  	err := os.Mkdir(s.AssertsDir, 0755)
   106  	c.Assert(err, IsNil)
   107  
   108  	c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), ErrorMatches, "seed must have a model assertion")
   109  }
   110  
   111  func (s *seed16Suite) TestLoadAssertionsTwoModelAssertionsError(c *C) {
   112  	err := os.Mkdir(s.AssertsDir, 0755)
   113  	c.Assert(err, IsNil)
   114  
   115  	headers := map[string]interface{}{
   116  		"architecture": "amd64",
   117  		"kernel":       "pc-kernel",
   118  		"gadget":       "pc",
   119  	}
   120  	modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
   121  	s.WriteAssertions("model.asserts", modelChain...)
   122  	modelChain = s.MakeModelAssertionChain("my-brand", "my-model-2", headers)
   123  	s.WriteAssertions("model2.asserts", modelChain...)
   124  
   125  	c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), ErrorMatches, "cannot have multiple model assertions in seed")
   126  }
   127  
   128  func (s *seed16Suite) TestLoadAssertionsConsistencyError(c *C) {
   129  	err := os.Mkdir(s.AssertsDir, 0755)
   130  	c.Assert(err, IsNil)
   131  
   132  	// write out only the model assertion
   133  	headers := map[string]interface{}{
   134  		"architecture": "amd64",
   135  		"kernel":       "pc-kernel",
   136  		"gadget":       "pc",
   137  	}
   138  	model := s.Brands.Model("my-brand", "my-model", headers)
   139  	s.WriteAssertions("model.asserts", model)
   140  
   141  	c.Check(s.seed16.LoadAssertions(s.db, s.commitTo), ErrorMatches, "cannot resolve prerequisite assertion: account-key .*")
   142  }
   143  
   144  func (s *seed16Suite) TestLoadAssertionsModelHappy(c *C) {
   145  	err := os.Mkdir(s.AssertsDir, 0755)
   146  	c.Assert(err, IsNil)
   147  
   148  	headers := map[string]interface{}{
   149  		"architecture": "amd64",
   150  		"kernel":       "pc-kernel",
   151  		"gadget":       "pc",
   152  	}
   153  	modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
   154  	s.WriteAssertions("model.asserts", modelChain...)
   155  
   156  	err = s.seed16.LoadAssertions(s.db, s.commitTo)
   157  	c.Assert(err, IsNil)
   158  
   159  	model, err := s.seed16.Model()
   160  	c.Assert(err, IsNil)
   161  	c.Check(model.Model(), Equals, "my-model")
   162  
   163  	_, err = s.db.Find(asserts.ModelType, map[string]string{
   164  		"series":   "16",
   165  		"brand-id": "my-brand",
   166  		"model":    "my-model",
   167  	})
   168  	c.Assert(err, IsNil)
   169  }
   170  
   171  func (s *seed16Suite) TestLoadAssertionsModelTempDBHappy(c *C) {
   172  	r := seed.MockTrusted(s.StoreSigning.Trusted)
   173  	defer r()
   174  
   175  	err := os.Mkdir(s.AssertsDir, 0755)
   176  	c.Assert(err, IsNil)
   177  
   178  	headers := map[string]interface{}{
   179  		"architecture": "amd64",
   180  		"kernel":       "pc-kernel",
   181  		"gadget":       "pc",
   182  	}
   183  	modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
   184  	s.WriteAssertions("model.asserts", modelChain...)
   185  
   186  	err = s.seed16.LoadAssertions(nil, nil)
   187  	c.Assert(err, IsNil)
   188  
   189  	model, err := s.seed16.Model()
   190  	c.Assert(err, IsNil)
   191  	c.Check(model.Model(), Equals, "my-model")
   192  }
   193  
   194  func (s *seed16Suite) TestSkippedLoadAssertion(c *C) {
   195  	_, err := s.seed16.Model()
   196  	c.Check(err, ErrorMatches, "internal error: model assertion unset")
   197  
   198  	err = s.seed16.LoadMeta(s.perfTimings)
   199  	c.Check(err, ErrorMatches, "internal error: model assertion unset")
   200  }
   201  
   202  func (s *seed16Suite) TestLoadMetaNoMeta(c *C) {
   203  	err := os.Mkdir(s.AssertsDir, 0755)
   204  	c.Assert(err, IsNil)
   205  
   206  	headers := map[string]interface{}{
   207  		"architecture": "amd64",
   208  		"kernel":       "pc-kernel",
   209  		"gadget":       "pc",
   210  	}
   211  	modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
   212  	s.WriteAssertions("model.asserts", modelChain...)
   213  
   214  	err = s.seed16.LoadAssertions(s.db, s.commitTo)
   215  	c.Assert(err, IsNil)
   216  
   217  	err = s.seed16.LoadMeta(s.perfTimings)
   218  	c.Check(err, Equals, seed.ErrNoMeta)
   219  }
   220  
   221  func (s *seed16Suite) TestLoadMetaInvalidSeedYaml(c *C) {
   222  	err := os.Mkdir(s.AssertsDir, 0755)
   223  	c.Assert(err, IsNil)
   224  
   225  	headers := map[string]interface{}{
   226  		"architecture": "amd64",
   227  		"kernel":       "pc-kernel",
   228  		"gadget":       "pc",
   229  	}
   230  	modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
   231  	s.WriteAssertions("model.asserts", modelChain...)
   232  
   233  	err = s.seed16.LoadAssertions(s.db, s.commitTo)
   234  	c.Assert(err, IsNil)
   235  
   236  	// create a seed.yaml
   237  	content, err := yaml.Marshal(map[string]interface{}{
   238  		"snaps": []*seed.Snap16{{
   239  			Name:    "core",
   240  			Channel: "track/not-a-risk",
   241  		}},
   242  	})
   243  	c.Assert(err, IsNil)
   244  	err = ioutil.WriteFile(filepath.Join(s.seedDir, "seed.yaml"), content, 0644)
   245  	c.Assert(err, IsNil)
   246  
   247  	err = s.seed16.LoadMeta(s.perfTimings)
   248  	c.Check(err, ErrorMatches, `cannot read seed yaml: invalid risk in channel name: track/not-a-risk`)
   249  }
   250  
   251  var snapYaml = map[string]string{
   252  	"core": `name: core
   253  type: os
   254  version: 1.0
   255  `,
   256  	"pc-kernel": `name: pc-kernel
   257  type: kernel
   258  version: 1.0
   259  `,
   260  	"pc": `name: pc
   261  type: gadget
   262  version: 1.0
   263  `,
   264  	"required": `name: required
   265  type: app
   266  version: 1.0
   267  `,
   268  	"snapd": `name: snapd
   269  type: snapd
   270  version: 1.0
   271  `,
   272  	"core18": `name: core18
   273  type: base
   274  version: 1.0
   275  `,
   276  	"pc-kernel=18": `name: pc-kernel
   277  type: kernel
   278  version: 1.0
   279  `,
   280  	"pc=18": `name: pc
   281  type: gadget
   282  base: core18
   283  version: 1.0
   284  `,
   285  	"required18": `name: required18
   286  type: app
   287  base: core18
   288  version: 1.0
   289  `,
   290  	"classic-snap": `name: classic-snap
   291  type: app
   292  confinement: classic
   293  version: 1.0
   294  `,
   295  	"classic-gadget": `name: classic-gadget
   296  type: gadget
   297  version: 1.0
   298  `,
   299  	"classic-gadget18": `name: classic-gadget18
   300  type: gadget
   301  base: core18
   302  version: 1.0
   303  `,
   304  	"private-snap": `name: private-snap
   305  base: core18
   306  version: 1.0
   307  `,
   308  	"contactable-snap": `name: contactable-snap
   309  base: core18
   310  version: 1.0
   311  `,
   312  }
   313  
   314  var snapPublishers = map[string]string{
   315  	"required": "developerid",
   316  }
   317  
   318  var (
   319  	coreSeed = &seed.Snap16{
   320  		Name:    "core",
   321  		Channel: "stable",
   322  	}
   323  	kernelSeed = &seed.Snap16{
   324  		Name:    "pc-kernel",
   325  		Channel: "stable",
   326  	}
   327  	gadgetSeed = &seed.Snap16{
   328  		Name:    "pc",
   329  		Channel: "stable",
   330  	}
   331  	requiredSeed = &seed.Snap16{
   332  		Name:    "required",
   333  		Channel: "stable",
   334  	}
   335  	// Core 18
   336  	snapdSeed = &seed.Snap16{
   337  		Name:    "snapd",
   338  		Channel: "stable",
   339  	}
   340  	core18Seed = &seed.Snap16{
   341  		Name:    "core18",
   342  		Channel: "stable",
   343  	}
   344  	kernel18Seed = &seed.Snap16{
   345  		Name:    "pc-kernel",
   346  		Channel: "18",
   347  	}
   348  	gadget18Seed = &seed.Snap16{
   349  		Name:    "pc",
   350  		Channel: "18",
   351  	}
   352  	required18Seed = &seed.Snap16{
   353  		Name:    "required18",
   354  		Channel: "stable",
   355  	}
   356  	classicSnapSeed = &seed.Snap16{
   357  		Name:    "classic-snap",
   358  		Channel: "stable",
   359  		Classic: true,
   360  	}
   361  	classicGadgetSeed = &seed.Snap16{
   362  		Name:    "classic-gadget",
   363  		Channel: "stable",
   364  	}
   365  	classicGadget18Seed = &seed.Snap16{
   366  		Name:    "classic-gadget18",
   367  		Channel: "stable",
   368  	}
   369  	privateSnapSeed = &seed.Snap16{
   370  		Name:    "private-snap",
   371  		Channel: "stable",
   372  		Private: true,
   373  	}
   374  	contactableSnapSeed = &seed.Snap16{
   375  		Name:    "contactable-snap",
   376  		Channel: "stable",
   377  		Contact: "author@example.com",
   378  	}
   379  )
   380  
   381  func (s *seed16Suite) makeSeed(c *C, modelHeaders map[string]interface{}, seedSnaps ...*seed.Snap16) []*seed.Snap16 {
   382  	coreHeaders := map[string]interface{}{
   383  		"architecture": "amd64",
   384  	}
   385  
   386  	if _, ok := modelHeaders["classic"]; !ok {
   387  		coreHeaders["kernel"] = "pc-kernel"
   388  		coreHeaders["gadget"] = "pc"
   389  	}
   390  
   391  	err := os.Mkdir(s.AssertsDir, 0755)
   392  	c.Assert(err, IsNil)
   393  
   394  	modelChain := s.MakeModelAssertionChain("my-brand", "my-model", coreHeaders, modelHeaders)
   395  	s.WriteAssertions("model.asserts", modelChain...)
   396  
   397  	err = os.Mkdir(s.SnapsDir, 0755)
   398  	c.Assert(err, IsNil)
   399  
   400  	var completeSeedSnaps []*seed.Snap16
   401  	for _, seedSnap := range seedSnaps {
   402  		completeSeedSnap := *seedSnap
   403  		var snapFname string
   404  		if seedSnap.Unasserted {
   405  			mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml[seedSnap.Name], nil)
   406  			snapFname = filepath.Base(mockSnapFile)
   407  			err := os.Rename(mockSnapFile, filepath.Join(s.seedDir, "snaps", snapFname))
   408  			c.Assert(err, IsNil)
   409  		} else {
   410  			publisher := snapPublishers[seedSnap.Name]
   411  			if publisher == "" {
   412  				publisher = "canonical"
   413  			}
   414  			whichYaml := seedSnap.Name
   415  			if seedSnap.Channel != "stable" {
   416  				whichYaml = whichYaml + "=" + seedSnap.Channel
   417  			}
   418  			fname, decl, rev := s.MakeAssertedSnap(c, snapYaml[whichYaml], nil, snap.R(1), publisher)
   419  			acct, err := s.StoreSigning.Find(asserts.AccountType, map[string]string{"account-id": publisher})
   420  			c.Assert(err, IsNil)
   421  			s.WriteAssertions(fmt.Sprintf("%s.asserts", seedSnap.Name), rev, decl, acct)
   422  			snapFname = fname
   423  		}
   424  		completeSeedSnap.File = snapFname
   425  		completeSeedSnaps = append(completeSeedSnaps, &completeSeedSnap)
   426  	}
   427  
   428  	// create a seed.yaml
   429  	content, err := yaml.Marshal(map[string]interface{}{
   430  		"snaps": completeSeedSnaps,
   431  	})
   432  	c.Assert(err, IsNil)
   433  	err = ioutil.WriteFile(filepath.Join(s.seedDir, "seed.yaml"), content, 0644)
   434  	c.Assert(err, IsNil)
   435  
   436  	return completeSeedSnaps
   437  }
   438  
   439  func (s *seed16Suite) expectedPath(snapName string) string {
   440  	return filepath.Join(s.seedDir, "snaps", filepath.Base(s.AssertedSnap(snapName)))
   441  }
   442  
   443  func (s *seed16Suite) TestLoadMetaCore16Minimal(c *C) {
   444  	s.makeSeed(c, nil, coreSeed, kernelSeed, gadgetSeed)
   445  
   446  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   447  	c.Assert(err, IsNil)
   448  
   449  	err = s.seed16.LoadMeta(s.perfTimings)
   450  	c.Assert(err, IsNil)
   451  
   452  	c.Check(s.seed16.UsesSnapdSnap(), Equals, false)
   453  
   454  	essSnaps := s.seed16.EssentialSnaps()
   455  	c.Check(essSnaps, HasLen, 3)
   456  
   457  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   458  		{
   459  			Path:      s.expectedPath("core"),
   460  			SideInfo:  &s.AssertedSnapInfo("core").SideInfo,
   461  			Essential: true,
   462  			Required:  true,
   463  			Channel:   "stable",
   464  		}, {
   465  			Path:      s.expectedPath("pc-kernel"),
   466  			SideInfo:  &s.AssertedSnapInfo("pc-kernel").SideInfo,
   467  			Essential: true,
   468  			Required:  true,
   469  			Channel:   "stable",
   470  		}, {
   471  			Path:      s.expectedPath("pc"),
   472  			SideInfo:  &s.AssertedSnapInfo("pc").SideInfo,
   473  			Essential: true,
   474  			Required:  true,
   475  			Channel:   "stable",
   476  		},
   477  	})
   478  
   479  	runSnaps, err := s.seed16.ModeSnaps("run")
   480  	c.Assert(err, IsNil)
   481  	c.Check(runSnaps, HasLen, 0)
   482  }
   483  
   484  func (s *seed16Suite) TestLoadMetaCore16(c *C) {
   485  	s.makeSeed(c, map[string]interface{}{
   486  		"required-snaps": []interface{}{"required"},
   487  	}, coreSeed, kernelSeed, gadgetSeed, requiredSeed)
   488  
   489  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   490  	c.Assert(err, IsNil)
   491  
   492  	err = s.seed16.LoadMeta(s.perfTimings)
   493  	c.Assert(err, IsNil)
   494  
   495  	essSnaps := s.seed16.EssentialSnaps()
   496  	c.Check(essSnaps, HasLen, 3)
   497  
   498  	runSnaps, err := s.seed16.ModeSnaps("run")
   499  	c.Assert(err, IsNil)
   500  	c.Check(runSnaps, HasLen, 1)
   501  
   502  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   503  		{
   504  			Path:     s.expectedPath("required"),
   505  			SideInfo: &s.AssertedSnapInfo("required").SideInfo,
   506  			Required: true,
   507  			Channel:  "stable",
   508  		},
   509  	})
   510  }
   511  
   512  func (s *seed16Suite) TestLoadMetaCore18Minimal(c *C) {
   513  	s.makeSeed(c, map[string]interface{}{
   514  		"base":   "core18",
   515  		"kernel": "pc-kernel=18",
   516  		"gadget": "pc=18",
   517  	}, snapdSeed, core18Seed, kernel18Seed, gadget18Seed)
   518  
   519  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   520  	c.Assert(err, IsNil)
   521  
   522  	err = s.seed16.LoadMeta(s.perfTimings)
   523  	c.Assert(err, IsNil)
   524  
   525  	c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
   526  
   527  	essSnaps := s.seed16.EssentialSnaps()
   528  	c.Check(essSnaps, HasLen, 4)
   529  
   530  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   531  		{
   532  			Path:      s.expectedPath("snapd"),
   533  			SideInfo:  &s.AssertedSnapInfo("snapd").SideInfo,
   534  			Essential: true,
   535  			Required:  true,
   536  			Channel:   "stable",
   537  		}, {
   538  			Path:      s.expectedPath("core18"),
   539  			SideInfo:  &s.AssertedSnapInfo("core18").SideInfo,
   540  			Essential: true,
   541  			Required:  true,
   542  			Channel:   "stable",
   543  		}, {
   544  			Path:      s.expectedPath("pc-kernel"),
   545  			SideInfo:  &s.AssertedSnapInfo("pc-kernel").SideInfo,
   546  			Essential: true,
   547  			Required:  true,
   548  			Channel:   "18",
   549  		}, {
   550  			Path:      s.expectedPath("pc"),
   551  			SideInfo:  &s.AssertedSnapInfo("pc").SideInfo,
   552  			Essential: true,
   553  			Required:  true,
   554  			Channel:   "18",
   555  		},
   556  	})
   557  
   558  	runSnaps, err := s.seed16.ModeSnaps("run")
   559  	c.Assert(err, IsNil)
   560  	c.Check(runSnaps, HasLen, 0)
   561  }
   562  
   563  func (s *seed16Suite) TestLoadMetaCore18(c *C) {
   564  	s.makeSeed(c, map[string]interface{}{
   565  		"base":           "core18",
   566  		"kernel":         "pc-kernel=18",
   567  		"gadget":         "pc=18",
   568  		"required-snaps": []interface{}{"core", "required", "required18"},
   569  	}, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, requiredSeed, coreSeed, required18Seed)
   570  
   571  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   572  	c.Assert(err, IsNil)
   573  
   574  	err = s.seed16.LoadMeta(s.perfTimings)
   575  	c.Assert(err, IsNil)
   576  
   577  	essSnaps := s.seed16.EssentialSnaps()
   578  	c.Check(essSnaps, HasLen, 4)
   579  
   580  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   581  		{
   582  			Path:      s.expectedPath("snapd"),
   583  			SideInfo:  &s.AssertedSnapInfo("snapd").SideInfo,
   584  			Essential: true,
   585  			Required:  true,
   586  			Channel:   "stable",
   587  		}, {
   588  			Path:      s.expectedPath("core18"),
   589  			SideInfo:  &s.AssertedSnapInfo("core18").SideInfo,
   590  			Essential: true,
   591  			Required:  true,
   592  			Channel:   "stable",
   593  		}, {
   594  			Path:      s.expectedPath("pc-kernel"),
   595  			SideInfo:  &s.AssertedSnapInfo("pc-kernel").SideInfo,
   596  			Essential: true,
   597  			Required:  true,
   598  			Channel:   "18",
   599  		}, {
   600  			Path:      s.expectedPath("pc"),
   601  			SideInfo:  &s.AssertedSnapInfo("pc").SideInfo,
   602  			Essential: true,
   603  			Required:  true,
   604  			Channel:   "18",
   605  		},
   606  	})
   607  
   608  	runSnaps, err := s.seed16.ModeSnaps("run")
   609  	c.Assert(err, IsNil)
   610  	c.Check(runSnaps, HasLen, 3)
   611  
   612  	// these are not sorted by type, firstboot will do that
   613  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   614  		{
   615  			Path:     s.expectedPath("required"),
   616  			SideInfo: &s.AssertedSnapInfo("required").SideInfo,
   617  			Required: true,
   618  			Channel:  "stable",
   619  		}, {
   620  			Path:     s.expectedPath("core"),
   621  			SideInfo: &s.AssertedSnapInfo("core").SideInfo,
   622  			Required: true,
   623  			Channel:  "stable",
   624  		}, {
   625  			Path:     s.expectedPath("required18"),
   626  			SideInfo: &s.AssertedSnapInfo("required18").SideInfo,
   627  			Required: true,
   628  			Channel:  "stable",
   629  		},
   630  	})
   631  }
   632  
   633  func (s *seed16Suite) TestLoadMetaClassicNothing(c *C) {
   634  	s.makeSeed(c, map[string]interface{}{
   635  		"classic": "true",
   636  	})
   637  
   638  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   639  	c.Assert(err, IsNil)
   640  
   641  	err = s.seed16.LoadMeta(s.perfTimings)
   642  	c.Assert(err, IsNil)
   643  
   644  	c.Check(s.seed16.UsesSnapdSnap(), Equals, false)
   645  
   646  	essSnaps := s.seed16.EssentialSnaps()
   647  	c.Check(essSnaps, HasLen, 0)
   648  
   649  	runSnaps, err := s.seed16.ModeSnaps("run")
   650  	c.Assert(err, IsNil)
   651  	c.Check(runSnaps, HasLen, 0)
   652  }
   653  
   654  func (s *seed16Suite) TestLoadMetaClassicCore(c *C) {
   655  	s.makeSeed(c, map[string]interface{}{
   656  		"classic": "true",
   657  	}, coreSeed, classicSnapSeed)
   658  
   659  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   660  	c.Assert(err, IsNil)
   661  
   662  	err = s.seed16.LoadMeta(s.perfTimings)
   663  	c.Assert(err, IsNil)
   664  
   665  	c.Check(s.seed16.UsesSnapdSnap(), Equals, false)
   666  
   667  	essSnaps := s.seed16.EssentialSnaps()
   668  	c.Check(essSnaps, HasLen, 1)
   669  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   670  		{
   671  			Path:      s.expectedPath("core"),
   672  			SideInfo:  &s.AssertedSnapInfo("core").SideInfo,
   673  			Essential: true,
   674  			Required:  true,
   675  			Channel:   "stable",
   676  		},
   677  	})
   678  
   679  	// classic-snap is not required, just an extra snap
   680  	runSnaps, err := s.seed16.ModeSnaps("run")
   681  	c.Assert(err, IsNil)
   682  	c.Check(runSnaps, HasLen, 1)
   683  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   684  		{
   685  			Path:     s.expectedPath("classic-snap"),
   686  			SideInfo: &s.AssertedSnapInfo("classic-snap").SideInfo,
   687  			Channel:  "stable",
   688  			Classic:  true,
   689  		},
   690  	})
   691  }
   692  
   693  func (s *seed16Suite) TestLoadMetaClassicCoreWithGadget(c *C) {
   694  	s.makeSeed(c, map[string]interface{}{
   695  		"classic": "true",
   696  		"gadget":  "classic-gadget",
   697  	}, coreSeed, classicGadgetSeed)
   698  
   699  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   700  	c.Assert(err, IsNil)
   701  
   702  	err = s.seed16.LoadMeta(s.perfTimings)
   703  	c.Assert(err, IsNil)
   704  
   705  	c.Check(s.seed16.UsesSnapdSnap(), Equals, false)
   706  
   707  	essSnaps := s.seed16.EssentialSnaps()
   708  	c.Check(essSnaps, HasLen, 2)
   709  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   710  		{
   711  			Path:      s.expectedPath("core"),
   712  			SideInfo:  &s.AssertedSnapInfo("core").SideInfo,
   713  			Essential: true,
   714  			Required:  true,
   715  			Channel:   "stable",
   716  		},
   717  		{
   718  			Path:      s.expectedPath("classic-gadget"),
   719  			SideInfo:  &s.AssertedSnapInfo("classic-gadget").SideInfo,
   720  			Essential: true,
   721  			Required:  true,
   722  			Channel:   "stable",
   723  		},
   724  	})
   725  
   726  	runSnaps, err := s.seed16.ModeSnaps("run")
   727  	c.Assert(err, IsNil)
   728  	c.Check(runSnaps, HasLen, 0)
   729  }
   730  
   731  func (s *seed16Suite) TestLoadMetaClassicSnapd(c *C) {
   732  	s.makeSeed(c, map[string]interface{}{
   733  		"classic":        "true",
   734  		"required-snaps": []interface{}{"core18", "required18"},
   735  	}, snapdSeed, core18Seed, required18Seed)
   736  
   737  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   738  	c.Assert(err, IsNil)
   739  
   740  	err = s.seed16.LoadMeta(s.perfTimings)
   741  	c.Assert(err, IsNil)
   742  
   743  	c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
   744  
   745  	essSnaps := s.seed16.EssentialSnaps()
   746  	c.Check(essSnaps, HasLen, 1)
   747  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   748  		{
   749  			Path:      s.expectedPath("snapd"),
   750  			SideInfo:  &s.AssertedSnapInfo("snapd").SideInfo,
   751  			Essential: true,
   752  			Required:  true,
   753  			Channel:   "stable",
   754  		},
   755  	})
   756  
   757  	runSnaps, err := s.seed16.ModeSnaps("run")
   758  	c.Assert(err, IsNil)
   759  	c.Check(runSnaps, HasLen, 2)
   760  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   761  		{
   762  			Path:     s.expectedPath("core18"),
   763  			SideInfo: &s.AssertedSnapInfo("core18").SideInfo,
   764  			Required: true,
   765  			Channel:  "stable",
   766  		}, {
   767  			Path:     s.expectedPath("required18"),
   768  			SideInfo: &s.AssertedSnapInfo("required18").SideInfo,
   769  			Required: true,
   770  			Channel:  "stable",
   771  		},
   772  	})
   773  }
   774  
   775  func (s *seed16Suite) TestLoadMetaClassicSnapdWithGadget(c *C) {
   776  	s.makeSeed(c, map[string]interface{}{
   777  		"classic": "true",
   778  		"gadget":  "classic-gadget",
   779  	}, snapdSeed, classicGadgetSeed, coreSeed)
   780  
   781  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   782  	c.Assert(err, IsNil)
   783  
   784  	err = s.seed16.LoadMeta(s.perfTimings)
   785  	c.Assert(err, IsNil)
   786  
   787  	c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
   788  
   789  	essSnaps := s.seed16.EssentialSnaps()
   790  	c.Check(essSnaps, HasLen, 3)
   791  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   792  		{
   793  			Path:      s.expectedPath("snapd"),
   794  			SideInfo:  &s.AssertedSnapInfo("snapd").SideInfo,
   795  			Essential: true,
   796  			Required:  true,
   797  			Channel:   "stable",
   798  		}, {
   799  			Path:      s.expectedPath("classic-gadget"),
   800  			SideInfo:  &s.AssertedSnapInfo("classic-gadget").SideInfo,
   801  			Essential: true,
   802  			Required:  true,
   803  			Channel:   "stable",
   804  		}, {
   805  			Path:      s.expectedPath("core"),
   806  			SideInfo:  &s.AssertedSnapInfo("core").SideInfo,
   807  			Essential: true,
   808  			Required:  true,
   809  			Channel:   "stable",
   810  		},
   811  	})
   812  
   813  	runSnaps, err := s.seed16.ModeSnaps("run")
   814  	c.Assert(err, IsNil)
   815  	c.Check(runSnaps, HasLen, 0)
   816  }
   817  
   818  func (s *seed16Suite) TestLoadMetaClassicSnapdWithGadget18(c *C) {
   819  	s.makeSeed(c, map[string]interface{}{
   820  		"classic":        "true",
   821  		"gadget":         "classic-gadget18",
   822  		"required-snaps": []interface{}{"core", "required"},
   823  	}, snapdSeed, coreSeed, requiredSeed, classicGadget18Seed, core18Seed)
   824  
   825  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   826  	c.Assert(err, IsNil)
   827  
   828  	err = s.seed16.LoadMeta(s.perfTimings)
   829  	c.Assert(err, IsNil)
   830  
   831  	c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
   832  
   833  	essSnaps := s.seed16.EssentialSnaps()
   834  	c.Check(essSnaps, HasLen, 3)
   835  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   836  		{
   837  			Path:      s.expectedPath("snapd"),
   838  			SideInfo:  &s.AssertedSnapInfo("snapd").SideInfo,
   839  			Essential: true,
   840  			Required:  true,
   841  			Channel:   "stable",
   842  		}, {
   843  			Path:      s.expectedPath("classic-gadget18"),
   844  			SideInfo:  &s.AssertedSnapInfo("classic-gadget18").SideInfo,
   845  			Essential: true,
   846  			Required:  true,
   847  			Channel:   "stable",
   848  		}, {
   849  			Path:      s.expectedPath("core18"),
   850  			SideInfo:  &s.AssertedSnapInfo("core18").SideInfo,
   851  			Essential: true,
   852  			Required:  true,
   853  			Channel:   "stable",
   854  		},
   855  	})
   856  
   857  	runSnaps, err := s.seed16.ModeSnaps("run")
   858  	c.Assert(err, IsNil)
   859  	c.Check(runSnaps, HasLen, 2)
   860  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   861  		{
   862  			Path:     s.expectedPath("core"),
   863  			SideInfo: &s.AssertedSnapInfo("core").SideInfo,
   864  			Required: true,
   865  			Channel:  "stable",
   866  		}, {
   867  			Path:     s.expectedPath("required"),
   868  			SideInfo: &s.AssertedSnapInfo("required").SideInfo,
   869  			Required: true,
   870  			Channel:  "stable",
   871  		},
   872  	})
   873  }
   874  
   875  func (s *seed16Suite) TestLoadMetaCore18Local(c *C) {
   876  	localRequired18Seed := &seed.Snap16{
   877  		Name:       "required18",
   878  		Unasserted: true,
   879  		DevMode:    true,
   880  	}
   881  	s.makeSeed(c, map[string]interface{}{
   882  		"base":           "core18",
   883  		"kernel":         "pc-kernel=18",
   884  		"gadget":         "pc=18",
   885  		"required-snaps": []interface{}{"core", "required18"},
   886  	}, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, localRequired18Seed)
   887  
   888  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   889  	c.Assert(err, IsNil)
   890  
   891  	err = s.seed16.LoadMeta(s.perfTimings)
   892  	c.Assert(err, IsNil)
   893  
   894  	essSnaps := s.seed16.EssentialSnaps()
   895  	c.Check(essSnaps, HasLen, 4)
   896  
   897  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   898  		{
   899  			Path:      s.expectedPath("snapd"),
   900  			SideInfo:  &s.AssertedSnapInfo("snapd").SideInfo,
   901  			Essential: true,
   902  			Required:  true,
   903  			Channel:   "stable",
   904  		}, {
   905  			Path:      s.expectedPath("core18"),
   906  			SideInfo:  &s.AssertedSnapInfo("core18").SideInfo,
   907  			Essential: true,
   908  			Required:  true,
   909  			Channel:   "stable",
   910  		}, {
   911  			Path:      s.expectedPath("pc-kernel"),
   912  			SideInfo:  &s.AssertedSnapInfo("pc-kernel").SideInfo,
   913  			Essential: true,
   914  			Required:  true,
   915  			Channel:   "18",
   916  		}, {
   917  			Path:      s.expectedPath("pc"),
   918  			SideInfo:  &s.AssertedSnapInfo("pc").SideInfo,
   919  			Essential: true,
   920  			Required:  true,
   921  			Channel:   "18",
   922  		},
   923  	})
   924  
   925  	runSnaps, err := s.seed16.ModeSnaps("run")
   926  	c.Assert(err, IsNil)
   927  	c.Check(runSnaps, HasLen, 1)
   928  
   929  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   930  		{
   931  			Path:     filepath.Join(s.seedDir, "snaps", "required18_1.0_all.snap"),
   932  			SideInfo: &snap.SideInfo{RealName: "required18"},
   933  			Required: true,
   934  			DevMode:  true,
   935  		},
   936  	})
   937  }
   938  
   939  func (s *seed16Suite) TestLoadMetaCore18StoreInfo(c *C) {
   940  	s.makeSeed(c, map[string]interface{}{
   941  		"base":   "core18",
   942  		"kernel": "pc-kernel=18",
   943  		"gadget": "pc=18",
   944  	}, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, privateSnapSeed, contactableSnapSeed)
   945  
   946  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   947  	c.Assert(err, IsNil)
   948  
   949  	err = s.seed16.LoadMeta(s.perfTimings)
   950  	c.Assert(err, IsNil)
   951  
   952  	essSnaps := s.seed16.EssentialSnaps()
   953  	c.Check(essSnaps, HasLen, 4)
   954  
   955  	runSnaps, err := s.seed16.ModeSnaps("run")
   956  	c.Assert(err, IsNil)
   957  	c.Check(runSnaps, HasLen, 2)
   958  
   959  	privateSnapSideInfo := s.AssertedSnapInfo("private-snap").SideInfo
   960  	privateSnapSideInfo.Private = true
   961  	contactableSnapSideInfo := s.AssertedSnapInfo("contactable-snap").SideInfo
   962  	contactableSnapSideInfo.Contact = "author@example.com"
   963  
   964  	// these are not sorted by type, firstboot will do that
   965  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   966  		{
   967  			Path:     s.expectedPath("private-snap"),
   968  			SideInfo: &privateSnapSideInfo,
   969  			Channel:  "stable",
   970  		}, {
   971  			Path:     s.expectedPath("contactable-snap"),
   972  			SideInfo: &contactableSnapSideInfo,
   973  			Channel:  "stable",
   974  		},
   975  	})
   976  }
   977  
   978  func (s *seed16Suite) TestLoadMetaBrokenSeed(c *C) {
   979  	seedSnap16s := s.makeSeed(c, map[string]interface{}{
   980  		"base":           "core18",
   981  		"kernel":         "pc-kernel=18",
   982  		"gadget":         "pc=18",
   983  		"required-snaps": []interface{}{"required18"},
   984  	}, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, required18Seed)
   985  
   986  	otherSnapFile := snaptest.MakeTestSnapWithFiles(c, `name: other
   987  version: other`, nil)
   988  	otherFname := filepath.Base(otherSnapFile)
   989  	err := os.Rename(otherSnapFile, filepath.Join(s.seedDir, "snaps", otherFname))
   990  	c.Assert(err, IsNil)
   991  
   992  	const otherBaseGadget = `name: pc
   993  type: gadget
   994  base: other-base
   995  version: other-base
   996  `
   997  	otherBaseGadgetFname, obgDecl, obgRev := s.MakeAssertedSnap(c, otherBaseGadget, nil, snap.R(3), "canonical")
   998  	s.WriteAssertions("other-gadget.asserts", obgDecl, obgRev)
   999  
  1000  	err = s.seed16.LoadAssertions(s.db, s.commitTo)
  1001  	c.Assert(err, IsNil)
  1002  
  1003  	omit := func(which int) func([]*seed.Snap16) []*seed.Snap16 {
  1004  		return func(snaps []*seed.Snap16) []*seed.Snap16 {
  1005  			broken := make([]*seed.Snap16, 0, len(snaps)-1)
  1006  			for i, sn := range snaps {
  1007  				if i == which {
  1008  					continue
  1009  				}
  1010  				broken = append(broken, sn)
  1011  			}
  1012  			return broken
  1013  		}
  1014  	}
  1015  	replaceFile := func(snapName, fname string) func([]*seed.Snap16) []*seed.Snap16 {
  1016  		return func(snaps []*seed.Snap16) []*seed.Snap16 {
  1017  			for i := range snaps {
  1018  				if snaps[i].Name != snapName {
  1019  					continue
  1020  				}
  1021  				sn := *snaps[i]
  1022  				sn.File = fname
  1023  				snaps[i] = &sn
  1024  			}
  1025  			return snaps
  1026  		}
  1027  	}
  1028  
  1029  	tests := []struct {
  1030  		breakSeed func([]*seed.Snap16) []*seed.Snap16
  1031  		err       string
  1032  	}{
  1033  		{omit(0), `essential snap "snapd" required by the model is missing in the seed`},
  1034  		{omit(1), `essential snap "core18" required by the model is missing in the seed`},
  1035  		{omit(2), `essential snap "pc-kernel" required by the model is missing in the seed`},
  1036  		{omit(3), `essential snap "pc" required by the model is missing in the seed`},
  1037  		// omitting "required18" currently doesn't error in any way
  1038  		{replaceFile("core18", otherFname), `cannot find signatures with metadata for snap "core18".*`},
  1039  		{replaceFile("required18", otherFname), `cannot find signatures with metadata for snap "required18".*`},
  1040  		{replaceFile("core18", "not-existent"), `cannot compute snap .* digest: .*`},
  1041  		{replaceFile("pc", otherBaseGadgetFname), `cannot use gadget snap because its base "other-base" is different from model base "core18"`},
  1042  	}
  1043  
  1044  	for _, t := range tests {
  1045  		testSeedSnap16s := make([]*seed.Snap16, 5)
  1046  		copy(testSeedSnap16s, seedSnap16s)
  1047  
  1048  		testSeedSnap16s = t.breakSeed(testSeedSnap16s)
  1049  		content, err := yaml.Marshal(map[string]interface{}{
  1050  			"snaps": testSeedSnap16s,
  1051  		})
  1052  		c.Assert(err, IsNil)
  1053  		err = ioutil.WriteFile(filepath.Join(s.seedDir, "seed.yaml"), content, 0644)
  1054  		c.Assert(err, IsNil)
  1055  
  1056  		c.Check(s.seed16.LoadMeta(s.perfTimings), ErrorMatches, t.err)
  1057  	}
  1058  }