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

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019-2020 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package seed_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"testing"
    28  
    29  	. "gopkg.in/check.v1"
    30  	"gopkg.in/yaml.v2"
    31  
    32  	"github.com/snapcore/snapd/asserts"
    33  	"github.com/snapcore/snapd/asserts/assertstest"
    34  	"github.com/snapcore/snapd/seed"
    35  	"github.com/snapcore/snapd/seed/seedtest"
    36  	"github.com/snapcore/snapd/snap"
    37  	"github.com/snapcore/snapd/snap/snaptest"
    38  	"github.com/snapcore/snapd/testutil"
    39  	"github.com/snapcore/snapd/timings"
    40  )
    41  
    42  func Test(t *testing.T) { TestingT(t) }
    43  
    44  type seed16Suite struct {
    45  	testutil.BaseTest
    46  
    47  	*seedtest.TestingSeed16
    48  	devAcct *asserts.Account
    49  
    50  	seedDir string
    51  
    52  	seed16 seed.Seed
    53  
    54  	db *asserts.Database
    55  
    56  	perfTimings timings.Measurer
    57  }
    58  
    59  var _ = Suite(&seed16Suite{})
    60  
    61  var (
    62  	brandPrivKey, _ = assertstest.GenerateKey(752)
    63  )
    64  
    65  func (s *seed16Suite) SetUpTest(c *C) {
    66  	s.BaseTest.SetUpTest(c)
    67  	s.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}))
    68  
    69  	s.TestingSeed16 = &seedtest.TestingSeed16{}
    70  	s.SetupAssertSigning("canonical")
    71  	s.Brands.Register("my-brand", brandPrivKey, map[string]interface{}{
    72  		"verification": "verified",
    73  	})
    74  
    75  	s.SeedDir = c.MkDir()
    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 := s.seed16.Model()
   160  	c.Check(model.Model(), Equals, "my-model")
   161  
   162  	_, err = s.db.Find(asserts.ModelType, map[string]string{
   163  		"series":   "16",
   164  		"brand-id": "my-brand",
   165  		"model":    "my-model",
   166  	})
   167  	c.Assert(err, IsNil)
   168  }
   169  
   170  func (s *seed16Suite) TestLoadAssertionsModelTempDBHappy(c *C) {
   171  	r := seed.MockTrusted(s.StoreSigning.Trusted)
   172  	defer r()
   173  
   174  	err := os.Mkdir(s.AssertsDir(), 0755)
   175  	c.Assert(err, IsNil)
   176  
   177  	headers := map[string]interface{}{
   178  		"architecture": "amd64",
   179  		"kernel":       "pc-kernel",
   180  		"gadget":       "pc",
   181  	}
   182  	modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
   183  	s.WriteAssertions("model.asserts", modelChain...)
   184  
   185  	err = s.seed16.LoadAssertions(nil, nil)
   186  	c.Assert(err, IsNil)
   187  
   188  	model := s.seed16.Model()
   189  	c.Check(model.Model(), Equals, "my-model")
   190  
   191  	brand, err := s.seed16.Brand()
   192  	c.Assert(err, IsNil)
   193  	c.Check(brand.AccountID(), Equals, "my-brand")
   194  	c.Check(brand.DisplayName(), Equals, "My-brand")
   195  }
   196  
   197  func (s *seed16Suite) TestLoadMetaNoMeta(c *C) {
   198  	err := os.Mkdir(s.AssertsDir(), 0755)
   199  	c.Assert(err, IsNil)
   200  
   201  	headers := map[string]interface{}{
   202  		"architecture": "amd64",
   203  		"kernel":       "pc-kernel",
   204  		"gadget":       "pc",
   205  	}
   206  	modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
   207  	s.WriteAssertions("model.asserts", modelChain...)
   208  
   209  	err = s.seed16.LoadAssertions(s.db, s.commitTo)
   210  	c.Assert(err, IsNil)
   211  
   212  	err = s.seed16.LoadMeta(s.perfTimings)
   213  	c.Check(err, Equals, seed.ErrNoMeta)
   214  }
   215  
   216  func (s *seed16Suite) TestLoadMetaInvalidSeedYaml(c *C) {
   217  	err := os.Mkdir(s.AssertsDir(), 0755)
   218  	c.Assert(err, IsNil)
   219  
   220  	headers := map[string]interface{}{
   221  		"architecture": "amd64",
   222  		"kernel":       "pc-kernel",
   223  		"gadget":       "pc",
   224  	}
   225  	modelChain := s.MakeModelAssertionChain("my-brand", "my-model", headers)
   226  	s.WriteAssertions("model.asserts", modelChain...)
   227  
   228  	err = s.seed16.LoadAssertions(s.db, s.commitTo)
   229  	c.Assert(err, IsNil)
   230  
   231  	// create a seed.yaml
   232  	content, err := yaml.Marshal(map[string]interface{}{
   233  		"snaps": []*seed.InternalSnap16{{
   234  			Name:    "core",
   235  			Channel: "track/not-a-risk",
   236  		}},
   237  	})
   238  	c.Assert(err, IsNil)
   239  	err = ioutil.WriteFile(filepath.Join(s.SeedDir, "seed.yaml"), content, 0644)
   240  	c.Assert(err, IsNil)
   241  
   242  	err = s.seed16.LoadMeta(s.perfTimings)
   243  	c.Check(err, ErrorMatches, `cannot read seed yaml: invalid risk in channel name: track/not-a-risk`)
   244  }
   245  
   246  var snapYaml = seedtest.MergeSampleSnapYaml(seedtest.SampleSnapYaml, map[string]string{
   247  	"private-snap": `name: private-snap
   248  base: core18
   249  version: 1.0
   250  `,
   251  	"contactable-snap": `name: contactable-snap
   252  base: core18
   253  version: 1.0
   254  `,
   255  	"cont-producer": `name: cont-producer
   256  type: app
   257  base: core18
   258  version: 1.1
   259  slots:
   260     cont:
   261       interface: content
   262       content: cont
   263  `,
   264  	"cont-consumer": `name: cont-consumer
   265  base: core18
   266  version: 1.0
   267  plugs:
   268     cont:
   269       interface: content
   270       content: cont
   271       default-provider: cont-producer
   272  `,
   273  })
   274  
   275  const pcGadgetYaml = `
   276  volumes:
   277    pc:
   278      bootloader: grub
   279  `
   280  
   281  var pcGadgetFiles = [][]string{
   282  	{"meta/gadget.yaml", pcGadgetYaml},
   283  }
   284  
   285  var snapFiles = map[string][][]string{
   286  	"pc":    pcGadgetFiles,
   287  	"pc=18": pcGadgetFiles,
   288  }
   289  
   290  var snapPublishers = map[string]string{
   291  	"required": "developerid",
   292  }
   293  
   294  var (
   295  	coreSeed = &seed.InternalSnap16{
   296  		Name:    "core",
   297  		Channel: "stable",
   298  	}
   299  	kernelSeed = &seed.InternalSnap16{
   300  		Name:    "pc-kernel",
   301  		Channel: "stable",
   302  	}
   303  	gadgetSeed = &seed.InternalSnap16{
   304  		Name:    "pc",
   305  		Channel: "stable",
   306  	}
   307  	requiredSeed = &seed.InternalSnap16{
   308  		Name:    "required",
   309  		Channel: "stable",
   310  	}
   311  	// Core 18
   312  	snapdSeed = &seed.InternalSnap16{
   313  		Name:    "snapd",
   314  		Channel: "stable",
   315  	}
   316  	core18Seed = &seed.InternalSnap16{
   317  		Name:    "core18",
   318  		Channel: "stable",
   319  	}
   320  	kernel18Seed = &seed.InternalSnap16{
   321  		Name:    "pc-kernel",
   322  		Channel: "18",
   323  	}
   324  	gadget18Seed = &seed.InternalSnap16{
   325  		Name:    "pc",
   326  		Channel: "18",
   327  	}
   328  	required18Seed = &seed.InternalSnap16{
   329  		Name:    "required18",
   330  		Channel: "stable",
   331  	}
   332  	classicSnapSeed = &seed.InternalSnap16{
   333  		Name:    "classic-snap",
   334  		Channel: "stable",
   335  		Classic: true,
   336  	}
   337  	classicGadgetSeed = &seed.InternalSnap16{
   338  		Name:    "classic-gadget",
   339  		Channel: "stable",
   340  	}
   341  	classicGadget18Seed = &seed.InternalSnap16{
   342  		Name:    "classic-gadget18",
   343  		Channel: "stable",
   344  	}
   345  	privateSnapSeed = &seed.InternalSnap16{
   346  		Name:    "private-snap",
   347  		Channel: "stable",
   348  		Private: true,
   349  	}
   350  	contactableSnapSeed = &seed.InternalSnap16{
   351  		Name:    "contactable-snap",
   352  		Channel: "stable",
   353  		Contact: "author@example.com",
   354  	}
   355  )
   356  
   357  func (s *seed16Suite) makeSeed(c *C, modelHeaders map[string]interface{}, seedSnaps ...*seed.InternalSnap16) []*seed.InternalSnap16 {
   358  	coreHeaders := map[string]interface{}{
   359  		"architecture": "amd64",
   360  	}
   361  
   362  	if _, ok := modelHeaders["classic"]; !ok {
   363  		coreHeaders["kernel"] = "pc-kernel"
   364  		coreHeaders["gadget"] = "pc"
   365  	}
   366  
   367  	err := os.Mkdir(s.AssertsDir(), 0755)
   368  	c.Assert(err, IsNil)
   369  
   370  	modelChain := s.MakeModelAssertionChain("my-brand", "my-model", coreHeaders, modelHeaders)
   371  	s.WriteAssertions("model.asserts", modelChain...)
   372  
   373  	err = os.Mkdir(s.SnapsDir(), 0755)
   374  	c.Assert(err, IsNil)
   375  
   376  	var completeSeedSnaps []*seed.InternalSnap16
   377  	for _, seedSnap := range seedSnaps {
   378  		completeSeedSnap := *seedSnap
   379  		var snapFname string
   380  		if seedSnap.Unasserted {
   381  			mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml[seedSnap.Name], snapFiles[seedSnap.Name])
   382  			snapFname = filepath.Base(mockSnapFile)
   383  			err := os.Rename(mockSnapFile, filepath.Join(s.SeedDir, "snaps", snapFname))
   384  			c.Assert(err, IsNil)
   385  		} else {
   386  			publisher := snapPublishers[seedSnap.Name]
   387  			if publisher == "" {
   388  				publisher = "canonical"
   389  			}
   390  			whichYaml := seedSnap.Name
   391  			if seedSnap.Channel != "stable" {
   392  				whichYaml = whichYaml + "=" + seedSnap.Channel
   393  			}
   394  			fname, decl, rev := s.MakeAssertedSnap(c, snapYaml[whichYaml], snapFiles[whichYaml], snap.R(1), publisher)
   395  			acct, err := s.StoreSigning.Find(asserts.AccountType, map[string]string{"account-id": publisher})
   396  			c.Assert(err, IsNil)
   397  			s.WriteAssertions(fmt.Sprintf("%s.asserts", seedSnap.Name), rev, decl, acct)
   398  			snapFname = fname
   399  		}
   400  		completeSeedSnap.File = snapFname
   401  		completeSeedSnaps = append(completeSeedSnaps, &completeSeedSnap)
   402  	}
   403  
   404  	s.writeSeed(c, completeSeedSnaps)
   405  
   406  	return completeSeedSnaps
   407  }
   408  
   409  func (s *seed16Suite) writeSeed(c *C, seedSnaps []*seed.InternalSnap16) {
   410  	// create a seed.yaml
   411  	content, err := yaml.Marshal(map[string]interface{}{
   412  		"snaps": seedSnaps,
   413  	})
   414  	c.Assert(err, IsNil)
   415  	err = ioutil.WriteFile(filepath.Join(s.SeedDir, "seed.yaml"), content, 0644)
   416  	c.Assert(err, IsNil)
   417  }
   418  
   419  func (s *seed16Suite) expectedPath(snapName string) string {
   420  	return filepath.Join(s.SeedDir, "snaps", filepath.Base(s.AssertedSnap(snapName)))
   421  }
   422  
   423  func (s *seed16Suite) TestLoadMetaCore16Minimal(c *C) {
   424  	s.makeSeed(c, nil, coreSeed, kernelSeed, gadgetSeed)
   425  
   426  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   427  	c.Assert(err, IsNil)
   428  
   429  	err = s.seed16.LoadMeta(s.perfTimings)
   430  	c.Assert(err, IsNil)
   431  
   432  	c.Check(s.seed16.UsesSnapdSnap(), Equals, false)
   433  
   434  	essSnaps := s.seed16.EssentialSnaps()
   435  	c.Check(essSnaps, HasLen, 3)
   436  
   437  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   438  		{
   439  			Path:          s.expectedPath("core"),
   440  			SideInfo:      &s.AssertedSnapInfo("core").SideInfo,
   441  			EssentialType: snap.TypeOS,
   442  			Essential:     true,
   443  			Required:      true,
   444  			Channel:       "stable",
   445  		}, {
   446  			Path:          s.expectedPath("pc-kernel"),
   447  			SideInfo:      &s.AssertedSnapInfo("pc-kernel").SideInfo,
   448  			EssentialType: snap.TypeKernel,
   449  			Essential:     true,
   450  			Required:      true,
   451  			Channel:       "stable",
   452  		}, {
   453  			Path:          s.expectedPath("pc"),
   454  			SideInfo:      &s.AssertedSnapInfo("pc").SideInfo,
   455  			EssentialType: snap.TypeGadget,
   456  			Essential:     true,
   457  			Required:      true,
   458  			Channel:       "stable",
   459  		},
   460  	})
   461  
   462  	runSnaps, err := s.seed16.ModeSnaps("run")
   463  	c.Assert(err, IsNil)
   464  	c.Check(runSnaps, HasLen, 0)
   465  }
   466  
   467  func (s *seed16Suite) TestLoadMetaCore16(c *C) {
   468  	s.makeSeed(c, map[string]interface{}{
   469  		"required-snaps": []interface{}{"required"},
   470  	}, coreSeed, kernelSeed, gadgetSeed, requiredSeed)
   471  
   472  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   473  	c.Assert(err, IsNil)
   474  
   475  	err = s.seed16.LoadMeta(s.perfTimings)
   476  	c.Assert(err, IsNil)
   477  
   478  	essSnaps := s.seed16.EssentialSnaps()
   479  	c.Check(essSnaps, HasLen, 3)
   480  
   481  	runSnaps, err := s.seed16.ModeSnaps("run")
   482  	c.Assert(err, IsNil)
   483  	c.Check(runSnaps, HasLen, 1)
   484  
   485  	// check that PlaceInfo method works
   486  	pi := essSnaps[0].PlaceInfo()
   487  	c.Check(pi.Filename(), Equals, "core_1.snap")
   488  	pi = essSnaps[1].PlaceInfo()
   489  	c.Check(pi.Filename(), Equals, "pc-kernel_1.snap")
   490  	pi = essSnaps[2].PlaceInfo()
   491  	c.Check(pi.Filename(), Equals, "pc_1.snap")
   492  
   493  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   494  		{
   495  			Path:     s.expectedPath("required"),
   496  			SideInfo: &s.AssertedSnapInfo("required").SideInfo,
   497  			Required: true,
   498  			Channel:  "stable",
   499  		},
   500  	})
   501  }
   502  
   503  func (s *seed16Suite) TestLoadMetaCore18Minimal(c *C) {
   504  	s.makeSeed(c, map[string]interface{}{
   505  		"base":   "core18",
   506  		"kernel": "pc-kernel=18",
   507  		"gadget": "pc=18",
   508  	}, snapdSeed, core18Seed, kernel18Seed, gadget18Seed)
   509  
   510  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   511  	c.Assert(err, IsNil)
   512  
   513  	err = s.seed16.LoadMeta(s.perfTimings)
   514  	c.Assert(err, IsNil)
   515  
   516  	c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
   517  
   518  	essSnaps := s.seed16.EssentialSnaps()
   519  	c.Check(essSnaps, HasLen, 4)
   520  
   521  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   522  		{
   523  			Path:          s.expectedPath("snapd"),
   524  			SideInfo:      &s.AssertedSnapInfo("snapd").SideInfo,
   525  			EssentialType: snap.TypeSnapd,
   526  			Essential:     true,
   527  			Required:      true,
   528  			Channel:       "stable",
   529  		}, {
   530  			Path:          s.expectedPath("core18"),
   531  			SideInfo:      &s.AssertedSnapInfo("core18").SideInfo,
   532  			EssentialType: snap.TypeBase,
   533  			Essential:     true,
   534  			Required:      true,
   535  			Channel:       "stable",
   536  		}, {
   537  			Path:          s.expectedPath("pc-kernel"),
   538  			SideInfo:      &s.AssertedSnapInfo("pc-kernel").SideInfo,
   539  			EssentialType: snap.TypeKernel,
   540  			Essential:     true,
   541  			Required:      true,
   542  			Channel:       "18",
   543  		}, {
   544  			Path:          s.expectedPath("pc"),
   545  			SideInfo:      &s.AssertedSnapInfo("pc").SideInfo,
   546  			EssentialType: snap.TypeGadget,
   547  			Essential:     true,
   548  			Required:      true,
   549  			Channel:       "18",
   550  		},
   551  	})
   552  
   553  	runSnaps, err := s.seed16.ModeSnaps("run")
   554  	c.Assert(err, IsNil)
   555  	c.Check(runSnaps, HasLen, 0)
   556  }
   557  
   558  func (s *seed16Suite) TestLoadMetaCore18(c *C) {
   559  	s.makeSeed(c, map[string]interface{}{
   560  		"base":           "core18",
   561  		"kernel":         "pc-kernel=18",
   562  		"gadget":         "pc=18",
   563  		"required-snaps": []interface{}{"core", "required", "required18"},
   564  	}, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, requiredSeed, coreSeed, required18Seed)
   565  
   566  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   567  	c.Assert(err, IsNil)
   568  
   569  	err = s.seed16.LoadMeta(s.perfTimings)
   570  	c.Assert(err, IsNil)
   571  
   572  	essSnaps := s.seed16.EssentialSnaps()
   573  	c.Check(essSnaps, HasLen, 4)
   574  
   575  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   576  		{
   577  			Path:          s.expectedPath("snapd"),
   578  			SideInfo:      &s.AssertedSnapInfo("snapd").SideInfo,
   579  			EssentialType: snap.TypeSnapd,
   580  			Essential:     true,
   581  			Required:      true,
   582  			Channel:       "stable",
   583  		}, {
   584  			Path:          s.expectedPath("core18"),
   585  			SideInfo:      &s.AssertedSnapInfo("core18").SideInfo,
   586  			EssentialType: snap.TypeBase,
   587  			Essential:     true,
   588  			Required:      true,
   589  			Channel:       "stable",
   590  		}, {
   591  			Path:          s.expectedPath("pc-kernel"),
   592  			SideInfo:      &s.AssertedSnapInfo("pc-kernel").SideInfo,
   593  			EssentialType: snap.TypeKernel,
   594  			Essential:     true,
   595  			Required:      true,
   596  			Channel:       "18",
   597  		}, {
   598  			Path:          s.expectedPath("pc"),
   599  			SideInfo:      &s.AssertedSnapInfo("pc").SideInfo,
   600  			EssentialType: snap.TypeGadget,
   601  			Essential:     true,
   602  			Required:      true,
   603  			Channel:       "18",
   604  		},
   605  	})
   606  
   607  	runSnaps, err := s.seed16.ModeSnaps("run")
   608  	c.Assert(err, IsNil)
   609  	c.Check(runSnaps, HasLen, 3)
   610  
   611  	// these are not sorted by type, firstboot will do that
   612  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   613  		{
   614  			Path:     s.expectedPath("required"),
   615  			SideInfo: &s.AssertedSnapInfo("required").SideInfo,
   616  			Required: true,
   617  			Channel:  "stable",
   618  		}, {
   619  			Path:     s.expectedPath("core"),
   620  			SideInfo: &s.AssertedSnapInfo("core").SideInfo,
   621  			Required: true,
   622  			Channel:  "stable",
   623  		}, {
   624  			Path:     s.expectedPath("required18"),
   625  			SideInfo: &s.AssertedSnapInfo("required18").SideInfo,
   626  			Required: true,
   627  			Channel:  "stable",
   628  		},
   629  	})
   630  }
   631  
   632  func (s *seed16Suite) TestLoadMetaClassicNothing(c *C) {
   633  	s.makeSeed(c, map[string]interface{}{
   634  		"classic": "true",
   635  	})
   636  
   637  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   638  	c.Assert(err, IsNil)
   639  
   640  	err = s.seed16.LoadMeta(s.perfTimings)
   641  	c.Assert(err, IsNil)
   642  
   643  	c.Check(s.seed16.UsesSnapdSnap(), Equals, false)
   644  
   645  	essSnaps := s.seed16.EssentialSnaps()
   646  	c.Check(essSnaps, HasLen, 0)
   647  
   648  	runSnaps, err := s.seed16.ModeSnaps("run")
   649  	c.Assert(err, IsNil)
   650  	c.Check(runSnaps, HasLen, 0)
   651  }
   652  
   653  func (s *seed16Suite) TestLoadMetaClassicCore(c *C) {
   654  	s.makeSeed(c, map[string]interface{}{
   655  		"classic": "true",
   656  	}, coreSeed, classicSnapSeed)
   657  
   658  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   659  	c.Assert(err, IsNil)
   660  
   661  	err = s.seed16.LoadMeta(s.perfTimings)
   662  	c.Assert(err, IsNil)
   663  
   664  	c.Check(s.seed16.UsesSnapdSnap(), Equals, false)
   665  
   666  	essSnaps := s.seed16.EssentialSnaps()
   667  	c.Check(essSnaps, HasLen, 1)
   668  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   669  		{
   670  			Path:          s.expectedPath("core"),
   671  			SideInfo:      &s.AssertedSnapInfo("core").SideInfo,
   672  			EssentialType: snap.TypeOS,
   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  			EssentialType: snap.TypeOS,
   714  			Essential:     true,
   715  			Required:      true,
   716  			Channel:       "stable",
   717  		},
   718  		{
   719  			Path:          s.expectedPath("classic-gadget"),
   720  			SideInfo:      &s.AssertedSnapInfo("classic-gadget").SideInfo,
   721  			EssentialType: snap.TypeGadget,
   722  			Essential:     true,
   723  			Required:      true,
   724  			Channel:       "stable",
   725  		},
   726  	})
   727  
   728  	runSnaps, err := s.seed16.ModeSnaps("run")
   729  	c.Assert(err, IsNil)
   730  	c.Check(runSnaps, HasLen, 0)
   731  }
   732  
   733  func (s *seed16Suite) TestLoadMetaClassicSnapd(c *C) {
   734  	s.makeSeed(c, map[string]interface{}{
   735  		"classic":        "true",
   736  		"required-snaps": []interface{}{"core18", "required18"},
   737  	}, snapdSeed, core18Seed, required18Seed)
   738  
   739  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   740  	c.Assert(err, IsNil)
   741  
   742  	err = s.seed16.LoadMeta(s.perfTimings)
   743  	c.Assert(err, IsNil)
   744  
   745  	c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
   746  
   747  	essSnaps := s.seed16.EssentialSnaps()
   748  	c.Check(essSnaps, HasLen, 1)
   749  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   750  		{
   751  			Path:          s.expectedPath("snapd"),
   752  			SideInfo:      &s.AssertedSnapInfo("snapd").SideInfo,
   753  			EssentialType: snap.TypeSnapd,
   754  			Essential:     true,
   755  			Required:      true,
   756  			Channel:       "stable",
   757  		},
   758  	})
   759  
   760  	runSnaps, err := s.seed16.ModeSnaps("run")
   761  	c.Assert(err, IsNil)
   762  	c.Check(runSnaps, HasLen, 2)
   763  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   764  		{
   765  			Path:     s.expectedPath("core18"),
   766  			SideInfo: &s.AssertedSnapInfo("core18").SideInfo,
   767  			Required: true,
   768  			Channel:  "stable",
   769  		}, {
   770  			Path:     s.expectedPath("required18"),
   771  			SideInfo: &s.AssertedSnapInfo("required18").SideInfo,
   772  			Required: true,
   773  			Channel:  "stable",
   774  		},
   775  	})
   776  }
   777  
   778  func (s *seed16Suite) TestLoadMetaClassicSnapdWithGadget(c *C) {
   779  	s.makeSeed(c, map[string]interface{}{
   780  		"classic": "true",
   781  		"gadget":  "classic-gadget",
   782  	}, snapdSeed, classicGadgetSeed, coreSeed)
   783  
   784  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   785  	c.Assert(err, IsNil)
   786  
   787  	err = s.seed16.LoadMeta(s.perfTimings)
   788  	c.Assert(err, IsNil)
   789  
   790  	c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
   791  
   792  	essSnaps := s.seed16.EssentialSnaps()
   793  	c.Check(essSnaps, HasLen, 3)
   794  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   795  		{
   796  			Path:          s.expectedPath("snapd"),
   797  			SideInfo:      &s.AssertedSnapInfo("snapd").SideInfo,
   798  			EssentialType: snap.TypeSnapd,
   799  
   800  			Essential: true,
   801  			Required:  true,
   802  			Channel:   "stable",
   803  		}, {
   804  			Path:          s.expectedPath("classic-gadget"),
   805  			SideInfo:      &s.AssertedSnapInfo("classic-gadget").SideInfo,
   806  			EssentialType: snap.TypeGadget,
   807  
   808  			Essential: true,
   809  			Required:  true,
   810  			Channel:   "stable",
   811  		}, {
   812  			Path:          s.expectedPath("core"),
   813  			SideInfo:      &s.AssertedSnapInfo("core").SideInfo,
   814  			EssentialType: snap.TypeOS,
   815  
   816  			Essential: true,
   817  			Required:  true,
   818  			Channel:   "stable",
   819  		},
   820  	})
   821  
   822  	runSnaps, err := s.seed16.ModeSnaps("run")
   823  	c.Assert(err, IsNil)
   824  	c.Check(runSnaps, HasLen, 0)
   825  }
   826  
   827  func (s *seed16Suite) TestLoadMetaClassicSnapdWithGadget18(c *C) {
   828  	s.makeSeed(c, map[string]interface{}{
   829  		"classic":        "true",
   830  		"gadget":         "classic-gadget18",
   831  		"required-snaps": []interface{}{"core", "required"},
   832  	}, snapdSeed, coreSeed, requiredSeed, classicGadget18Seed, core18Seed)
   833  
   834  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   835  	c.Assert(err, IsNil)
   836  
   837  	err = s.seed16.LoadMeta(s.perfTimings)
   838  	c.Assert(err, IsNil)
   839  
   840  	c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
   841  
   842  	essSnaps := s.seed16.EssentialSnaps()
   843  	c.Check(essSnaps, HasLen, 3)
   844  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   845  		{
   846  			Path:          s.expectedPath("snapd"),
   847  			SideInfo:      &s.AssertedSnapInfo("snapd").SideInfo,
   848  			EssentialType: snap.TypeSnapd,
   849  
   850  			Essential: true,
   851  			Required:  true,
   852  			Channel:   "stable",
   853  		}, {
   854  			Path:          s.expectedPath("classic-gadget18"),
   855  			SideInfo:      &s.AssertedSnapInfo("classic-gadget18").SideInfo,
   856  			EssentialType: snap.TypeGadget,
   857  
   858  			Essential: true,
   859  			Required:  true,
   860  			Channel:   "stable",
   861  		}, {
   862  			Path:          s.expectedPath("core18"),
   863  			SideInfo:      &s.AssertedSnapInfo("core18").SideInfo,
   864  			EssentialType: snap.TypeBase,
   865  
   866  			Essential: true,
   867  			Required:  true,
   868  			Channel:   "stable",
   869  		},
   870  	})
   871  
   872  	runSnaps, err := s.seed16.ModeSnaps("run")
   873  	c.Assert(err, IsNil)
   874  	c.Check(runSnaps, HasLen, 2)
   875  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   876  		{
   877  			Path:     s.expectedPath("core"),
   878  			SideInfo: &s.AssertedSnapInfo("core").SideInfo,
   879  			Required: true,
   880  			Channel:  "stable",
   881  		}, {
   882  			Path:     s.expectedPath("required"),
   883  			SideInfo: &s.AssertedSnapInfo("required").SideInfo,
   884  			Required: true,
   885  			Channel:  "stable",
   886  		},
   887  	})
   888  }
   889  
   890  func (s *seed16Suite) TestLoadMetaCore18Local(c *C) {
   891  	localRequired18Seed := &seed.InternalSnap16{
   892  		Name:       "required18",
   893  		Unasserted: true,
   894  		DevMode:    true,
   895  	}
   896  	s.makeSeed(c, map[string]interface{}{
   897  		"base":           "core18",
   898  		"kernel":         "pc-kernel=18",
   899  		"gadget":         "pc=18",
   900  		"required-snaps": []interface{}{"core", "required18"},
   901  	}, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, localRequired18Seed)
   902  
   903  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   904  	c.Assert(err, IsNil)
   905  
   906  	err = s.seed16.LoadMeta(s.perfTimings)
   907  	c.Assert(err, IsNil)
   908  
   909  	essSnaps := s.seed16.EssentialSnaps()
   910  	c.Check(essSnaps, HasLen, 4)
   911  
   912  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
   913  		{
   914  			Path:          s.expectedPath("snapd"),
   915  			SideInfo:      &s.AssertedSnapInfo("snapd").SideInfo,
   916  			EssentialType: snap.TypeSnapd,
   917  			Essential:     true,
   918  			Required:      true,
   919  			Channel:       "stable",
   920  		}, {
   921  			Path:          s.expectedPath("core18"),
   922  			SideInfo:      &s.AssertedSnapInfo("core18").SideInfo,
   923  			EssentialType: snap.TypeBase,
   924  			Essential:     true,
   925  			Required:      true,
   926  			Channel:       "stable",
   927  		}, {
   928  			Path:          s.expectedPath("pc-kernel"),
   929  			SideInfo:      &s.AssertedSnapInfo("pc-kernel").SideInfo,
   930  			EssentialType: snap.TypeKernel,
   931  			Essential:     true,
   932  			Required:      true,
   933  			Channel:       "18",
   934  		}, {
   935  			Path:          s.expectedPath("pc"),
   936  			SideInfo:      &s.AssertedSnapInfo("pc").SideInfo,
   937  			EssentialType: snap.TypeGadget,
   938  			Essential:     true,
   939  			Required:      true,
   940  			Channel:       "18",
   941  		},
   942  	})
   943  
   944  	runSnaps, err := s.seed16.ModeSnaps("run")
   945  	c.Assert(err, IsNil)
   946  	c.Check(runSnaps, HasLen, 1)
   947  
   948  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   949  		{
   950  			Path:     filepath.Join(s.SeedDir, "snaps", "required18_1.0_all.snap"),
   951  			SideInfo: &snap.SideInfo{RealName: "required18"},
   952  			Required: true,
   953  			DevMode:  true,
   954  		},
   955  	})
   956  }
   957  
   958  func (s *seed16Suite) TestLoadMetaCore18StoreInfo(c *C) {
   959  	s.makeSeed(c, map[string]interface{}{
   960  		"base":   "core18",
   961  		"kernel": "pc-kernel=18",
   962  		"gadget": "pc=18",
   963  	}, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, privateSnapSeed, contactableSnapSeed)
   964  
   965  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
   966  	c.Assert(err, IsNil)
   967  
   968  	err = s.seed16.LoadMeta(s.perfTimings)
   969  	c.Assert(err, IsNil)
   970  
   971  	essSnaps := s.seed16.EssentialSnaps()
   972  	c.Check(essSnaps, HasLen, 4)
   973  
   974  	runSnaps, err := s.seed16.ModeSnaps("run")
   975  	c.Assert(err, IsNil)
   976  	c.Check(runSnaps, HasLen, 2)
   977  
   978  	privateSnapSideInfo := s.AssertedSnapInfo("private-snap").SideInfo
   979  	privateSnapSideInfo.Private = true
   980  	contactableSnapSideInfo := s.AssertedSnapInfo("contactable-snap").SideInfo
   981  	contactableSnapSideInfo.Contact = "author@example.com"
   982  
   983  	// these are not sorted by type, firstboot will do that
   984  	c.Check(runSnaps, DeepEquals, []*seed.Snap{
   985  		{
   986  			Path:     s.expectedPath("private-snap"),
   987  			SideInfo: &privateSnapSideInfo,
   988  			Channel:  "stable",
   989  		}, {
   990  			Path:     s.expectedPath("contactable-snap"),
   991  			SideInfo: &contactableSnapSideInfo,
   992  			Channel:  "stable",
   993  		},
   994  	})
   995  }
   996  
   997  func (s *seed16Suite) TestLoadMetaCore18EnforcePinnedTracks(c *C) {
   998  	seedSnaps := s.makeSeed(c, map[string]interface{}{
   999  		"base":   "core18",
  1000  		"kernel": "pc-kernel=18",
  1001  		"gadget": "pc=18",
  1002  	}, snapdSeed, core18Seed, kernel18Seed, gadget18Seed)
  1003  
  1004  	// tweak channels
  1005  	for _, sn := range seedSnaps {
  1006  		switch sn.Name {
  1007  		case "pc":
  1008  			sn.Channel = "edge"
  1009  		case "pc-kernel":
  1010  			sn.Channel = "latest/candidate"
  1011  		}
  1012  	}
  1013  	s.writeSeed(c, seedSnaps)
  1014  
  1015  	err := s.seed16.LoadAssertions(s.db, s.commitTo)
  1016  	c.Assert(err, IsNil)
  1017  
  1018  	err = s.seed16.LoadMeta(s.perfTimings)
  1019  	c.Assert(err, IsNil)
  1020  
  1021  	c.Check(s.seed16.UsesSnapdSnap(), Equals, true)
  1022  
  1023  	essSnaps := s.seed16.EssentialSnaps()
  1024  	c.Check(essSnaps, HasLen, 4)
  1025  
  1026  	c.Check(essSnaps, DeepEquals, []*seed.Snap{
  1027  		{
  1028  			Path:          s.expectedPath("snapd"),
  1029  			SideInfo:      &s.AssertedSnapInfo("snapd").SideInfo,
  1030  			EssentialType: snap.TypeSnapd,
  1031  			Essential:     true,
  1032  			Required:      true,
  1033  			Channel:       "stable",
  1034  		}, {
  1035  			Path:          s.expectedPath("core18"),
  1036  			SideInfo:      &s.AssertedSnapInfo("core18").SideInfo,
  1037  			EssentialType: snap.TypeBase,
  1038  			Essential:     true,
  1039  			Required:      true,
  1040  			Channel:       "stable",
  1041  		}, {
  1042  			Path:          s.expectedPath("pc-kernel"),
  1043  			SideInfo:      &s.AssertedSnapInfo("pc-kernel").SideInfo,
  1044  			EssentialType: snap.TypeKernel,
  1045  			Essential:     true,
  1046  			Required:      true,
  1047  			Channel:       "18",
  1048  		}, {
  1049  			Path:          s.expectedPath("pc"),
  1050  			SideInfo:      &s.AssertedSnapInfo("pc").SideInfo,
  1051  			EssentialType: snap.TypeGadget,
  1052  			Essential:     true,
  1053  			Required:      true,
  1054  			Channel:       "18/edge",
  1055  		},
  1056  	})
  1057  
  1058  	runSnaps, err := s.seed16.ModeSnaps("run")
  1059  	c.Assert(err, IsNil)
  1060  	c.Check(runSnaps, HasLen, 0)
  1061  }
  1062  
  1063  func (s *seed16Suite) TestLoadMetaBrokenSeed(c *C) {
  1064  	seedSnap16s := s.makeSeed(c, map[string]interface{}{
  1065  		"base":           "core18",
  1066  		"kernel":         "pc-kernel=18",
  1067  		"gadget":         "pc=18",
  1068  		"required-snaps": []interface{}{"required18"},
  1069  	}, snapdSeed, core18Seed, kernel18Seed, gadget18Seed, required18Seed)
  1070  
  1071  	otherSnapFile := snaptest.MakeTestSnapWithFiles(c, `name: other
  1072  version: other`, nil)
  1073  	otherFname := filepath.Base(otherSnapFile)
  1074  	err := os.Rename(otherSnapFile, filepath.Join(s.SeedDir, "snaps", otherFname))
  1075  	c.Assert(err, IsNil)
  1076  
  1077  	const otherBaseGadget = `name: pc
  1078  type: gadget
  1079  base: other-base
  1080  version: other-base
  1081  `
  1082  	otherBaseGadgetFname, obgDecl, obgRev := s.MakeAssertedSnap(c, otherBaseGadget, snapFiles["pc"], snap.R(3), "canonical")
  1083  	s.WriteAssertions("other-gadget.asserts", obgDecl, obgRev)
  1084  
  1085  	err = s.seed16.LoadAssertions(s.db, s.commitTo)
  1086  	c.Assert(err, IsNil)
  1087  
  1088  	omit := func(which int) func([]*seed.InternalSnap16) []*seed.InternalSnap16 {
  1089  		return func(snaps []*seed.InternalSnap16) []*seed.InternalSnap16 {
  1090  			broken := make([]*seed.InternalSnap16, 0, len(snaps)-1)
  1091  			for i, sn := range snaps {
  1092  				if i == which {
  1093  					continue
  1094  				}
  1095  				broken = append(broken, sn)
  1096  			}
  1097  			return broken
  1098  		}
  1099  	}
  1100  	replaceFile := func(snapName, fname string) func([]*seed.InternalSnap16) []*seed.InternalSnap16 {
  1101  		return func(snaps []*seed.InternalSnap16) []*seed.InternalSnap16 {
  1102  			for i := range snaps {
  1103  				if snaps[i].Name != snapName {
  1104  					continue
  1105  				}
  1106  				sn := *snaps[i]
  1107  				sn.File = fname
  1108  				snaps[i] = &sn
  1109  			}
  1110  			return snaps
  1111  		}
  1112  	}
  1113  
  1114  	tests := []struct {
  1115  		breakSeed func([]*seed.InternalSnap16) []*seed.InternalSnap16
  1116  		err       string
  1117  	}{
  1118  		{omit(0), `essential snap "snapd" required by the model is missing in the seed`},
  1119  		{omit(1), `essential snap "core18" required by the model is missing in the seed`},
  1120  		{omit(2), `essential snap "pc-kernel" required by the model is missing in the seed`},
  1121  		{omit(3), `essential snap "pc" required by the model is missing in the seed`},
  1122  		// omitting "required18" currently doesn't error in any way
  1123  		{replaceFile("core18", otherFname), `cannot find signatures with metadata for snap "core18".*`},
  1124  		{replaceFile("required18", otherFname), `cannot find signatures with metadata for snap "required18".*`},
  1125  		{replaceFile("core18", "not-existent"), `cannot compute snap .* digest: .*`},
  1126  		{replaceFile("pc", otherBaseGadgetFname), `cannot use gadget snap because its base "other-base" is different from model base "core18"`},
  1127  	}
  1128  
  1129  	for _, t := range tests {
  1130  		testSeedSnap16s := make([]*seed.InternalSnap16, 5)
  1131  		copy(testSeedSnap16s, seedSnap16s)
  1132  
  1133  		testSeedSnap16s = t.breakSeed(testSeedSnap16s)
  1134  		s.writeSeed(c, testSeedSnap16s)
  1135  
  1136  		c.Check(s.seed16.LoadMeta(s.perfTimings), ErrorMatches, t.err)
  1137  	}
  1138  }