github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/devicestate/firstboot_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2015-2018 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 devicestate_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"strconv"
    28  	"strings"
    29  	"time"
    30  
    31  	. "gopkg.in/check.v1"
    32  	"gopkg.in/tomb.v2"
    33  
    34  	"github.com/snapcore/snapd/asserts"
    35  	"github.com/snapcore/snapd/asserts/assertstest"
    36  	"github.com/snapcore/snapd/bootloader"
    37  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    38  	"github.com/snapcore/snapd/dirs"
    39  	"github.com/snapcore/snapd/osutil"
    40  	"github.com/snapcore/snapd/overlord"
    41  	"github.com/snapcore/snapd/overlord/assertstate"
    42  	"github.com/snapcore/snapd/overlord/auth"
    43  	"github.com/snapcore/snapd/overlord/configstate"
    44  	"github.com/snapcore/snapd/overlord/configstate/config"
    45  	"github.com/snapcore/snapd/overlord/devicestate"
    46  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    47  	"github.com/snapcore/snapd/overlord/hookstate"
    48  	"github.com/snapcore/snapd/overlord/ifacestate"
    49  	"github.com/snapcore/snapd/overlord/snapstate"
    50  	"github.com/snapcore/snapd/overlord/state"
    51  	"github.com/snapcore/snapd/release"
    52  	"github.com/snapcore/snapd/seed"
    53  	"github.com/snapcore/snapd/seed/seedtest"
    54  	"github.com/snapcore/snapd/snap"
    55  	"github.com/snapcore/snapd/snap/snaptest"
    56  	"github.com/snapcore/snapd/systemd"
    57  	"github.com/snapcore/snapd/testutil"
    58  	"github.com/snapcore/snapd/timings"
    59  )
    60  
    61  type FirstBootTestSuite struct {
    62  	testutil.BaseTest
    63  
    64  	systemctl *testutil.MockCmd
    65  
    66  	// TestingSeed helps populating seeds (it provides
    67  	// MakeAssertedSnap, WriteAssertions etc.) for tests.
    68  	*seedtest.TestingSeed
    69  
    70  	devAcct *asserts.Account
    71  
    72  	overlord *overlord.Overlord
    73  
    74  	perfTimings timings.Measurer
    75  }
    76  
    77  var _ = Suite(&FirstBootTestSuite{})
    78  
    79  func (s *FirstBootTestSuite) SetUpTest(c *C) {
    80  	s.BaseTest.SetUpTest(c)
    81  
    82  	tempdir := c.MkDir()
    83  	dirs.SetRootDir(tempdir)
    84  	s.AddCleanup(func() { dirs.SetRootDir("/") })
    85  
    86  	s.AddCleanup(release.MockOnClassic(false))
    87  
    88  	// mock the world!
    89  	err := os.MkdirAll(filepath.Join(dirs.SnapSeedDir, "snaps"), 0755)
    90  	c.Assert(err, IsNil)
    91  	err = os.MkdirAll(filepath.Join(dirs.SnapSeedDir, "assertions"), 0755)
    92  	c.Assert(err, IsNil)
    93  
    94  	err = os.MkdirAll(dirs.SnapServicesDir, 0755)
    95  	c.Assert(err, IsNil)
    96  	os.Setenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS", "1")
    97  	s.AddCleanup(func() { os.Unsetenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") })
    98  	s.systemctl = testutil.MockCommand(c, "systemctl", "")
    99  	s.AddCleanup(s.systemctl.Restore)
   100  
   101  	err = ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), nil, 0644)
   102  	c.Assert(err, IsNil)
   103  
   104  	s.TestingSeed = &seedtest.TestingSeed{}
   105  	s.SetupAssertSigning("can0nical", s)
   106  	s.Brands.Register("my-brand", brandPrivKey, map[string]interface{}{
   107  		"verification": "verified",
   108  	})
   109  
   110  	s.SnapsDir = filepath.Join(dirs.SnapSeedDir, "snaps")
   111  	s.AssertsDir = filepath.Join(dirs.SnapSeedDir, "assertions")
   112  
   113  	s.devAcct = assertstest.NewAccount(s.StoreSigning, "developer", map[string]interface{}{
   114  		"account-id": "developerid",
   115  	}, "")
   116  
   117  	s.AddCleanup(ifacestate.MockSecurityBackends(nil))
   118  
   119  	ovld, err := overlord.New(nil)
   120  	c.Assert(err, IsNil)
   121  	ovld.InterfaceManager().DisableUDevMonitor()
   122  	s.overlord = ovld
   123  	c.Assert(ovld.StartUp(), IsNil)
   124  
   125  	// don't actually try to talk to the store on snapstate.Ensure
   126  	// needs doing after the call to devicestate.Manager (which happens in overlord.New)
   127  	snapstate.CanAutoRefresh = nil
   128  
   129  	s.perfTimings = timings.New(nil)
   130  }
   131  
   132  func checkTrivialSeeding(c *C, tsAll []*state.TaskSet) {
   133  	// run internal core config and  mark seeded
   134  	c.Check(tsAll, HasLen, 2)
   135  	tasks := tsAll[0].Tasks()
   136  	c.Check(tasks, HasLen, 1)
   137  	c.Assert(tasks[0].Kind(), Equals, "run-hook")
   138  	var hooksup hookstate.HookSetup
   139  	err := tasks[0].Get("hook-setup", &hooksup)
   140  	c.Assert(err, IsNil)
   141  	c.Check(hooksup.Hook, Equals, "configure")
   142  	c.Check(hooksup.Snap, Equals, "core")
   143  	tasks = tsAll[1].Tasks()
   144  	c.Check(tasks, HasLen, 1)
   145  	c.Check(tasks[0].Kind(), Equals, "mark-seeded")
   146  }
   147  
   148  func (s *FirstBootTestSuite) modelHeaders(modelStr string, reqSnaps ...string) map[string]interface{} {
   149  	headers := map[string]interface{}{
   150  		"architecture": "amd64",
   151  		"store":        "canonical",
   152  	}
   153  	if strings.HasSuffix(modelStr, "-classic") {
   154  		headers["classic"] = "true"
   155  	} else {
   156  		headers["kernel"] = "pc-kernel"
   157  		headers["gadget"] = "pc"
   158  	}
   159  	if len(reqSnaps) != 0 {
   160  		reqs := make([]interface{}, len(reqSnaps))
   161  		for i, req := range reqSnaps {
   162  			reqs[i] = req
   163  		}
   164  		headers["required-snaps"] = reqs
   165  	}
   166  	return headers
   167  }
   168  
   169  func (s *FirstBootTestSuite) makeModelAssertionChain(c *C, modName string, extraHeaders map[string]interface{}, reqSnaps ...string) []asserts.Assertion {
   170  	return s.MakeModelAssertionChain("my-brand", modName, s.modelHeaders(modName, reqSnaps...), extraHeaders)
   171  }
   172  
   173  func (s *FirstBootTestSuite) TestPopulateFromSeedOnClassicNoop(c *C) {
   174  	restore := release.MockOnClassic(true)
   175  	defer restore()
   176  
   177  	st := s.overlord.State()
   178  	st.Lock()
   179  	defer st.Unlock()
   180  
   181  	err := os.Remove(filepath.Join(dirs.SnapSeedDir, "assertions"))
   182  	c.Assert(err, IsNil)
   183  
   184  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
   185  	c.Assert(err, IsNil)
   186  	checkTrivialSeeding(c, tsAll)
   187  
   188  	// already set the fallback model
   189  
   190  	// verify that the model was added
   191  	db := assertstate.DB(st)
   192  	as, err := db.Find(asserts.ModelType, map[string]string{
   193  		"series":   "16",
   194  		"brand-id": "generic",
   195  		"model":    "generic-classic",
   196  	})
   197  	c.Assert(err, IsNil)
   198  	_, ok := as.(*asserts.Model)
   199  	c.Check(ok, Equals, true)
   200  
   201  	ds, err := devicestatetest.Device(st)
   202  	c.Assert(err, IsNil)
   203  	c.Check(ds.Brand, Equals, "generic")
   204  	c.Check(ds.Model, Equals, "generic-classic")
   205  }
   206  
   207  func (s *FirstBootTestSuite) TestPopulateFromSeedOnClassicNoSeedYaml(c *C) {
   208  	restore := release.MockOnClassic(true)
   209  	defer restore()
   210  
   211  	ovld, err := overlord.New(nil)
   212  	c.Assert(err, IsNil)
   213  	st := ovld.State()
   214  
   215  	// add the model assertion and its chain
   216  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil)
   217  	s.WriteAssertions("model.asserts", assertsChain...)
   218  
   219  	err = os.Remove(filepath.Join(dirs.SnapSeedDir, "seed.yaml"))
   220  	c.Assert(err, IsNil)
   221  
   222  	st.Lock()
   223  	defer st.Unlock()
   224  
   225  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
   226  	c.Assert(err, IsNil)
   227  	checkTrivialSeeding(c, tsAll)
   228  
   229  	ds, err := devicestatetest.Device(st)
   230  	c.Assert(err, IsNil)
   231  	c.Check(ds.Brand, Equals, "my-brand")
   232  	c.Check(ds.Model, Equals, "my-model-classic")
   233  }
   234  
   235  func (s *FirstBootTestSuite) TestPopulateFromSeedOnClassicEmptySeedYaml(c *C) {
   236  	restore := release.MockOnClassic(true)
   237  	defer restore()
   238  
   239  	ovld, err := overlord.New(nil)
   240  	c.Assert(err, IsNil)
   241  	st := ovld.State()
   242  
   243  	// add the model assertion and its chain
   244  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil)
   245  	s.WriteAssertions("model.asserts", assertsChain...)
   246  
   247  	// create an empty seed.yaml
   248  	err = ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), nil, 0644)
   249  	c.Assert(err, IsNil)
   250  
   251  	st.Lock()
   252  	defer st.Unlock()
   253  
   254  	_, err = devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
   255  	c.Assert(err, ErrorMatches, "cannot proceed, no snaps to seed")
   256  }
   257  
   258  func (s *FirstBootTestSuite) TestPopulateFromSeedOnClassicNoSeedYamlWithCloudInstanceData(c *C) {
   259  	restore := release.MockOnClassic(true)
   260  	defer restore()
   261  
   262  	st := s.overlord.State()
   263  
   264  	// add the model assertion and its chain
   265  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil)
   266  	s.WriteAssertions("model.asserts", assertsChain...)
   267  
   268  	err := os.Remove(filepath.Join(dirs.SnapSeedDir, "seed.yaml"))
   269  	c.Assert(err, IsNil)
   270  
   271  	// write cloud instance data
   272  	const instData = `{
   273   "v1": {
   274    "availability-zone": "us-east-2b",
   275    "cloud-name": "aws",
   276    "instance-id": "i-03bdbe0d89f4c8ec9",
   277    "local-hostname": "ip-10-41-41-143",
   278    "region": "us-east-2"
   279   }
   280  }`
   281  	err = os.MkdirAll(filepath.Dir(dirs.CloudInstanceDataFile), 0755)
   282  	c.Assert(err, IsNil)
   283  	err = ioutil.WriteFile(dirs.CloudInstanceDataFile, []byte(instData), 0600)
   284  	c.Assert(err, IsNil)
   285  
   286  	st.Lock()
   287  	defer st.Unlock()
   288  
   289  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
   290  	c.Assert(err, IsNil)
   291  	checkTrivialSeeding(c, tsAll)
   292  
   293  	ds, err := devicestatetest.Device(st)
   294  	c.Assert(err, IsNil)
   295  	c.Check(ds.Brand, Equals, "my-brand")
   296  	c.Check(ds.Model, Equals, "my-model-classic")
   297  
   298  	// now run the change and check the result
   299  	// use the expected kind otherwise settle will start another one
   300  	chg := st.NewChange("seed", "run the populate from seed changes")
   301  	for _, ts := range tsAll {
   302  		chg.AddAll(ts)
   303  	}
   304  	c.Assert(st.Changes(), HasLen, 1)
   305  
   306  	// avoid device reg
   307  	chg1 := st.NewChange("become-operational", "init device")
   308  	chg1.SetStatus(state.DoingStatus)
   309  
   310  	st.Unlock()
   311  	err = s.overlord.Settle(settleTimeout)
   312  	st.Lock()
   313  	c.Assert(chg.Err(), IsNil)
   314  	c.Assert(err, IsNil)
   315  
   316  	// check marked seeded
   317  	var seeded bool
   318  	err = st.Get("seeded", &seeded)
   319  	c.Assert(err, IsNil)
   320  	c.Check(seeded, Equals, true)
   321  
   322  	// check captured cloud information
   323  	tr := config.NewTransaction(st)
   324  	var cloud auth.CloudInfo
   325  	err = tr.Get("core", "cloud", &cloud)
   326  	c.Assert(err, IsNil)
   327  	c.Check(cloud.Name, Equals, "aws")
   328  	c.Check(cloud.Region, Equals, "us-east-2")
   329  	c.Check(cloud.AvailabilityZone, Equals, "us-east-2b")
   330  }
   331  
   332  func (s *FirstBootTestSuite) TestPopulateFromSeedErrorsOnState(c *C) {
   333  	st := s.overlord.State()
   334  	st.Lock()
   335  	defer st.Unlock()
   336  	st.Set("seeded", true)
   337  
   338  	_, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
   339  	c.Assert(err, ErrorMatches, "cannot populate state: already seeded")
   340  }
   341  
   342  func (s *FirstBootTestSuite) makeCoreSnaps(c *C, extraGadgetYaml string) (coreFname, kernelFname, gadgetFname string) {
   343  	files := [][]string{}
   344  	if strings.Contains(extraGadgetYaml, "defaults:") {
   345  		files = [][]string{{"meta/hooks/configure", ""}}
   346  	}
   347  
   348  	// put core snap into the SnapBlobDir
   349  	snapYaml := `name: core
   350  version: 1.0
   351  type: os`
   352  	coreFname, coreDecl, coreRev := s.MakeAssertedSnap(c, snapYaml, files, snap.R(1), "canonical")
   353  	s.WriteAssertions("core.asserts", coreRev, coreDecl)
   354  
   355  	// put kernel snap into the SnapBlobDir
   356  	snapYaml = `name: pc-kernel
   357  version: 1.0
   358  type: kernel`
   359  	kernelFname, kernelDecl, kernelRev := s.MakeAssertedSnap(c, snapYaml, files, snap.R(1), "canonical")
   360  	s.WriteAssertions("kernel.asserts", kernelRev, kernelDecl)
   361  
   362  	gadgetYaml := `
   363  volumes:
   364      volume-id:
   365          bootloader: grub
   366  `
   367  	gadgetYaml += extraGadgetYaml
   368  
   369  	// put gadget snap into the SnapBlobDir
   370  	files = append(files, []string{"meta/gadget.yaml", gadgetYaml})
   371  
   372  	snapYaml = `name: pc
   373  version: 1.0
   374  type: gadget`
   375  	gadgetFname, gadgetDecl, gadgetRev := s.MakeAssertedSnap(c, snapYaml, files, snap.R(1), "canonical")
   376  	s.WriteAssertions("gadget.asserts", gadgetRev, gadgetDecl)
   377  
   378  	return coreFname, kernelFname, gadgetFname
   379  }
   380  
   381  func checkOrder(c *C, tsAll []*state.TaskSet, snaps ...string) {
   382  	matched := 0
   383  	var prevTask *state.Task
   384  	for i, ts := range tsAll {
   385  		task0 := ts.Tasks()[0]
   386  		waitTasks := task0.WaitTasks()
   387  		if i == 0 {
   388  			c.Check(waitTasks, HasLen, 0)
   389  		} else {
   390  			c.Check(waitTasks, testutil.Contains, prevTask)
   391  		}
   392  		prevTask = task0
   393  		if task0.Kind() != "prerequisites" {
   394  			continue
   395  		}
   396  		snapsup, err := snapstate.TaskSnapSetup(task0)
   397  		c.Assert(err, IsNil, Commentf("%#v", task0))
   398  		c.Check(snapsup.InstanceName(), Equals, snaps[matched])
   399  		matched++
   400  	}
   401  	c.Check(matched, Equals, len(snaps))
   402  }
   403  
   404  func checkSeedTasks(c *C, tsAll []*state.TaskSet) {
   405  	// the tasks of the last taskset must be gadget-connect, mark-seeded
   406  	lastTasks := tsAll[len(tsAll)-1].Tasks()
   407  	c.Check(lastTasks, HasLen, 2)
   408  	gadgetConnectTask := lastTasks[0]
   409  	markSeededTask := lastTasks[1]
   410  	c.Check(gadgetConnectTask.Kind(), Equals, "gadget-connect")
   411  	c.Check(markSeededTask.Kind(), Equals, "mark-seeded")
   412  	// and the gadget-connect must wait for the other tasks
   413  	prevTasks := tsAll[len(tsAll)-2].Tasks()
   414  	otherTask := prevTasks[len(prevTasks)-1]
   415  	c.Check(gadgetConnectTask.WaitTasks(), testutil.Contains, otherTask)
   416  	// add the mark-seeded waits for gadget-connects
   417  	c.Check(markSeededTask.WaitTasks(), DeepEquals, []*state.Task{gadgetConnectTask})
   418  }
   419  
   420  func (s *FirstBootTestSuite) makeSeedChange(c *C, st *state.State) *state.Change {
   421  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "")
   422  
   423  	s.WriteAssertions("developer.account", s.devAcct)
   424  
   425  	// put a firstboot snap into the SnapBlobDir
   426  	snapYaml := `name: foo
   427  version: 1.0`
   428  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
   429  	s.WriteAssertions("foo.snap-declaration", fooDecl)
   430  	s.WriteAssertions("foo.snap-revision", fooRev)
   431  
   432  	// put a firstboot local snap into the SnapBlobDir
   433  	snapYaml = `name: local
   434  version: 1.0`
   435  	mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml, nil)
   436  	targetSnapFile2 := filepath.Join(dirs.SnapSeedDir, "snaps", filepath.Base(mockSnapFile))
   437  	err := os.Rename(mockSnapFile, targetSnapFile2)
   438  	c.Assert(err, IsNil)
   439  
   440  	// add a model assertion and its chain
   441  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo")
   442  	for i, as := range assertsChain {
   443  		s.WriteAssertions(strconv.Itoa(i), as)
   444  	}
   445  
   446  	// create a seed.yaml
   447  	content := []byte(fmt.Sprintf(`
   448  snaps:
   449   - name: core
   450     file: %s
   451   - name: pc-kernel
   452     file: %s
   453   - name: pc
   454     file: %s
   455   - name: foo
   456     file: %s
   457     devmode: true
   458     contact: mailto:some.guy@example.com
   459   - name: local
   460     unasserted: true
   461     file: %s
   462  `, coreFname, kernelFname, gadgetFname, fooFname, filepath.Base(targetSnapFile2)))
   463  	err = ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
   464  	c.Assert(err, IsNil)
   465  
   466  	// run the firstboot stuff
   467  	st.Lock()
   468  	defer st.Unlock()
   469  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
   470  	c.Assert(err, IsNil)
   471  
   472  	checkOrder(c, tsAll, "core", "pc-kernel", "pc", "foo", "local")
   473  	checkSeedTasks(c, tsAll)
   474  
   475  	// now run the change and check the result
   476  	// use the expected kind otherwise settle with start another one
   477  	chg := st.NewChange("seed", "run the populate from seed changes")
   478  	for _, ts := range tsAll {
   479  		chg.AddAll(ts)
   480  	}
   481  	c.Assert(st.Changes(), HasLen, 1)
   482  
   483  	// avoid device reg
   484  	chg1 := st.NewChange("become-operational", "init device")
   485  	chg1.SetStatus(state.DoingStatus)
   486  
   487  	return chg
   488  }
   489  
   490  func (s *FirstBootTestSuite) TestPopulateFromSeedHappy(c *C) {
   491  	bloader := bootloadertest.Mock("mock", c.MkDir())
   492  	bootloader.Force(bloader)
   493  	defer bootloader.Force(nil)
   494  	bloader.SetBootKernel("pc-kernel_1.snap")
   495  	bloader.SetBootBase("core_1.snap")
   496  
   497  	st := s.overlord.State()
   498  	chg := s.makeSeedChange(c, st)
   499  	err := s.overlord.Settle(settleTimeout)
   500  	c.Assert(err, IsNil)
   501  
   502  	st.Lock()
   503  	defer st.Unlock()
   504  
   505  	c.Assert(chg.Err(), IsNil)
   506  
   507  	// and check the snap got correctly installed
   508  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true)
   509  
   510  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "local", "x1", "meta", "snap.yaml")), Equals, true)
   511  
   512  	// verify
   513  	r, err := os.Open(dirs.SnapStateFile)
   514  	c.Assert(err, IsNil)
   515  	state, err := state.ReadState(nil, r)
   516  	c.Assert(err, IsNil)
   517  
   518  	state.Lock()
   519  	defer state.Unlock()
   520  	// check core, kernel, gadget
   521  	_, err = snapstate.CurrentInfo(state, "core")
   522  	c.Assert(err, IsNil)
   523  	_, err = snapstate.CurrentInfo(state, "pc-kernel")
   524  	c.Assert(err, IsNil)
   525  	_, err = snapstate.CurrentInfo(state, "pc")
   526  	c.Assert(err, IsNil)
   527  
   528  	// ensure required flag is set on all essential snaps
   529  	var snapst snapstate.SnapState
   530  	for _, reqName := range []string{"core", "pc-kernel", "pc"} {
   531  		err = snapstate.Get(state, reqName, &snapst)
   532  		c.Assert(err, IsNil)
   533  		c.Assert(snapst.Required, Equals, true, Commentf("required not set for %v", reqName))
   534  	}
   535  
   536  	// check foo
   537  	info, err := snapstate.CurrentInfo(state, "foo")
   538  	c.Assert(err, IsNil)
   539  	c.Assert(info.SnapID, Equals, "foodidididididididididididididid")
   540  	c.Assert(info.Revision, Equals, snap.R(128))
   541  	c.Assert(info.Contact, Equals, "mailto:some.guy@example.com")
   542  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
   543  	c.Assert(err, IsNil)
   544  	c.Check(pubAcct.AccountID(), Equals, "developerid")
   545  
   546  	err = snapstate.Get(state, "foo", &snapst)
   547  	c.Assert(err, IsNil)
   548  	c.Assert(snapst.DevMode, Equals, true)
   549  	c.Assert(snapst.Required, Equals, true)
   550  
   551  	// check local
   552  	info, err = snapstate.CurrentInfo(state, "local")
   553  	c.Assert(err, IsNil)
   554  	c.Assert(info.SnapID, Equals, "")
   555  	c.Assert(info.Revision, Equals, snap.R("x1"))
   556  
   557  	var snapst2 snapstate.SnapState
   558  	err = snapstate.Get(state, "local", &snapst2)
   559  	c.Assert(err, IsNil)
   560  	c.Assert(snapst2.Required, Equals, false)
   561  
   562  	// and ensure state is now considered seeded
   563  	var seeded bool
   564  	err = state.Get("seeded", &seeded)
   565  	c.Assert(err, IsNil)
   566  	c.Check(seeded, Equals, true)
   567  
   568  	// check we set seed-time
   569  	var seedTime time.Time
   570  	err = state.Get("seed-time", &seedTime)
   571  	c.Assert(err, IsNil)
   572  	c.Check(seedTime.IsZero(), Equals, false)
   573  }
   574  
   575  func (s *FirstBootTestSuite) TestPopulateFromSeedMissingBootloader(c *C) {
   576  	st0 := s.overlord.State()
   577  	st0.Lock()
   578  	db := assertstate.DB(st0)
   579  	st0.Unlock()
   580  
   581  	// we run only with the relevant managers to produce the error
   582  	// situation
   583  	o := overlord.Mock()
   584  	st := o.State()
   585  	snapmgr, err := snapstate.Manager(st, o.TaskRunner())
   586  	c.Assert(err, IsNil)
   587  	o.AddManager(snapmgr)
   588  
   589  	ifacemgr, err := ifacestate.Manager(st, nil, o.TaskRunner(), nil, nil)
   590  	c.Assert(err, IsNil)
   591  	o.AddManager(ifacemgr)
   592  	c.Assert(o.StartUp(), IsNil)
   593  
   594  	st.Lock()
   595  	assertstate.ReplaceDB(st, db.(*asserts.Database))
   596  	st.Unlock()
   597  
   598  	o.AddManager(o.TaskRunner())
   599  
   600  	chg := s.makeSeedChange(c, st)
   601  
   602  	se := o.StateEngine()
   603  	// we cannot use Settle because the Change will not become Clean
   604  	// under the subset of managers
   605  	for i := 0; i < 25 && !chg.IsReady(); i++ {
   606  		se.Ensure()
   607  		se.Wait()
   608  	}
   609  
   610  	st.Lock()
   611  	defer st.Unlock()
   612  	c.Assert(chg.Err(), ErrorMatches, `(?s).* cannot determine bootloader.*`)
   613  }
   614  
   615  func (s *FirstBootTestSuite) TestPopulateFromSeedHappyMultiAssertsFiles(c *C) {
   616  	bloader := bootloadertest.Mock("mock", c.MkDir())
   617  	bootloader.Force(bloader)
   618  	defer bootloader.Force(nil)
   619  	bloader.SetBootKernel("pc-kernel_1.snap")
   620  	bloader.SetBootBase("core_1.snap")
   621  
   622  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "")
   623  
   624  	// put a firstboot snap into the SnapBlobDir
   625  	snapYaml := `name: foo
   626  version: 1.0`
   627  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
   628  	s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl)
   629  
   630  	// put a 2nd firstboot snap into the SnapBlobDir
   631  	snapYaml = `name: bar
   632  version: 1.0`
   633  	barFname, barDecl, barRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(65), "developerid")
   634  	s.WriteAssertions("bar.asserts", s.devAcct, barDecl, barRev)
   635  
   636  	// add a model assertion and its chain
   637  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
   638  	s.WriteAssertions("model.asserts", assertsChain...)
   639  
   640  	// create a seed.yaml
   641  	content := []byte(fmt.Sprintf(`
   642  snaps:
   643   - name: core
   644     file: %s
   645   - name: pc-kernel
   646     file: %s
   647   - name: pc
   648     file: %s
   649   - name: foo
   650     file: %s
   651   - name: bar
   652     file: %s
   653  `, coreFname, kernelFname, gadgetFname, fooFname, barFname))
   654  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
   655  	c.Assert(err, IsNil)
   656  
   657  	// run the firstboot stuff
   658  	st := s.overlord.State()
   659  	st.Lock()
   660  	defer st.Unlock()
   661  
   662  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
   663  	c.Assert(err, IsNil)
   664  	// use the expected kind otherwise settle with start another one
   665  	chg := st.NewChange("seed", "run the populate from seed changes")
   666  	for _, ts := range tsAll {
   667  		chg.AddAll(ts)
   668  	}
   669  	c.Assert(st.Changes(), HasLen, 1)
   670  
   671  	// avoid device reg
   672  	chg1 := st.NewChange("become-operational", "init device")
   673  	chg1.SetStatus(state.DoingStatus)
   674  
   675  	st.Unlock()
   676  	err = s.overlord.Settle(settleTimeout)
   677  	st.Lock()
   678  	c.Assert(chg.Err(), IsNil)
   679  	c.Assert(err, IsNil)
   680  
   681  	// and check the snap got correctly installed
   682  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true)
   683  
   684  	// and check the snap got correctly installed
   685  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "bar", "65", "meta", "snap.yaml")), Equals, true)
   686  
   687  	// verify
   688  	r, err := os.Open(dirs.SnapStateFile)
   689  	c.Assert(err, IsNil)
   690  	state, err := state.ReadState(nil, r)
   691  	c.Assert(err, IsNil)
   692  
   693  	state.Lock()
   694  	defer state.Unlock()
   695  	// check foo
   696  	info, err := snapstate.CurrentInfo(state, "foo")
   697  	c.Assert(err, IsNil)
   698  	c.Check(info.SnapID, Equals, "foodidididididididididididididid")
   699  	c.Check(info.Revision, Equals, snap.R(128))
   700  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
   701  	c.Assert(err, IsNil)
   702  	c.Check(pubAcct.AccountID(), Equals, "developerid")
   703  
   704  	// check bar
   705  	info, err = snapstate.CurrentInfo(state, "bar")
   706  	c.Assert(err, IsNil)
   707  	c.Check(info.SnapID, Equals, "bardidididididididididididididid")
   708  	c.Check(info.Revision, Equals, snap.R(65))
   709  	pubAcct, err = assertstate.Publisher(st, info.SnapID)
   710  	c.Assert(err, IsNil)
   711  	c.Check(pubAcct.AccountID(), Equals, "developerid")
   712  }
   713  
   714  func (s *FirstBootTestSuite) TestPopulateFromSeedConfigureHappy(c *C) {
   715  	bloader := bootloadertest.Mock("mock", c.MkDir())
   716  	bootloader.Force(bloader)
   717  	defer bootloader.Force(nil)
   718  	bloader.SetBootKernel("pc-kernel_1.snap")
   719  	bloader.SetBootBase("core_1.snap")
   720  
   721  	const defaultsYaml = `
   722  defaults:
   723      foodidididididididididididididid:
   724         foo-cfg: foo.
   725      coreidididididididididididididid:
   726         core-cfg: core_cfg_defl
   727      pckernelidididididididididididid:
   728         pc-kernel-cfg: pc-kernel_cfg_defl
   729      pcididididididididididididididid:
   730         pc-cfg: pc_cfg_defl
   731  `
   732  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, defaultsYaml)
   733  
   734  	s.WriteAssertions("developer.account", s.devAcct)
   735  
   736  	// put a firstboot snap into the SnapBlobDir
   737  	files := [][]string{{"meta/hooks/configure", ""}}
   738  	snapYaml := `name: foo
   739  version: 1.0`
   740  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, files, snap.R(128), "developerid")
   741  	s.WriteAssertions("foo.asserts", fooDecl, fooRev)
   742  
   743  	// add a model assertion and its chain
   744  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo")
   745  	s.WriteAssertions("model.asserts", assertsChain...)
   746  
   747  	// create a seed.yaml
   748  	content := []byte(fmt.Sprintf(`
   749  snaps:
   750   - name: core
   751     file: %s
   752   - name: pc-kernel
   753     file: %s
   754   - name: pc
   755     file: %s
   756   - name: foo
   757     file: %s
   758  `, coreFname, kernelFname, gadgetFname, fooFname))
   759  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
   760  	c.Assert(err, IsNil)
   761  
   762  	// run the firstboot stuff
   763  	st := s.overlord.State()
   764  	st.Lock()
   765  	defer st.Unlock()
   766  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
   767  	c.Assert(err, IsNil)
   768  
   769  	checkSeedTasks(c, tsAll)
   770  
   771  	// now run the change and check the result
   772  	// use the expected kind otherwise settle with start another one
   773  	chg := st.NewChange("seed", "run the populate from seed changes")
   774  	for _, ts := range tsAll {
   775  		chg.AddAll(ts)
   776  	}
   777  	c.Assert(st.Changes(), HasLen, 1)
   778  
   779  	var configured []string
   780  	hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) {
   781  		ctx.Lock()
   782  		defer ctx.Unlock()
   783  		// we have a gadget at this point(s)
   784  		ok, err := snapstate.HasSnapOfType(st, snap.TypeGadget)
   785  		c.Check(err, IsNil)
   786  		c.Check(ok, Equals, true)
   787  		configured = append(configured, ctx.InstanceName())
   788  		return nil, nil
   789  	}
   790  
   791  	rhk := hookstate.MockRunHook(hookInvoke)
   792  	defer rhk()
   793  
   794  	// ensure we have something that captures the core config
   795  	restore := configstate.MockConfigcoreRun(func(config.Conf) error {
   796  		configured = append(configured, "configcore")
   797  		return nil
   798  	})
   799  	defer restore()
   800  
   801  	// avoid device reg
   802  	chg1 := st.NewChange("become-operational", "init device")
   803  	chg1.SetStatus(state.DoingStatus)
   804  
   805  	st.Unlock()
   806  	err = s.overlord.Settle(settleTimeout)
   807  	st.Lock()
   808  	c.Assert(chg.Err(), IsNil)
   809  	c.Assert(err, IsNil)
   810  
   811  	// and check the snap got correctly installed
   812  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true)
   813  
   814  	// verify
   815  	r, err := os.Open(dirs.SnapStateFile)
   816  	c.Assert(err, IsNil)
   817  	state, err := state.ReadState(nil, r)
   818  	c.Assert(err, IsNil)
   819  
   820  	state.Lock()
   821  	defer state.Unlock()
   822  	tr := config.NewTransaction(state)
   823  	var val string
   824  
   825  	// check core, kernel, gadget
   826  	_, err = snapstate.CurrentInfo(state, "core")
   827  	c.Assert(err, IsNil)
   828  	err = tr.Get("core", "core-cfg", &val)
   829  	c.Assert(err, IsNil)
   830  	c.Check(val, Equals, "core_cfg_defl")
   831  
   832  	_, err = snapstate.CurrentInfo(state, "pc-kernel")
   833  	c.Assert(err, IsNil)
   834  	err = tr.Get("pc-kernel", "pc-kernel-cfg", &val)
   835  	c.Assert(err, IsNil)
   836  	c.Check(val, Equals, "pc-kernel_cfg_defl")
   837  
   838  	_, err = snapstate.CurrentInfo(state, "pc")
   839  	c.Assert(err, IsNil)
   840  	err = tr.Get("pc", "pc-cfg", &val)
   841  	c.Assert(err, IsNil)
   842  	c.Check(val, Equals, "pc_cfg_defl")
   843  
   844  	// check foo
   845  	info, err := snapstate.CurrentInfo(state, "foo")
   846  	c.Assert(err, IsNil)
   847  	c.Assert(info.SnapID, Equals, "foodidididididididididididididid")
   848  	c.Assert(info.Revision, Equals, snap.R(128))
   849  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
   850  	c.Assert(err, IsNil)
   851  	c.Check(pubAcct.AccountID(), Equals, "developerid")
   852  
   853  	// check foo config
   854  	err = tr.Get("foo", "foo-cfg", &val)
   855  	c.Assert(err, IsNil)
   856  	c.Check(val, Equals, "foo.")
   857  
   858  	c.Check(configured, DeepEquals, []string{"configcore", "pc-kernel", "pc", "foo"})
   859  
   860  	// and ensure state is now considered seeded
   861  	var seeded bool
   862  	err = state.Get("seeded", &seeded)
   863  	c.Assert(err, IsNil)
   864  	c.Check(seeded, Equals, true)
   865  }
   866  
   867  func (s *FirstBootTestSuite) TestPopulateFromSeedGadgetConnectHappy(c *C) {
   868  	bloader := bootloadertest.Mock("mock", c.MkDir())
   869  	bootloader.Force(bloader)
   870  	defer bootloader.Force(nil)
   871  	bloader.SetBootKernel("pc-kernel_1.snap")
   872  	bloader.SetBootBase("core_1.snap")
   873  
   874  	const connectionsYaml = `
   875  connections:
   876    - plug: foodidididididididididididididid:network-control
   877  `
   878  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, connectionsYaml)
   879  
   880  	s.WriteAssertions("developer.account", s.devAcct)
   881  
   882  	snapYaml := `name: foo
   883  version: 1.0
   884  plugs:
   885    network-control:
   886  `
   887  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
   888  	s.WriteAssertions("foo.asserts", fooDecl, fooRev)
   889  
   890  	// add a model assertion and its chain
   891  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo")
   892  	s.WriteAssertions("model.asserts", assertsChain...)
   893  
   894  	// create a seed.yaml
   895  	content := []byte(fmt.Sprintf(`
   896  snaps:
   897   - name: core
   898     file: %s
   899   - name: pc-kernel
   900     file: %s
   901   - name: pc
   902     file: %s
   903   - name: foo
   904     file: %s
   905  `, coreFname, kernelFname, gadgetFname, fooFname))
   906  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
   907  	c.Assert(err, IsNil)
   908  
   909  	// run the firstboot stuff
   910  	st := s.overlord.State()
   911  	st.Lock()
   912  	defer st.Unlock()
   913  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
   914  	c.Assert(err, IsNil)
   915  
   916  	checkSeedTasks(c, tsAll)
   917  
   918  	// now run the change and check the result
   919  	// use the expected kind otherwise settle with start another one
   920  	chg := st.NewChange("seed", "run the populate from seed changes")
   921  	for _, ts := range tsAll {
   922  		chg.AddAll(ts)
   923  	}
   924  	c.Assert(st.Changes(), HasLen, 1)
   925  
   926  	// avoid device reg
   927  	chg1 := st.NewChange("become-operational", "init device")
   928  	chg1.SetStatus(state.DoingStatus)
   929  
   930  	st.Unlock()
   931  	err = s.overlord.Settle(settleTimeout)
   932  	st.Lock()
   933  	c.Assert(chg.Err(), IsNil)
   934  	c.Assert(err, IsNil)
   935  
   936  	// and check the snap got correctly installed
   937  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true)
   938  
   939  	// verify
   940  	r, err := os.Open(dirs.SnapStateFile)
   941  	c.Assert(err, IsNil)
   942  	state, err := state.ReadState(nil, r)
   943  	c.Assert(err, IsNil)
   944  
   945  	state.Lock()
   946  	defer state.Unlock()
   947  
   948  	// check foo
   949  	info, err := snapstate.CurrentInfo(state, "foo")
   950  	c.Assert(err, IsNil)
   951  	c.Assert(info.SnapID, Equals, "foodidididididididididididididid")
   952  	c.Assert(info.Revision, Equals, snap.R(128))
   953  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
   954  	c.Assert(err, IsNil)
   955  	c.Check(pubAcct.AccountID(), Equals, "developerid")
   956  
   957  	// check connection
   958  	var conns map[string]interface{}
   959  	err = state.Get("conns", &conns)
   960  	c.Assert(err, IsNil)
   961  	c.Check(conns, HasLen, 1)
   962  	c.Check(conns, DeepEquals, map[string]interface{}{
   963  		"foo:network-control core:network-control": map[string]interface{}{
   964  			"interface": "network-control", "auto": true, "by-gadget": true,
   965  		},
   966  	})
   967  
   968  	// and ensure state is now considered seeded
   969  	var seeded bool
   970  	err = state.Get("seeded", &seeded)
   971  	c.Assert(err, IsNil)
   972  	c.Check(seeded, Equals, true)
   973  }
   974  
   975  func (s *FirstBootTestSuite) TestImportAssertionsFromSeedClassicModelMismatch(c *C) {
   976  	restore := release.MockOnClassic(true)
   977  	defer restore()
   978  
   979  	ovld, err := overlord.New(nil)
   980  	c.Assert(err, IsNil)
   981  	st := ovld.State()
   982  
   983  	// add the odel assertion and its chain
   984  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
   985  	s.WriteAssertions("model.asserts", assertsChain...)
   986  
   987  	// import them
   988  	st.Lock()
   989  	defer st.Unlock()
   990  
   991  	deviceSeed, err := seed.Open(dirs.SnapSeedDir)
   992  	c.Assert(err, IsNil)
   993  
   994  	_, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
   995  	c.Assert(err, ErrorMatches, "cannot seed a classic system with an all-snaps model")
   996  }
   997  
   998  func (s *FirstBootTestSuite) TestImportAssertionsFromSeedAllSnapsModelMismatch(c *C) {
   999  	ovld, err := overlord.New(nil)
  1000  	c.Assert(err, IsNil)
  1001  	st := ovld.State()
  1002  
  1003  	// add the model assertion and its chain
  1004  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil)
  1005  	s.WriteAssertions("model.asserts", assertsChain...)
  1006  
  1007  	// import them
  1008  	st.Lock()
  1009  	defer st.Unlock()
  1010  
  1011  	deviceSeed, err := seed.Open(dirs.SnapSeedDir)
  1012  	c.Assert(err, IsNil)
  1013  
  1014  	_, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
  1015  	c.Assert(err, ErrorMatches, "cannot seed an all-snaps system with a classic model")
  1016  }
  1017  
  1018  func (s *FirstBootTestSuite) TestImportAssertionsFromSeedHappy(c *C) {
  1019  	ovld, err := overlord.New(nil)
  1020  	c.Assert(err, IsNil)
  1021  	st := ovld.State()
  1022  
  1023  	// add a bunch of assertions (model assertion and its chain)
  1024  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1025  	for i, as := range assertsChain {
  1026  		fname := strconv.Itoa(i)
  1027  		if as.Type() == asserts.ModelType {
  1028  			fname = "model"
  1029  		}
  1030  		s.WriteAssertions(fname, as)
  1031  	}
  1032  
  1033  	// import them
  1034  	st.Lock()
  1035  	defer st.Unlock()
  1036  
  1037  	deviceSeed, err := seed.Open(dirs.SnapSeedDir)
  1038  	c.Assert(err, IsNil)
  1039  
  1040  	model, err := devicestate.ImportAssertionsFromSeed(st, deviceSeed)
  1041  	c.Assert(err, IsNil)
  1042  	c.Assert(model, NotNil)
  1043  
  1044  	// verify that the model was added
  1045  	db := assertstate.DB(st)
  1046  	as, err := db.Find(asserts.ModelType, map[string]string{
  1047  		"series":   "16",
  1048  		"brand-id": "my-brand",
  1049  		"model":    "my-model",
  1050  	})
  1051  	c.Assert(err, IsNil)
  1052  	_, ok := as.(*asserts.Model)
  1053  	c.Check(ok, Equals, true)
  1054  
  1055  	ds, err := devicestatetest.Device(st)
  1056  	c.Assert(err, IsNil)
  1057  	c.Check(ds.Brand, Equals, "my-brand")
  1058  	c.Check(ds.Model, Equals, "my-model")
  1059  
  1060  	c.Check(model.BrandID(), Equals, "my-brand")
  1061  	c.Check(model.Model(), Equals, "my-model")
  1062  }
  1063  
  1064  func (s *FirstBootTestSuite) TestImportAssertionsFromSeedMissingSig(c *C) {
  1065  	st := s.overlord.State()
  1066  	st.Lock()
  1067  	defer st.Unlock()
  1068  
  1069  	// write out only the model assertion
  1070  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1071  	for _, as := range assertsChain {
  1072  		if as.Type() == asserts.ModelType {
  1073  			s.WriteAssertions("model", as)
  1074  			break
  1075  		}
  1076  	}
  1077  
  1078  	deviceSeed, err := seed.Open(dirs.SnapSeedDir)
  1079  	c.Assert(err, IsNil)
  1080  
  1081  	// try import and verify that its rejects because other assertions are
  1082  	// missing
  1083  	_, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
  1084  	c.Assert(err, ErrorMatches, "cannot resolve prerequisite assertion: account-key .*")
  1085  }
  1086  
  1087  func (s *FirstBootTestSuite) TestImportAssertionsFromSeedTwoModelAsserts(c *C) {
  1088  	st := s.overlord.State()
  1089  	st.Lock()
  1090  	defer st.Unlock()
  1091  
  1092  	// write out two model assertions
  1093  	model := s.Brands.Model("my-brand", "my-model", s.modelHeaders("my-model"))
  1094  	s.WriteAssertions("model", model)
  1095  
  1096  	model2 := s.Brands.Model("my-brand", "my-second-model", s.modelHeaders("my-second-model"))
  1097  	s.WriteAssertions("model2", model2)
  1098  
  1099  	deviceSeed, err := seed.Open(dirs.SnapSeedDir)
  1100  	c.Assert(err, IsNil)
  1101  
  1102  	// try import and verify that its rejects because other assertions are
  1103  	// missing
  1104  	_, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
  1105  	c.Assert(err, ErrorMatches, "cannot have multiple model assertions in seed")
  1106  }
  1107  
  1108  func (s *FirstBootTestSuite) TestImportAssertionsFromSeedNoModelAsserts(c *C) {
  1109  	st := s.overlord.State()
  1110  	st.Lock()
  1111  	defer st.Unlock()
  1112  
  1113  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1114  	for i, as := range assertsChain {
  1115  		if as.Type() != asserts.ModelType {
  1116  			s.WriteAssertions(strconv.Itoa(i), as)
  1117  		}
  1118  	}
  1119  
  1120  	deviceSeed, err := seed.Open(dirs.SnapSeedDir)
  1121  	c.Assert(err, IsNil)
  1122  
  1123  	// try import and verify that its rejects because other assertions are
  1124  	// missing
  1125  	_, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
  1126  	c.Assert(err, ErrorMatches, "seed must have a model assertion")
  1127  }
  1128  
  1129  type core18SnapsOpts struct {
  1130  	classic bool
  1131  	gadget  bool
  1132  }
  1133  
  1134  func (s *FirstBootTestSuite) makeCore18Snaps(c *C, opts *core18SnapsOpts) (core18Fn, snapdFn, kernelFn, gadgetFn string) {
  1135  	if opts == nil {
  1136  		opts = &core18SnapsOpts{}
  1137  	}
  1138  
  1139  	files := [][]string{}
  1140  
  1141  	core18Yaml := `name: core18
  1142  version: 1.0
  1143  type: base`
  1144  	core18Fname, core18Decl, core18Rev := s.MakeAssertedSnap(c, core18Yaml, files, snap.R(1), "canonical")
  1145  	s.WriteAssertions("core18.asserts", core18Rev, core18Decl)
  1146  
  1147  	snapdYaml := `name: snapd
  1148  version: 1.0
  1149  `
  1150  	snapdFname, snapdDecl, snapdRev := s.MakeAssertedSnap(c, snapdYaml, nil, snap.R(2), "canonical")
  1151  	s.WriteAssertions("snapd.asserts", snapdRev, snapdDecl)
  1152  
  1153  	var kernelFname string
  1154  	if !opts.classic {
  1155  		kernelYaml := `name: pc-kernel
  1156  version: 1.0
  1157  type: kernel`
  1158  		fname, kernelDecl, kernelRev := s.MakeAssertedSnap(c, kernelYaml, files, snap.R(1), "canonical")
  1159  		s.WriteAssertions("kernel.asserts", kernelRev, kernelDecl)
  1160  		kernelFname = fname
  1161  	}
  1162  
  1163  	if !opts.classic {
  1164  		gadgetYaml := `
  1165  volumes:
  1166      volume-id:
  1167          bootloader: grub
  1168  `
  1169  		files = append(files, []string{"meta/gadget.yaml", gadgetYaml})
  1170  	}
  1171  
  1172  	var gadgetFname string
  1173  	if !opts.classic || opts.gadget {
  1174  		gaYaml := `name: pc
  1175  version: 1.0
  1176  type: gadget
  1177  base: core18
  1178  `
  1179  		fname, gadgetDecl, gadgetRev := s.MakeAssertedSnap(c, gaYaml, files, snap.R(1), "canonical")
  1180  		s.WriteAssertions("gadget.asserts", gadgetRev, gadgetDecl)
  1181  		gadgetFname = fname
  1182  	}
  1183  
  1184  	return core18Fname, snapdFname, kernelFname, gadgetFname
  1185  }
  1186  
  1187  func (s *FirstBootTestSuite) TestPopulateFromSeedWithBaseHappy(c *C) {
  1188  	var sysdLog [][]string
  1189  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1190  		sysdLog = append(sysdLog, cmd)
  1191  		return []byte("ActiveState=inactive\n"), nil
  1192  	})
  1193  	defer systemctlRestorer()
  1194  
  1195  	bloader := bootloadertest.Mock("mock", c.MkDir())
  1196  	bootloader.Force(bloader)
  1197  	defer bootloader.Force(nil)
  1198  	bloader.SetBootKernel("pc-kernel_1.snap")
  1199  	bloader.SetBootBase("core18_1.snap")
  1200  
  1201  	core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil)
  1202  
  1203  	s.WriteAssertions("developer.account", s.devAcct)
  1204  
  1205  	// add a model assertion and its chain
  1206  	assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"})
  1207  	s.WriteAssertions("model.asserts", assertsChain...)
  1208  
  1209  	// create a seed.yaml
  1210  	content := []byte(fmt.Sprintf(`
  1211  snaps:
  1212   - name: snapd
  1213     file: %s
  1214   - name: core18
  1215     file: %s
  1216   - name: pc-kernel
  1217     file: %s
  1218   - name: pc
  1219     file: %s
  1220  `, snapdFname, core18Fname, kernelFname, gadgetFname))
  1221  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1222  	c.Assert(err, IsNil)
  1223  
  1224  	// run the firstboot stuff
  1225  	st := s.overlord.State()
  1226  	st.Lock()
  1227  	defer st.Unlock()
  1228  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
  1229  	c.Assert(err, IsNil)
  1230  
  1231  	checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc")
  1232  
  1233  	// now run the change and check the result
  1234  	// use the expected kind otherwise settle with start another one
  1235  	chg := st.NewChange("seed", "run the populate from seed changes")
  1236  	for _, ts := range tsAll {
  1237  		chg.AddAll(ts)
  1238  	}
  1239  	c.Assert(st.Changes(), HasLen, 1)
  1240  
  1241  	c.Assert(chg.Err(), IsNil)
  1242  
  1243  	// avoid device reg
  1244  	chg1 := st.NewChange("become-operational", "init device")
  1245  	chg1.SetStatus(state.DoingStatus)
  1246  
  1247  	// run change until it wants to restart
  1248  	st.Unlock()
  1249  	err = s.overlord.Settle(settleTimeout)
  1250  	st.Lock()
  1251  	c.Assert(err, IsNil)
  1252  
  1253  	// at this point the system is "restarting", pretend the restart has
  1254  	// happened
  1255  	c.Assert(chg.Status(), Equals, state.DoingStatus)
  1256  	state.MockRestarting(st, state.RestartUnset)
  1257  	st.Unlock()
  1258  	err = s.overlord.Settle(settleTimeout)
  1259  	st.Lock()
  1260  	c.Assert(err, IsNil)
  1261  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1262  
  1263  	// verify
  1264  	r, err := os.Open(dirs.SnapStateFile)
  1265  	c.Assert(err, IsNil)
  1266  	state, err := state.ReadState(nil, r)
  1267  	c.Assert(err, IsNil)
  1268  
  1269  	state.Lock()
  1270  	defer state.Unlock()
  1271  	// check snapd, core18, kernel, gadget
  1272  	_, err = snapstate.CurrentInfo(state, "snapd")
  1273  	c.Check(err, IsNil)
  1274  	_, err = snapstate.CurrentInfo(state, "core18")
  1275  	c.Check(err, IsNil)
  1276  	_, err = snapstate.CurrentInfo(state, "pc-kernel")
  1277  	c.Check(err, IsNil)
  1278  	_, err = snapstate.CurrentInfo(state, "pc")
  1279  	c.Check(err, IsNil)
  1280  
  1281  	// ensure required flag is set on all essential snaps
  1282  	var snapst snapstate.SnapState
  1283  	for _, reqName := range []string{"snapd", "core18", "pc-kernel", "pc"} {
  1284  		err = snapstate.Get(state, reqName, &snapst)
  1285  		c.Assert(err, IsNil)
  1286  		c.Assert(snapst.Required, Equals, true, Commentf("required not set for %v", reqName))
  1287  	}
  1288  
  1289  	// the right systemd commands were run
  1290  	c.Check(sysdLog, testutil.DeepContains, []string{"start", "usr-lib-snapd.mount"})
  1291  
  1292  	// and ensure state is now considered seeded
  1293  	var seeded bool
  1294  	err = state.Get("seeded", &seeded)
  1295  	c.Assert(err, IsNil)
  1296  	c.Check(seeded, Equals, true)
  1297  
  1298  	// check we set seed-time
  1299  	var seedTime time.Time
  1300  	err = state.Get("seed-time", &seedTime)
  1301  	c.Assert(err, IsNil)
  1302  	c.Check(seedTime.IsZero(), Equals, false)
  1303  }
  1304  
  1305  func (s *FirstBootTestSuite) TestPopulateFromSeedOrdering(c *C) {
  1306  	s.WriteAssertions("developer.account", s.devAcct)
  1307  
  1308  	// add a model assertion and its chain
  1309  	assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"})
  1310  	s.WriteAssertions("model.asserts", assertsChain...)
  1311  
  1312  	core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil)
  1313  
  1314  	snapYaml := `name: snap-req-other-base
  1315  version: 1.0
  1316  base: other-base
  1317  `
  1318  	snapFname, snapDecl, snapRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1319  	s.WriteAssertions("snap-req-other-base.asserts", s.devAcct, snapRev, snapDecl)
  1320  	baseYaml := `name: other-base
  1321  version: 1.0
  1322  type: base
  1323  `
  1324  	baseFname, baseDecl, baseRev := s.MakeAssertedSnap(c, baseYaml, nil, snap.R(127), "developerid")
  1325  	s.WriteAssertions("other-base.asserts", s.devAcct, baseRev, baseDecl)
  1326  
  1327  	// create a seed.yaml
  1328  	content := []byte(fmt.Sprintf(`
  1329  snaps:
  1330   - name: snapd
  1331     file: %s
  1332   - name: core18
  1333     file: %s
  1334   - name: pc-kernel
  1335     file: %s
  1336   - name: pc
  1337     file: %s
  1338   - name: snap-req-other-base
  1339     file: %s
  1340   - name: other-base
  1341     file: %s
  1342  `, snapdFname, core18Fname, kernelFname, gadgetFname, snapFname, baseFname))
  1343  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1344  	c.Assert(err, IsNil)
  1345  
  1346  	// run the firstboot stuff
  1347  	st := s.overlord.State()
  1348  	st.Lock()
  1349  	defer st.Unlock()
  1350  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
  1351  	c.Assert(err, IsNil)
  1352  
  1353  	checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc", "other-base", "snap-req-other-base")
  1354  }
  1355  
  1356  func (s *FirstBootTestSuite) TestFirstbootGadgetBaseModelBaseMismatch(c *C) {
  1357  	s.WriteAssertions("developer.account", s.devAcct)
  1358  
  1359  	// add a model assertion and its chain
  1360  	assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"})
  1361  	s.WriteAssertions("model.asserts", assertsChain...)
  1362  
  1363  	core18Fname, snapdFname, kernelFname, _ := s.makeCore18Snaps(c, nil)
  1364  	// take the gadget without "base: core18"
  1365  	_, _, gadgetFname := s.makeCoreSnaps(c, "")
  1366  
  1367  	// create a seed.yaml
  1368  	content := []byte(fmt.Sprintf(`
  1369  snaps:
  1370   - name: snapd
  1371     file: %s
  1372   - name: core18
  1373     file: %s
  1374   - name: pc-kernel
  1375     file: %s
  1376   - name: pc
  1377     file: %s
  1378  `, snapdFname, core18Fname, kernelFname, gadgetFname))
  1379  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1380  	c.Assert(err, IsNil)
  1381  
  1382  	// run the firstboot stuff
  1383  	st := s.overlord.State()
  1384  	st.Lock()
  1385  	defer st.Unlock()
  1386  
  1387  	_, err = devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
  1388  	c.Assert(err, ErrorMatches, `cannot use gadget snap because its base "core" is different from model base "core18"`)
  1389  }
  1390  
  1391  func (s *FirstBootTestSuite) TestPopulateFromSeedWrongContentProviderOrder(c *C) {
  1392  	bloader := bootloadertest.Mock("mock", c.MkDir())
  1393  	bootloader.Force(bloader)
  1394  	defer bootloader.Force(nil)
  1395  	bloader.SetBootKernel("pc-kernel_1.snap")
  1396  	bloader.SetBootBase("core_1.snap")
  1397  
  1398  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "")
  1399  
  1400  	// a snap that uses content providers
  1401  	snapYaml := `name: gnome-calculator
  1402  version: 1.0
  1403  plugs:
  1404   gtk-3-themes:
  1405    interface: content
  1406    default-provider: gtk-common-themes
  1407    target: $SNAP/data-dir/themes
  1408  `
  1409  	calcFname, calcDecl, calcRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1410  	s.WriteAssertions("calc.asserts", s.devAcct, calcRev, calcDecl)
  1411  
  1412  	// put a 2nd firstboot snap into the SnapBlobDir
  1413  	snapYaml = `name: gtk-common-themes
  1414  version: 1.0
  1415  slots:
  1416   gtk-3-themes:
  1417    interface: content
  1418    source:
  1419     read:
  1420      - $SNAP/share/themes/Adawaita
  1421  `
  1422  	themesFname, themesDecl, themesRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(65), "developerid")
  1423  	s.WriteAssertions("themes.asserts", s.devAcct, themesDecl, themesRev)
  1424  
  1425  	// add a model assertion and its chain
  1426  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1427  	s.WriteAssertions("model.asserts", assertsChain...)
  1428  
  1429  	// create a seed.yaml
  1430  	content := []byte(fmt.Sprintf(`
  1431  snaps:
  1432   - name: core
  1433     file: %s
  1434   - name: pc-kernel
  1435     file: %s
  1436   - name: pc
  1437     file: %s
  1438   - name: gnome-calculator
  1439     file: %s
  1440   - name: gtk-common-themes
  1441     file: %s
  1442  `, coreFname, kernelFname, gadgetFname, calcFname, themesFname))
  1443  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1444  	c.Assert(err, IsNil)
  1445  
  1446  	// run the firstboot stuff
  1447  	st := s.overlord.State()
  1448  	st.Lock()
  1449  	defer st.Unlock()
  1450  
  1451  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
  1452  	c.Assert(err, IsNil)
  1453  	// use the expected kind otherwise settle with start another one
  1454  	chg := st.NewChange("seed", "run the populate from seed changes")
  1455  	for _, ts := range tsAll {
  1456  		chg.AddAll(ts)
  1457  	}
  1458  	c.Assert(st.Changes(), HasLen, 1)
  1459  
  1460  	// avoid device reg
  1461  	chg1 := st.NewChange("become-operational", "init device")
  1462  	chg1.SetStatus(state.DoingStatus)
  1463  
  1464  	st.Unlock()
  1465  	err = s.overlord.Settle(settleTimeout)
  1466  	st.Lock()
  1467  
  1468  	c.Assert(chg.Err(), IsNil)
  1469  	c.Assert(err, IsNil)
  1470  
  1471  	// verify the result
  1472  	var conns map[string]interface{}
  1473  	err = st.Get("conns", &conns)
  1474  	c.Assert(err, IsNil)
  1475  	c.Check(conns, HasLen, 1)
  1476  	conn, hasConn := conns["gnome-calculator:gtk-3-themes gtk-common-themes:gtk-3-themes"]
  1477  	c.Check(hasConn, Equals, true)
  1478  	c.Check(conn.(map[string]interface{})["auto"], Equals, true)
  1479  	c.Check(conn.(map[string]interface{})["interface"], Equals, "content")
  1480  }
  1481  
  1482  func (s *FirstBootTestSuite) TestPopulateFromSeedMissingBase(c *C) {
  1483  	s.WriteAssertions("developer.account", s.devAcct)
  1484  
  1485  	// add a model assertion and its chain
  1486  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1487  	s.WriteAssertions("model.asserts", assertsChain...)
  1488  
  1489  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "")
  1490  
  1491  	// TODO: this test doesn't particularly need to use a local snap
  1492  	// local snap with unknown base
  1493  	snapYaml = `name: local
  1494  base: foo
  1495  version: 1.0`
  1496  	mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml, nil)
  1497  	localFname := filepath.Base(mockSnapFile)
  1498  	targetSnapFile2 := filepath.Join(dirs.SnapSeedDir, "snaps", localFname)
  1499  	c.Assert(os.Rename(mockSnapFile, targetSnapFile2), IsNil)
  1500  
  1501  	// create a seed.yaml
  1502  	content := []byte(fmt.Sprintf(`
  1503  snaps:
  1504   - name: core
  1505     file: %s
  1506   - name: pc-kernel
  1507     file: %s
  1508   - name: pc
  1509     file: %s
  1510   - name: local
  1511     unasserted: true
  1512     file: %s
  1513  `, coreFname, kernelFname, gadgetFname, localFname))
  1514  
  1515  	c.Assert(ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644), IsNil)
  1516  
  1517  	// run the firstboot stuff
  1518  	st := s.overlord.State()
  1519  	st.Lock()
  1520  	defer st.Unlock()
  1521  	_, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
  1522  	c.Assert(err, ErrorMatches, `cannot use snap "local": base "foo" is missing`)
  1523  }
  1524  
  1525  func (s *FirstBootTestSuite) TestPopulateFromSeedOnClassicWithSnapdOnlyHappy(c *C) {
  1526  	restore := release.MockOnClassic(true)
  1527  	defer restore()
  1528  
  1529  	var sysdLog [][]string
  1530  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1531  		sysdLog = append(sysdLog, cmd)
  1532  		return []byte("ActiveState=inactive\n"), nil
  1533  	})
  1534  	defer systemctlRestorer()
  1535  
  1536  	core18Fname, snapdFname, _, _ := s.makeCore18Snaps(c, &core18SnapsOpts{
  1537  		classic: true,
  1538  	})
  1539  
  1540  	// put a firstboot snap into the SnapBlobDir
  1541  	snapYaml := `name: foo
  1542  version: 1.0
  1543  base: core18
  1544  `
  1545  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1546  	s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl)
  1547  
  1548  	// add a model assertion and its chain
  1549  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil)
  1550  	s.WriteAssertions("model.asserts", assertsChain...)
  1551  
  1552  	// create a seed.yaml
  1553  	content := []byte(fmt.Sprintf(`
  1554  snaps:
  1555   - name: snapd
  1556     file: %s
  1557   - name: foo
  1558     file: %s
  1559   - name: core18
  1560     file: %s
  1561  `, snapdFname, fooFname, core18Fname))
  1562  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1563  	c.Assert(err, IsNil)
  1564  
  1565  	// run the firstboot stuff
  1566  	st := s.overlord.State()
  1567  	st.Lock()
  1568  	defer st.Unlock()
  1569  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
  1570  	c.Assert(err, IsNil)
  1571  
  1572  	checkOrder(c, tsAll, "snapd", "core18", "foo")
  1573  
  1574  	// now run the change and check the result
  1575  	// use the expected kind otherwise settle with start another one
  1576  	chg := st.NewChange("seed", "run the populate from seed changes")
  1577  	for _, ts := range tsAll {
  1578  		chg.AddAll(ts)
  1579  	}
  1580  	c.Assert(st.Changes(), HasLen, 1)
  1581  
  1582  	c.Assert(chg.Err(), IsNil)
  1583  
  1584  	// avoid device reg
  1585  	chg1 := st.NewChange("become-operational", "init device")
  1586  	chg1.SetStatus(state.DoingStatus)
  1587  
  1588  	// run change until it wants to restart
  1589  	st.Unlock()
  1590  	err = s.overlord.Settle(settleTimeout)
  1591  	st.Lock()
  1592  	c.Assert(err, IsNil)
  1593  
  1594  	// at this point the system is "restarting", pretend the restart has
  1595  	// happened
  1596  	c.Assert(chg.Status(), Equals, state.DoingStatus)
  1597  	state.MockRestarting(st, state.RestartUnset)
  1598  	st.Unlock()
  1599  	err = s.overlord.Settle(settleTimeout)
  1600  	st.Lock()
  1601  	c.Assert(err, IsNil)
  1602  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1603  
  1604  	// verify
  1605  	r, err := os.Open(dirs.SnapStateFile)
  1606  	c.Assert(err, IsNil)
  1607  	state, err := state.ReadState(nil, r)
  1608  	c.Assert(err, IsNil)
  1609  
  1610  	state.Lock()
  1611  	defer state.Unlock()
  1612  	// check snapd, core18, kernel, gadget
  1613  	_, err = snapstate.CurrentInfo(state, "snapd")
  1614  	c.Check(err, IsNil)
  1615  	_, err = snapstate.CurrentInfo(state, "core18")
  1616  	c.Check(err, IsNil)
  1617  	_, err = snapstate.CurrentInfo(state, "foo")
  1618  	c.Check(err, IsNil)
  1619  
  1620  	// and ensure state is now considered seeded
  1621  	var seeded bool
  1622  	err = state.Get("seeded", &seeded)
  1623  	c.Assert(err, IsNil)
  1624  	c.Check(seeded, Equals, true)
  1625  
  1626  	// check we set seed-time
  1627  	var seedTime time.Time
  1628  	err = state.Get("seed-time", &seedTime)
  1629  	c.Assert(err, IsNil)
  1630  	c.Check(seedTime.IsZero(), Equals, false)
  1631  }
  1632  
  1633  func (s *FirstBootTestSuite) TestPopulateFromSeedOnClassicWithSnapdOnlyAndGadgetHappy(c *C) {
  1634  	restore := release.MockOnClassic(true)
  1635  	defer restore()
  1636  
  1637  	var sysdLog [][]string
  1638  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1639  		sysdLog = append(sysdLog, cmd)
  1640  		return []byte("ActiveState=inactive\n"), nil
  1641  	})
  1642  	defer systemctlRestorer()
  1643  
  1644  	core18Fname, snapdFname, _, gadgetFname := s.makeCore18Snaps(c, &core18SnapsOpts{
  1645  		classic: true,
  1646  		gadget:  true,
  1647  	})
  1648  
  1649  	// put a firstboot snap into the SnapBlobDir
  1650  	snapYaml := `name: foo
  1651  version: 1.0
  1652  base: core18
  1653  `
  1654  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1655  
  1656  	s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl)
  1657  
  1658  	// add a model assertion and its chain
  1659  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", map[string]interface{}{"gadget": "pc"})
  1660  	s.WriteAssertions("model.asserts", assertsChain...)
  1661  
  1662  	// create a seed.yaml
  1663  	content := []byte(fmt.Sprintf(`
  1664  snaps:
  1665   - name: snapd
  1666     file: %s
  1667   - name: foo
  1668     file: %s
  1669   - name: core18
  1670     file: %s
  1671   - name: pc
  1672     file: %s
  1673  `, snapdFname, fooFname, core18Fname, gadgetFname))
  1674  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1675  	c.Assert(err, IsNil)
  1676  
  1677  	// run the firstboot stuff
  1678  	st := s.overlord.State()
  1679  	st.Lock()
  1680  	defer st.Unlock()
  1681  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, s.perfTimings)
  1682  	c.Assert(err, IsNil)
  1683  
  1684  	checkOrder(c, tsAll, "snapd", "core18", "pc", "foo")
  1685  
  1686  	// now run the change and check the result
  1687  	// use the expected kind otherwise settle with start another one
  1688  	chg := st.NewChange("seed", "run the populate from seed changes")
  1689  	for _, ts := range tsAll {
  1690  		chg.AddAll(ts)
  1691  	}
  1692  	c.Assert(st.Changes(), HasLen, 1)
  1693  
  1694  	c.Assert(chg.Err(), IsNil)
  1695  
  1696  	// avoid device reg
  1697  	chg1 := st.NewChange("become-operational", "init device")
  1698  	chg1.SetStatus(state.DoingStatus)
  1699  
  1700  	// run change until it wants to restart
  1701  	st.Unlock()
  1702  	err = s.overlord.Settle(settleTimeout)
  1703  	st.Lock()
  1704  	c.Assert(err, IsNil)
  1705  
  1706  	// at this point the system is "restarting", pretend the restart has
  1707  	// happened
  1708  	c.Assert(chg.Status(), Equals, state.DoingStatus)
  1709  	state.MockRestarting(st, state.RestartUnset)
  1710  	st.Unlock()
  1711  	err = s.overlord.Settle(settleTimeout)
  1712  	st.Lock()
  1713  	c.Assert(err, IsNil)
  1714  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%s", chg.Err()))
  1715  
  1716  	// verify
  1717  	r, err := os.Open(dirs.SnapStateFile)
  1718  	c.Assert(err, IsNil)
  1719  	state, err := state.ReadState(nil, r)
  1720  	c.Assert(err, IsNil)
  1721  
  1722  	state.Lock()
  1723  	defer state.Unlock()
  1724  	// check snapd, core18, kernel, gadget
  1725  	_, err = snapstate.CurrentInfo(state, "snapd")
  1726  	c.Check(err, IsNil)
  1727  	_, err = snapstate.CurrentInfo(state, "core18")
  1728  	c.Check(err, IsNil)
  1729  	_, err = snapstate.CurrentInfo(state, "pc")
  1730  	c.Check(err, IsNil)
  1731  	_, err = snapstate.CurrentInfo(state, "foo")
  1732  	c.Check(err, IsNil)
  1733  
  1734  	// and ensure state is now considered seeded
  1735  	var seeded bool
  1736  	err = state.Get("seeded", &seeded)
  1737  	c.Assert(err, IsNil)
  1738  	c.Check(seeded, Equals, true)
  1739  
  1740  	// check we set seed-time
  1741  	var seedTime time.Time
  1742  	err = state.Get("seed-time", &seedTime)
  1743  	c.Assert(err, IsNil)
  1744  	c.Check(seedTime.IsZero(), Equals, false)
  1745  }