github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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 }