github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/devicestate/handlers_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019-2020 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  	"context"
    24  	"fmt"
    25  	"path/filepath"
    26  	"time"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/asserts"
    31  	"github.com/snapcore/snapd/dirs"
    32  	"github.com/snapcore/snapd/interfaces"
    33  	"github.com/snapcore/snapd/overlord/assertstate"
    34  	"github.com/snapcore/snapd/overlord/auth"
    35  	"github.com/snapcore/snapd/overlord/devicestate"
    36  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    37  	"github.com/snapcore/snapd/overlord/snapstate"
    38  	"github.com/snapcore/snapd/overlord/state"
    39  	"github.com/snapcore/snapd/overlord/storecontext"
    40  	"github.com/snapcore/snapd/snap"
    41  	"github.com/snapcore/snapd/snap/snaptest"
    42  	"github.com/snapcore/snapd/snapdenv"
    43  	"github.com/snapcore/snapd/testutil"
    44  	"github.com/snapcore/snapd/timings"
    45  )
    46  
    47  // TODO: should we move this into a new handlers suite?
    48  func (s *deviceMgrSuite) TestSetModelHandlerNewRevision(c *C) {
    49  	s.state.Lock()
    50  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
    51  		Brand: "canonical",
    52  		Model: "pc-model",
    53  	})
    54  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
    55  		"architecture":   "amd64",
    56  		"kernel":         "pc-kernel",
    57  		"gadget":         "pc",
    58  		"revision":       "1",
    59  		"required-snaps": []interface{}{"foo", "bar"},
    60  	})
    61  	// foo and bar
    62  	fooSI := &snap.SideInfo{
    63  		RealName: "foo",
    64  		Revision: snap.R(1),
    65  	}
    66  	barSI := &snap.SideInfo{
    67  		RealName: "foo",
    68  		Revision: snap.R(1),
    69  	}
    70  	pcKernelSI := &snap.SideInfo{
    71  		RealName: "pc-kernel",
    72  		Revision: snap.R(1),
    73  	}
    74  	snapstate.Set(s.state, "foo", &snapstate.SnapState{
    75  		SnapType: "app",
    76  		Active:   true,
    77  		Sequence: []*snap.SideInfo{fooSI},
    78  		Current:  fooSI.Revision,
    79  		Flags:    snapstate.Flags{Required: true},
    80  	})
    81  	snapstate.Set(s.state, "bar", &snapstate.SnapState{
    82  		SnapType: "app",
    83  		Active:   true,
    84  		Sequence: []*snap.SideInfo{barSI},
    85  		Current:  barSI.Revision,
    86  		Flags:    snapstate.Flags{Required: true},
    87  	})
    88  	snapstate.Set(s.state, "pc-kernel", &snapstate.SnapState{
    89  		SnapType: "kernel",
    90  		Active:   true,
    91  		Sequence: []*snap.SideInfo{pcKernelSI},
    92  		Current:  pcKernelSI.Revision,
    93  		Flags:    snapstate.Flags{Required: true},
    94  	})
    95  	s.state.Unlock()
    96  
    97  	newModel := s.brands.Model("canonical", "pc-model", map[string]interface{}{
    98  		"architecture":   "amd64",
    99  		"kernel":         "other-kernel",
   100  		"gadget":         "pc",
   101  		"revision":       "2",
   102  		"required-snaps": []interface{}{"foo"},
   103  	})
   104  
   105  	s.state.Lock()
   106  	t := s.state.NewTask("set-model", "set-model test")
   107  	chg := s.state.NewChange("dummy", "...")
   108  	chg.Set("new-model", string(asserts.Encode(newModel)))
   109  	chg.AddTask(t)
   110  
   111  	s.state.Unlock()
   112  
   113  	s.se.Ensure()
   114  	s.se.Wait()
   115  
   116  	s.state.Lock()
   117  	defer s.state.Unlock()
   118  	m, err := s.mgr.Model()
   119  	c.Assert(err, IsNil)
   120  	c.Assert(m, DeepEquals, newModel)
   121  
   122  	c.Assert(chg.Err(), IsNil)
   123  
   124  	// check required
   125  	var fooState snapstate.SnapState
   126  	var barState snapstate.SnapState
   127  	err = snapstate.Get(s.state, "foo", &fooState)
   128  	c.Assert(err, IsNil)
   129  	err = snapstate.Get(s.state, "bar", &barState)
   130  	c.Assert(err, IsNil)
   131  	c.Check(fooState.Flags.Required, Equals, true)
   132  	c.Check(barState.Flags.Required, Equals, false)
   133  	// the kernel is no longer required
   134  	var kernelState snapstate.SnapState
   135  	err = snapstate.Get(s.state, "pc-kernel", &kernelState)
   136  	c.Assert(err, IsNil)
   137  	c.Check(kernelState.Flags.Required, Equals, false)
   138  }
   139  
   140  func (s *deviceMgrSuite) TestSetModelHandlerSameRevisionNoError(c *C) {
   141  	model := s.brands.Model("canonical", "pc-model", map[string]interface{}{
   142  		"architecture": "amd64",
   143  		"kernel":       "pc-kernel",
   144  		"gadget":       "pc",
   145  		"revision":     "1",
   146  	})
   147  
   148  	s.state.Lock()
   149  
   150  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   151  		Brand: "canonical",
   152  		Model: "pc-model",
   153  	})
   154  	err := assertstate.Add(s.state, model)
   155  	c.Assert(err, IsNil)
   156  
   157  	t := s.state.NewTask("set-model", "set-model test")
   158  	chg := s.state.NewChange("dummy", "...")
   159  	chg.Set("new-model", string(asserts.Encode(model)))
   160  	chg.AddTask(t)
   161  
   162  	s.state.Unlock()
   163  
   164  	s.se.Ensure()
   165  	s.se.Wait()
   166  
   167  	s.state.Lock()
   168  	defer s.state.Unlock()
   169  	c.Assert(chg.Err(), IsNil)
   170  }
   171  
   172  func (s *deviceMgrSuite) TestSetModelHandlerStoreSwitch(c *C) {
   173  	s.state.Lock()
   174  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   175  		Brand: "canonical",
   176  		Model: "pc-model",
   177  	})
   178  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
   179  		"architecture": "amd64",
   180  		"kernel":       "pc-kernel",
   181  		"gadget":       "pc",
   182  		"revision":     "1",
   183  	})
   184  	s.state.Unlock()
   185  
   186  	newModel := s.brands.Model("canonical", "pc-model", map[string]interface{}{
   187  		"architecture": "amd64",
   188  		"kernel":       "pc-kernel",
   189  		"gadget":       "pc",
   190  		"store":        "switched-store",
   191  		"revision":     "2",
   192  	})
   193  
   194  	s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService {
   195  		mod, err := devBE.Model()
   196  		c.Check(err, IsNil)
   197  		if err == nil {
   198  			c.Check(mod, DeepEquals, newModel)
   199  		}
   200  		return &freshSessionStore{}
   201  	}
   202  
   203  	s.state.Lock()
   204  	t := s.state.NewTask("set-model", "set-model test")
   205  	chg := s.state.NewChange("dummy", "...")
   206  	chg.Set("new-model", string(asserts.Encode(newModel)))
   207  	chg.Set("device", auth.DeviceState{
   208  		Brand:           "canonical",
   209  		Model:           "pc-model",
   210  		SessionMacaroon: "switched-store-session",
   211  	})
   212  	chg.AddTask(t)
   213  
   214  	s.state.Unlock()
   215  
   216  	s.se.Ensure()
   217  	s.se.Wait()
   218  
   219  	s.state.Lock()
   220  	defer s.state.Unlock()
   221  	c.Assert(chg.Err(), IsNil)
   222  
   223  	m, err := s.mgr.Model()
   224  	c.Assert(err, IsNil)
   225  	c.Assert(m, DeepEquals, newModel)
   226  
   227  	device, err := devicestatetest.Device(s.state)
   228  	c.Assert(err, IsNil)
   229  	c.Check(device, DeepEquals, &auth.DeviceState{
   230  		Brand:           "canonical",
   231  		Model:           "pc-model",
   232  		SessionMacaroon: "switched-store-session",
   233  	})
   234  
   235  	// cleanup
   236  	_, ok := devicestate.CachedRemodelCtx(chg)
   237  	c.Check(ok, Equals, true)
   238  
   239  	s.state.Unlock()
   240  
   241  	s.se.Ensure()
   242  	s.se.Wait()
   243  
   244  	s.state.Lock()
   245  
   246  	_, ok = devicestate.CachedRemodelCtx(chg)
   247  	c.Check(ok, Equals, false)
   248  }
   249  
   250  func (s *deviceMgrSuite) TestSetModelHandlerRereg(c *C) {
   251  	s.state.Lock()
   252  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   253  		Brand:  "canonical",
   254  		Model:  "pc-model",
   255  		Serial: "orig-serial",
   256  	})
   257  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
   258  		"architecture": "amd64",
   259  		"kernel":       "pc-kernel",
   260  		"gadget":       "pc",
   261  	})
   262  	s.makeSerialAssertionInState(c, "canonical", "pc-model", "orig-serial")
   263  	s.state.Unlock()
   264  
   265  	newModel := s.brands.Model("canonical", "rereg-model", map[string]interface{}{
   266  		"architecture": "amd64",
   267  		"kernel":       "pc-kernel",
   268  		"gadget":       "pc",
   269  	})
   270  
   271  	s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService {
   272  		mod, err := devBE.Model()
   273  		c.Check(err, IsNil)
   274  		if err == nil {
   275  			c.Check(mod, DeepEquals, newModel)
   276  		}
   277  		return &freshSessionStore{}
   278  	}
   279  
   280  	s.state.Lock()
   281  	t := s.state.NewTask("set-model", "set-model test")
   282  	chg := s.state.NewChange("dummy", "...")
   283  	chg.Set("new-model", string(asserts.Encode(newModel)))
   284  	chg.Set("device", auth.DeviceState{
   285  		Brand:           "canonical",
   286  		Model:           "rereg-model",
   287  		Serial:          "orig-serial",
   288  		SessionMacaroon: "switched-store-session",
   289  	})
   290  	chg.AddTask(t)
   291  
   292  	s.state.Unlock()
   293  
   294  	s.se.Ensure()
   295  	s.se.Wait()
   296  
   297  	s.state.Lock()
   298  	defer s.state.Unlock()
   299  	c.Assert(chg.Err(), IsNil)
   300  
   301  	m, err := s.mgr.Model()
   302  	c.Assert(err, IsNil)
   303  	c.Assert(m, DeepEquals, newModel)
   304  
   305  	device, err := devicestatetest.Device(s.state)
   306  	c.Assert(err, IsNil)
   307  	c.Check(device, DeepEquals, &auth.DeviceState{
   308  		Brand:           "canonical",
   309  		Model:           "rereg-model",
   310  		Serial:          "orig-serial",
   311  		SessionMacaroon: "switched-store-session",
   312  	})
   313  }
   314  
   315  func (s *deviceMgrSuite) TestDoPrepareRemodeling(c *C) {
   316  	s.state.Lock()
   317  	defer s.state.Unlock()
   318  	s.state.Set("seeded", true)
   319  	s.state.Set("refresh-privacy-key", "some-privacy-key")
   320  
   321  	var testStore snapstate.StoreService
   322  
   323  	restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) {
   324  		c.Check(flags.Required, Equals, true)
   325  		c.Check(deviceCtx, NotNil)
   326  		c.Check(deviceCtx.ForRemodeling(), Equals, true)
   327  
   328  		tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name))
   329  		tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name))
   330  		tValidate.WaitFor(tDownload)
   331  		tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name))
   332  		tInstall.WaitFor(tValidate)
   333  		ts := state.NewTaskSet(tDownload, tValidate, tInstall)
   334  		ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge)
   335  		return ts, nil
   336  	})
   337  	defer restore()
   338  
   339  	// set a model assertion
   340  	s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{
   341  		"architecture": "amd64",
   342  		"kernel":       "pc-kernel",
   343  		"gadget":       "pc",
   344  		"base":         "core18",
   345  	})
   346  	s.makeSerialAssertionInState(c, "canonical", "pc-model", "orig-serial")
   347  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   348  		Brand:           "canonical",
   349  		Model:           "pc-model",
   350  		Serial:          "orig-serial",
   351  		SessionMacaroon: "old-session",
   352  	})
   353  
   354  	new := s.brands.Model("canonical", "rereg-model", map[string]interface{}{
   355  		"architecture":   "amd64",
   356  		"kernel":         "pc-kernel",
   357  		"gadget":         "pc",
   358  		"base":           "core18",
   359  		"required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"},
   360  	})
   361  
   362  	freshStore := &freshSessionStore{}
   363  	testStore = freshStore
   364  
   365  	s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService {
   366  		mod, err := devBE.Model()
   367  		c.Check(err, IsNil)
   368  		if err == nil {
   369  			c.Check(mod, DeepEquals, new)
   370  		}
   371  		return testStore
   372  	}
   373  
   374  	cur, err := s.mgr.Model()
   375  	c.Assert(err, IsNil)
   376  
   377  	remodCtx, err := devicestate.RemodelCtx(s.state, cur, new)
   378  	c.Assert(err, IsNil)
   379  
   380  	c.Check(remodCtx.Kind(), Equals, devicestate.ReregRemodel)
   381  
   382  	chg := s.state.NewChange("remodel", "...")
   383  	remodCtx.Init(chg)
   384  	t := s.state.NewTask("prepare-remodeling", "...")
   385  	chg.AddTask(t)
   386  
   387  	// set new serial
   388  	s.makeSerialAssertionInState(c, "canonical", "rereg-model", "orig-serial")
   389  	chg.Set("device", auth.DeviceState{
   390  		Brand:           "canonical",
   391  		Model:           "rereg-model",
   392  		Serial:          "orig-serial",
   393  		SessionMacaroon: "switched-store-session",
   394  	})
   395  
   396  	s.state.Unlock()
   397  
   398  	s.se.Ensure()
   399  	s.se.Wait()
   400  
   401  	s.state.Lock()
   402  	c.Assert(chg.Err(), IsNil)
   403  
   404  	c.Check(freshStore.ensureDeviceSession, Equals, 1)
   405  
   406  	// check that the expected tasks were injected
   407  	tl := chg.Tasks()
   408  	// 1 prepare-remodeling
   409  	// 2 snaps * 3 tasks (from the mock install above) +
   410  	// 1 "set-model" task at the end
   411  	c.Assert(tl, HasLen, 1+2*3+1)
   412  
   413  	// sanity
   414  	c.Check(tl[1].Kind(), Equals, "fake-download")
   415  	c.Check(tl[1+2*3].Kind(), Equals, "set-model")
   416  
   417  	// cleanup
   418  	// fake completion
   419  	for _, t := range tl[1:] {
   420  		t.SetStatus(state.DoneStatus)
   421  	}
   422  	_, ok := devicestate.CachedRemodelCtx(chg)
   423  	c.Check(ok, Equals, true)
   424  
   425  	s.state.Unlock()
   426  
   427  	s.se.Ensure()
   428  	s.se.Wait()
   429  
   430  	s.state.Lock()
   431  
   432  	_, ok = devicestate.CachedRemodelCtx(chg)
   433  	c.Check(ok, Equals, false)
   434  }
   435  
   436  type preseedBaseSuite struct {
   437  	deviceMgrBaseSuite
   438  
   439  	cmdUmount    *testutil.MockCmd
   440  	cmdSystemctl *testutil.MockCmd
   441  }
   442  
   443  func (s *preseedBaseSuite) SetUpTest(c *C, preseed bool) {
   444  	r := snapdenv.MockPreseeding(preseed)
   445  
   446  	// preseed mode helper needs to be mocked before setting up
   447  	// deviceMgrBaseSuite due to device Manager init.
   448  	s.deviceMgrBaseSuite.SetUpTest(c)
   449  
   450  	// can use cleanup only after having called base SetUpTest
   451  	s.AddCleanup(r)
   452  
   453  	s.AddCleanup(interfaces.MockSystemKey(`{"build-id":"abcde"}`))
   454  	c.Assert(interfaces.WriteSystemKey(), IsNil)
   455  
   456  	s.cmdUmount = testutil.MockCommand(c, "umount", "")
   457  	s.cmdSystemctl = testutil.MockCommand(c, "systemctl", "")
   458  	s.AddCleanup(func() {
   459  		s.cmdUmount.Restore()
   460  		s.cmdSystemctl.Restore()
   461  	})
   462  
   463  	st := s.state
   464  	st.Lock()
   465  	defer st.Unlock()
   466  
   467  	si := &snap.SideInfo{RealName: "test-snap", Revision: snap.R(3), SnapID: "test-snap-id"}
   468  	snaptest.MockSnap(c, `name: test-snap
   469  version: 1.0
   470  apps:
   471   srv:
   472    command: bin/service
   473    daemon: simple
   474  `, si)
   475  
   476  	snapstate.Set(st, "test-snap", &snapstate.SnapState{
   477  		Active:   true,
   478  		Sequence: []*snap.SideInfo{si},
   479  		Current:  si.Revision,
   480  		SnapType: "app",
   481  	})
   482  }
   483  
   484  // TODO: rename preesed mode to just preseeding as much as possible,
   485  // preseed mode souns like a UC20 system mode but is just a snapd mode
   486  // but preseed snapd mode is a mouthful
   487  type preseedModeSuite struct {
   488  	preseedBaseSuite
   489  }
   490  
   491  var _ = Suite(&preseedModeSuite{})
   492  
   493  func (s *preseedModeSuite) SetUpTest(c *C) {
   494  	s.preseedBaseSuite.SetUpTest(c, true)
   495  }
   496  
   497  func (s *preseedModeSuite) TearDownTest(c *C) {
   498  	s.preseedBaseSuite.TearDownTest(c)
   499  }
   500  
   501  func (s *preseedModeSuite) TestDoMarkPreseeded(c *C) {
   502  	now := time.Now()
   503  	restore := devicestate.MockTimeNow(func() time.Time {
   504  		return now
   505  	})
   506  	defer restore()
   507  
   508  	st := s.state
   509  	st.Lock()
   510  	defer st.Unlock()
   511  
   512  	chg := st.NewChange("firstboot seeding", "...")
   513  	t := st.NewTask("mark-preseeded", "...")
   514  	chg.AddTask(t)
   515  
   516  	st.Unlock()
   517  	s.se.Ensure()
   518  	s.se.Wait()
   519  	st.Lock()
   520  
   521  	// mark-preseeded task is left in Doing, meaning it will be re-executed
   522  	// after restart in normal (not preseeding) mode.
   523  	c.Check(t.Status(), Equals, state.DoingStatus)
   524  
   525  	var preseeded bool
   526  	c.Check(t.Get("preseeded", &preseeded), IsNil)
   527  	c.Check(preseeded, Equals, true)
   528  
   529  	c.Assert(st.Get("preseeded", &preseeded), IsNil)
   530  	c.Check(preseeded, Equals, true)
   531  
   532  	var systemKey map[string]interface{}
   533  	c.Assert(st.Get("seed-restart-system-key", &systemKey), Equals, state.ErrNoState)
   534  	c.Assert(st.Get("preseed-system-key", &systemKey), IsNil)
   535  	c.Check(systemKey["build-id"], Equals, "abcde")
   536  
   537  	var preseededTime time.Time
   538  	c.Assert(st.Get("preseed-time", &preseededTime), IsNil)
   539  	c.Check(preseededTime.Equal(now), Equals, true)
   540  
   541  	// core snap was "manually" unmounted
   542  	c.Check(s.cmdUmount.Calls(), DeepEquals, [][]string{
   543  		{"umount", "-d", "-l", filepath.Join(dirs.SnapMountDir, "test-snap/3")},
   544  	})
   545  
   546  	// and snapd stop was requested
   547  	c.Check(s.restartRequests, DeepEquals, []state.RestartType{state.StopDaemon})
   548  
   549  	s.cmdUmount.ForgetCalls()
   550  
   551  	// re-trying mark-preseeded task has no effect
   552  	st.Unlock()
   553  	s.se.Ensure()
   554  	s.se.Wait()
   555  	st.Lock()
   556  
   557  	c.Check(s.cmdUmount.Calls(), HasLen, 0)
   558  	c.Check(t.Status(), Equals, state.DoingStatus)
   559  }
   560  
   561  func (s *preseedModeSuite) TestEnsureSeededPreseedFlag(c *C) {
   562  	now := time.Now()
   563  	restoreTimeNow := devicestate.MockTimeNow(func() time.Time {
   564  		return now
   565  	})
   566  	defer restoreTimeNow()
   567  
   568  	called := false
   569  	restore := devicestate.MockPopulateStateFromSeed(func(st *state.State, opts *devicestate.PopulateStateFromSeedOptions, tm timings.Measurer) ([]*state.TaskSet, error) {
   570  		called = true
   571  		c.Check(opts.Preseed, Equals, true)
   572  		return nil, nil
   573  	})
   574  	defer restore()
   575  
   576  	err := devicestate.EnsureSeeded(s.mgr)
   577  	c.Assert(err, IsNil)
   578  	c.Check(called, Equals, true)
   579  
   580  	s.state.Lock()
   581  	defer s.state.Unlock()
   582  
   583  	var preseedStartTime time.Time
   584  	c.Assert(s.state.Get("preseed-start-time", &preseedStartTime), IsNil)
   585  	c.Check(preseedStartTime.Equal(now), Equals, true)
   586  }
   587  
   588  type preseedDoneSuite struct {
   589  	preseedBaseSuite
   590  }
   591  
   592  var _ = Suite(&preseedDoneSuite{})
   593  
   594  func (s *preseedDoneSuite) SetUpTest(c *C) {
   595  	s.preseedBaseSuite.SetUpTest(c, false)
   596  }
   597  
   598  func (s *preseedDoneSuite) TearDownTest(c *C) {
   599  	s.preseedBaseSuite.TearDownTest(c)
   600  }
   601  
   602  func (s *preseedDoneSuite) TestDoMarkPreseededAfterFirstboot(c *C) {
   603  	st := s.state
   604  	st.Lock()
   605  	defer st.Unlock()
   606  
   607  	chg := st.NewChange("firstboot seeding", "...")
   608  	t := st.NewTask("mark-preseeded", "...")
   609  	chg.AddTask(t)
   610  	t.SetStatus(state.DoingStatus)
   611  
   612  	st.Unlock()
   613  	s.se.Ensure()
   614  	s.se.Wait()
   615  	st.Lock()
   616  
   617  	// no umount calls expected, just transitioned to Done status.
   618  	c.Check(chg.Status(), Equals, state.DoneStatus)
   619  	c.Check(s.cmdUmount.Calls(), HasLen, 0)
   620  	c.Check(s.restartRequests, HasLen, 0)
   621  
   622  	var systemKey map[string]interface{}
   623  	// in real world preseed-system-key would be present at this point because
   624  	// mark-preseeded would be run twice (before & after preseeding); this is
   625  	// not the case in this test.
   626  	c.Assert(st.Get("preseed-system-key", &systemKey), Equals, state.ErrNoState)
   627  	c.Assert(st.Get("seed-restart-system-key", &systemKey), IsNil)
   628  	c.Check(systemKey["build-id"], Equals, "abcde")
   629  
   630  	var seedRestartTime time.Time
   631  	c.Assert(st.Get("seed-restart-time", &seedRestartTime), IsNil)
   632  	c.Check(seedRestartTime.Equal(devicestate.StartTime()), Equals, true)
   633  }