github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/boot/initramfs_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 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 boot_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  
    28  	. "gopkg.in/check.v1"
    29  
    30  	"github.com/snapcore/snapd/boot"
    31  	"github.com/snapcore/snapd/boot/boottest"
    32  	"github.com/snapcore/snapd/bootloader"
    33  	"github.com/snapcore/snapd/bootloader/bootloadertest"
    34  	"github.com/snapcore/snapd/dirs"
    35  	"github.com/snapcore/snapd/snap"
    36  )
    37  
    38  type initramfsSuite struct {
    39  	baseBootenvSuite
    40  }
    41  
    42  var _ = Suite(&initramfsSuite{})
    43  
    44  func (s *initramfsSuite) SetUpTest(c *C) {
    45  	s.baseBootenvSuite.SetUpTest(c)
    46  }
    47  
    48  func (s *initramfsSuite) TestEnsureNextBootToRunMode(c *C) {
    49  	// with no bootloader available we can't mark successful
    50  	err := boot.EnsureNextBootToRunMode("label")
    51  	c.Assert(err, ErrorMatches, "cannot determine bootloader")
    52  
    53  	// forcing a bootloader works
    54  	bloader := bootloadertest.Mock("mock", c.MkDir())
    55  	bootloader.Force(bloader)
    56  	defer bootloader.Force(nil)
    57  
    58  	err = boot.EnsureNextBootToRunMode("label")
    59  	c.Assert(err, IsNil)
    60  
    61  	// the bloader vars have been updated
    62  	m, err := bloader.GetBootVars("snapd_recovery_mode", "snapd_recovery_system")
    63  	c.Assert(err, IsNil)
    64  	c.Assert(m, DeepEquals, map[string]string{
    65  		"snapd_recovery_mode":   "run",
    66  		"snapd_recovery_system": "label",
    67  	})
    68  }
    69  
    70  func (s *initramfsSuite) TestEnsureNextBootToRunModeRealBootloader(c *C) {
    71  	// create a real grub.cfg on ubuntu-seed
    72  	err := os.MkdirAll(filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI/ubuntu"), 0755)
    73  	c.Assert(err, IsNil)
    74  
    75  	err = ioutil.WriteFile(filepath.Join(boot.InitramfsUbuntuSeedDir, "EFI/ubuntu", "grub.cfg"), nil, 0644)
    76  	c.Assert(err, IsNil)
    77  
    78  	err = boot.EnsureNextBootToRunMode("somelabel")
    79  	c.Assert(err, IsNil)
    80  
    81  	opts := &bootloader.Options{
    82  		// setup the recovery bootloader
    83  		Role: bootloader.RoleRecovery,
    84  	}
    85  	bloader, err := bootloader.Find(boot.InitramfsUbuntuSeedDir, opts)
    86  	c.Assert(err, IsNil)
    87  	c.Assert(bloader.Name(), Equals, "grub")
    88  
    89  	// the bloader vars have been updated
    90  	m, err := bloader.GetBootVars("snapd_recovery_mode", "snapd_recovery_system")
    91  	c.Assert(err, IsNil)
    92  	c.Assert(m, DeepEquals, map[string]string{
    93  		"snapd_recovery_mode":   "run",
    94  		"snapd_recovery_system": "somelabel",
    95  	})
    96  }
    97  
    98  func makeSnapFilesOnInitramfsUbuntuData(c *C, comment CommentInterface, snaps ...snap.PlaceInfo) (restore func()) {
    99  	// also make sure the snaps also exist on ubuntu-data
   100  	snapDir := dirs.SnapBlobDirUnder(boot.InitramfsWritableDir)
   101  	err := os.MkdirAll(snapDir, 0755)
   102  	c.Assert(err, IsNil, comment)
   103  	paths := make([]string, 0, len(snaps))
   104  	for _, sn := range snaps {
   105  		snPath := filepath.Join(snapDir, sn.Filename())
   106  		paths = append(paths, snPath)
   107  		err = ioutil.WriteFile(snPath, nil, 0644)
   108  		c.Assert(err, IsNil, comment)
   109  	}
   110  	return func() {
   111  		for _, path := range paths {
   112  			err := os.Remove(path)
   113  			c.Assert(err, IsNil, comment)
   114  		}
   115  	}
   116  }
   117  
   118  func (s *initramfsSuite) TestInitramfsRunModeSelectSnapsToMount(c *C) {
   119  	// make some snap infos we will use in the tests
   120  	kernel1, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap")
   121  	c.Assert(err, IsNil)
   122  
   123  	kernel2, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_2.snap")
   124  	c.Assert(err, IsNil)
   125  
   126  	base1, err := snap.ParsePlaceInfoFromSnapFileName("core20_1.snap")
   127  	c.Assert(err, IsNil)
   128  
   129  	base2, err := snap.ParsePlaceInfoFromSnapFileName("core20_2.snap")
   130  	c.Assert(err, IsNil)
   131  
   132  	baseT := snap.TypeBase
   133  	kernelT := snap.TypeKernel
   134  
   135  	tt := []struct {
   136  		m              *boot.Modeenv
   137  		expectedM      *boot.Modeenv
   138  		typs           []snap.Type
   139  		kernel         snap.PlaceInfo
   140  		trykernel      snap.PlaceInfo
   141  		blvars         map[string]string
   142  		snapsToMake    []snap.PlaceInfo
   143  		expected       map[snap.Type]snap.PlaceInfo
   144  		errPattern     string
   145  		comment        string
   146  		expRebootPanic string
   147  	}{
   148  		//
   149  		// default paths
   150  		//
   151  
   152  		// default base path
   153  		{
   154  			m:           &boot.Modeenv{Mode: "run", Base: base1.Filename()},
   155  			typs:        []snap.Type{baseT},
   156  			snapsToMake: []snap.PlaceInfo{base1},
   157  			expected:    map[snap.Type]snap.PlaceInfo{baseT: base1},
   158  			comment:     "default base path",
   159  		},
   160  		// default kernel path
   161  		{
   162  			m:           &boot.Modeenv{Mode: "run", CurrentKernels: []string{kernel1.Filename()}},
   163  			kernel:      kernel1,
   164  			typs:        []snap.Type{kernelT},
   165  			snapsToMake: []snap.PlaceInfo{kernel1},
   166  			expected:    map[snap.Type]snap.PlaceInfo{kernelT: kernel1},
   167  			comment:     "default kernel path",
   168  		},
   169  
   170  		//
   171  		// happy kernel upgrade paths
   172  		//
   173  
   174  		// kernel upgrade path
   175  		{
   176  			m:           &boot.Modeenv{Mode: "run", CurrentKernels: []string{kernel1.Filename(), kernel2.Filename()}},
   177  			kernel:      kernel1,
   178  			trykernel:   kernel2,
   179  			typs:        []snap.Type{kernelT},
   180  			blvars:      map[string]string{"kernel_status": boot.TryingStatus},
   181  			snapsToMake: []snap.PlaceInfo{kernel1, kernel2},
   182  			expected:    map[snap.Type]snap.PlaceInfo{kernelT: kernel2},
   183  			comment:     "successful kernel upgrade path",
   184  		},
   185  		// extraneous kernel extracted/set, but kernel_status is default,
   186  		// so the bootloader will ignore that and boot the default kernel
   187  		// note that this test case is a bit ambiguous as we don't actually know
   188  		// in the initramfs that the bootloader actually booted the default
   189  		// kernel, we are just assuming that the bootloader implementation in
   190  		// the real world is robust enough to only boot the try kernel if and
   191  		// only if kernel_status is not DefaultStatus
   192  		{
   193  			m:           &boot.Modeenv{Mode: "run", CurrentKernels: []string{kernel1.Filename(), kernel2.Filename()}},
   194  			kernel:      kernel1,
   195  			trykernel:   kernel2,
   196  			typs:        []snap.Type{kernelT},
   197  			blvars:      map[string]string{"kernel_status": boot.DefaultStatus},
   198  			snapsToMake: []snap.PlaceInfo{kernel1, kernel2},
   199  			expected:    map[snap.Type]snap.PlaceInfo{kernelT: kernel1},
   200  			comment:     "fallback kernel upgrade path, due to kernel_status empty (default)",
   201  		},
   202  
   203  		//
   204  		// unhappy reboot fallback kernel paths
   205  		//
   206  
   207  		// kernel upgrade path, but reboots to fallback due to untrusted kernel from modeenv
   208  		{
   209  			m:              &boot.Modeenv{Mode: "run", CurrentKernels: []string{kernel1.Filename()}},
   210  			kernel:         kernel1,
   211  			trykernel:      kernel2,
   212  			typs:           []snap.Type{kernelT},
   213  			blvars:         map[string]string{"kernel_status": boot.TryingStatus},
   214  			snapsToMake:    []snap.PlaceInfo{kernel1, kernel2},
   215  			expRebootPanic: "reboot due to modeenv untrusted try kernel",
   216  			comment:        "fallback kernel upgrade path, due to modeenv untrusted try kernel",
   217  		},
   218  		// kernel upgrade path, but reboots to fallback due to try kernel file not existing
   219  		{
   220  			m:              &boot.Modeenv{Mode: "run", CurrentKernels: []string{kernel1.Filename(), kernel2.Filename()}},
   221  			kernel:         kernel1,
   222  			trykernel:      kernel2,
   223  			typs:           []snap.Type{kernelT},
   224  			blvars:         map[string]string{"kernel_status": boot.TryingStatus},
   225  			snapsToMake:    []snap.PlaceInfo{kernel1},
   226  			expRebootPanic: "reboot due to try kernel file not existing",
   227  			comment:        "fallback kernel upgrade path, due to try kernel file not existing",
   228  		},
   229  		// kernel upgrade path, but reboots to fallback due to invalid kernel_status
   230  		{
   231  			m:              &boot.Modeenv{Mode: "run", CurrentKernels: []string{kernel1.Filename(), kernel2.Filename()}},
   232  			kernel:         kernel1,
   233  			trykernel:      kernel2,
   234  			typs:           []snap.Type{kernelT},
   235  			blvars:         map[string]string{"kernel_status": boot.TryStatus},
   236  			snapsToMake:    []snap.PlaceInfo{kernel1, kernel2},
   237  			expRebootPanic: "reboot due to kernel_status wrong",
   238  			comment:        "fallback kernel upgrade path, due to kernel_status wrong",
   239  		},
   240  
   241  		//
   242  		// unhappy initramfs fail kernel paths
   243  		//
   244  
   245  		// fallback kernel not trusted in modeenv
   246  		{
   247  			m:           &boot.Modeenv{Mode: "run"},
   248  			kernel:      kernel1,
   249  			typs:        []snap.Type{kernelT},
   250  			snapsToMake: []snap.PlaceInfo{kernel1},
   251  			errPattern:  fmt.Sprintf("fallback kernel snap %q is not trusted in the modeenv", kernel1.Filename()),
   252  			comment:     "fallback kernel not trusted in modeenv",
   253  		},
   254  		// fallback kernel file doesn't exist
   255  		{
   256  			m:          &boot.Modeenv{Mode: "run", CurrentKernels: []string{kernel1.Filename()}},
   257  			kernel:     kernel1,
   258  			typs:       []snap.Type{kernelT},
   259  			errPattern: fmt.Sprintf("kernel snap %q does not exist on ubuntu-data", kernel1.Filename()),
   260  			comment:    "fallback kernel file doesn't exist",
   261  		},
   262  
   263  		//
   264  		// happy base upgrade paths
   265  		//
   266  
   267  		// successful base upgrade path
   268  		{
   269  			m: &boot.Modeenv{
   270  				Mode:       "run",
   271  				Base:       base1.Filename(),
   272  				TryBase:    base2.Filename(),
   273  				BaseStatus: boot.TryStatus,
   274  			},
   275  			expectedM: &boot.Modeenv{
   276  				Mode:       "run",
   277  				Base:       base1.Filename(),
   278  				TryBase:    base2.Filename(),
   279  				BaseStatus: boot.TryingStatus,
   280  			},
   281  			typs:        []snap.Type{baseT},
   282  			snapsToMake: []snap.PlaceInfo{base1, base2},
   283  			expected:    map[snap.Type]snap.PlaceInfo{baseT: base2},
   284  			comment:     "successful base upgrade path",
   285  		},
   286  		// base upgrade path, but uses fallback due to try base file not existing
   287  		{
   288  			m: &boot.Modeenv{
   289  				Mode:       "run",
   290  				Base:       base1.Filename(),
   291  				TryBase:    base2.Filename(),
   292  				BaseStatus: boot.TryStatus,
   293  			},
   294  			expectedM: &boot.Modeenv{
   295  				Mode:       "run",
   296  				Base:       base1.Filename(),
   297  				TryBase:    base2.Filename(),
   298  				BaseStatus: boot.TryStatus,
   299  			},
   300  			typs:        []snap.Type{baseT},
   301  			snapsToMake: []snap.PlaceInfo{base1},
   302  			expected:    map[snap.Type]snap.PlaceInfo{baseT: base1},
   303  			comment:     "fallback base upgrade path, due to missing try base file",
   304  		},
   305  		// base upgrade path, but uses fallback due to base_status trying
   306  		{
   307  			m: &boot.Modeenv{
   308  				Mode:       "run",
   309  				Base:       base1.Filename(),
   310  				TryBase:    base2.Filename(),
   311  				BaseStatus: boot.TryingStatus,
   312  			},
   313  			expectedM: &boot.Modeenv{
   314  				Mode:       "run",
   315  				Base:       base1.Filename(),
   316  				TryBase:    base2.Filename(),
   317  				BaseStatus: boot.DefaultStatus,
   318  			},
   319  			typs:        []snap.Type{baseT},
   320  			snapsToMake: []snap.PlaceInfo{base1, base2},
   321  			expected:    map[snap.Type]snap.PlaceInfo{baseT: base1},
   322  			comment:     "fallback base upgrade path, due to base_status trying",
   323  		},
   324  		// base upgrade path, but uses fallback due to base_status default
   325  		{
   326  			m: &boot.Modeenv{
   327  				Mode:       "run",
   328  				Base:       base1.Filename(),
   329  				TryBase:    base2.Filename(),
   330  				BaseStatus: boot.DefaultStatus,
   331  			},
   332  			expectedM: &boot.Modeenv{
   333  				Mode:       "run",
   334  				Base:       base1.Filename(),
   335  				TryBase:    base2.Filename(),
   336  				BaseStatus: boot.DefaultStatus,
   337  			},
   338  			typs:        []snap.Type{baseT},
   339  			snapsToMake: []snap.PlaceInfo{base1, base2},
   340  			expected:    map[snap.Type]snap.PlaceInfo{baseT: base1},
   341  			comment:     "fallback base upgrade path, due to missing base_status",
   342  		},
   343  
   344  		//
   345  		// unhappy base paths
   346  		//
   347  
   348  		// base snap unset
   349  		{
   350  			m:           &boot.Modeenv{Mode: "run"},
   351  			typs:        []snap.Type{baseT},
   352  			snapsToMake: []snap.PlaceInfo{base1},
   353  			errPattern:  "fallback base snap unusable: cannot get snap revision: modeenv base boot variable is empty",
   354  			comment:     "base snap unset in modeenv",
   355  		},
   356  		// base snap file doesn't exist
   357  		{
   358  			m:          &boot.Modeenv{Mode: "run", Base: base1.Filename()},
   359  			typs:       []snap.Type{baseT},
   360  			errPattern: fmt.Sprintf("base snap %q does not exist on ubuntu-data", base1.Filename()),
   361  			comment:    "base snap unset in modeenv",
   362  		},
   363  		// unhappy, but silent path with fallback, due to invalid try base snap name
   364  		{
   365  			m: &boot.Modeenv{
   366  				Mode:       "run",
   367  				Base:       base1.Filename(),
   368  				TryBase:    "bogusname",
   369  				BaseStatus: boot.TryStatus,
   370  			},
   371  			typs:        []snap.Type{baseT},
   372  			snapsToMake: []snap.PlaceInfo{base1},
   373  			expected:    map[snap.Type]snap.PlaceInfo{baseT: base1},
   374  			comment:     "corrupted base snap name",
   375  		},
   376  
   377  		//
   378  		// combined cases
   379  		//
   380  
   381  		// default
   382  		{
   383  			m: &boot.Modeenv{
   384  				Mode:           "run",
   385  				Base:           base1.Filename(),
   386  				CurrentKernels: []string{kernel1.Filename()},
   387  			},
   388  			expectedM: &boot.Modeenv{
   389  				Mode:           "run",
   390  				Base:           base1.Filename(),
   391  				CurrentKernels: []string{kernel1.Filename()},
   392  			},
   393  			kernel:      kernel1,
   394  			typs:        []snap.Type{baseT, kernelT},
   395  			snapsToMake: []snap.PlaceInfo{base1, kernel1},
   396  			expected: map[snap.Type]snap.PlaceInfo{
   397  				baseT:   base1,
   398  				kernelT: kernel1,
   399  			},
   400  			comment: "default combined kernel + base",
   401  		},
   402  		// combined, upgrade only the kernel
   403  		{
   404  			m: &boot.Modeenv{
   405  				Mode:           "run",
   406  				Base:           base1.Filename(),
   407  				CurrentKernels: []string{kernel1.Filename(), kernel2.Filename()},
   408  			},
   409  			expectedM: &boot.Modeenv{
   410  				Mode:           "run",
   411  				Base:           base1.Filename(),
   412  				CurrentKernels: []string{kernel1.Filename(), kernel2.Filename()},
   413  			},
   414  			kernel:      kernel1,
   415  			trykernel:   kernel2,
   416  			typs:        []snap.Type{baseT, kernelT},
   417  			blvars:      map[string]string{"kernel_status": boot.TryingStatus},
   418  			snapsToMake: []snap.PlaceInfo{base1, kernel1, kernel2},
   419  			expected: map[snap.Type]snap.PlaceInfo{
   420  				baseT:   base1,
   421  				kernelT: kernel2,
   422  			},
   423  			comment: "combined kernel + base, successful kernel upgrade",
   424  		},
   425  		// combined, upgrade only the base
   426  		{
   427  			m: &boot.Modeenv{
   428  				Mode:           "run",
   429  				Base:           base1.Filename(),
   430  				TryBase:        base2.Filename(),
   431  				BaseStatus:     boot.TryStatus,
   432  				CurrentKernels: []string{kernel1.Filename()},
   433  			},
   434  			expectedM: &boot.Modeenv{
   435  				Mode:           "run",
   436  				Base:           base1.Filename(),
   437  				TryBase:        base2.Filename(),
   438  				BaseStatus:     boot.TryingStatus,
   439  				CurrentKernels: []string{kernel1.Filename()},
   440  			},
   441  			kernel:      kernel1,
   442  			typs:        []snap.Type{baseT, kernelT},
   443  			snapsToMake: []snap.PlaceInfo{base1, base2, kernel1},
   444  			expected: map[snap.Type]snap.PlaceInfo{
   445  				baseT:   base2,
   446  				kernelT: kernel1,
   447  			},
   448  			comment: "combined kernel + base, successful base upgrade",
   449  		},
   450  		// bonus points: combined upgrade kernel and base
   451  		{
   452  			m: &boot.Modeenv{
   453  				Mode:           "run",
   454  				Base:           base1.Filename(),
   455  				TryBase:        base2.Filename(),
   456  				BaseStatus:     boot.TryStatus,
   457  				CurrentKernels: []string{kernel1.Filename(), kernel2.Filename()},
   458  			},
   459  			expectedM: &boot.Modeenv{
   460  				Mode:           "run",
   461  				Base:           base1.Filename(),
   462  				TryBase:        base2.Filename(),
   463  				BaseStatus:     boot.TryingStatus,
   464  				CurrentKernels: []string{kernel1.Filename(), kernel2.Filename()},
   465  			},
   466  			kernel:      kernel1,
   467  			trykernel:   kernel2,
   468  			typs:        []snap.Type{baseT, kernelT},
   469  			blvars:      map[string]string{"kernel_status": boot.TryingStatus},
   470  			snapsToMake: []snap.PlaceInfo{base1, base2, kernel1, kernel2},
   471  			expected: map[snap.Type]snap.PlaceInfo{
   472  				baseT:   base2,
   473  				kernelT: kernel2,
   474  			},
   475  			comment: "combined kernel + base, successful base + kernel upgrade",
   476  		},
   477  		// combined, fallback upgrade on kernel
   478  		{
   479  			m: &boot.Modeenv{
   480  				Mode:           "run",
   481  				Base:           base1.Filename(),
   482  				CurrentKernels: []string{kernel1.Filename(), kernel2.Filename()},
   483  			},
   484  			expectedM: &boot.Modeenv{
   485  				Mode:           "run",
   486  				Base:           base1.Filename(),
   487  				CurrentKernels: []string{kernel1.Filename(), kernel2.Filename()},
   488  			},
   489  			kernel:      kernel1,
   490  			trykernel:   kernel2,
   491  			typs:        []snap.Type{baseT, kernelT},
   492  			blvars:      map[string]string{"kernel_status": boot.DefaultStatus},
   493  			snapsToMake: []snap.PlaceInfo{base1, kernel1, kernel2},
   494  			expected: map[snap.Type]snap.PlaceInfo{
   495  				baseT:   base1,
   496  				kernelT: kernel1,
   497  			},
   498  			comment: "combined kernel + base, fallback kernel upgrade, due to missing boot var",
   499  		},
   500  		// combined, fallback upgrade on base
   501  		{
   502  			m: &boot.Modeenv{
   503  				Mode:           "run",
   504  				Base:           base1.Filename(),
   505  				TryBase:        base2.Filename(),
   506  				BaseStatus:     boot.TryingStatus,
   507  				CurrentKernels: []string{kernel1.Filename()},
   508  			},
   509  			expectedM: &boot.Modeenv{
   510  				Mode:           "run",
   511  				Base:           base1.Filename(),
   512  				TryBase:        base2.Filename(),
   513  				BaseStatus:     boot.DefaultStatus,
   514  				CurrentKernels: []string{kernel1.Filename()},
   515  			},
   516  			kernel:      kernel1,
   517  			typs:        []snap.Type{baseT, kernelT},
   518  			snapsToMake: []snap.PlaceInfo{base1, base2, kernel1},
   519  			expected: map[snap.Type]snap.PlaceInfo{
   520  				baseT:   base1,
   521  				kernelT: kernel1,
   522  			},
   523  			comment: "combined kernel + base, fallback base upgrade, due to base_status trying",
   524  		},
   525  	}
   526  
   527  	// do both the normal uc20 bootloader and the env ref bootloader
   528  	bloaderTable := []struct {
   529  		bl interface {
   530  			bootloader.Bootloader
   531  			SetEnabledKernel(s snap.PlaceInfo) (restore func())
   532  			SetEnabledTryKernel(s snap.PlaceInfo) (restore func())
   533  		}
   534  		name string
   535  	}{
   536  		{
   537  			boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir())),
   538  			"env ref extracted kernel",
   539  		},
   540  		{
   541  			boottest.MockUC20EnvRefExtractedKernelRunBootenv(bootloadertest.Mock("mock", c.MkDir())),
   542  			"extracted run kernel image",
   543  		},
   544  	}
   545  
   546  	for _, tbl := range bloaderTable {
   547  		bl := tbl.bl
   548  		for _, t := range tt {
   549  			var cleanups []func()
   550  
   551  			comment := Commentf("[%s] %s", tbl.name, t.comment)
   552  
   553  			// we use a panic to simulate a reboot
   554  			if t.expRebootPanic != "" {
   555  				r := boot.MockInitramfsReboot(func() error {
   556  					panic(t.expRebootPanic)
   557  				})
   558  				cleanups = append(cleanups, r)
   559  			}
   560  
   561  			bootloader.Force(bl)
   562  			cleanups = append(cleanups, func() { bootloader.Force(nil) })
   563  
   564  			// set the bl kernel / try kernel
   565  			if t.kernel != nil {
   566  				cleanups = append(cleanups, bl.SetEnabledKernel(t.kernel))
   567  			}
   568  
   569  			if t.trykernel != nil {
   570  				cleanups = append(cleanups, bl.SetEnabledTryKernel(t.trykernel))
   571  			}
   572  
   573  			if t.blvars != nil {
   574  				c.Assert(bl.SetBootVars(t.blvars), IsNil, comment)
   575  				cleanBootVars := make(map[string]string, len(t.blvars))
   576  				for k := range t.blvars {
   577  					cleanBootVars[k] = ""
   578  				}
   579  				cleanups = append(cleanups, func() {
   580  					c.Assert(bl.SetBootVars(cleanBootVars), IsNil, comment)
   581  				})
   582  			}
   583  
   584  			if len(t.snapsToMake) != 0 {
   585  				r := makeSnapFilesOnInitramfsUbuntuData(c, comment, t.snapsToMake...)
   586  				cleanups = append(cleanups, r)
   587  			}
   588  
   589  			// write the modeenv to somewhere so we can read it and pass that to
   590  			// InitramfsRunModeChooseSnapsToMount
   591  			err := t.m.WriteTo(boot.InitramfsWritableDir)
   592  			// remove it because we are writing many modeenvs in this single test
   593  			cleanups = append(cleanups, func() {
   594  				c.Assert(os.Remove(dirs.SnapModeenvFileUnder(boot.InitramfsWritableDir)), IsNil, Commentf(t.comment))
   595  			})
   596  			c.Assert(err, IsNil, comment)
   597  
   598  			m, err := boot.ReadModeenv(boot.InitramfsWritableDir)
   599  			c.Assert(err, IsNil, comment)
   600  
   601  			if t.expRebootPanic != "" {
   602  				f := func() { boot.InitramfsRunModeSelectSnapsToMount(t.typs, m) }
   603  				c.Assert(f, PanicMatches, t.expRebootPanic, comment)
   604  			} else {
   605  				mountSnaps, err := boot.InitramfsRunModeSelectSnapsToMount(t.typs, m)
   606  				if t.errPattern != "" {
   607  					c.Assert(err, ErrorMatches, t.errPattern, comment)
   608  				} else {
   609  					c.Assert(err, IsNil, comment)
   610  					c.Assert(mountSnaps, DeepEquals, t.expected, comment)
   611  				}
   612  			}
   613  
   614  			// check that the modeenv changed as expected
   615  			if t.expectedM != nil {
   616  				newM, err := boot.ReadModeenv(boot.InitramfsWritableDir)
   617  				c.Assert(err, IsNil, comment)
   618  				c.Assert(newM.Base, Equals, t.expectedM.Base, comment)
   619  				c.Assert(newM.BaseStatus, Equals, t.expectedM.BaseStatus, comment)
   620  				c.Assert(newM.TryBase, Equals, t.expectedM.TryBase, comment)
   621  
   622  				// shouldn't be changing in the initramfs, but be safe
   623  				c.Assert(newM.CurrentKernels, DeepEquals, t.expectedM.CurrentKernels, comment)
   624  			}
   625  
   626  			// clean up
   627  			for _, r := range cleanups {
   628  				r()
   629  			}
   630  		}
   631  	}
   632  }