github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/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  	// now run the change and check the result
   518  	// use the expected kind otherwise settle with start another one
   519  	chg := st.NewChange("seed", "run the populate from seed changes")
   520  	for _, ts := range tsAll {
   521  		chg.AddAll(ts)
   522  	}
   523  	c.Assert(st.Changes(), HasLen, 1)
   524  
   525  	checkOrder(c, tsAll, "core", "pc-kernel", "pc", "foo", "local")
   526  	checkTasks(c, tsAll)
   527  
   528  	// avoid device reg
   529  	chg1 := st.NewChange("become-operational", "init device")
   530  	chg1.SetStatus(state.DoingStatus)
   531  
   532  	return chg, model
   533  }
   534  
   535  func (s *firstBoot16Suite) TestPopulateFromSeedHappy(c *C) {
   536  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
   537  	bootloader.Force(bloader)
   538  	defer bootloader.Force(nil)
   539  	bloader.SetBootKernel("pc-kernel_1.snap")
   540  	bloader.SetBootBase("core_1.snap")
   541  
   542  	st := s.overlord.State()
   543  	chg, model := s.makeSeedChange(c, st, nil, checkSeedTasks, checkOrder)
   544  	err := s.overlord.Settle(settleTimeout)
   545  	c.Assert(err, IsNil)
   546  
   547  	st.Lock()
   548  	defer st.Unlock()
   549  
   550  	c.Assert(chg.Err(), IsNil)
   551  
   552  	// and check the snap got correctly installed
   553  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true)
   554  
   555  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "local", "x1", "meta", "snap.yaml")), Equals, true)
   556  
   557  	// verify
   558  	r, err := os.Open(dirs.SnapStateFile)
   559  	c.Assert(err, IsNil)
   560  	state, err := state.ReadState(nil, r)
   561  	c.Assert(err, IsNil)
   562  
   563  	state.Lock()
   564  	defer state.Unlock()
   565  	// check core, kernel, gadget
   566  	_, err = snapstate.CurrentInfo(state, "core")
   567  	c.Assert(err, IsNil)
   568  	_, err = snapstate.CurrentInfo(state, "pc-kernel")
   569  	c.Assert(err, IsNil)
   570  	_, err = snapstate.CurrentInfo(state, "pc")
   571  	c.Assert(err, IsNil)
   572  
   573  	// ensure required flag is set on all essential snaps
   574  	var snapst snapstate.SnapState
   575  	for _, reqName := range []string{"core", "pc-kernel", "pc"} {
   576  		err = snapstate.Get(state, reqName, &snapst)
   577  		c.Assert(err, IsNil)
   578  		c.Assert(snapst.Required, Equals, true, Commentf("required not set for %v", reqName))
   579  	}
   580  
   581  	// check foo
   582  	info, err := snapstate.CurrentInfo(state, "foo")
   583  	c.Assert(err, IsNil)
   584  	c.Assert(info.SnapID, Equals, "foodidididididididididididididid")
   585  	c.Assert(info.Revision, Equals, snap.R(128))
   586  	c.Assert(info.Contact, Equals, "mailto:some.guy@example.com")
   587  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
   588  	c.Assert(err, IsNil)
   589  	c.Check(pubAcct.AccountID(), Equals, "developerid")
   590  
   591  	err = snapstate.Get(state, "foo", &snapst)
   592  	c.Assert(err, IsNil)
   593  	c.Assert(snapst.DevMode, Equals, true)
   594  	c.Assert(snapst.Required, Equals, true)
   595  
   596  	// check local
   597  	info, err = snapstate.CurrentInfo(state, "local")
   598  	c.Assert(err, IsNil)
   599  	c.Assert(info.SnapID, Equals, "")
   600  	c.Assert(info.Revision, Equals, snap.R("x1"))
   601  
   602  	var snapst2 snapstate.SnapState
   603  	err = snapstate.Get(state, "local", &snapst2)
   604  	c.Assert(err, IsNil)
   605  	c.Assert(snapst2.Required, Equals, false)
   606  
   607  	// and ensure state is now considered seeded
   608  	var seeded bool
   609  	err = state.Get("seeded", &seeded)
   610  	c.Assert(err, IsNil)
   611  	c.Check(seeded, Equals, true)
   612  
   613  	// check we set seed-time
   614  	var seedTime time.Time
   615  	err = state.Get("seed-time", &seedTime)
   616  	c.Assert(err, IsNil)
   617  	c.Check(seedTime.IsZero(), Equals, false)
   618  
   619  	var whatseeded []devicestate.SeededSystem
   620  	err = state.Get("seeded-systems", &whatseeded)
   621  	c.Assert(err, IsNil)
   622  	c.Assert(whatseeded, DeepEquals, []devicestate.SeededSystem{{
   623  		System:    "",
   624  		Model:     "my-model",
   625  		BrandID:   "my-brand",
   626  		Revision:  model.Revision(),
   627  		Timestamp: model.Timestamp(),
   628  		SeedTime:  seedTime,
   629  	}})
   630  }
   631  
   632  func (s *firstBoot16Suite) TestPopulateFromSeedMissingBootloader(c *C) {
   633  	st0 := s.overlord.State()
   634  	st0.Lock()
   635  	db := assertstate.DB(st0)
   636  	st0.Unlock()
   637  
   638  	// we run only with the relevant managers to produce the error
   639  	// situation
   640  	o := overlord.Mock()
   641  	st := o.State()
   642  	snapmgr, err := snapstate.Manager(st, o.TaskRunner())
   643  	c.Assert(err, IsNil)
   644  	o.AddManager(snapmgr)
   645  
   646  	ifacemgr, err := ifacestate.Manager(st, nil, o.TaskRunner(), nil, nil)
   647  	c.Assert(err, IsNil)
   648  	o.AddManager(ifacemgr)
   649  	c.Assert(o.StartUp(), IsNil)
   650  
   651  	hookMgr, err := hookstate.Manager(st, o.TaskRunner())
   652  	c.Assert(err, IsNil)
   653  	_, err = devicestate.Manager(st, hookMgr, o.TaskRunner(), nil)
   654  	c.Assert(err, IsNil)
   655  
   656  	st.Lock()
   657  	assertstate.ReplaceDB(st, db.(*asserts.Database))
   658  	st.Unlock()
   659  
   660  	o.AddManager(o.TaskRunner())
   661  
   662  	chg, _ := s.makeSeedChange(c, st, nil, checkSeedTasks, checkOrder)
   663  
   664  	se := o.StateEngine()
   665  	// we cannot use Settle because the Change will not become Clean
   666  	// under the subset of managers
   667  	for i := 0; i < 25 && !chg.IsReady(); i++ {
   668  		se.Ensure()
   669  		se.Wait()
   670  	}
   671  
   672  	st.Lock()
   673  	defer st.Unlock()
   674  	c.Assert(chg.Err(), ErrorMatches, `(?s).* cannot determine bootloader.*`)
   675  }
   676  
   677  func (s *firstBoot16Suite) TestPopulateFromSeedHappyMultiAssertsFiles(c *C) {
   678  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
   679  	bootloader.Force(bloader)
   680  	defer bootloader.Force(nil)
   681  	bloader.SetBootKernel("pc-kernel_1.snap")
   682  	bloader.SetBootBase("core_1.snap")
   683  
   684  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "")
   685  
   686  	// put a firstboot snap into the SnapBlobDir
   687  	snapYaml := `name: foo
   688  version: 1.0`
   689  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
   690  	s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl)
   691  
   692  	// put a 2nd firstboot snap into the SnapBlobDir
   693  	snapYaml = `name: bar
   694  version: 1.0`
   695  	barFname, barDecl, barRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(65), "developerid")
   696  	s.WriteAssertions("bar.asserts", s.devAcct, barDecl, barRev)
   697  
   698  	// add a model assertion and its chain
   699  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
   700  	s.WriteAssertions("model.asserts", assertsChain...)
   701  
   702  	// create a seed.yaml
   703  	content := []byte(fmt.Sprintf(`
   704  snaps:
   705   - name: core
   706     file: %s
   707   - name: pc-kernel
   708     file: %s
   709   - name: pc
   710     file: %s
   711   - name: foo
   712     file: %s
   713   - name: bar
   714     file: %s
   715  `, coreFname, kernelFname, gadgetFname, fooFname, barFname))
   716  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
   717  	c.Assert(err, IsNil)
   718  
   719  	// run the firstboot stuff
   720  	st := s.overlord.State()
   721  	st.Lock()
   722  	defer st.Unlock()
   723  
   724  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
   725  	c.Assert(err, IsNil)
   726  	// use the expected kind otherwise settle with start another one
   727  	chg := st.NewChange("seed", "run the populate from seed changes")
   728  	for _, ts := range tsAll {
   729  		chg.AddAll(ts)
   730  	}
   731  	c.Assert(st.Changes(), HasLen, 1)
   732  
   733  	// avoid device reg
   734  	chg1 := st.NewChange("become-operational", "init device")
   735  	chg1.SetStatus(state.DoingStatus)
   736  
   737  	st.Unlock()
   738  	err = s.overlord.Settle(settleTimeout)
   739  	st.Lock()
   740  	c.Assert(chg.Err(), IsNil)
   741  	c.Assert(err, IsNil)
   742  
   743  	// and check the snap got correctly installed
   744  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true)
   745  
   746  	// and check the snap got correctly installed
   747  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "bar", "65", "meta", "snap.yaml")), Equals, true)
   748  
   749  	// verify
   750  	r, err := os.Open(dirs.SnapStateFile)
   751  	c.Assert(err, IsNil)
   752  	state, err := state.ReadState(nil, r)
   753  	c.Assert(err, IsNil)
   754  
   755  	state.Lock()
   756  	defer state.Unlock()
   757  	// check foo
   758  	info, err := snapstate.CurrentInfo(state, "foo")
   759  	c.Assert(err, IsNil)
   760  	c.Check(info.SnapID, Equals, "foodidididididididididididididid")
   761  	c.Check(info.Revision, Equals, snap.R(128))
   762  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
   763  	c.Assert(err, IsNil)
   764  	c.Check(pubAcct.AccountID(), Equals, "developerid")
   765  
   766  	// check bar
   767  	info, err = snapstate.CurrentInfo(state, "bar")
   768  	c.Assert(err, IsNil)
   769  	c.Check(info.SnapID, Equals, "bardidididididididididididididid")
   770  	c.Check(info.Revision, Equals, snap.R(65))
   771  	pubAcct, err = assertstate.Publisher(st, info.SnapID)
   772  	c.Assert(err, IsNil)
   773  	c.Check(pubAcct.AccountID(), Equals, "developerid")
   774  }
   775  
   776  func (s *firstBoot16Suite) TestPopulateFromSeedConfigureHappy(c *C) {
   777  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
   778  	bootloader.Force(bloader)
   779  	defer bootloader.Force(nil)
   780  	bloader.SetBootKernel("pc-kernel_1.snap")
   781  	bloader.SetBootBase("core_1.snap")
   782  
   783  	const defaultsYaml = `
   784  defaults:
   785      foodidididididididididididididid:
   786         foo-cfg: foo.
   787      99T7MUlRhtI3U0QFgl5mXXESAiSwt776:  # core
   788         core-cfg: core_cfg_defl
   789      pckernelidididididididididididid:
   790         pc-kernel-cfg: pc-kernel_cfg_defl
   791      pcididididididididididididididid:
   792         pc-cfg: pc_cfg_defl
   793  `
   794  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, defaultsYaml)
   795  
   796  	s.WriteAssertions("developer.account", s.devAcct)
   797  
   798  	// put a firstboot snap into the SnapBlobDir
   799  	files := [][]string{{"meta/hooks/configure", ""}}
   800  	snapYaml := `name: foo
   801  version: 1.0`
   802  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, files, snap.R(128), "developerid")
   803  	s.WriteAssertions("foo.asserts", fooDecl, fooRev)
   804  
   805  	// add a model assertion and its chain
   806  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo")
   807  	s.WriteAssertions("model.asserts", assertsChain...)
   808  
   809  	// create a seed.yaml
   810  	content := []byte(fmt.Sprintf(`
   811  snaps:
   812   - name: core
   813     file: %s
   814   - name: pc-kernel
   815     file: %s
   816   - name: pc
   817     file: %s
   818   - name: foo
   819     file: %s
   820  `, coreFname, kernelFname, gadgetFname, fooFname))
   821  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
   822  	c.Assert(err, IsNil)
   823  
   824  	// run the firstboot stuff
   825  	st := s.overlord.State()
   826  	st.Lock()
   827  	defer st.Unlock()
   828  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
   829  	c.Assert(err, IsNil)
   830  
   831  	checkSeedTasks(c, tsAll)
   832  
   833  	// now run the change and check the result
   834  	// use the expected kind otherwise settle with start another one
   835  	chg := st.NewChange("seed", "run the populate from seed changes")
   836  	for _, ts := range tsAll {
   837  		chg.AddAll(ts)
   838  	}
   839  	c.Assert(st.Changes(), HasLen, 1)
   840  
   841  	var configured []string
   842  	hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) {
   843  		ctx.Lock()
   844  		defer ctx.Unlock()
   845  		// we have a gadget at this point(s)
   846  		ok, err := snapstate.HasSnapOfType(st, snap.TypeGadget)
   847  		c.Check(err, IsNil)
   848  		c.Check(ok, Equals, true)
   849  		configured = append(configured, ctx.InstanceName())
   850  		return nil, nil
   851  	}
   852  
   853  	rhk := hookstate.MockRunHook(hookInvoke)
   854  	defer rhk()
   855  
   856  	// ensure we have something that captures the core config
   857  	restore := configstate.MockConfigcoreRun(func(config.Conf) error {
   858  		configured = append(configured, "configcore")
   859  		return nil
   860  	})
   861  	defer restore()
   862  
   863  	// avoid device reg
   864  	chg1 := st.NewChange("become-operational", "init device")
   865  	chg1.SetStatus(state.DoingStatus)
   866  
   867  	st.Unlock()
   868  	err = s.overlord.Settle(settleTimeout)
   869  	st.Lock()
   870  	c.Assert(chg.Err(), IsNil)
   871  	c.Assert(err, IsNil)
   872  
   873  	// and check the snap got correctly installed
   874  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true)
   875  
   876  	// verify
   877  	r, err := os.Open(dirs.SnapStateFile)
   878  	c.Assert(err, IsNil)
   879  	state, err := state.ReadState(nil, r)
   880  	c.Assert(err, IsNil)
   881  
   882  	state.Lock()
   883  	defer state.Unlock()
   884  	tr := config.NewTransaction(state)
   885  	var val string
   886  
   887  	// check core, kernel, gadget
   888  	_, err = snapstate.CurrentInfo(state, "core")
   889  	c.Assert(err, IsNil)
   890  	err = tr.Get("core", "core-cfg", &val)
   891  	c.Assert(err, IsNil)
   892  	c.Check(val, Equals, "core_cfg_defl")
   893  
   894  	_, err = snapstate.CurrentInfo(state, "pc-kernel")
   895  	c.Assert(err, IsNil)
   896  	err = tr.Get("pc-kernel", "pc-kernel-cfg", &val)
   897  	c.Assert(err, IsNil)
   898  	c.Check(val, Equals, "pc-kernel_cfg_defl")
   899  
   900  	_, err = snapstate.CurrentInfo(state, "pc")
   901  	c.Assert(err, IsNil)
   902  	err = tr.Get("pc", "pc-cfg", &val)
   903  	c.Assert(err, IsNil)
   904  	c.Check(val, Equals, "pc_cfg_defl")
   905  
   906  	// check foo
   907  	info, err := snapstate.CurrentInfo(state, "foo")
   908  	c.Assert(err, IsNil)
   909  	c.Assert(info.SnapID, Equals, "foodidididididididididididididid")
   910  	c.Assert(info.Revision, Equals, snap.R(128))
   911  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
   912  	c.Assert(err, IsNil)
   913  	c.Check(pubAcct.AccountID(), Equals, "developerid")
   914  
   915  	// check foo config
   916  	err = tr.Get("foo", "foo-cfg", &val)
   917  	c.Assert(err, IsNil)
   918  	c.Check(val, Equals, "foo.")
   919  
   920  	c.Check(configured, DeepEquals, []string{"configcore", "pc-kernel", "pc", "foo"})
   921  
   922  	// and ensure state is now considered seeded
   923  	var seeded bool
   924  	err = state.Get("seeded", &seeded)
   925  	c.Assert(err, IsNil)
   926  	c.Check(seeded, Equals, true)
   927  }
   928  
   929  func (s *firstBoot16Suite) TestPopulateFromSeedGadgetConnectHappy(c *C) {
   930  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
   931  	bootloader.Force(bloader)
   932  	defer bootloader.Force(nil)
   933  	bloader.SetBootKernel("pc-kernel_1.snap")
   934  	bloader.SetBootBase("core_1.snap")
   935  
   936  	const connectionsYaml = `
   937  connections:
   938    - plug: foodidididididididididididididid:network-control
   939  `
   940  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, connectionsYaml)
   941  
   942  	s.WriteAssertions("developer.account", s.devAcct)
   943  
   944  	snapYaml := `name: foo
   945  version: 1.0
   946  plugs:
   947    network-control:
   948  `
   949  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
   950  	s.WriteAssertions("foo.asserts", fooDecl, fooRev)
   951  
   952  	// add a model assertion and its chain
   953  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo")
   954  	s.WriteAssertions("model.asserts", assertsChain...)
   955  
   956  	// create a seed.yaml
   957  	content := []byte(fmt.Sprintf(`
   958  snaps:
   959   - name: core
   960     file: %s
   961   - name: pc-kernel
   962     file: %s
   963   - name: pc
   964     file: %s
   965   - name: foo
   966     file: %s
   967  `, coreFname, kernelFname, gadgetFname, fooFname))
   968  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
   969  	c.Assert(err, IsNil)
   970  
   971  	// run the firstboot stuff
   972  	st := s.overlord.State()
   973  	st.Lock()
   974  	defer st.Unlock()
   975  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
   976  	c.Assert(err, IsNil)
   977  
   978  	checkSeedTasks(c, tsAll)
   979  
   980  	// now run the change and check the result
   981  	// use the expected kind otherwise settle with start another one
   982  	chg := st.NewChange("seed", "run the populate from seed changes")
   983  	for _, ts := range tsAll {
   984  		chg.AddAll(ts)
   985  	}
   986  	c.Assert(st.Changes(), HasLen, 1)
   987  
   988  	// avoid device reg
   989  	chg1 := st.NewChange("become-operational", "init device")
   990  	chg1.SetStatus(state.DoingStatus)
   991  
   992  	st.Unlock()
   993  	err = s.overlord.Settle(settleTimeout)
   994  	st.Lock()
   995  	c.Assert(chg.Err(), IsNil)
   996  	c.Assert(err, IsNil)
   997  
   998  	// and check the snap got correctly installed
   999  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true)
  1000  
  1001  	// verify
  1002  	r, err := os.Open(dirs.SnapStateFile)
  1003  	c.Assert(err, IsNil)
  1004  	state, err := state.ReadState(nil, r)
  1005  	c.Assert(err, IsNil)
  1006  
  1007  	state.Lock()
  1008  	defer state.Unlock()
  1009  
  1010  	// check foo
  1011  	info, err := snapstate.CurrentInfo(state, "foo")
  1012  	c.Assert(err, IsNil)
  1013  	c.Assert(info.SnapID, Equals, "foodidididididididididididididid")
  1014  	c.Assert(info.Revision, Equals, snap.R(128))
  1015  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
  1016  	c.Assert(err, IsNil)
  1017  	c.Check(pubAcct.AccountID(), Equals, "developerid")
  1018  
  1019  	// check connection
  1020  	var conns map[string]interface{}
  1021  	err = state.Get("conns", &conns)
  1022  	c.Assert(err, IsNil)
  1023  	c.Check(conns, HasLen, 1)
  1024  	c.Check(conns, DeepEquals, map[string]interface{}{
  1025  		"foo:network-control core:network-control": map[string]interface{}{
  1026  			"interface": "network-control", "auto": true, "by-gadget": true,
  1027  		},
  1028  	})
  1029  
  1030  	// and ensure state is now considered seeded
  1031  	var seeded bool
  1032  	err = state.Get("seeded", &seeded)
  1033  	c.Assert(err, IsNil)
  1034  	c.Check(seeded, Equals, true)
  1035  }
  1036  
  1037  func (s *firstBoot16Suite) TestImportAssertionsFromSeedClassicModelMismatch(c *C) {
  1038  	restore := release.MockOnClassic(true)
  1039  	defer restore()
  1040  
  1041  	ovld, err := overlord.New(nil)
  1042  	c.Assert(err, IsNil)
  1043  	st := ovld.State()
  1044  
  1045  	// add the odel assertion and its chain
  1046  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1047  	s.WriteAssertions("model.asserts", assertsChain...)
  1048  
  1049  	// import them
  1050  	st.Lock()
  1051  	defer st.Unlock()
  1052  
  1053  	deviceSeed, err := seed.Open(dirs.SnapSeedDir, "")
  1054  	c.Assert(err, IsNil)
  1055  
  1056  	_, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
  1057  	c.Assert(err, ErrorMatches, "cannot seed a classic system with an all-snaps model")
  1058  }
  1059  
  1060  func (s *firstBoot16Suite) TestImportAssertionsFromSeedAllSnapsModelMismatch(c *C) {
  1061  	ovld, err := overlord.New(nil)
  1062  	c.Assert(err, IsNil)
  1063  	st := ovld.State()
  1064  
  1065  	// add the model assertion and its chain
  1066  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil)
  1067  	s.WriteAssertions("model.asserts", assertsChain...)
  1068  
  1069  	// import them
  1070  	st.Lock()
  1071  	defer st.Unlock()
  1072  
  1073  	deviceSeed, err := seed.Open(dirs.SnapSeedDir, "")
  1074  	c.Assert(err, IsNil)
  1075  
  1076  	_, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
  1077  	c.Assert(err, ErrorMatches, "cannot seed an all-snaps system with a classic model")
  1078  }
  1079  
  1080  func (s *firstBoot16Suite) TestImportAssertionsFromSeedHappy(c *C) {
  1081  	ovld, err := overlord.New(nil)
  1082  	c.Assert(err, IsNil)
  1083  	st := ovld.State()
  1084  
  1085  	// add a bunch of assertions (model assertion and its chain)
  1086  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1087  	for i, as := range assertsChain {
  1088  		fname := strconv.Itoa(i)
  1089  		if as.Type() == asserts.ModelType {
  1090  			fname = "model"
  1091  		}
  1092  		s.WriteAssertions(fname, as)
  1093  	}
  1094  
  1095  	// import them
  1096  	st.Lock()
  1097  	defer st.Unlock()
  1098  
  1099  	deviceSeed, err := seed.Open(dirs.SnapSeedDir, "")
  1100  	c.Assert(err, IsNil)
  1101  
  1102  	model, err := devicestate.ImportAssertionsFromSeed(st, deviceSeed)
  1103  	c.Assert(err, IsNil)
  1104  	c.Assert(model, NotNil)
  1105  
  1106  	// verify that the model was added
  1107  	db := assertstate.DB(st)
  1108  	as, err := db.Find(asserts.ModelType, map[string]string{
  1109  		"series":   "16",
  1110  		"brand-id": "my-brand",
  1111  		"model":    "my-model",
  1112  	})
  1113  	c.Assert(err, IsNil)
  1114  	_, ok := as.(*asserts.Model)
  1115  	c.Check(ok, Equals, true)
  1116  
  1117  	ds, err := devicestatetest.Device(st)
  1118  	c.Assert(err, IsNil)
  1119  	c.Check(ds.Brand, Equals, "my-brand")
  1120  	c.Check(ds.Model, Equals, "my-model")
  1121  
  1122  	c.Check(model.BrandID(), Equals, "my-brand")
  1123  	c.Check(model.Model(), Equals, "my-model")
  1124  }
  1125  
  1126  func (s *firstBoot16Suite) TestImportAssertionsFromSeedMissingSig(c *C) {
  1127  	st := s.overlord.State()
  1128  	st.Lock()
  1129  	defer st.Unlock()
  1130  
  1131  	// write out only the model assertion
  1132  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1133  	for _, as := range assertsChain {
  1134  		if as.Type() == asserts.ModelType {
  1135  			s.WriteAssertions("model", as)
  1136  			break
  1137  		}
  1138  	}
  1139  
  1140  	deviceSeed, err := seed.Open(dirs.SnapSeedDir, "")
  1141  	c.Assert(err, IsNil)
  1142  
  1143  	// try import and verify that its rejects because other assertions are
  1144  	// missing
  1145  	_, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
  1146  	c.Assert(err, ErrorMatches, "cannot resolve prerequisite assertion: account-key .*")
  1147  }
  1148  
  1149  func (s *firstBoot16Suite) TestImportAssertionsFromSeedTwoModelAsserts(c *C) {
  1150  	st := s.overlord.State()
  1151  	st.Lock()
  1152  	defer st.Unlock()
  1153  
  1154  	// write out two model assertions
  1155  	model := s.Brands.Model("my-brand", "my-model", modelHeaders("my-model"))
  1156  	s.WriteAssertions("model", model)
  1157  
  1158  	model2 := s.Brands.Model("my-brand", "my-second-model", modelHeaders("my-second-model"))
  1159  	s.WriteAssertions("model2", model2)
  1160  
  1161  	deviceSeed, err := seed.Open(dirs.SnapSeedDir, "")
  1162  	c.Assert(err, IsNil)
  1163  
  1164  	// try import and verify that its rejects because other assertions are
  1165  	// missing
  1166  	_, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
  1167  	c.Assert(err, ErrorMatches, "cannot have multiple model assertions in seed")
  1168  }
  1169  
  1170  func (s *firstBoot16Suite) TestImportAssertionsFromSeedNoModelAsserts(c *C) {
  1171  	st := s.overlord.State()
  1172  	st.Lock()
  1173  	defer st.Unlock()
  1174  
  1175  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1176  	for i, as := range assertsChain {
  1177  		if as.Type() != asserts.ModelType {
  1178  			s.WriteAssertions(strconv.Itoa(i), as)
  1179  		}
  1180  	}
  1181  
  1182  	deviceSeed, err := seed.Open(dirs.SnapSeedDir, "")
  1183  	c.Assert(err, IsNil)
  1184  
  1185  	// try import and verify that its rejects because other assertions are
  1186  	// missing
  1187  	_, err = devicestate.ImportAssertionsFromSeed(st, deviceSeed)
  1188  	c.Assert(err, ErrorMatches, "seed must have a model assertion")
  1189  }
  1190  
  1191  type core18SnapsOpts struct {
  1192  	classic bool
  1193  	gadget  bool
  1194  }
  1195  
  1196  func (s *firstBoot16BaseTest) makeCore18Snaps(c *C, opts *core18SnapsOpts) (core18Fn, snapdFn, kernelFn, gadgetFn string) {
  1197  	if opts == nil {
  1198  		opts = &core18SnapsOpts{}
  1199  	}
  1200  
  1201  	files := [][]string{}
  1202  
  1203  	core18Yaml := `name: core18
  1204  version: 1.0
  1205  type: base`
  1206  	core18Fname, core18Decl, core18Rev := s.MakeAssertedSnap(c, core18Yaml, files, snap.R(1), "canonical")
  1207  	s.WriteAssertions("core18.asserts", core18Rev, core18Decl)
  1208  
  1209  	snapdYaml := `name: snapd
  1210  version: 1.0
  1211  `
  1212  	snapdFname, snapdDecl, snapdRev := s.MakeAssertedSnap(c, snapdYaml, nil, snap.R(2), "canonical")
  1213  	s.WriteAssertions("snapd.asserts", snapdRev, snapdDecl)
  1214  
  1215  	var kernelFname string
  1216  	if !opts.classic {
  1217  		kernelYaml := `name: pc-kernel
  1218  version: 1.0
  1219  type: kernel`
  1220  		fname, kernelDecl, kernelRev := s.MakeAssertedSnap(c, kernelYaml, files, snap.R(1), "canonical")
  1221  		s.WriteAssertions("kernel.asserts", kernelRev, kernelDecl)
  1222  		kernelFname = fname
  1223  	}
  1224  
  1225  	if !opts.classic {
  1226  		gadgetYaml := `
  1227  volumes:
  1228      volume-id:
  1229          bootloader: grub
  1230  `
  1231  		files = append(files, []string{"meta/gadget.yaml", gadgetYaml})
  1232  	}
  1233  
  1234  	var gadgetFname string
  1235  	if !opts.classic || opts.gadget {
  1236  		gaYaml := `name: pc
  1237  version: 1.0
  1238  type: gadget
  1239  base: core18
  1240  `
  1241  		fname, gadgetDecl, gadgetRev := s.MakeAssertedSnap(c, gaYaml, files, snap.R(1), "canonical")
  1242  		s.WriteAssertions("gadget.asserts", gadgetRev, gadgetDecl)
  1243  		gadgetFname = fname
  1244  	}
  1245  
  1246  	return core18Fname, snapdFname, kernelFname, gadgetFname
  1247  }
  1248  
  1249  func (s *firstBoot16Suite) TestPopulateFromSeedWithBaseHappy(c *C) {
  1250  	var sysdLog [][]string
  1251  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1252  		sysdLog = append(sysdLog, cmd)
  1253  		return []byte("ActiveState=inactive\n"), nil
  1254  	})
  1255  	defer systemctlRestorer()
  1256  
  1257  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  1258  	bootloader.Force(bloader)
  1259  	defer bootloader.Force(nil)
  1260  	bloader.SetBootKernel("pc-kernel_1.snap")
  1261  	bloader.SetBootBase("core18_1.snap")
  1262  
  1263  	core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil)
  1264  
  1265  	s.WriteAssertions("developer.account", s.devAcct)
  1266  
  1267  	// add a model assertion and its chain
  1268  	assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"})
  1269  	s.WriteAssertions("model.asserts", assertsChain...)
  1270  
  1271  	// create a seed.yaml
  1272  	content := []byte(fmt.Sprintf(`
  1273  snaps:
  1274   - name: snapd
  1275     file: %s
  1276   - name: core18
  1277     file: %s
  1278   - name: pc-kernel
  1279     file: %s
  1280   - name: pc
  1281     file: %s
  1282  `, snapdFname, core18Fname, kernelFname, gadgetFname))
  1283  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1284  	c.Assert(err, IsNil)
  1285  
  1286  	// run the firstboot stuff
  1287  	st := s.overlord.State()
  1288  	st.Lock()
  1289  	defer st.Unlock()
  1290  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1291  	c.Assert(err, IsNil)
  1292  
  1293  	checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc")
  1294  
  1295  	// now run the change and check the result
  1296  	// use the expected kind otherwise settle with start another one
  1297  	chg := st.NewChange("seed", "run the populate from seed changes")
  1298  	for _, ts := range tsAll {
  1299  		chg.AddAll(ts)
  1300  	}
  1301  	c.Assert(st.Changes(), HasLen, 1)
  1302  
  1303  	c.Assert(chg.Err(), IsNil)
  1304  
  1305  	// avoid device reg
  1306  	chg1 := st.NewChange("become-operational", "init device")
  1307  	chg1.SetStatus(state.DoingStatus)
  1308  
  1309  	// run change until it wants to restart
  1310  	st.Unlock()
  1311  	err = s.overlord.Settle(settleTimeout)
  1312  	st.Lock()
  1313  	c.Assert(err, IsNil)
  1314  
  1315  	// at this point the system is "restarting", pretend the restart has
  1316  	// happened
  1317  	c.Assert(chg.Status(), Equals, state.DoingStatus)
  1318  	state.MockRestarting(st, state.RestartUnset)
  1319  	st.Unlock()
  1320  	err = s.overlord.Settle(settleTimeout)
  1321  	st.Lock()
  1322  	c.Assert(err, IsNil)
  1323  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1324  
  1325  	// verify
  1326  	r, err := os.Open(dirs.SnapStateFile)
  1327  	c.Assert(err, IsNil)
  1328  	state, err := state.ReadState(nil, r)
  1329  	c.Assert(err, IsNil)
  1330  
  1331  	state.Lock()
  1332  	defer state.Unlock()
  1333  	// check snapd, core18, kernel, gadget
  1334  	_, err = snapstate.CurrentInfo(state, "snapd")
  1335  	c.Check(err, IsNil)
  1336  	_, err = snapstate.CurrentInfo(state, "core18")
  1337  	c.Check(err, IsNil)
  1338  	_, err = snapstate.CurrentInfo(state, "pc-kernel")
  1339  	c.Check(err, IsNil)
  1340  	_, err = snapstate.CurrentInfo(state, "pc")
  1341  	c.Check(err, IsNil)
  1342  
  1343  	// ensure required flag is set on all essential snaps
  1344  	var snapst snapstate.SnapState
  1345  	for _, reqName := range []string{"snapd", "core18", "pc-kernel", "pc"} {
  1346  		err = snapstate.Get(state, reqName, &snapst)
  1347  		c.Assert(err, IsNil)
  1348  		c.Assert(snapst.Required, Equals, true, Commentf("required not set for %v", reqName))
  1349  	}
  1350  
  1351  	// the right systemd commands were run
  1352  	c.Check(sysdLog, testutil.DeepContains, []string{"start", "usr-lib-snapd.mount"})
  1353  
  1354  	// and ensure state is now considered seeded
  1355  	var seeded bool
  1356  	err = state.Get("seeded", &seeded)
  1357  	c.Assert(err, IsNil)
  1358  	c.Check(seeded, Equals, true)
  1359  
  1360  	// check we set seed-time
  1361  	var seedTime time.Time
  1362  	err = state.Get("seed-time", &seedTime)
  1363  	c.Assert(err, IsNil)
  1364  	c.Check(seedTime.IsZero(), Equals, false)
  1365  }
  1366  
  1367  func (s *firstBoot16Suite) TestPopulateFromSeedOrdering(c *C) {
  1368  	s.WriteAssertions("developer.account", s.devAcct)
  1369  
  1370  	// add a model assertion and its chain
  1371  	assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"})
  1372  	s.WriteAssertions("model.asserts", assertsChain...)
  1373  
  1374  	core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil)
  1375  
  1376  	snapYaml := `name: snap-req-other-base
  1377  version: 1.0
  1378  base: other-base
  1379  `
  1380  	snapFname, snapDecl, snapRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1381  	s.WriteAssertions("snap-req-other-base.asserts", s.devAcct, snapRev, snapDecl)
  1382  	baseYaml := `name: other-base
  1383  version: 1.0
  1384  type: base
  1385  `
  1386  	baseFname, baseDecl, baseRev := s.MakeAssertedSnap(c, baseYaml, nil, snap.R(127), "developerid")
  1387  	s.WriteAssertions("other-base.asserts", s.devAcct, baseRev, baseDecl)
  1388  
  1389  	// create a seed.yaml
  1390  	content := []byte(fmt.Sprintf(`
  1391  snaps:
  1392   - name: snapd
  1393     file: %s
  1394   - name: core18
  1395     file: %s
  1396   - name: pc-kernel
  1397     file: %s
  1398   - name: pc
  1399     file: %s
  1400   - name: snap-req-other-base
  1401     file: %s
  1402   - name: other-base
  1403     file: %s
  1404  `, snapdFname, core18Fname, kernelFname, gadgetFname, snapFname, baseFname))
  1405  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1406  	c.Assert(err, IsNil)
  1407  
  1408  	// run the firstboot stuff
  1409  	st := s.overlord.State()
  1410  	st.Lock()
  1411  	defer st.Unlock()
  1412  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1413  	c.Assert(err, IsNil)
  1414  
  1415  	checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc", "other-base", "snap-req-other-base")
  1416  }
  1417  
  1418  func (s *firstBoot16Suite) TestFirstbootGadgetBaseModelBaseMismatch(c *C) {
  1419  	s.WriteAssertions("developer.account", s.devAcct)
  1420  
  1421  	// add a model assertion and its chain
  1422  	assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"})
  1423  	s.WriteAssertions("model.asserts", assertsChain...)
  1424  
  1425  	core18Fname, snapdFname, kernelFname, _ := s.makeCore18Snaps(c, nil)
  1426  	// take the gadget without "base: core18"
  1427  	_, _, gadgetFname := s.makeCoreSnaps(c, "")
  1428  
  1429  	// create a seed.yaml
  1430  	content := []byte(fmt.Sprintf(`
  1431  snaps:
  1432   - name: snapd
  1433     file: %s
  1434   - name: core18
  1435     file: %s
  1436   - name: pc-kernel
  1437     file: %s
  1438   - name: pc
  1439     file: %s
  1440  `, snapdFname, core18Fname, kernelFname, gadgetFname))
  1441  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1442  	c.Assert(err, IsNil)
  1443  
  1444  	// run the firstboot stuff
  1445  	st := s.overlord.State()
  1446  	st.Lock()
  1447  	defer st.Unlock()
  1448  
  1449  	_, err = devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1450  	c.Assert(err, ErrorMatches, `cannot use gadget snap because its base "core" is different from model base "core18"`)
  1451  	// note, cannot use st.Tasks() here as it filters out tasks with no change
  1452  	c.Check(st.TaskCount(), Equals, 0)
  1453  }
  1454  
  1455  func (s *firstBoot16Suite) TestPopulateFromSeedWrongContentProviderOrder(c *C) {
  1456  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  1457  	bootloader.Force(bloader)
  1458  	defer bootloader.Force(nil)
  1459  	bloader.SetBootKernel("pc-kernel_1.snap")
  1460  	bloader.SetBootBase("core_1.snap")
  1461  
  1462  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "")
  1463  
  1464  	// a snap that uses content providers
  1465  	snapYaml := `name: gnome-calculator
  1466  version: 1.0
  1467  plugs:
  1468   gtk-3-themes:
  1469    interface: content
  1470    default-provider: gtk-common-themes
  1471    target: $SNAP/data-dir/themes
  1472  `
  1473  	calcFname, calcDecl, calcRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1474  	s.WriteAssertions("calc.asserts", s.devAcct, calcRev, calcDecl)
  1475  
  1476  	// put a 2nd firstboot snap into the SnapBlobDir
  1477  	snapYaml = `name: gtk-common-themes
  1478  version: 1.0
  1479  slots:
  1480   gtk-3-themes:
  1481    interface: content
  1482    source:
  1483     read:
  1484      - $SNAP/share/themes/Adawaita
  1485  `
  1486  	themesFname, themesDecl, themesRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(65), "developerid")
  1487  	s.WriteAssertions("themes.asserts", s.devAcct, themesDecl, themesRev)
  1488  
  1489  	// add a model assertion and its chain
  1490  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1491  	s.WriteAssertions("model.asserts", assertsChain...)
  1492  
  1493  	// create a seed.yaml
  1494  	content := []byte(fmt.Sprintf(`
  1495  snaps:
  1496   - name: core
  1497     file: %s
  1498   - name: pc-kernel
  1499     file: %s
  1500   - name: pc
  1501     file: %s
  1502   - name: gnome-calculator
  1503     file: %s
  1504   - name: gtk-common-themes
  1505     file: %s
  1506  `, coreFname, kernelFname, gadgetFname, calcFname, themesFname))
  1507  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1508  	c.Assert(err, IsNil)
  1509  
  1510  	// run the firstboot stuff
  1511  	st := s.overlord.State()
  1512  	st.Lock()
  1513  	defer st.Unlock()
  1514  
  1515  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1516  	c.Assert(err, IsNil)
  1517  	// use the expected kind otherwise settle with start another one
  1518  	chg := st.NewChange("seed", "run the populate from seed changes")
  1519  	for _, ts := range tsAll {
  1520  		chg.AddAll(ts)
  1521  	}
  1522  	c.Assert(st.Changes(), HasLen, 1)
  1523  
  1524  	// avoid device reg
  1525  	chg1 := st.NewChange("become-operational", "init device")
  1526  	chg1.SetStatus(state.DoingStatus)
  1527  
  1528  	st.Unlock()
  1529  	err = s.overlord.Settle(settleTimeout)
  1530  	st.Lock()
  1531  
  1532  	c.Assert(chg.Err(), IsNil)
  1533  	c.Assert(err, IsNil)
  1534  
  1535  	// verify the result
  1536  	var conns map[string]interface{}
  1537  	err = st.Get("conns", &conns)
  1538  	c.Assert(err, IsNil)
  1539  	c.Check(conns, HasLen, 1)
  1540  	conn, hasConn := conns["gnome-calculator:gtk-3-themes gtk-common-themes:gtk-3-themes"]
  1541  	c.Check(hasConn, Equals, true)
  1542  	c.Check(conn.(map[string]interface{})["auto"], Equals, true)
  1543  	c.Check(conn.(map[string]interface{})["interface"], Equals, "content")
  1544  }
  1545  
  1546  func (s *firstBoot16Suite) TestPopulateFromSeedMissingBase(c *C) {
  1547  	s.WriteAssertions("developer.account", s.devAcct)
  1548  
  1549  	// add a model assertion and its chain
  1550  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1551  	s.WriteAssertions("model.asserts", assertsChain...)
  1552  
  1553  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "")
  1554  
  1555  	// TODO: this test doesn't particularly need to use a local snap
  1556  	// local snap with unknown base
  1557  	snapYaml = `name: local
  1558  base: foo
  1559  version: 1.0`
  1560  	mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml, nil)
  1561  	localFname := filepath.Base(mockSnapFile)
  1562  	targetSnapFile2 := filepath.Join(dirs.SnapSeedDir, "snaps", localFname)
  1563  	c.Assert(os.Rename(mockSnapFile, targetSnapFile2), IsNil)
  1564  
  1565  	// create a seed.yaml
  1566  	content := []byte(fmt.Sprintf(`
  1567  snaps:
  1568   - name: core
  1569     file: %s
  1570   - name: pc-kernel
  1571     file: %s
  1572   - name: pc
  1573     file: %s
  1574   - name: local
  1575     unasserted: true
  1576     file: %s
  1577  `, coreFname, kernelFname, gadgetFname, localFname))
  1578  
  1579  	c.Assert(ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644), IsNil)
  1580  
  1581  	// run the firstboot stuff
  1582  	st := s.overlord.State()
  1583  	st.Lock()
  1584  	defer st.Unlock()
  1585  	_, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1586  	c.Assert(err, ErrorMatches, `cannot use snap "local": base "foo" is missing`)
  1587  }
  1588  
  1589  func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicWithSnapdOnlyHappy(c *C) {
  1590  	restore := release.MockOnClassic(true)
  1591  	defer restore()
  1592  
  1593  	var sysdLog [][]string
  1594  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1595  		sysdLog = append(sysdLog, cmd)
  1596  		return []byte("ActiveState=inactive\n"), nil
  1597  	})
  1598  	defer systemctlRestorer()
  1599  
  1600  	core18Fname, snapdFname, _, _ := s.makeCore18Snaps(c, &core18SnapsOpts{
  1601  		classic: true,
  1602  	})
  1603  
  1604  	// put a firstboot snap into the SnapBlobDir
  1605  	snapYaml := `name: foo
  1606  version: 1.0
  1607  base: core18
  1608  `
  1609  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1610  	s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl)
  1611  
  1612  	// add a model assertion and its chain
  1613  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil)
  1614  	s.WriteAssertions("model.asserts", assertsChain...)
  1615  
  1616  	// create a seed.yaml
  1617  	content := []byte(fmt.Sprintf(`
  1618  snaps:
  1619   - name: snapd
  1620     file: %s
  1621   - name: foo
  1622     file: %s
  1623   - name: core18
  1624     file: %s
  1625  `, snapdFname, fooFname, core18Fname))
  1626  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1627  	c.Assert(err, IsNil)
  1628  
  1629  	// run the firstboot stuff
  1630  	st := s.overlord.State()
  1631  	st.Lock()
  1632  	defer st.Unlock()
  1633  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1634  	c.Assert(err, IsNil)
  1635  
  1636  	checkOrder(c, tsAll, "snapd", "core18", "foo")
  1637  
  1638  	// now run the change and check the result
  1639  	// use the expected kind otherwise settle with start another one
  1640  	chg := st.NewChange("seed", "run the populate from seed changes")
  1641  	for _, ts := range tsAll {
  1642  		chg.AddAll(ts)
  1643  	}
  1644  	c.Assert(st.Changes(), HasLen, 1)
  1645  
  1646  	c.Assert(chg.Err(), IsNil)
  1647  
  1648  	// avoid device reg
  1649  	chg1 := st.NewChange("become-operational", "init device")
  1650  	chg1.SetStatus(state.DoingStatus)
  1651  
  1652  	// run change until it wants to restart
  1653  	st.Unlock()
  1654  	err = s.overlord.Settle(settleTimeout)
  1655  	st.Lock()
  1656  	c.Assert(err, IsNil)
  1657  
  1658  	// at this point the system is "restarting", pretend the restart has
  1659  	// happened
  1660  	c.Assert(chg.Status(), Equals, state.DoingStatus)
  1661  	state.MockRestarting(st, state.RestartUnset)
  1662  	st.Unlock()
  1663  	err = s.overlord.Settle(settleTimeout)
  1664  	st.Lock()
  1665  	c.Assert(err, IsNil)
  1666  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1667  
  1668  	// verify
  1669  	r, err := os.Open(dirs.SnapStateFile)
  1670  	c.Assert(err, IsNil)
  1671  	state, err := state.ReadState(nil, r)
  1672  	c.Assert(err, IsNil)
  1673  
  1674  	state.Lock()
  1675  	defer state.Unlock()
  1676  	// check snapd, core18, kernel, gadget
  1677  	_, err = snapstate.CurrentInfo(state, "snapd")
  1678  	c.Check(err, IsNil)
  1679  	_, err = snapstate.CurrentInfo(state, "core18")
  1680  	c.Check(err, IsNil)
  1681  	_, err = snapstate.CurrentInfo(state, "foo")
  1682  	c.Check(err, IsNil)
  1683  
  1684  	// and ensure state is now considered seeded
  1685  	var seeded bool
  1686  	err = state.Get("seeded", &seeded)
  1687  	c.Assert(err, IsNil)
  1688  	c.Check(seeded, Equals, true)
  1689  
  1690  	// check we set seed-time
  1691  	var seedTime time.Time
  1692  	err = state.Get("seed-time", &seedTime)
  1693  	c.Assert(err, IsNil)
  1694  	c.Check(seedTime.IsZero(), Equals, false)
  1695  }
  1696  
  1697  func (s *firstBoot16Suite) TestPopulateFromSeedMissingAssertions(c *C) {
  1698  	restore := release.MockOnClassic(true)
  1699  	defer restore()
  1700  
  1701  	core18Fname, snapdFname, _, _ := s.makeCore18Snaps(c, &core18SnapsOpts{})
  1702  
  1703  	// create a seed.yaml
  1704  	content := []byte(fmt.Sprintf(`
  1705  snaps:
  1706   - name: snapd
  1707     file: %s
  1708   - name: core18
  1709     file: %s
  1710  `, snapdFname, core18Fname))
  1711  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1712  	c.Assert(err, IsNil)
  1713  
  1714  	// run the firstboot stuff
  1715  	st := s.overlord.State()
  1716  	st.Lock()
  1717  	defer st.Unlock()
  1718  	_, err = devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1719  	c.Assert(err, NotNil)
  1720  	// note, cannot use st.Tasks() here as it filters out tasks with no change
  1721  	c.Check(st.TaskCount(), Equals, 0)
  1722  }
  1723  
  1724  func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicWithSnapdOnlyAndGadgetHappy(c *C) {
  1725  	restore := release.MockOnClassic(true)
  1726  	defer restore()
  1727  
  1728  	var sysdLog [][]string
  1729  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1730  		sysdLog = append(sysdLog, cmd)
  1731  		return []byte("ActiveState=inactive\n"), nil
  1732  	})
  1733  	defer systemctlRestorer()
  1734  
  1735  	core18Fname, snapdFname, _, gadgetFname := s.makeCore18Snaps(c, &core18SnapsOpts{
  1736  		classic: true,
  1737  		gadget:  true,
  1738  	})
  1739  
  1740  	// put a firstboot snap into the SnapBlobDir
  1741  	snapYaml := `name: foo
  1742  version: 1.0
  1743  base: core18
  1744  `
  1745  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1746  
  1747  	s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl)
  1748  
  1749  	// add a model assertion and its chain
  1750  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", map[string]interface{}{"gadget": "pc"})
  1751  	s.WriteAssertions("model.asserts", assertsChain...)
  1752  
  1753  	// create a seed.yaml
  1754  	content := []byte(fmt.Sprintf(`
  1755  snaps:
  1756   - name: snapd
  1757     file: %s
  1758   - name: foo
  1759     file: %s
  1760   - name: core18
  1761     file: %s
  1762   - name: pc
  1763     file: %s
  1764  `, snapdFname, fooFname, core18Fname, gadgetFname))
  1765  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1766  	c.Assert(err, IsNil)
  1767  
  1768  	// run the firstboot stuff
  1769  	st := s.overlord.State()
  1770  	st.Lock()
  1771  	defer st.Unlock()
  1772  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1773  	c.Assert(err, IsNil)
  1774  
  1775  	checkOrder(c, tsAll, "snapd", "core18", "pc", "foo")
  1776  
  1777  	// now run the change and check the result
  1778  	// use the expected kind otherwise settle with start another one
  1779  	chg := st.NewChange("seed", "run the populate from seed changes")
  1780  	for _, ts := range tsAll {
  1781  		chg.AddAll(ts)
  1782  	}
  1783  	c.Assert(st.Changes(), HasLen, 1)
  1784  
  1785  	c.Assert(chg.Err(), IsNil)
  1786  
  1787  	// avoid device reg
  1788  	chg1 := st.NewChange("become-operational", "init device")
  1789  	chg1.SetStatus(state.DoingStatus)
  1790  
  1791  	// run change until it wants to restart
  1792  	st.Unlock()
  1793  	err = s.overlord.Settle(settleTimeout)
  1794  	st.Lock()
  1795  	c.Assert(err, IsNil)
  1796  
  1797  	// at this point the system is "restarting", pretend the restart has
  1798  	// happened
  1799  	c.Assert(chg.Status(), Equals, state.DoingStatus)
  1800  	state.MockRestarting(st, state.RestartUnset)
  1801  	st.Unlock()
  1802  	err = s.overlord.Settle(settleTimeout)
  1803  	st.Lock()
  1804  	c.Assert(err, IsNil)
  1805  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%s", chg.Err()))
  1806  
  1807  	// verify
  1808  	r, err := os.Open(dirs.SnapStateFile)
  1809  	c.Assert(err, IsNil)
  1810  	state, err := state.ReadState(nil, r)
  1811  	c.Assert(err, IsNil)
  1812  
  1813  	state.Lock()
  1814  	defer state.Unlock()
  1815  	// check snapd, core18, kernel, gadget
  1816  	_, err = snapstate.CurrentInfo(state, "snapd")
  1817  	c.Check(err, IsNil)
  1818  	_, err = snapstate.CurrentInfo(state, "core18")
  1819  	c.Check(err, IsNil)
  1820  	_, err = snapstate.CurrentInfo(state, "pc")
  1821  	c.Check(err, IsNil)
  1822  	_, err = snapstate.CurrentInfo(state, "foo")
  1823  	c.Check(err, IsNil)
  1824  
  1825  	// and ensure state is now considered seeded
  1826  	var seeded bool
  1827  	err = state.Get("seeded", &seeded)
  1828  	c.Assert(err, IsNil)
  1829  	c.Check(seeded, Equals, true)
  1830  
  1831  	// check we set seed-time
  1832  	var seedTime time.Time
  1833  	err = state.Get("seed-time", &seedTime)
  1834  	c.Assert(err, IsNil)
  1835  	c.Check(seedTime.IsZero(), Equals, false)
  1836  }
  1837  
  1838  func (s *firstBoot16Suite) TestCriticalTaskEdgesForPreseed(c *C) {
  1839  	st := s.overlord.State()
  1840  	st.Lock()
  1841  	defer st.Unlock()
  1842  
  1843  	t1 := st.NewTask("task1", "")
  1844  	t2 := st.NewTask("task2", "")
  1845  	t3 := st.NewTask("task2", "")
  1846  
  1847  	ts := state.NewTaskSet(t1, t2, t3)
  1848  	ts.MarkEdge(t1, snapstate.BeginEdge)
  1849  	ts.MarkEdge(t2, snapstate.BeforeHooksEdge)
  1850  	ts.MarkEdge(t3, snapstate.HooksEdge)
  1851  
  1852  	beginEdge, beforeHooksEdge, hooksEdge, err := devicestate.CriticalTaskEdges(ts)
  1853  	c.Assert(err, IsNil)
  1854  	c.Assert(beginEdge, NotNil)
  1855  	c.Assert(beforeHooksEdge, NotNil)
  1856  	c.Assert(hooksEdge, NotNil)
  1857  
  1858  	c.Check(beginEdge.Kind(), Equals, "task1")
  1859  	c.Check(beforeHooksEdge.Kind(), Equals, "task2")
  1860  	c.Check(hooksEdge.Kind(), Equals, "task2")
  1861  }
  1862  
  1863  func (s *firstBoot16Suite) TestCriticalTaskEdgesForPreseedMissing(c *C) {
  1864  	st := s.overlord.State()
  1865  	st.Lock()
  1866  	defer st.Unlock()
  1867  
  1868  	t1 := st.NewTask("task1", "")
  1869  	t2 := st.NewTask("task2", "")
  1870  	t3 := st.NewTask("task2", "")
  1871  
  1872  	ts := state.NewTaskSet(t1, t2, t3)
  1873  	ts.MarkEdge(t1, snapstate.BeginEdge)
  1874  
  1875  	_, _, _, err := devicestate.CriticalTaskEdges(ts)
  1876  	c.Assert(err, NotNil)
  1877  
  1878  	ts = state.NewTaskSet(t1, t2, t3)
  1879  	ts.MarkEdge(t1, snapstate.BeginEdge)
  1880  	ts.MarkEdge(t2, snapstate.BeforeHooksEdge)
  1881  	_, _, _, err = devicestate.CriticalTaskEdges(ts)
  1882  	c.Assert(err, NotNil)
  1883  
  1884  	ts = state.NewTaskSet(t1, t2, t3)
  1885  	ts.MarkEdge(t1, snapstate.BeginEdge)
  1886  	ts.MarkEdge(t3, snapstate.HooksEdge)
  1887  	_, _, _, err = devicestate.CriticalTaskEdges(ts)
  1888  	c.Assert(err, NotNil)
  1889  }