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