github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/devicestate/firstboot_test.go (about)

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