github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/devicestate/devicestate_install_mode_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  	"bytes"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  
    29  	. "gopkg.in/check.v1"
    30  
    31  	"github.com/snapcore/snapd/asserts"
    32  	"github.com/snapcore/snapd/boot"
    33  	"github.com/snapcore/snapd/dirs"
    34  	"github.com/snapcore/snapd/gadget"
    35  	"github.com/snapcore/snapd/gadget/install"
    36  	"github.com/snapcore/snapd/overlord/auth"
    37  	"github.com/snapcore/snapd/overlord/devicestate"
    38  	"github.com/snapcore/snapd/overlord/devicestate/devicestatetest"
    39  	"github.com/snapcore/snapd/overlord/snapstate"
    40  	"github.com/snapcore/snapd/overlord/state"
    41  	"github.com/snapcore/snapd/release"
    42  	"github.com/snapcore/snapd/snap"
    43  	"github.com/snapcore/snapd/snap/snaptest"
    44  	"github.com/snapcore/snapd/sysconfig"
    45  	"github.com/snapcore/snapd/testutil"
    46  )
    47  
    48  type deviceMgrInstallModeSuite struct {
    49  	deviceMgrBaseSuite
    50  
    51  	configureRunSystemOptsPassed []*sysconfig.Options
    52  	configureRunSystemErr        error
    53  }
    54  
    55  var _ = Suite(&deviceMgrInstallModeSuite{})
    56  
    57  func (s *deviceMgrInstallModeSuite) findInstallSystem() *state.Change {
    58  	for _, chg := range s.state.Changes() {
    59  		if chg.Kind() == "install-system" {
    60  			return chg
    61  		}
    62  	}
    63  	return nil
    64  }
    65  
    66  func (s *deviceMgrInstallModeSuite) SetUpTest(c *C) {
    67  	s.deviceMgrBaseSuite.SetUpTest(c)
    68  
    69  	s.configureRunSystemOptsPassed = nil
    70  	s.configureRunSystemErr = nil
    71  	restore := devicestate.MockSysconfigConfigureRunSystem(func(opts *sysconfig.Options) error {
    72  		s.configureRunSystemOptsPassed = append(s.configureRunSystemOptsPassed, opts)
    73  		return s.configureRunSystemErr
    74  	})
    75  	s.AddCleanup(restore)
    76  
    77  	restore = devicestate.MockSecbootCheckKeySealingSupported(func() error {
    78  		return fmt.Errorf("TPM not available")
    79  	})
    80  	s.AddCleanup(restore)
    81  
    82  	s.state.Lock()
    83  	defer s.state.Unlock()
    84  	s.state.Set("seeded", true)
    85  }
    86  
    87  func (s *deviceMgrInstallModeSuite) makeMockInstalledPcGadget(c *C, grade, gadgetDefaultsYaml string) *asserts.Model {
    88  	const (
    89  		pcSnapID       = "pcididididididididididididididid"
    90  		pcKernelSnapID = "pckernelidididididididididididid"
    91  		core20SnapID   = "core20ididididididididididididid"
    92  	)
    93  	si := &snap.SideInfo{
    94  		RealName: "pc-kernel",
    95  		Revision: snap.R(1),
    96  		SnapID:   pcKernelSnapID,
    97  	}
    98  	snapstate.Set(s.state, "pc-kernel", &snapstate.SnapState{
    99  		SnapType: "kernel",
   100  		Sequence: []*snap.SideInfo{si},
   101  		Current:  si.Revision,
   102  		Active:   true,
   103  	})
   104  	kernelInfo := snaptest.MockSnapWithFiles(c, "name: pc-kernel\ntype: kernel", si, nil)
   105  	kernelFn := snaptest.MakeTestSnapWithFiles(c, "name: pc-kernel\ntype: kernel\nversion: 1.0", nil)
   106  	err := os.Rename(kernelFn, kernelInfo.MountFile())
   107  	c.Assert(err, IsNil)
   108  
   109  	si = &snap.SideInfo{
   110  		RealName: "pc",
   111  		Revision: snap.R(1),
   112  		SnapID:   pcSnapID,
   113  	}
   114  	snapstate.Set(s.state, "pc", &snapstate.SnapState{
   115  		SnapType: "gadget",
   116  		Sequence: []*snap.SideInfo{si},
   117  		Current:  si.Revision,
   118  		Active:   true,
   119  	})
   120  	snaptest.MockSnapWithFiles(c, "name: pc\ntype: gadget", si, [][]string{
   121  		{"meta/gadget.yaml", gadgetYaml + gadgetDefaultsYaml},
   122  	})
   123  
   124  	si = &snap.SideInfo{
   125  		RealName: "core20",
   126  		Revision: snap.R(2),
   127  		SnapID:   core20SnapID,
   128  	}
   129  	snapstate.Set(s.state, "core20", &snapstate.SnapState{
   130  		SnapType: "base",
   131  		Sequence: []*snap.SideInfo{si},
   132  		Current:  si.Revision,
   133  		Active:   true,
   134  	})
   135  	snaptest.MockSnapWithFiles(c, "name: core20\ntype: base", si, nil)
   136  
   137  	mockModel := s.makeModelAssertionInState(c, "my-brand", "my-model", map[string]interface{}{
   138  		"display-name": "my model",
   139  		"architecture": "amd64",
   140  		"base":         "core20",
   141  		"grade":        grade,
   142  		"snaps": []interface{}{
   143  			map[string]interface{}{
   144  				"name":            "pc-kernel",
   145  				"id":              pcKernelSnapID,
   146  				"type":            "kernel",
   147  				"default-channel": "20",
   148  			},
   149  			map[string]interface{}{
   150  				"name":            "pc",
   151  				"id":              pcSnapID,
   152  				"type":            "gadget",
   153  				"default-channel": "20",
   154  			}},
   155  	})
   156  	devicestatetest.SetDevice(s.state, &auth.DeviceState{
   157  		Brand: "my-brand",
   158  		Model: "my-model",
   159  		// no serial in install mode
   160  	})
   161  
   162  	return mockModel
   163  }
   164  
   165  type encTestCase struct {
   166  	tpm     bool
   167  	bypass  bool
   168  	encrypt bool
   169  }
   170  
   171  func (s *deviceMgrInstallModeSuite) doRunChangeTestWithEncryption(c *C, grade string, tc encTestCase) error {
   172  	restore := release.MockOnClassic(false)
   173  	defer restore()
   174  
   175  	var brGadgetRoot, brDevice string
   176  	var brOpts install.Options
   177  	var installRunCalled int
   178  	var sealingObserver gadget.ContentObserver
   179  	restore = devicestate.MockInstallRun(func(gadgetRoot, device string, options install.Options, obs install.SystemInstallObserver) error {
   180  		// ensure we can grab the lock here, i.e. that it's not taken
   181  		s.state.Lock()
   182  		s.state.Unlock()
   183  
   184  		brGadgetRoot = gadgetRoot
   185  		brDevice = device
   186  		brOpts = options
   187  		sealingObserver = obs
   188  		installRunCalled++
   189  		return nil
   190  	})
   191  	defer restore()
   192  
   193  	restore = devicestate.MockSecbootCheckKeySealingSupported(func() error {
   194  		if tc.tpm {
   195  			return nil
   196  		} else {
   197  			return fmt.Errorf("TPM not available")
   198  		}
   199  	})
   200  	defer restore()
   201  
   202  	s.state.Lock()
   203  	mockModel := s.makeMockInstalledPcGadget(c, grade, "")
   204  	s.state.Unlock()
   205  
   206  	bypassEncryptionPath := filepath.Join(boot.InitramfsUbuntuSeedDir, ".force-unencrypted")
   207  	if tc.bypass {
   208  		err := os.MkdirAll(filepath.Dir(bypassEncryptionPath), 0755)
   209  		c.Assert(err, IsNil)
   210  		f, err := os.Create(bypassEncryptionPath)
   211  		c.Assert(err, IsNil)
   212  		f.Close()
   213  	} else {
   214  		os.RemoveAll(bypassEncryptionPath)
   215  	}
   216  
   217  	bootMakeBootableCalled := 0
   218  	restore = devicestate.MockBootMakeBootable(func(model *asserts.Model, rootdir string, bootWith *boot.BootableSet, seal *boot.TrustedAssetsInstallObserver) error {
   219  		c.Check(model, DeepEquals, mockModel)
   220  		c.Check(rootdir, Equals, dirs.GlobalRootDir)
   221  		c.Check(bootWith.KernelPath, Matches, ".*/var/lib/snapd/snaps/pc-kernel_1.snap")
   222  		c.Check(bootWith.BasePath, Matches, ".*/var/lib/snapd/snaps/core20_2.snap")
   223  		c.Check(bootWith.RecoverySystemDir, Matches, "/systems/20191218")
   224  		c.Check(bootWith.UnpackedGadgetDir, Equals, filepath.Join(dirs.SnapMountDir, "pc/1"))
   225  		if tc.encrypt {
   226  			c.Check(seal, NotNil)
   227  		}
   228  		bootMakeBootableCalled++
   229  		return nil
   230  	})
   231  	defer restore()
   232  
   233  	modeenv := boot.Modeenv{
   234  		Mode:           "install",
   235  		RecoverySystem: "20191218",
   236  	}
   237  	c.Assert(modeenv.WriteTo(""), IsNil)
   238  	devicestate.SetSystemMode(s.mgr, "install")
   239  
   240  	// normally done by snap-bootstrap
   241  	err := os.MkdirAll(boot.InitramfsUbuntuBootDir, 0755)
   242  	c.Assert(err, IsNil)
   243  
   244  	s.settle(c)
   245  
   246  	// the install-system change is created
   247  	s.state.Lock()
   248  	defer s.state.Unlock()
   249  	installSystem := s.findInstallSystem()
   250  	c.Assert(installSystem, NotNil)
   251  
   252  	// and was run successfully
   253  	if err := installSystem.Err(); err != nil {
   254  		// we failed, no further checks needed
   255  		return err
   256  	}
   257  
   258  	c.Assert(installSystem.Status(), Equals, state.DoneStatus)
   259  
   260  	// in the right way
   261  	if tc.encrypt {
   262  		c.Assert(brGadgetRoot, Equals, filepath.Join(dirs.SnapMountDir, "/pc/1"))
   263  		c.Assert(brDevice, Equals, "")
   264  		c.Assert(brOpts, DeepEquals, install.Options{
   265  			Mount:   true,
   266  			Encrypt: true,
   267  		})
   268  
   269  		// inteface is not nil
   270  		c.Assert(sealingObserver, NotNil)
   271  		// we expect a very specific type
   272  		trustedInstallObserver, ok := sealingObserver.(*boot.TrustedAssetsInstallObserver)
   273  		c.Assert(ok, Equals, true, Commentf("unexpected type: %T", sealingObserver))
   274  		c.Assert(trustedInstallObserver, NotNil)
   275  	} else {
   276  		c.Assert(brGadgetRoot, Equals, filepath.Join(dirs.SnapMountDir, "/pc/1"))
   277  		c.Assert(brDevice, Equals, "")
   278  		c.Assert(brOpts, DeepEquals, install.Options{
   279  			Mount: true,
   280  		})
   281  	}
   282  	c.Assert(installRunCalled, Equals, 1)
   283  	c.Assert(bootMakeBootableCalled, Equals, 1)
   284  	c.Assert(s.restartRequests, DeepEquals, []state.RestartType{state.RestartSystemNow})
   285  
   286  	return nil
   287  }
   288  
   289  func (s *deviceMgrInstallModeSuite) TestInstallTaskErrors(c *C) {
   290  	restore := release.MockOnClassic(false)
   291  	defer restore()
   292  
   293  	restore = devicestate.MockInstallRun(func(gadgetRoot, device string, options install.Options, _ install.SystemInstallObserver) error {
   294  		return fmt.Errorf("The horror, The horror")
   295  	})
   296  	defer restore()
   297  
   298  	err := ioutil.WriteFile(filepath.Join(dirs.GlobalRootDir, "/var/lib/snapd/modeenv"),
   299  		[]byte("mode=install\n"), 0644)
   300  	c.Assert(err, IsNil)
   301  
   302  	s.state.Lock()
   303  	s.makeMockInstalledPcGadget(c, "dangerous", "")
   304  	devicestate.SetSystemMode(s.mgr, "install")
   305  	s.state.Unlock()
   306  
   307  	s.settle(c)
   308  
   309  	s.state.Lock()
   310  	defer s.state.Unlock()
   311  
   312  	installSystem := s.findInstallSystem()
   313  	c.Check(installSystem.Err(), ErrorMatches, `(?ms)cannot perform the following tasks:
   314  - Setup system for run mode \(cannot create partitions: The horror, The horror\)`)
   315  	// no restart request on failure
   316  	c.Check(s.restartRequests, HasLen, 0)
   317  }
   318  
   319  func (s *deviceMgrInstallModeSuite) TestInstallModeNotInstallmodeNoChg(c *C) {
   320  	restore := release.MockOnClassic(false)
   321  	defer restore()
   322  
   323  	s.state.Lock()
   324  	devicestate.SetSystemMode(s.mgr, "")
   325  	s.state.Unlock()
   326  
   327  	s.settle(c)
   328  
   329  	s.state.Lock()
   330  	defer s.state.Unlock()
   331  
   332  	// the install-system change is *not* created (not in install mode)
   333  	installSystem := s.findInstallSystem()
   334  	c.Assert(installSystem, IsNil)
   335  }
   336  
   337  func (s *deviceMgrInstallModeSuite) TestInstallModeNotClassic(c *C) {
   338  	restore := release.MockOnClassic(true)
   339  	defer restore()
   340  
   341  	s.state.Lock()
   342  	devicestate.SetSystemMode(s.mgr, "install")
   343  	s.state.Unlock()
   344  
   345  	s.settle(c)
   346  
   347  	s.state.Lock()
   348  	defer s.state.Unlock()
   349  
   350  	// the install-system change is *not* created (we're on classic)
   351  	installSystem := s.findInstallSystem()
   352  	c.Assert(installSystem, IsNil)
   353  }
   354  
   355  func (s *deviceMgrInstallModeSuite) TestInstallDangerous(c *C) {
   356  	err := s.doRunChangeTestWithEncryption(c, "dangerous", encTestCase{tpm: false, bypass: false, encrypt: false})
   357  	c.Assert(err, IsNil)
   358  }
   359  
   360  func (s *deviceMgrInstallModeSuite) TestInstallDangerousWithTPM(c *C) {
   361  	err := s.doRunChangeTestWithEncryption(c, "dangerous", encTestCase{tpm: true, bypass: false, encrypt: true})
   362  	c.Assert(err, IsNil)
   363  }
   364  
   365  func (s *deviceMgrInstallModeSuite) TestInstallDangerousBypassEncryption(c *C) {
   366  	err := s.doRunChangeTestWithEncryption(c, "dangerous", encTestCase{tpm: false, bypass: true, encrypt: false})
   367  	c.Assert(err, IsNil)
   368  }
   369  
   370  func (s *deviceMgrInstallModeSuite) TestInstallDangerousWithTPMBypassEncryption(c *C) {
   371  	err := s.doRunChangeTestWithEncryption(c, "dangerous", encTestCase{tpm: true, bypass: true, encrypt: false})
   372  	c.Assert(err, IsNil)
   373  }
   374  
   375  func (s *deviceMgrInstallModeSuite) TestInstallSigned(c *C) {
   376  	err := s.doRunChangeTestWithEncryption(c, "signed", encTestCase{tpm: false, bypass: false, encrypt: false})
   377  	c.Assert(err, IsNil)
   378  }
   379  
   380  func (s *deviceMgrInstallModeSuite) TestInstallSignedWithTPM(c *C) {
   381  	err := s.doRunChangeTestWithEncryption(c, "signed", encTestCase{tpm: true, bypass: false, encrypt: true})
   382  	c.Assert(err, IsNil)
   383  }
   384  
   385  func (s *deviceMgrInstallModeSuite) TestInstallSignedBypassEncryption(c *C) {
   386  	err := s.doRunChangeTestWithEncryption(c, "signed", encTestCase{tpm: false, bypass: true, encrypt: false})
   387  	c.Assert(err, IsNil)
   388  }
   389  
   390  func (s *deviceMgrInstallModeSuite) TestInstallSecured(c *C) {
   391  	err := s.doRunChangeTestWithEncryption(c, "secured", encTestCase{tpm: false, bypass: false, encrypt: false})
   392  	c.Assert(err, ErrorMatches, "(?s).*cannot encrypt secured device: TPM not available.*")
   393  }
   394  
   395  func (s *deviceMgrInstallModeSuite) TestInstallSecuredWithTPM(c *C) {
   396  	err := s.doRunChangeTestWithEncryption(c, "secured", encTestCase{tpm: true, bypass: false, encrypt: true})
   397  	c.Assert(err, IsNil)
   398  }
   399  
   400  func (s *deviceMgrInstallModeSuite) TestInstallSecuredBypassEncryption(c *C) {
   401  	err := s.doRunChangeTestWithEncryption(c, "secured", encTestCase{tpm: false, bypass: true, encrypt: false})
   402  	c.Assert(err, ErrorMatches, "(?s).*cannot encrypt secured device: TPM not available.*")
   403  }
   404  
   405  func (s *deviceMgrInstallModeSuite) mockInstallModeChange(c *C, modelGrade, gadgetDefaultsYaml string) *asserts.Model {
   406  	restore := release.MockOnClassic(false)
   407  	defer restore()
   408  
   409  	restore = devicestate.MockInstallRun(func(gadgetRoot, device string, options install.Options, _ install.SystemInstallObserver) error {
   410  		return nil
   411  	})
   412  	defer restore()
   413  
   414  	s.state.Lock()
   415  	mockModel := s.makeMockInstalledPcGadget(c, modelGrade, gadgetDefaultsYaml)
   416  	s.state.Unlock()
   417  	c.Check(mockModel.Grade(), Equals, asserts.ModelGrade(modelGrade))
   418  
   419  	restore = devicestate.MockBootMakeBootable(func(model *asserts.Model, rootdir string, bootWith *boot.BootableSet, seal *boot.TrustedAssetsInstallObserver) error {
   420  		return nil
   421  	})
   422  	defer restore()
   423  
   424  	modeenv := boot.Modeenv{
   425  		Mode:           "install",
   426  		RecoverySystem: "20191218",
   427  	}
   428  	c.Assert(modeenv.WriteTo(""), IsNil)
   429  	devicestate.SetSystemMode(s.mgr, "install")
   430  
   431  	// normally done by snap-bootstrap
   432  	err := os.MkdirAll(boot.InitramfsUbuntuBootDir, 0755)
   433  	c.Assert(err, IsNil)
   434  
   435  	s.settle(c)
   436  
   437  	return mockModel
   438  }
   439  
   440  func (s *deviceMgrInstallModeSuite) TestInstallModeRunSysconfig(c *C) {
   441  	s.mockInstallModeChange(c, "dangerous", "")
   442  
   443  	s.state.Lock()
   444  	defer s.state.Unlock()
   445  
   446  	// the install-system change is created
   447  	installSystem := s.findInstallSystem()
   448  	c.Assert(installSystem, NotNil)
   449  
   450  	// and was run successfully
   451  	c.Check(installSystem.Err(), IsNil)
   452  	c.Check(installSystem.Status(), Equals, state.DoneStatus)
   453  
   454  	// and sysconfig.ConfigureRunSystem was run exactly once
   455  	c.Assert(s.configureRunSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   456  		{
   457  			AllowCloudInit: true,
   458  			TargetRootDir:  boot.InstallHostWritableDir,
   459  			GadgetDir:      filepath.Join(dirs.SnapMountDir, "pc/1/"),
   460  		},
   461  	})
   462  }
   463  
   464  func (s *deviceMgrInstallModeSuite) TestInstallModeRunSysconfigErr(c *C) {
   465  	s.configureRunSystemErr = fmt.Errorf("error from sysconfig.ConfigureRunSystem")
   466  	s.mockInstallModeChange(c, "dangerous", "")
   467  
   468  	s.state.Lock()
   469  	defer s.state.Unlock()
   470  
   471  	// the install-system was run but errorred as specified in the above mock
   472  	installSystem := s.findInstallSystem()
   473  	c.Check(installSystem.Err(), ErrorMatches, `(?ms)cannot perform the following tasks:
   474  - Setup system for run mode \(error from sysconfig.ConfigureRunSystem\)`)
   475  	// and sysconfig.ConfigureRunSystem was run exactly once
   476  	c.Assert(s.configureRunSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   477  		{
   478  			AllowCloudInit: true,
   479  			TargetRootDir:  boot.InstallHostWritableDir,
   480  			GadgetDir:      filepath.Join(dirs.SnapMountDir, "pc/1/"),
   481  		},
   482  	})
   483  }
   484  
   485  func (s *deviceMgrInstallModeSuite) TestInstallModeSupportsCloudInitInDangerous(c *C) {
   486  	// pretend we have a cloud-init config on the seed partition
   487  	cloudCfg := filepath.Join(boot.InitramfsUbuntuSeedDir, "data/etc/cloud/cloud.cfg.d")
   488  	err := os.MkdirAll(cloudCfg, 0755)
   489  	c.Assert(err, IsNil)
   490  	for _, mockCfg := range []string{"foo.cfg", "bar.cfg"} {
   491  		err = ioutil.WriteFile(filepath.Join(cloudCfg, mockCfg), []byte(fmt.Sprintf("%s config", mockCfg)), 0644)
   492  		c.Assert(err, IsNil)
   493  	}
   494  
   495  	s.mockInstallModeChange(c, "dangerous", "")
   496  
   497  	// and did tell sysconfig about the cloud-init files
   498  	c.Assert(s.configureRunSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   499  		{
   500  			AllowCloudInit:  true,
   501  			CloudInitSrcDir: filepath.Join(boot.InitramfsUbuntuSeedDir, "data/etc/cloud/cloud.cfg.d"),
   502  			TargetRootDir:   boot.InstallHostWritableDir,
   503  			GadgetDir:       filepath.Join(dirs.SnapMountDir, "pc/1/"),
   504  		},
   505  	})
   506  }
   507  
   508  func (s *deviceMgrInstallModeSuite) TestInstallModeSignedNoUbuntuSeedCloudInit(c *C) {
   509  	// pretend we have a cloud-init config on the seed partition
   510  	cloudCfg := filepath.Join(boot.InitramfsUbuntuSeedDir, "data/etc/cloud/cloud.cfg.d")
   511  	err := os.MkdirAll(cloudCfg, 0755)
   512  	c.Assert(err, IsNil)
   513  	for _, mockCfg := range []string{"foo.cfg", "bar.cfg"} {
   514  		err = ioutil.WriteFile(filepath.Join(cloudCfg, mockCfg), []byte(fmt.Sprintf("%s config", mockCfg)), 0644)
   515  		c.Assert(err, IsNil)
   516  	}
   517  
   518  	s.mockInstallModeChange(c, "signed", "")
   519  
   520  	// and did NOT tell sysconfig about the cloud-init file, but also did not
   521  	// explicitly disable cloud init
   522  	c.Assert(s.configureRunSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   523  		{
   524  			AllowCloudInit: true,
   525  			TargetRootDir:  boot.InstallHostWritableDir,
   526  			GadgetDir:      filepath.Join(dirs.SnapMountDir, "pc/1/"),
   527  		},
   528  	})
   529  }
   530  
   531  func (s *deviceMgrInstallModeSuite) TestInstallModeSecuredGadgetCloudConfCloudInit(c *C) {
   532  	// pretend we have a cloud.conf from the gadget
   533  	gadgetDir := filepath.Join(dirs.SnapMountDir, "pc/1/")
   534  	err := os.MkdirAll(gadgetDir, 0755)
   535  	c.Assert(err, IsNil)
   536  	err = ioutil.WriteFile(filepath.Join(gadgetDir, "cloud.conf"), nil, 0644)
   537  	c.Assert(err, IsNil)
   538  
   539  	err = s.doRunChangeTestWithEncryption(c, "secured", encTestCase{tpm: true, bypass: false, encrypt: true})
   540  	c.Assert(err, IsNil)
   541  
   542  	c.Assert(s.configureRunSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   543  		{
   544  			AllowCloudInit: true,
   545  			TargetRootDir:  boot.InstallHostWritableDir,
   546  			GadgetDir:      filepath.Join(dirs.SnapMountDir, "pc/1/"),
   547  		},
   548  	})
   549  }
   550  
   551  func (s *deviceMgrInstallModeSuite) TestInstallModeSecuredNoUbuntuSeedCloudInit(c *C) {
   552  	// pretend we have a cloud-init config on the seed partition
   553  	cloudCfg := filepath.Join(boot.InitramfsUbuntuSeedDir, "data/etc/cloud/cloud.cfg.d")
   554  	err := os.MkdirAll(cloudCfg, 0755)
   555  	c.Assert(err, IsNil)
   556  	for _, mockCfg := range []string{"foo.cfg", "bar.cfg"} {
   557  		err = ioutil.WriteFile(filepath.Join(cloudCfg, mockCfg), []byte(fmt.Sprintf("%s config", mockCfg)), 0644)
   558  		c.Assert(err, IsNil)
   559  	}
   560  
   561  	err = s.doRunChangeTestWithEncryption(c, "secured", encTestCase{tpm: true, bypass: false, encrypt: true})
   562  	c.Assert(err, IsNil)
   563  
   564  	// and did NOT tell sysconfig about the cloud-init files, instead it was
   565  	// disabled because only gadget cloud-init is allowed
   566  	c.Assert(s.configureRunSystemOptsPassed, DeepEquals, []*sysconfig.Options{
   567  		{
   568  			AllowCloudInit: false,
   569  			TargetRootDir:  boot.InstallHostWritableDir,
   570  			GadgetDir:      filepath.Join(dirs.SnapMountDir, "pc/1/"),
   571  		},
   572  	})
   573  }
   574  
   575  func (s *deviceMgrInstallModeSuite) TestInstallModeWritesModel(c *C) {
   576  	// pretend we have a cloud-init config on the seed partition
   577  	model := s.mockInstallModeChange(c, "dangerous", "")
   578  
   579  	var buf bytes.Buffer
   580  	err := asserts.NewEncoder(&buf).Encode(model)
   581  	c.Assert(err, IsNil)
   582  
   583  	s.state.Lock()
   584  	defer s.state.Unlock()
   585  
   586  	installSystem := s.findInstallSystem()
   587  	c.Assert(installSystem, NotNil)
   588  
   589  	// and was run successfully
   590  	c.Check(installSystem.Err(), IsNil)
   591  	c.Check(installSystem.Status(), Equals, state.DoneStatus)
   592  
   593  	c.Check(filepath.Join(boot.InitramfsUbuntuBootDir, "model"), testutil.FileEquals, buf.String())
   594  }