github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/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() (sysconfig.Device, *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  	dev, gi, err := devicestate.PreloadGadget(s.overlord.DeviceManager())
   842  	c.Assert(err, IsNil)
   843  	c.Check(gi.Defaults, HasLen, 4)
   844  	c.Check(dev.RunMode(), Equals, true)
   845  	c.Check(dev.Classic(), Equals, false)
   846  	c.Check(dev.HasModeenv(), Equals, false)
   847  	c.Check(dev.Kernel(), Equals, "pc-kernel")
   848  
   849  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
   850  	c.Assert(err, IsNil)
   851  
   852  	checkSeedTasks(c, tsAll)
   853  
   854  	// now run the change and check the result
   855  	// use the expected kind otherwise settle with start another one
   856  	chg := st.NewChange("seed", "run the populate from seed changes")
   857  	for _, ts := range tsAll {
   858  		chg.AddAll(ts)
   859  	}
   860  	c.Assert(st.Changes(), HasLen, 1)
   861  
   862  	var configured []string
   863  	hookInvoke := func(ctx *hookstate.Context, tomb *tomb.Tomb) ([]byte, error) {
   864  		ctx.Lock()
   865  		defer ctx.Unlock()
   866  		// we have a gadget at this point(s)
   867  		ok, err := snapstate.HasSnapOfType(st, snap.TypeGadget)
   868  		c.Check(err, IsNil)
   869  		c.Check(ok, Equals, true)
   870  		configured = append(configured, ctx.InstanceName())
   871  		return nil, nil
   872  	}
   873  
   874  	rhk := hookstate.MockRunHook(hookInvoke)
   875  	defer rhk()
   876  
   877  	// ensure we have something that captures the core config
   878  	restore := configstate.MockConfigcoreRun(func(sysconfig.Device, config.Conf) error {
   879  		configured = append(configured, "configcore")
   880  		return nil
   881  	})
   882  	defer restore()
   883  
   884  	// avoid device reg
   885  	chg1 := st.NewChange("become-operational", "init device")
   886  	chg1.SetStatus(state.DoingStatus)
   887  
   888  	st.Unlock()
   889  	err = s.overlord.Settle(settleTimeout)
   890  	st.Lock()
   891  	c.Assert(chg.Err(), IsNil)
   892  	c.Assert(err, IsNil)
   893  
   894  	// and check the snap got correctly installed
   895  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true)
   896  
   897  	// verify
   898  	r, err := os.Open(dirs.SnapStateFile)
   899  	c.Assert(err, IsNil)
   900  	state, err := state.ReadState(nil, r)
   901  	c.Assert(err, IsNil)
   902  
   903  	state.Lock()
   904  	defer state.Unlock()
   905  	tr := config.NewTransaction(state)
   906  	var val string
   907  
   908  	// check core, kernel, gadget
   909  	_, err = snapstate.CurrentInfo(state, "core")
   910  	c.Assert(err, IsNil)
   911  	err = tr.Get("core", "core-cfg", &val)
   912  	c.Assert(err, IsNil)
   913  	c.Check(val, Equals, "core_cfg_defl")
   914  
   915  	_, err = snapstate.CurrentInfo(state, "pc-kernel")
   916  	c.Assert(err, IsNil)
   917  	err = tr.Get("pc-kernel", "pc-kernel-cfg", &val)
   918  	c.Assert(err, IsNil)
   919  	c.Check(val, Equals, "pc-kernel_cfg_defl")
   920  
   921  	_, err = snapstate.CurrentInfo(state, "pc")
   922  	c.Assert(err, IsNil)
   923  	err = tr.Get("pc", "pc-cfg", &val)
   924  	c.Assert(err, IsNil)
   925  	c.Check(val, Equals, "pc_cfg_defl")
   926  
   927  	// check foo
   928  	info, err := snapstate.CurrentInfo(state, "foo")
   929  	c.Assert(err, IsNil)
   930  	c.Assert(info.SnapID, Equals, "foodidididididididididididididid")
   931  	c.Assert(info.Revision, Equals, snap.R(128))
   932  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
   933  	c.Assert(err, IsNil)
   934  	c.Check(pubAcct.AccountID(), Equals, "developerid")
   935  
   936  	// check foo config
   937  	err = tr.Get("foo", "foo-cfg", &val)
   938  	c.Assert(err, IsNil)
   939  	c.Check(val, Equals, "foo.")
   940  
   941  	c.Check(configured, DeepEquals, []string{"configcore", "pc-kernel", "pc", "foo"})
   942  
   943  	// and ensure state is now considered seeded
   944  	var seeded bool
   945  	err = state.Get("seeded", &seeded)
   946  	c.Assert(err, IsNil)
   947  	c.Check(seeded, Equals, true)
   948  }
   949  
   950  func (s *firstBoot16Suite) TestPopulateFromSeedGadgetConnectHappy(c *C) {
   951  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
   952  	bootloader.Force(bloader)
   953  	defer bootloader.Force(nil)
   954  	bloader.SetBootKernel("pc-kernel_1.snap")
   955  	bloader.SetBootBase("core_1.snap")
   956  
   957  	const connectionsYaml = `
   958  connections:
   959    - plug: foodidididididididididididididid:network-control
   960  `
   961  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, connectionsYaml)
   962  
   963  	s.WriteAssertions("developer.account", s.devAcct)
   964  
   965  	snapYaml := `name: foo
   966  version: 1.0
   967  plugs:
   968    network-control:
   969  `
   970  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
   971  	s.WriteAssertions("foo.asserts", fooDecl, fooRev)
   972  
   973  	// add a model assertion and its chain
   974  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil, "foo")
   975  	s.WriteAssertions("model.asserts", assertsChain...)
   976  
   977  	// create a seed.yaml
   978  	content := []byte(fmt.Sprintf(`
   979  snaps:
   980   - name: core
   981     file: %s
   982   - name: pc-kernel
   983     file: %s
   984   - name: pc
   985     file: %s
   986   - name: foo
   987     file: %s
   988  `, coreFname, kernelFname, gadgetFname, fooFname))
   989  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
   990  	c.Assert(err, IsNil)
   991  
   992  	// run the firstboot stuff
   993  	st := s.overlord.State()
   994  	st.Lock()
   995  	defer st.Unlock()
   996  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
   997  	c.Assert(err, IsNil)
   998  
   999  	checkSeedTasks(c, tsAll)
  1000  
  1001  	// now run the change and check the result
  1002  	// use the expected kind otherwise settle with start another one
  1003  	chg := st.NewChange("seed", "run the populate from seed changes")
  1004  	for _, ts := range tsAll {
  1005  		chg.AddAll(ts)
  1006  	}
  1007  	c.Assert(st.Changes(), HasLen, 1)
  1008  
  1009  	// avoid device reg
  1010  	chg1 := st.NewChange("become-operational", "init device")
  1011  	chg1.SetStatus(state.DoingStatus)
  1012  
  1013  	st.Unlock()
  1014  	err = s.overlord.Settle(settleTimeout)
  1015  	st.Lock()
  1016  	c.Assert(chg.Err(), IsNil)
  1017  	c.Assert(err, IsNil)
  1018  
  1019  	// and check the snap got correctly installed
  1020  	c.Check(osutil.FileExists(filepath.Join(dirs.SnapMountDir, "foo", "128", "meta", "snap.yaml")), Equals, true)
  1021  
  1022  	// verify
  1023  	r, err := os.Open(dirs.SnapStateFile)
  1024  	c.Assert(err, IsNil)
  1025  	state, err := state.ReadState(nil, r)
  1026  	c.Assert(err, IsNil)
  1027  
  1028  	state.Lock()
  1029  	defer state.Unlock()
  1030  
  1031  	// check foo
  1032  	info, err := snapstate.CurrentInfo(state, "foo")
  1033  	c.Assert(err, IsNil)
  1034  	c.Assert(info.SnapID, Equals, "foodidididididididididididididid")
  1035  	c.Assert(info.Revision, Equals, snap.R(128))
  1036  	pubAcct, err := assertstate.Publisher(st, info.SnapID)
  1037  	c.Assert(err, IsNil)
  1038  	c.Check(pubAcct.AccountID(), Equals, "developerid")
  1039  
  1040  	// check connection
  1041  	var conns map[string]interface{}
  1042  	err = state.Get("conns", &conns)
  1043  	c.Assert(err, IsNil)
  1044  	c.Check(conns, HasLen, 1)
  1045  	c.Check(conns, DeepEquals, map[string]interface{}{
  1046  		"foo:network-control core:network-control": map[string]interface{}{
  1047  			"interface": "network-control", "auto": true, "by-gadget": true,
  1048  		},
  1049  	})
  1050  
  1051  	// and ensure state is now considered seeded
  1052  	var seeded bool
  1053  	err = state.Get("seeded", &seeded)
  1054  	c.Assert(err, IsNil)
  1055  	c.Check(seeded, Equals, true)
  1056  }
  1057  
  1058  func (s *firstBoot16Suite) TestImportAssertionsFromSeedClassicModelMismatch(c *C) {
  1059  	restore := release.MockOnClassic(true)
  1060  	defer restore()
  1061  
  1062  	ovld, err := overlord.New(nil)
  1063  	c.Assert(err, IsNil)
  1064  	st := ovld.State()
  1065  
  1066  	// add the odel assertion and its chain
  1067  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1068  	s.WriteAssertions("model.asserts", assertsChain...)
  1069  
  1070  	// import them
  1071  	st.Lock()
  1072  	defer st.Unlock()
  1073  
  1074  	_, err = devicestate.ImportAssertionsFromSeed(st, "")
  1075  	c.Assert(err, ErrorMatches, "cannot seed a classic system with an all-snaps model")
  1076  }
  1077  
  1078  func (s *firstBoot16Suite) TestImportAssertionsFromSeedAllSnapsModelMismatch(c *C) {
  1079  	ovld, err := overlord.New(nil)
  1080  	c.Assert(err, IsNil)
  1081  	st := ovld.State()
  1082  
  1083  	// add the model assertion and its chain
  1084  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil)
  1085  	s.WriteAssertions("model.asserts", assertsChain...)
  1086  
  1087  	// import them
  1088  	st.Lock()
  1089  	defer st.Unlock()
  1090  
  1091  	_, err = devicestate.ImportAssertionsFromSeed(st, "")
  1092  	c.Assert(err, ErrorMatches, "cannot seed an all-snaps system with a classic model")
  1093  }
  1094  
  1095  func (s *firstBoot16Suite) TestLoadDeviceSeed(c *C) {
  1096  	ovld, err := overlord.New(nil)
  1097  	c.Assert(err, IsNil)
  1098  	st := ovld.State()
  1099  
  1100  	// add a bunch of assertions (model assertion and its chain)
  1101  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1102  	for i, as := range assertsChain {
  1103  		fname := strconv.Itoa(i)
  1104  		if as.Type() == asserts.ModelType {
  1105  			fname = "model"
  1106  		}
  1107  		s.WriteAssertions(fname, as)
  1108  	}
  1109  
  1110  	// load them
  1111  	st.Lock()
  1112  	defer st.Unlock()
  1113  
  1114  	deviceSeed, err := devicestate.LoadDeviceSeed(st, "")
  1115  	c.Assert(err, IsNil)
  1116  
  1117  	c.Check(deviceSeed.Model().BrandID(), Equals, "my-brand")
  1118  	c.Check(deviceSeed.Model().Model(), Equals, "my-model")
  1119  
  1120  	// verify that the model was added
  1121  	db := assertstate.DB(st)
  1122  	as, err := db.Find(asserts.ModelType, map[string]string{
  1123  		"series":   "16",
  1124  		"brand-id": "my-brand",
  1125  		"model":    "my-model",
  1126  	})
  1127  	c.Assert(err, IsNil)
  1128  	c.Check(as, DeepEquals, deviceSeed.Model())
  1129  }
  1130  
  1131  func (s *firstBoot16Suite) TestLoadDeviceSeedCaching(c *C) {
  1132  	ovld, err := overlord.New(nil)
  1133  	c.Assert(err, IsNil)
  1134  	st := ovld.State()
  1135  
  1136  	// add a bunch of assertions (model assertion and its chain)
  1137  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1138  	for i, as := range assertsChain {
  1139  		fname := strconv.Itoa(i)
  1140  		if as.Type() == asserts.ModelType {
  1141  			fname = "model"
  1142  		}
  1143  		s.WriteAssertions(fname, as)
  1144  	}
  1145  
  1146  	// load them
  1147  	st.Lock()
  1148  	defer st.Unlock()
  1149  
  1150  	deviceSeed, err := devicestate.LoadDeviceSeed(st, "")
  1151  	c.Assert(err, IsNil)
  1152  
  1153  	c.Check(deviceSeed.Model().Model(), Equals, "my-model")
  1154  
  1155  	// break the seed
  1156  	modelAway := filepath.Join(c.MkDir(), "model")
  1157  	modelFn := filepath.Join(s.AssertsDir(), "model")
  1158  	err = os.Rename(modelFn, modelAway)
  1159  	c.Assert(err, IsNil)
  1160  
  1161  	// result is still cached
  1162  	deviceSeed, err = devicestate.LoadDeviceSeed(st, "")
  1163  	c.Assert(err, IsNil)
  1164  
  1165  	c.Check(deviceSeed.Model().Model(), Equals, "my-model")
  1166  
  1167  	// unload cached result
  1168  	devicestate.UnloadDeviceSeed(st)
  1169  
  1170  	// error now
  1171  	_, err1 := devicestate.LoadDeviceSeed(st, "")
  1172  	c.Assert(err1, ErrorMatches, "seed must have a model assertion")
  1173  
  1174  	// refix the seed
  1175  	err = os.Rename(modelAway, modelFn)
  1176  	c.Assert(err, IsNil)
  1177  
  1178  	// error is also cached
  1179  	_, err = devicestate.LoadDeviceSeed(st, "")
  1180  	c.Assert(err, Equals, err1)
  1181  
  1182  	// unload cached error
  1183  	devicestate.UnloadDeviceSeed(st)
  1184  
  1185  	// soundness check: loading works again
  1186  	_, err = devicestate.LoadDeviceSeed(st, "")
  1187  	c.Assert(err, IsNil)
  1188  }
  1189  
  1190  func (s *firstBoot16Suite) TestImportAssertionsFromSeedHappy(c *C) {
  1191  	ovld, err := overlord.New(nil)
  1192  	c.Assert(err, IsNil)
  1193  	st := ovld.State()
  1194  
  1195  	// add a bunch of assertions (model assertion and its chain)
  1196  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1197  	for i, as := range assertsChain {
  1198  		fname := strconv.Itoa(i)
  1199  		if as.Type() == asserts.ModelType {
  1200  			fname = "model"
  1201  		}
  1202  		s.WriteAssertions(fname, as)
  1203  	}
  1204  
  1205  	// import them
  1206  	st.Lock()
  1207  	defer st.Unlock()
  1208  
  1209  	deviceSeed, err := devicestate.ImportAssertionsFromSeed(st, "")
  1210  	c.Assert(err, IsNil)
  1211  	c.Assert(deviceSeed, NotNil)
  1212  
  1213  	model := deviceSeed.Model()
  1214  
  1215  	// verify that the model was added
  1216  	db := assertstate.DB(st)
  1217  	as, err := db.Find(asserts.ModelType, map[string]string{
  1218  		"series":   "16",
  1219  		"brand-id": "my-brand",
  1220  		"model":    "my-model",
  1221  	})
  1222  	c.Assert(err, IsNil)
  1223  	_, ok := as.(*asserts.Model)
  1224  	c.Check(ok, Equals, true)
  1225  
  1226  	ds, err := devicestatetest.Device(st)
  1227  	c.Assert(err, IsNil)
  1228  	c.Check(ds.Brand, Equals, "my-brand")
  1229  	c.Check(ds.Model, Equals, "my-model")
  1230  
  1231  	c.Check(model.BrandID(), Equals, "my-brand")
  1232  	c.Check(model.Model(), Equals, "my-model")
  1233  }
  1234  
  1235  func (s *firstBoot16Suite) TestImportAssertionsFromSeedMissingSig(c *C) {
  1236  	st := s.overlord.State()
  1237  	st.Lock()
  1238  	defer st.Unlock()
  1239  
  1240  	// write out only the model assertion
  1241  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1242  	for _, as := range assertsChain {
  1243  		if as.Type() == asserts.ModelType {
  1244  			s.WriteAssertions("model", as)
  1245  			break
  1246  		}
  1247  	}
  1248  
  1249  	// try import and verify that its rejects because other assertions are
  1250  	// missing
  1251  	_, err := devicestate.ImportAssertionsFromSeed(st, "")
  1252  	c.Assert(err, ErrorMatches, "cannot resolve prerequisite assertion: account-key .*")
  1253  }
  1254  
  1255  func (s *firstBoot16Suite) TestImportAssertionsFromSeedTwoModelAsserts(c *C) {
  1256  	st := s.overlord.State()
  1257  	st.Lock()
  1258  	defer st.Unlock()
  1259  
  1260  	// write out two model assertions
  1261  	model := s.Brands.Model("my-brand", "my-model", modelHeaders("my-model"))
  1262  	s.WriteAssertions("model", model)
  1263  
  1264  	model2 := s.Brands.Model("my-brand", "my-second-model", modelHeaders("my-second-model"))
  1265  	s.WriteAssertions("model2", model2)
  1266  
  1267  	// try import and verify that its rejects because other assertions are
  1268  	// missing
  1269  	_, err := devicestate.ImportAssertionsFromSeed(st, "")
  1270  	c.Assert(err, ErrorMatches, "cannot have multiple model assertions in seed")
  1271  }
  1272  
  1273  func (s *firstBoot16Suite) TestImportAssertionsFromSeedNoModelAsserts(c *C) {
  1274  	st := s.overlord.State()
  1275  	st.Lock()
  1276  	defer st.Unlock()
  1277  
  1278  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1279  	for i, as := range assertsChain {
  1280  		if as.Type() != asserts.ModelType {
  1281  			s.WriteAssertions(strconv.Itoa(i), as)
  1282  		}
  1283  	}
  1284  
  1285  	// try import and verify that its rejects because other assertions are
  1286  	// missing
  1287  	_, err := devicestate.ImportAssertionsFromSeed(st, "")
  1288  	c.Assert(err, ErrorMatches, "seed must have a model assertion")
  1289  }
  1290  
  1291  type core18SnapsOpts struct {
  1292  	classic bool
  1293  	gadget  bool
  1294  }
  1295  
  1296  func (s *firstBoot16BaseTest) makeCore18Snaps(c *C, opts *core18SnapsOpts) (core18Fn, snapdFn, kernelFn, gadgetFn string) {
  1297  	if opts == nil {
  1298  		opts = &core18SnapsOpts{}
  1299  	}
  1300  
  1301  	files := [][]string{}
  1302  
  1303  	core18Yaml := `name: core18
  1304  version: 1.0
  1305  type: base`
  1306  	core18Fname, core18Decl, core18Rev := s.MakeAssertedSnap(c, core18Yaml, files, snap.R(1), "canonical")
  1307  	s.WriteAssertions("core18.asserts", core18Rev, core18Decl)
  1308  
  1309  	snapdYaml := `name: snapd
  1310  version: 1.0
  1311  `
  1312  	snapdFname, snapdDecl, snapdRev := s.MakeAssertedSnap(c, snapdYaml, nil, snap.R(2), "canonical")
  1313  	s.WriteAssertions("snapd.asserts", snapdRev, snapdDecl)
  1314  
  1315  	var kernelFname string
  1316  	if !opts.classic {
  1317  		kernelYaml := `name: pc-kernel
  1318  version: 1.0
  1319  type: kernel`
  1320  		fname, kernelDecl, kernelRev := s.MakeAssertedSnap(c, kernelYaml, files, snap.R(1), "canonical")
  1321  		s.WriteAssertions("kernel.asserts", kernelRev, kernelDecl)
  1322  		kernelFname = fname
  1323  	}
  1324  
  1325  	if !opts.classic {
  1326  		gadgetYaml := `
  1327  volumes:
  1328      volume-id:
  1329          bootloader: grub
  1330  `
  1331  		files = append(files, []string{"meta/gadget.yaml", gadgetYaml})
  1332  	}
  1333  
  1334  	var gadgetFname string
  1335  	if !opts.classic || opts.gadget {
  1336  		gaYaml := `name: pc
  1337  version: 1.0
  1338  type: gadget
  1339  base: core18
  1340  `
  1341  		fname, gadgetDecl, gadgetRev := s.MakeAssertedSnap(c, gaYaml, files, snap.R(1), "canonical")
  1342  		s.WriteAssertions("gadget.asserts", gadgetRev, gadgetDecl)
  1343  		gadgetFname = fname
  1344  	}
  1345  
  1346  	return core18Fname, snapdFname, kernelFname, gadgetFname
  1347  }
  1348  
  1349  func (s *firstBoot16Suite) TestPopulateFromSeedWithBaseHappy(c *C) {
  1350  	var sysdLog [][]string
  1351  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1352  		sysdLog = append(sysdLog, cmd)
  1353  		return []byte("ActiveState=inactive\n"), nil
  1354  	})
  1355  	defer systemctlRestorer()
  1356  
  1357  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  1358  	bootloader.Force(bloader)
  1359  	defer bootloader.Force(nil)
  1360  	bloader.SetBootKernel("pc-kernel_1.snap")
  1361  	bloader.SetBootBase("core18_1.snap")
  1362  
  1363  	core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil)
  1364  
  1365  	s.WriteAssertions("developer.account", s.devAcct)
  1366  
  1367  	// add a model assertion and its chain
  1368  	assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"})
  1369  	s.WriteAssertions("model.asserts", assertsChain...)
  1370  
  1371  	// create a seed.yaml
  1372  	content := []byte(fmt.Sprintf(`
  1373  snaps:
  1374   - name: snapd
  1375     file: %s
  1376   - name: core18
  1377     file: %s
  1378   - name: pc-kernel
  1379     file: %s
  1380   - name: pc
  1381     file: %s
  1382  `, snapdFname, core18Fname, kernelFname, gadgetFname))
  1383  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1384  	c.Assert(err, IsNil)
  1385  
  1386  	// run the firstboot stuff
  1387  	st := s.overlord.State()
  1388  	st.Lock()
  1389  	defer st.Unlock()
  1390  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1391  	c.Assert(err, IsNil)
  1392  
  1393  	checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc")
  1394  
  1395  	// now run the change and check the result
  1396  	// use the expected kind otherwise settle with start another one
  1397  	chg := st.NewChange("seed", "run the populate from seed changes")
  1398  	for _, ts := range tsAll {
  1399  		chg.AddAll(ts)
  1400  	}
  1401  	c.Assert(st.Changes(), HasLen, 1)
  1402  
  1403  	c.Assert(chg.Err(), IsNil)
  1404  
  1405  	// avoid device reg
  1406  	chg1 := st.NewChange("become-operational", "init device")
  1407  	chg1.SetStatus(state.DoingStatus)
  1408  
  1409  	// run change until it wants to restart
  1410  	st.Unlock()
  1411  	err = s.overlord.Settle(settleTimeout)
  1412  	st.Lock()
  1413  	c.Assert(err, IsNil)
  1414  
  1415  	// at this point the system is "restarting", pretend the restart has
  1416  	// happened
  1417  	c.Assert(chg.Status(), Equals, state.DoingStatus)
  1418  	state.MockRestarting(st, state.RestartUnset)
  1419  	st.Unlock()
  1420  	err = s.overlord.Settle(settleTimeout)
  1421  	st.Lock()
  1422  	c.Assert(err, IsNil)
  1423  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1424  
  1425  	// verify
  1426  	r, err := os.Open(dirs.SnapStateFile)
  1427  	c.Assert(err, IsNil)
  1428  	state, err := state.ReadState(nil, r)
  1429  	c.Assert(err, IsNil)
  1430  
  1431  	state.Lock()
  1432  	defer state.Unlock()
  1433  	// check snapd, core18, kernel, gadget
  1434  	_, err = snapstate.CurrentInfo(state, "snapd")
  1435  	c.Check(err, IsNil)
  1436  	_, err = snapstate.CurrentInfo(state, "core18")
  1437  	c.Check(err, IsNil)
  1438  	_, err = snapstate.CurrentInfo(state, "pc-kernel")
  1439  	c.Check(err, IsNil)
  1440  	_, err = snapstate.CurrentInfo(state, "pc")
  1441  	c.Check(err, IsNil)
  1442  
  1443  	// ensure required flag is set on all essential snaps
  1444  	var snapst snapstate.SnapState
  1445  	for _, reqName := range []string{"snapd", "core18", "pc-kernel", "pc"} {
  1446  		err = snapstate.Get(state, reqName, &snapst)
  1447  		c.Assert(err, IsNil)
  1448  		c.Assert(snapst.Required, Equals, true, Commentf("required not set for %v", reqName))
  1449  	}
  1450  
  1451  	// the right systemd commands were run
  1452  	c.Check(sysdLog, testutil.DeepContains, []string{"start", "usr-lib-snapd.mount"})
  1453  
  1454  	// and ensure state is now considered seeded
  1455  	var seeded bool
  1456  	err = state.Get("seeded", &seeded)
  1457  	c.Assert(err, IsNil)
  1458  	c.Check(seeded, Equals, true)
  1459  
  1460  	// check we set seed-time
  1461  	var seedTime time.Time
  1462  	err = state.Get("seed-time", &seedTime)
  1463  	c.Assert(err, IsNil)
  1464  	c.Check(seedTime.IsZero(), Equals, false)
  1465  }
  1466  
  1467  func (s *firstBoot16Suite) TestPopulateFromSeedOrdering(c *C) {
  1468  	s.WriteAssertions("developer.account", s.devAcct)
  1469  
  1470  	// add a model assertion and its chain
  1471  	assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"})
  1472  	s.WriteAssertions("model.asserts", assertsChain...)
  1473  
  1474  	core18Fname, snapdFname, kernelFname, gadgetFname := s.makeCore18Snaps(c, nil)
  1475  
  1476  	snapYaml := `name: snap-req-other-base
  1477  version: 1.0
  1478  base: other-base
  1479  `
  1480  	snapFname, snapDecl, snapRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1481  	s.WriteAssertions("snap-req-other-base.asserts", s.devAcct, snapRev, snapDecl)
  1482  	baseYaml := `name: other-base
  1483  version: 1.0
  1484  type: base
  1485  `
  1486  	baseFname, baseDecl, baseRev := s.MakeAssertedSnap(c, baseYaml, nil, snap.R(127), "developerid")
  1487  	s.WriteAssertions("other-base.asserts", s.devAcct, baseRev, baseDecl)
  1488  
  1489  	// create a seed.yaml
  1490  	content := []byte(fmt.Sprintf(`
  1491  snaps:
  1492   - name: snapd
  1493     file: %s
  1494   - name: core18
  1495     file: %s
  1496   - name: pc-kernel
  1497     file: %s
  1498   - name: pc
  1499     file: %s
  1500   - name: snap-req-other-base
  1501     file: %s
  1502   - name: other-base
  1503     file: %s
  1504  `, snapdFname, core18Fname, kernelFname, gadgetFname, snapFname, baseFname))
  1505  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1506  	c.Assert(err, IsNil)
  1507  
  1508  	// run the firstboot stuff
  1509  	st := s.overlord.State()
  1510  	st.Lock()
  1511  	defer st.Unlock()
  1512  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1513  	c.Assert(err, IsNil)
  1514  
  1515  	checkOrder(c, tsAll, "snapd", "pc-kernel", "core18", "pc", "other-base", "snap-req-other-base")
  1516  }
  1517  
  1518  func (s *firstBoot16Suite) TestFirstbootGadgetBaseModelBaseMismatch(c *C) {
  1519  	s.WriteAssertions("developer.account", s.devAcct)
  1520  
  1521  	// add a model assertion and its chain
  1522  	assertsChain := s.makeModelAssertionChain(c, "my-model", map[string]interface{}{"base": "core18"})
  1523  	s.WriteAssertions("model.asserts", assertsChain...)
  1524  
  1525  	core18Fname, snapdFname, kernelFname, _ := s.makeCore18Snaps(c, nil)
  1526  	// take the gadget without "base: core18"
  1527  	_, _, gadgetFname := s.makeCoreSnaps(c, "")
  1528  
  1529  	// create a seed.yaml
  1530  	content := []byte(fmt.Sprintf(`
  1531  snaps:
  1532   - name: snapd
  1533     file: %s
  1534   - name: core18
  1535     file: %s
  1536   - name: pc-kernel
  1537     file: %s
  1538   - name: pc
  1539     file: %s
  1540  `, snapdFname, core18Fname, kernelFname, gadgetFname))
  1541  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1542  	c.Assert(err, IsNil)
  1543  
  1544  	// run the firstboot stuff
  1545  	st := s.overlord.State()
  1546  	st.Lock()
  1547  	defer st.Unlock()
  1548  
  1549  	_, err = devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1550  	c.Assert(err, ErrorMatches, `cannot use gadget snap because its base "core" is different from model base "core18"`)
  1551  	// note, cannot use st.Tasks() here as it filters out tasks with no change
  1552  	c.Check(st.TaskCount(), Equals, 0)
  1553  }
  1554  
  1555  func (s *firstBoot16Suite) TestPopulateFromSeedWrongContentProviderOrder(c *C) {
  1556  	bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir()))
  1557  	bootloader.Force(bloader)
  1558  	defer bootloader.Force(nil)
  1559  	bloader.SetBootKernel("pc-kernel_1.snap")
  1560  	bloader.SetBootBase("core_1.snap")
  1561  
  1562  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "")
  1563  
  1564  	// a snap that uses content providers
  1565  	snapYaml := `name: gnome-calculator
  1566  version: 1.0
  1567  plugs:
  1568   gtk-3-themes:
  1569    interface: content
  1570    default-provider: gtk-common-themes
  1571    target: $SNAP/data-dir/themes
  1572  `
  1573  	calcFname, calcDecl, calcRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1574  	s.WriteAssertions("calc.asserts", s.devAcct, calcRev, calcDecl)
  1575  
  1576  	// put a 2nd firstboot snap into the SnapBlobDir
  1577  	snapYaml = `name: gtk-common-themes
  1578  version: 1.0
  1579  slots:
  1580   gtk-3-themes:
  1581    interface: content
  1582    source:
  1583     read:
  1584      - $SNAP/share/themes/Adawaita
  1585  `
  1586  	themesFname, themesDecl, themesRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(65), "developerid")
  1587  	s.WriteAssertions("themes.asserts", s.devAcct, themesDecl, themesRev)
  1588  
  1589  	// add a model assertion and its chain
  1590  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1591  	s.WriteAssertions("model.asserts", assertsChain...)
  1592  
  1593  	// create a seed.yaml
  1594  	content := []byte(fmt.Sprintf(`
  1595  snaps:
  1596   - name: core
  1597     file: %s
  1598   - name: pc-kernel
  1599     file: %s
  1600   - name: pc
  1601     file: %s
  1602   - name: gnome-calculator
  1603     file: %s
  1604   - name: gtk-common-themes
  1605     file: %s
  1606  `, coreFname, kernelFname, gadgetFname, calcFname, themesFname))
  1607  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1608  	c.Assert(err, IsNil)
  1609  
  1610  	// run the firstboot stuff
  1611  	st := s.overlord.State()
  1612  	st.Lock()
  1613  	defer st.Unlock()
  1614  
  1615  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1616  	c.Assert(err, IsNil)
  1617  	// use the expected kind otherwise settle with start another one
  1618  	chg := st.NewChange("seed", "run the populate from seed changes")
  1619  	for _, ts := range tsAll {
  1620  		chg.AddAll(ts)
  1621  	}
  1622  	c.Assert(st.Changes(), HasLen, 1)
  1623  
  1624  	// avoid device reg
  1625  	chg1 := st.NewChange("become-operational", "init device")
  1626  	chg1.SetStatus(state.DoingStatus)
  1627  
  1628  	st.Unlock()
  1629  	err = s.overlord.Settle(settleTimeout)
  1630  	st.Lock()
  1631  
  1632  	c.Assert(chg.Err(), IsNil)
  1633  	c.Assert(err, IsNil)
  1634  
  1635  	// verify the result
  1636  	var conns map[string]interface{}
  1637  	err = st.Get("conns", &conns)
  1638  	c.Assert(err, IsNil)
  1639  	c.Check(conns, HasLen, 1)
  1640  	conn, hasConn := conns["gnome-calculator:gtk-3-themes gtk-common-themes:gtk-3-themes"]
  1641  	c.Check(hasConn, Equals, true)
  1642  	c.Check(conn.(map[string]interface{})["auto"], Equals, true)
  1643  	c.Check(conn.(map[string]interface{})["interface"], Equals, "content")
  1644  }
  1645  
  1646  func (s *firstBoot16Suite) TestPopulateFromSeedMissingBase(c *C) {
  1647  	s.WriteAssertions("developer.account", s.devAcct)
  1648  
  1649  	// add a model assertion and its chain
  1650  	assertsChain := s.makeModelAssertionChain(c, "my-model", nil)
  1651  	s.WriteAssertions("model.asserts", assertsChain...)
  1652  
  1653  	coreFname, kernelFname, gadgetFname := s.makeCoreSnaps(c, "")
  1654  
  1655  	// TODO: this test doesn't particularly need to use a local snap
  1656  	// local snap with unknown base
  1657  	snapYaml = `name: local
  1658  base: foo
  1659  version: 1.0`
  1660  	mockSnapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml, nil)
  1661  	localFname := filepath.Base(mockSnapFile)
  1662  	targetSnapFile2 := filepath.Join(dirs.SnapSeedDir, "snaps", localFname)
  1663  	c.Assert(os.Rename(mockSnapFile, targetSnapFile2), IsNil)
  1664  
  1665  	// create a seed.yaml
  1666  	content := []byte(fmt.Sprintf(`
  1667  snaps:
  1668   - name: core
  1669     file: %s
  1670   - name: pc-kernel
  1671     file: %s
  1672   - name: pc
  1673     file: %s
  1674   - name: local
  1675     unasserted: true
  1676     file: %s
  1677  `, coreFname, kernelFname, gadgetFname, localFname))
  1678  
  1679  	c.Assert(ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644), IsNil)
  1680  
  1681  	// run the firstboot stuff
  1682  	st := s.overlord.State()
  1683  	st.Lock()
  1684  	defer st.Unlock()
  1685  	_, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1686  	c.Assert(err, ErrorMatches, `cannot use snap "local": base "foo" is missing`)
  1687  }
  1688  
  1689  func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicWithSnapdOnlyHappy(c *C) {
  1690  	restore := release.MockOnClassic(true)
  1691  	defer restore()
  1692  
  1693  	var sysdLog [][]string
  1694  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1695  		sysdLog = append(sysdLog, cmd)
  1696  		return []byte("ActiveState=inactive\n"), nil
  1697  	})
  1698  	defer systemctlRestorer()
  1699  
  1700  	core18Fname, snapdFname, _, _ := s.makeCore18Snaps(c, &core18SnapsOpts{
  1701  		classic: true,
  1702  	})
  1703  
  1704  	// put a firstboot snap into the SnapBlobDir
  1705  	snapYaml := `name: foo
  1706  version: 1.0
  1707  base: core18
  1708  `
  1709  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1710  	s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl)
  1711  
  1712  	// add a model assertion and its chain
  1713  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", nil)
  1714  	s.WriteAssertions("model.asserts", assertsChain...)
  1715  
  1716  	// create a seed.yaml
  1717  	content := []byte(fmt.Sprintf(`
  1718  snaps:
  1719   - name: snapd
  1720     file: %s
  1721   - name: foo
  1722     file: %s
  1723   - name: core18
  1724     file: %s
  1725  `, snapdFname, fooFname, core18Fname))
  1726  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1727  	c.Assert(err, IsNil)
  1728  
  1729  	// run the firstboot stuff
  1730  	st := s.overlord.State()
  1731  	st.Lock()
  1732  	defer st.Unlock()
  1733  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1734  	c.Assert(err, IsNil)
  1735  
  1736  	checkOrder(c, tsAll, "snapd", "core18", "foo")
  1737  
  1738  	// now run the change and check the result
  1739  	// use the expected kind otherwise settle with start another one
  1740  	chg := st.NewChange("seed", "run the populate from seed changes")
  1741  	for _, ts := range tsAll {
  1742  		chg.AddAll(ts)
  1743  	}
  1744  	c.Assert(st.Changes(), HasLen, 1)
  1745  
  1746  	c.Assert(chg.Err(), IsNil)
  1747  
  1748  	// avoid device reg
  1749  	chg1 := st.NewChange("become-operational", "init device")
  1750  	chg1.SetStatus(state.DoingStatus)
  1751  
  1752  	// run change until it wants to restart
  1753  	st.Unlock()
  1754  	err = s.overlord.Settle(settleTimeout)
  1755  	st.Lock()
  1756  	c.Assert(err, IsNil)
  1757  
  1758  	// at this point the system is "restarting", pretend the restart has
  1759  	// happened
  1760  	c.Assert(chg.Status(), Equals, state.DoingStatus)
  1761  	state.MockRestarting(st, state.RestartUnset)
  1762  	st.Unlock()
  1763  	err = s.overlord.Settle(settleTimeout)
  1764  	st.Lock()
  1765  	c.Assert(err, IsNil)
  1766  	c.Assert(chg.Status(), Equals, state.DoneStatus)
  1767  
  1768  	// verify
  1769  	r, err := os.Open(dirs.SnapStateFile)
  1770  	c.Assert(err, IsNil)
  1771  	state, err := state.ReadState(nil, r)
  1772  	c.Assert(err, IsNil)
  1773  
  1774  	state.Lock()
  1775  	defer state.Unlock()
  1776  	// check snapd, core18, kernel, gadget
  1777  	_, err = snapstate.CurrentInfo(state, "snapd")
  1778  	c.Check(err, IsNil)
  1779  	_, err = snapstate.CurrentInfo(state, "core18")
  1780  	c.Check(err, IsNil)
  1781  	_, err = snapstate.CurrentInfo(state, "foo")
  1782  	c.Check(err, IsNil)
  1783  
  1784  	// and ensure state is now considered seeded
  1785  	var seeded bool
  1786  	err = state.Get("seeded", &seeded)
  1787  	c.Assert(err, IsNil)
  1788  	c.Check(seeded, Equals, true)
  1789  
  1790  	// check we set seed-time
  1791  	var seedTime time.Time
  1792  	err = state.Get("seed-time", &seedTime)
  1793  	c.Assert(err, IsNil)
  1794  	c.Check(seedTime.IsZero(), Equals, false)
  1795  }
  1796  
  1797  func (s *firstBoot16Suite) TestPopulateFromSeedMissingAssertions(c *C) {
  1798  	restore := release.MockOnClassic(true)
  1799  	defer restore()
  1800  
  1801  	core18Fname, snapdFname, _, _ := s.makeCore18Snaps(c, &core18SnapsOpts{})
  1802  
  1803  	// create a seed.yaml
  1804  	content := []byte(fmt.Sprintf(`
  1805  snaps:
  1806   - name: snapd
  1807     file: %s
  1808   - name: core18
  1809     file: %s
  1810  `, snapdFname, core18Fname))
  1811  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1812  	c.Assert(err, IsNil)
  1813  
  1814  	// run the firstboot stuff
  1815  	st := s.overlord.State()
  1816  	st.Lock()
  1817  	defer st.Unlock()
  1818  	_, err = devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1819  	c.Assert(err, NotNil)
  1820  	// note, cannot use st.Tasks() here as it filters out tasks with no change
  1821  	c.Check(st.TaskCount(), Equals, 0)
  1822  }
  1823  
  1824  func (s *firstBoot16Suite) TestPopulateFromSeedOnClassicWithSnapdOnlyAndGadgetHappy(c *C) {
  1825  	restore := release.MockOnClassic(true)
  1826  	defer restore()
  1827  
  1828  	var sysdLog [][]string
  1829  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
  1830  		sysdLog = append(sysdLog, cmd)
  1831  		return []byte("ActiveState=inactive\n"), nil
  1832  	})
  1833  	defer systemctlRestorer()
  1834  
  1835  	core18Fname, snapdFname, _, gadgetFname := s.makeCore18Snaps(c, &core18SnapsOpts{
  1836  		classic: true,
  1837  		gadget:  true,
  1838  	})
  1839  
  1840  	// put a firstboot snap into the SnapBlobDir
  1841  	snapYaml := `name: foo
  1842  version: 1.0
  1843  base: core18
  1844  `
  1845  	fooFname, fooDecl, fooRev := s.MakeAssertedSnap(c, snapYaml, nil, snap.R(128), "developerid")
  1846  
  1847  	s.WriteAssertions("foo.asserts", s.devAcct, fooRev, fooDecl)
  1848  
  1849  	// add a model assertion and its chain
  1850  	assertsChain := s.makeModelAssertionChain(c, "my-model-classic", map[string]interface{}{"gadget": "pc"})
  1851  	s.WriteAssertions("model.asserts", assertsChain...)
  1852  
  1853  	// create a seed.yaml
  1854  	content := []byte(fmt.Sprintf(`
  1855  snaps:
  1856   - name: snapd
  1857     file: %s
  1858   - name: foo
  1859     file: %s
  1860   - name: core18
  1861     file: %s
  1862   - name: pc
  1863     file: %s
  1864  `, snapdFname, fooFname, core18Fname, gadgetFname))
  1865  	err := ioutil.WriteFile(filepath.Join(dirs.SnapSeedDir, "seed.yaml"), content, 0644)
  1866  	c.Assert(err, IsNil)
  1867  
  1868  	// run the firstboot stuff
  1869  	st := s.overlord.State()
  1870  	st.Lock()
  1871  	defer st.Unlock()
  1872  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, nil, s.perfTimings)
  1873  	c.Assert(err, IsNil)
  1874  
  1875  	checkOrder(c, tsAll, "snapd", "core18", "pc", "foo")
  1876  
  1877  	// now run the change and check the result
  1878  	// use the expected kind otherwise settle with start another one
  1879  	chg := st.NewChange("seed", "run the populate from seed changes")
  1880  	for _, ts := range tsAll {
  1881  		chg.AddAll(ts)
  1882  	}
  1883  	c.Assert(st.Changes(), HasLen, 1)
  1884  
  1885  	c.Assert(chg.Err(), IsNil)
  1886  
  1887  	// avoid device reg
  1888  	chg1 := st.NewChange("become-operational", "init device")
  1889  	chg1.SetStatus(state.DoingStatus)
  1890  
  1891  	// run change until it wants to restart
  1892  	st.Unlock()
  1893  	err = s.overlord.Settle(settleTimeout)
  1894  	st.Lock()
  1895  	c.Assert(err, IsNil)
  1896  
  1897  	// at this point the system is "restarting", pretend the restart has
  1898  	// happened
  1899  	c.Assert(chg.Status(), Equals, state.DoingStatus)
  1900  	state.MockRestarting(st, state.RestartUnset)
  1901  	st.Unlock()
  1902  	err = s.overlord.Settle(settleTimeout)
  1903  	st.Lock()
  1904  	c.Assert(err, IsNil)
  1905  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%s", chg.Err()))
  1906  
  1907  	// verify
  1908  	r, err := os.Open(dirs.SnapStateFile)
  1909  	c.Assert(err, IsNil)
  1910  	state, err := state.ReadState(nil, r)
  1911  	c.Assert(err, IsNil)
  1912  
  1913  	state.Lock()
  1914  	defer state.Unlock()
  1915  	// check snapd, core18, kernel, gadget
  1916  	_, err = snapstate.CurrentInfo(state, "snapd")
  1917  	c.Check(err, IsNil)
  1918  	_, err = snapstate.CurrentInfo(state, "core18")
  1919  	c.Check(err, IsNil)
  1920  	_, err = snapstate.CurrentInfo(state, "pc")
  1921  	c.Check(err, IsNil)
  1922  	_, err = snapstate.CurrentInfo(state, "foo")
  1923  	c.Check(err, IsNil)
  1924  
  1925  	// and ensure state is now considered seeded
  1926  	var seeded bool
  1927  	err = state.Get("seeded", &seeded)
  1928  	c.Assert(err, IsNil)
  1929  	c.Check(seeded, Equals, true)
  1930  
  1931  	// check we set seed-time
  1932  	var seedTime time.Time
  1933  	err = state.Get("seed-time", &seedTime)
  1934  	c.Assert(err, IsNil)
  1935  	c.Check(seedTime.IsZero(), Equals, false)
  1936  }
  1937  
  1938  func (s *firstBoot16Suite) TestCriticalTaskEdgesForPreseed(c *C) {
  1939  	st := s.overlord.State()
  1940  	st.Lock()
  1941  	defer st.Unlock()
  1942  
  1943  	t1 := st.NewTask("task1", "")
  1944  	t2 := st.NewTask("task2", "")
  1945  	t3 := st.NewTask("task2", "")
  1946  
  1947  	ts := state.NewTaskSet(t1, t2, t3)
  1948  	ts.MarkEdge(t1, snapstate.BeginEdge)
  1949  	ts.MarkEdge(t2, snapstate.BeforeHooksEdge)
  1950  	ts.MarkEdge(t3, snapstate.HooksEdge)
  1951  
  1952  	beginEdge, beforeHooksEdge, hooksEdge, err := devicestate.CriticalTaskEdges(ts)
  1953  	c.Assert(err, IsNil)
  1954  	c.Assert(beginEdge, NotNil)
  1955  	c.Assert(beforeHooksEdge, NotNil)
  1956  	c.Assert(hooksEdge, NotNil)
  1957  
  1958  	c.Check(beginEdge.Kind(), Equals, "task1")
  1959  	c.Check(beforeHooksEdge.Kind(), Equals, "task2")
  1960  	c.Check(hooksEdge.Kind(), Equals, "task2")
  1961  }
  1962  
  1963  func (s *firstBoot16Suite) TestCriticalTaskEdgesForPreseedMissing(c *C) {
  1964  	st := s.overlord.State()
  1965  	st.Lock()
  1966  	defer st.Unlock()
  1967  
  1968  	t1 := st.NewTask("task1", "")
  1969  	t2 := st.NewTask("task2", "")
  1970  	t3 := st.NewTask("task2", "")
  1971  
  1972  	ts := state.NewTaskSet(t1, t2, t3)
  1973  	ts.MarkEdge(t1, snapstate.BeginEdge)
  1974  
  1975  	_, _, _, err := devicestate.CriticalTaskEdges(ts)
  1976  	c.Assert(err, NotNil)
  1977  
  1978  	ts = state.NewTaskSet(t1, t2, t3)
  1979  	ts.MarkEdge(t1, snapstate.BeginEdge)
  1980  	ts.MarkEdge(t2, snapstate.BeforeHooksEdge)
  1981  	_, _, _, err = devicestate.CriticalTaskEdges(ts)
  1982  	c.Assert(err, NotNil)
  1983  
  1984  	ts = state.NewTaskSet(t1, t2, t3)
  1985  	ts.MarkEdge(t1, snapstate.BeginEdge)
  1986  	ts.MarkEdge(t3, snapstate.HooksEdge)
  1987  	_, _, _, err = devicestate.CriticalTaskEdges(ts)
  1988  	c.Assert(err, NotNil)
  1989  }