github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/overlord/devicestate/firstboot20_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 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  	"os"
    24  	"path/filepath"
    25  	"strings"
    26  	"time"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/asserts"
    31  	"github.com/snapcore/snapd/boot"
    32  	"github.com/snapcore/snapd/bootloader"
    33  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    34  	"github.com/snapcore/snapd/dirs"
    35  	"github.com/snapcore/snapd/features"
    36  	"github.com/snapcore/snapd/osutil"
    37  	"github.com/snapcore/snapd/overlord"
    38  	"github.com/snapcore/snapd/overlord/assertstate"
    39  	"github.com/snapcore/snapd/overlord/configstate/config"
    40  	"github.com/snapcore/snapd/overlord/devicestate"
    41  	"github.com/snapcore/snapd/overlord/ifacestate"
    42  	"github.com/snapcore/snapd/overlord/snapstate"
    43  	"github.com/snapcore/snapd/overlord/state"
    44  	"github.com/snapcore/snapd/release"
    45  	"github.com/snapcore/snapd/seed/seedtest"
    46  	"github.com/snapcore/snapd/snap"
    47  	"github.com/snapcore/snapd/strutil"
    48  	"github.com/snapcore/snapd/systemd"
    49  	"github.com/snapcore/snapd/testutil"
    50  )
    51  
    52  type firstBoot20Suite struct {
    53  	firstBootBaseTest
    54  
    55  	extraSnapYaml map[string]string
    56  
    57  	// TestingSeed20 helps populating seeds (it provides
    58  	// MakeAssertedSnap, MakeSeed) for tests.
    59  	*seedtest.TestingSeed20
    60  }
    61  
    62  var (
    63  	allGrades = []asserts.ModelGrade{
    64  		asserts.ModelDangerous,
    65  	}
    66  )
    67  
    68  var _ = Suite(&firstBoot20Suite{})
    69  
    70  func (s *firstBoot20Suite) SetUpTest(c *C) {
    71  	s.extraSnapYaml = make(map[string]string)
    72  
    73  	s.TestingSeed20 = &seedtest.TestingSeed20{}
    74  
    75  	s.setupBaseTest(c, &s.TestingSeed20.SeedSnaps)
    76  
    77  	// don't start the overlord here so that we can mock different modeenvs
    78  	// later, which is needed by devicestart manager startup with uc20 booting
    79  
    80  	s.SeedDir = dirs.SnapSeedDir
    81  
    82  	// mock the snap mapper as snapd here
    83  	s.AddCleanup(ifacestate.MockSnapMapper(&ifacestate.CoreSnapdSystemMapper{}))
    84  
    85  	r := release.MockReleaseInfo(&release.OS{ID: "ubuntu-core", VersionID: "20"})
    86  	s.AddCleanup(r)
    87  }
    88  
    89  func (s *firstBoot20Suite) snapYaml(snp string) string {
    90  	if yml, ok := seedtest.SampleSnapYaml[snp]; ok {
    91  		return yml
    92  	}
    93  	return s.extraSnapYaml[snp]
    94  }
    95  
    96  func (s *firstBoot20Suite) setupCore20Seed(c *C, sysLabel string, modelGrade asserts.ModelGrade, extraGadgetYaml string, extraSnaps ...string) *asserts.Model {
    97  	gadgetYaml := `
    98  volumes:
    99      volume-id:
   100          bootloader: grub
   101          structure:
   102          - name: ubuntu-seed
   103            role: system-seed
   104            type: EF,C12A7328-F81F-11D2-BA4B-00A0C93EC93B
   105            size: 1G
   106          - name: ubuntu-data
   107            role: system-data
   108            type: 83,0FC63DAF-8483-4772-8E79-3D69D8477DE4
   109            size: 2G
   110  `
   111  
   112  	gadgetYaml += extraGadgetYaml
   113  
   114  	makeSnap := func(yamlKey string) {
   115  		var files [][]string
   116  		if yamlKey == "pc=20" {
   117  			files = append(files, []string{"meta/gadget.yaml", gadgetYaml})
   118  		}
   119  		s.MakeAssertedSnap(c, s.snapYaml(yamlKey), files, snap.R(1), "canonical", s.StoreSigning.Database)
   120  	}
   121  
   122  	makeSnap("snapd")
   123  	makeSnap("pc-kernel=20")
   124  	makeSnap("core20")
   125  	makeSnap("pc=20")
   126  	for _, sn := range extraSnaps {
   127  		makeSnap(sn)
   128  	}
   129  
   130  	model := map[string]interface{}{
   131  		"display-name": "my model",
   132  		"architecture": "amd64",
   133  		"base":         "core20",
   134  		"grade":        string(modelGrade),
   135  		"snaps": []interface{}{
   136  			map[string]interface{}{
   137  				"name":            "pc-kernel",
   138  				"id":              s.AssertedSnapID("pc-kernel"),
   139  				"type":            "kernel",
   140  				"default-channel": "20",
   141  			},
   142  			map[string]interface{}{
   143  				"name":            "pc",
   144  				"id":              s.AssertedSnapID("pc"),
   145  				"type":            "gadget",
   146  				"default-channel": "20",
   147  			},
   148  			map[string]interface{}{
   149  				"name": "snapd",
   150  				"id":   s.AssertedSnapID("snapd"),
   151  				"type": "snapd",
   152  			},
   153  			map[string]interface{}{
   154  				"name": "core20",
   155  				"id":   s.AssertedSnapID("core20"),
   156  				"type": "base",
   157  			},
   158  		},
   159  	}
   160  
   161  	for _, sn := range extraSnaps {
   162  		name, channel := splitSnapNameWithChannel(sn)
   163  		model["snaps"] = append(model["snaps"].([]interface{}), map[string]interface{}{
   164  			"name":            name,
   165  			"type":            "app",
   166  			"id":              s.AssertedSnapID(name),
   167  			"default-channel": channel,
   168  		})
   169  	}
   170  
   171  	return s.MakeSeed(c, sysLabel, "my-brand", "my-model", model, nil)
   172  }
   173  
   174  func splitSnapNameWithChannel(sn string) (name, channel string) {
   175  	nameParts := strings.SplitN(sn, "=", 2)
   176  	name = nameParts[0]
   177  	channel = ""
   178  	if len(nameParts) == 2 {
   179  		channel = nameParts[1]
   180  	}
   181  	return name, channel
   182  }
   183  
   184  func stripSnapNamesWithChannels(snaps []string) []string {
   185  	names := []string{}
   186  	for _, sn := range snaps {
   187  		name, _ := splitSnapNameWithChannel(sn)
   188  		names = append(names, name)
   189  	}
   190  	return names
   191  }
   192  
   193  func checkSnapstateDevModeFlags(c *C, tsAll []*state.TaskSet, snapsWithDevModeFlag ...string) {
   194  	allDevModeSnaps := stripSnapNamesWithChannels(snapsWithDevModeFlag)
   195  
   196  	// XXX: mostly same code from checkOrder helper in firstboot_test.go, maybe
   197  	// combine someday?
   198  	matched := 0
   199  	var prevTask *state.Task
   200  	for i, ts := range tsAll {
   201  		task0 := ts.Tasks()[0]
   202  		waitTasks := task0.WaitTasks()
   203  		if i == 0 {
   204  			c.Check(waitTasks, HasLen, 0)
   205  		} else {
   206  			c.Check(waitTasks, testutil.Contains, prevTask)
   207  		}
   208  		prevTask = task0
   209  		if task0.Kind() != "prerequisites" {
   210  			continue
   211  		}
   212  		snapsup, err := snapstate.TaskSnapSetup(task0)
   213  		c.Assert(err, IsNil, Commentf("%#v", task0))
   214  		if strutil.ListContains(allDevModeSnaps, snapsup.InstanceName()) {
   215  			c.Assert(snapsup.DevMode, Equals, true)
   216  			matched++
   217  		} else {
   218  			// it should not have DevMode true
   219  			c.Assert(snapsup.DevMode, Equals, false)
   220  		}
   221  	}
   222  	c.Check(matched, Equals, len(snapsWithDevModeFlag))
   223  }
   224  
   225  func (s *firstBoot20Suite) earlySetup(c *C, m *boot.Modeenv, modelGrade asserts.ModelGrade, extraGadgetYaml string, extraSnaps ...string) (model *asserts.Model, bloader *bootloadertest.MockExtractedRunKernelImageBootloader) {
   226  	c.Assert(m, NotNil, Commentf("missing modeenv test data"))
   227  	err := m.WriteTo("")
   228  	c.Assert(err, IsNil)
   229  
   230  	sysLabel := m.RecoverySystem
   231  	model = s.setupCore20Seed(c, sysLabel, modelGrade, extraGadgetYaml, extraSnaps...)
   232  	// sanity check that our returned model has the expected grade
   233  	c.Assert(model.Grade(), Equals, modelGrade)
   234  
   235  	bloader = bootloadertest.Mock("mock", c.MkDir()).WithExtractedRunKernelImage()
   236  	bootloader.Force(bloader)
   237  	s.AddCleanup(func() { bootloader.Force(nil) })
   238  
   239  	// since we are in runmode, MakeBootable will already have run from install
   240  	// mode, and extracted the kernel assets for the kernel snap into the
   241  	// bootloader, so set the current kernel there
   242  	kernel, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap")
   243  	c.Assert(err, IsNil)
   244  	r := bloader.SetEnabledKernel(kernel)
   245  	s.AddCleanup(r)
   246  
   247  	return model, bloader
   248  }
   249  
   250  func (s *firstBoot20Suite) testPopulateFromSeedCore20Happy(c *C, m *boot.Modeenv, modelGrade asserts.ModelGrade, extraDevModeSnaps ...string) {
   251  	var sysdLog [][]string
   252  	systemctlRestorer := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) {
   253  		sysdLog = append(sysdLog, cmd)
   254  		return []byte("ActiveState=inactive\n"), nil
   255  	})
   256  	defer systemctlRestorer()
   257  
   258  	model, bloader := s.earlySetup(c, m, modelGrade, "", extraDevModeSnaps...)
   259  	// create overlord and pick up the modeenv
   260  	s.startOverlord(c)
   261  
   262  	opts := devicestate.PopulateStateFromSeedOptions{
   263  		Label: m.RecoverySystem,
   264  		Mode:  m.Mode,
   265  	}
   266  
   267  	// run the firstboot stuff
   268  	st := s.overlord.State()
   269  	st.Lock()
   270  	defer st.Unlock()
   271  	tsAll, err := devicestate.PopulateStateFromSeedImpl(st, &opts, s.perfTimings)
   272  	c.Assert(err, IsNil)
   273  
   274  	snaps := []string{"snapd", "pc-kernel", "core20", "pc"}
   275  	allDevModeSnaps := stripSnapNamesWithChannels(extraDevModeSnaps)
   276  	if len(extraDevModeSnaps) != 0 {
   277  		snaps = append(snaps, allDevModeSnaps...)
   278  	}
   279  	checkOrder(c, tsAll, snaps...)
   280  
   281  	// if the model is dangerous check that the devmode snaps in the model have
   282  	// the flag set in snapstate for DevMode confinement
   283  	// XXX: eventually we may need more complicated checks here and for
   284  	// non-dangerous models only specific snaps may have this flag set
   285  	if modelGrade == asserts.ModelDangerous {
   286  		checkSnapstateDevModeFlags(c, tsAll, allDevModeSnaps...)
   287  	}
   288  
   289  	// now run the change and check the result
   290  	// use the expected kind otherwise settle with start another one
   291  	chg := st.NewChange("seed", "run the populate from seed changes")
   292  	for _, ts := range tsAll {
   293  		chg.AddAll(ts)
   294  	}
   295  	c.Assert(st.Changes(), HasLen, 1)
   296  
   297  	c.Assert(chg.Err(), IsNil)
   298  
   299  	// avoid device reg
   300  	chg1 := st.NewChange("become-operational", "init device")
   301  	chg1.SetStatus(state.DoingStatus)
   302  
   303  	// run change until it wants to restart
   304  	st.Unlock()
   305  	err = s.overlord.Settle(settleTimeout)
   306  	st.Lock()
   307  	c.Assert(err, IsNil)
   308  
   309  	// at this point the system is "restarting", pretend the restart has
   310  	// happened
   311  	c.Assert(chg.Status(), Equals, state.DoingStatus)
   312  	state.MockRestarting(st, state.RestartUnset)
   313  	st.Unlock()
   314  	err = s.overlord.Settle(settleTimeout)
   315  	st.Lock()
   316  	c.Assert(err, IsNil)
   317  	c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("%s", chg.Err()))
   318  
   319  	// verify
   320  	f, err := os.Open(dirs.SnapStateFile)
   321  	c.Assert(err, IsNil)
   322  	state, err := state.ReadState(nil, f)
   323  	c.Assert(err, IsNil)
   324  
   325  	state.Lock()
   326  	defer state.Unlock()
   327  	// check snapd, core20, kernel, gadget
   328  	_, err = snapstate.CurrentInfo(state, "snapd")
   329  	c.Check(err, IsNil)
   330  	_, err = snapstate.CurrentInfo(state, "core20")
   331  	c.Check(err, IsNil)
   332  	_, err = snapstate.CurrentInfo(state, "pc-kernel")
   333  	c.Check(err, IsNil)
   334  	_, err = snapstate.CurrentInfo(state, "pc")
   335  	c.Check(err, IsNil)
   336  
   337  	// ensure required flag is set on all essential snaps
   338  	var snapst snapstate.SnapState
   339  	for _, reqName := range []string{"snapd", "core20", "pc-kernel", "pc"} {
   340  		err = snapstate.Get(state, reqName, &snapst)
   341  		c.Assert(err, IsNil)
   342  		c.Assert(snapst.Required, Equals, true, Commentf("required not set for %v", reqName))
   343  
   344  		if m.Mode == "run" {
   345  			// also ensure that in run mode none of the snaps are installed as
   346  			// symlinks, they must be copied onto ubuntu-data
   347  			files, err := filepath.Glob(filepath.Join(dirs.SnapBlobDir, reqName+"_*.snap"))
   348  			c.Assert(err, IsNil)
   349  			c.Assert(files, HasLen, 1)
   350  			c.Assert(osutil.IsSymlink(files[0]), Equals, false)
   351  		}
   352  	}
   353  
   354  	// the right systemd commands were run
   355  	c.Check(sysdLog, testutil.DeepContains, []string{"start", "usr-lib-snapd.mount"})
   356  
   357  	// and ensure state is now considered seeded
   358  	var seeded bool
   359  	err = state.Get("seeded", &seeded)
   360  	c.Assert(err, IsNil)
   361  	c.Check(seeded, Equals, true)
   362  
   363  	// check we set seed-time
   364  	var seedTime time.Time
   365  	err = state.Get("seed-time", &seedTime)
   366  	c.Assert(err, IsNil)
   367  	c.Check(seedTime.IsZero(), Equals, false)
   368  
   369  	// check that we removed recovery_system from modeenv
   370  	m2, err := boot.ReadModeenv("")
   371  	c.Assert(err, IsNil)
   372  	if m.Mode == "run" {
   373  		// recovery system is cleared in run mode
   374  		c.Assert(m2.RecoverySystem, Equals, "")
   375  	} else {
   376  		// but kept intact in other modes
   377  		c.Assert(m2.RecoverySystem, Equals, m.RecoverySystem)
   378  	}
   379  	c.Assert(m2.Base, Equals, m.Base)
   380  	c.Assert(m2.Mode, Equals, m.Mode)
   381  	// Note that we don't check CurrentKernels in the modeenv, even though in a
   382  	// real first boot that would also be set here, because setting that is done
   383  	// in the snapstate manager, not the devicestate manager
   384  
   385  	// check that the default device ctx has a Modeenv
   386  	dev, err := devicestate.DeviceCtx(s.overlord.State(), nil, nil)
   387  	c.Assert(err, IsNil)
   388  	c.Assert(dev.HasModeenv(), Equals, true)
   389  
   390  	// check that we marked the boot successful with bootstate20 methods, namely
   391  	// that we called SetNext, which since it was called on the kernel we
   392  	// already booted from, we should only have checked what the current kernel
   393  	// is
   394  
   395  	if m.Mode == "run" {
   396  		// only relevant in run mode
   397  
   398  		// the 3 calls here are :
   399  		// * 1 from MarkBootSuccessful() from ensureBootOk() before we restart
   400  		// * 1 from boot.SetNextBoot() from LinkSnap() from doInstall() from InstallPath() from
   401  		//     installSeedSnap() after restart
   402  		// * 1 from boot.GetCurrentBoot() from FinishRestart after restart
   403  		_, numKernelCalls := bloader.GetRunKernelImageFunctionSnapCalls("Kernel")
   404  		c.Assert(numKernelCalls, Equals, 3)
   405  	}
   406  	actual, _ := bloader.GetRunKernelImageFunctionSnapCalls("EnableKernel")
   407  	c.Assert(actual, HasLen, 0)
   408  	actual, _ = bloader.GetRunKernelImageFunctionSnapCalls("DisableTryKernel")
   409  	c.Assert(actual, HasLen, 0)
   410  	actual, _ = bloader.GetRunKernelImageFunctionSnapCalls("EnableTryKernel")
   411  	c.Assert(actual, HasLen, 0)
   412  
   413  	var whatseeded []devicestate.SeededSystem
   414  	err = state.Get("seeded-systems", &whatseeded)
   415  	if m.Mode == "run" {
   416  		c.Assert(err, IsNil)
   417  		c.Assert(whatseeded, DeepEquals, []devicestate.SeededSystem{{
   418  			System:    m.RecoverySystem,
   419  			Model:     "my-model",
   420  			BrandID:   "my-brand",
   421  			Revision:  model.Revision(),
   422  			Timestamp: model.Timestamp(),
   423  			SeedTime:  seedTime,
   424  		}})
   425  	} else {
   426  		c.Assert(err, NotNil)
   427  	}
   428  }
   429  
   430  func (s *firstBoot20Suite) TestPopulateFromSeedCore20RunModeDangerousWithDevmode(c *C) {
   431  	m := boot.Modeenv{
   432  		Mode:           "run",
   433  		RecoverySystem: "20191018",
   434  		Base:           "core20_1.snap",
   435  	}
   436  	s.testPopulateFromSeedCore20Happy(c, &m, asserts.ModelDangerous, "test-devmode=20")
   437  }
   438  
   439  func (s *firstBoot20Suite) TestPopulateFromSeedCore20RunMode(c *C) {
   440  	m := boot.Modeenv{
   441  		Mode:           "run",
   442  		RecoverySystem: "20191018",
   443  		Base:           "core20_1.snap",
   444  	}
   445  	for _, grade := range allGrades {
   446  		s.testPopulateFromSeedCore20Happy(c, &m, grade)
   447  	}
   448  }
   449  
   450  func (s *firstBoot20Suite) TestPopulateFromSeedCore20InstallMode(c *C) {
   451  	m := boot.Modeenv{
   452  		Mode:           "install",
   453  		RecoverySystem: "20191019",
   454  		Base:           "core20_1.snap",
   455  	}
   456  	for _, grade := range allGrades {
   457  		s.testPopulateFromSeedCore20Happy(c, &m, grade)
   458  	}
   459  }
   460  
   461  func (s *firstBoot20Suite) TestPopulateFromSeedCore20RecoverMode(c *C) {
   462  	m := boot.Modeenv{
   463  		Mode:           "recover",
   464  		RecoverySystem: "20191020",
   465  		Base:           "core20_1.snap",
   466  	}
   467  	for _, grade := range allGrades {
   468  		s.testPopulateFromSeedCore20Happy(c, &m, grade)
   469  	}
   470  }
   471  
   472  func (s *firstBoot20Suite) TestLoadDeviceSeedCore20(c *C) {
   473  	m := boot.Modeenv{
   474  		Mode:           "run",
   475  		RecoverySystem: "20191018",
   476  		Base:           "core20_1.snap",
   477  	}
   478  
   479  	s.earlySetup(c, &m, "signed", "")
   480  
   481  	o, err := overlord.New(nil)
   482  	c.Assert(err, IsNil)
   483  	st := o.State()
   484  
   485  	st.Lock()
   486  	defer st.Unlock()
   487  
   488  	deviceSeed, err := devicestate.LoadDeviceSeed(st, m.RecoverySystem)
   489  	c.Assert(err, IsNil)
   490  
   491  	c.Check(deviceSeed.Model().BrandID(), Equals, "my-brand")
   492  	c.Check(deviceSeed.Model().Model(), Equals, "my-model")
   493  	c.Check(deviceSeed.Model().Base(), Equals, "core20")
   494  
   495  	// verify that the model was added
   496  	db := assertstate.DB(st)
   497  	as, err := db.Find(asserts.ModelType, map[string]string{
   498  		"series":   "16",
   499  		"brand-id": "my-brand",
   500  		"model":    "my-model",
   501  	})
   502  	c.Assert(err, IsNil)
   503  	c.Check(as, DeepEquals, deviceSeed.Model())
   504  
   505  	// inconsistent seed request
   506  	_, err = devicestate.LoadDeviceSeed(st, "20210201")
   507  	c.Assert(err, ErrorMatches, `internal error: requested inconsistent device seed: 20210201 \(was 20191018\)`)
   508  }
   509  
   510  func (s *firstBoot20Suite) TestPopulateFromSeedCore20RunModeUserServiceTasks(c *C) {
   511  	// check that this is test is still valid
   512  	// TODO: have a test for an early config option that is not an
   513  	// experimental flag
   514  	c.Assert(features.UserDaemons.IsEnabledWhenUnset(), Equals, false, Commentf("user-daemons is not experimental anymore, this test is not useful anymore"))
   515  
   516  	s.extraSnapYaml["user-daemons1"] = `name: user-daemons1
   517  version: 1.0
   518  type: app
   519  base: core20
   520  
   521  apps:
   522    foo:
   523      daemon: simple
   524      daemon-scope: user
   525  `
   526  	m := boot.Modeenv{
   527  		Mode:           "run",
   528  		RecoverySystem: "20191018",
   529  		Base:           "core20_1.snap",
   530  	}
   531  
   532  	defaultsGadgetYaml := `
   533  defaults:
   534     system:
   535        experimental:
   536          user-daemons: true
   537  `
   538  
   539  	s.earlySetup(c, &m, "signed", defaultsGadgetYaml, "user-daemons1")
   540  
   541  	// create a new overlord and pick up the modeenv
   542  	// this overlord will use the proper EarlyConfig implementation
   543  	o, err := overlord.New(nil)
   544  	c.Assert(err, IsNil)
   545  	o.InterfaceManager().DisableUDevMonitor()
   546  	c.Assert(o.StartUp(), IsNil)
   547  
   548  	st := o.State()
   549  	st.Lock()
   550  	defer st.Unlock()
   551  
   552  	// early config set the flag to enabled
   553  	tr := config.NewTransaction(st)
   554  	enabled, _ := features.Flag(tr, features.UserDaemons)
   555  	c.Check(enabled, Equals, true)
   556  
   557  	opts := devicestate.PopulateStateFromSeedOptions{
   558  		Label: m.RecoverySystem,
   559  		Mode:  m.Mode,
   560  	}
   561  
   562  	_, err = devicestate.PopulateStateFromSeedImpl(st, &opts, s.perfTimings)
   563  	c.Assert(err, IsNil)
   564  }
   565  
   566  func (s *firstBoot20Suite) TestUsersCreateAutomaticIsAvailableEarly(c *C) {
   567  	m := boot.Modeenv{
   568  		Mode:           "run",
   569  		RecoverySystem: "20191018",
   570  		Base:           "core20_1.snap",
   571  	}
   572  
   573  	defaultsGadgetYaml := `
   574  defaults:
   575     system:
   576        users:
   577          create.automatic: false
   578  `
   579  
   580  	s.earlySetup(c, &m, "signed", defaultsGadgetYaml)
   581  
   582  	// create a new overlord and pick up the modeenv
   583  	// this overlord will use the proper EarlyConfig implementation
   584  	o, err := overlord.New(nil)
   585  	c.Assert(err, IsNil)
   586  	o.InterfaceManager().DisableUDevMonitor()
   587  	c.Assert(o.StartUp(), IsNil)
   588  
   589  	st := o.State()
   590  	st.Lock()
   591  	defer st.Unlock()
   592  
   593  	// early config in StartUp made the option available already
   594  	tr := config.NewTransaction(st)
   595  	var enabled bool
   596  	err = tr.Get("core", "users.create.automatic", &enabled)
   597  	c.Assert(err, IsNil)
   598  	c.Check(enabled, Equals, false)
   599  }