github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/bootloader/bootloader_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2015 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 bootloader_test 21 22 import ( 23 "errors" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "testing" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/bootloader" 32 "github.com/snapcore/snapd/bootloader/assets" 33 "github.com/snapcore/snapd/bootloader/bootloadertest" 34 "github.com/snapcore/snapd/snap" 35 "github.com/snapcore/snapd/testutil" 36 ) 37 38 // Hook up check.v1 into the "go test" runner 39 func Test(t *testing.T) { TestingT(t) } 40 41 const packageKernel = ` 42 name: ubuntu-kernel 43 version: 4.0-1 44 type: kernel 45 vendor: Someone 46 ` 47 48 type baseBootenvTestSuite struct { 49 testutil.BaseTest 50 51 rootdir string 52 } 53 54 func (s *baseBootenvTestSuite) SetUpTest(c *C) { 55 s.BaseTest.SetUpTest(c) 56 s.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 57 s.rootdir = c.MkDir() 58 } 59 60 type bootenvTestSuite struct { 61 baseBootenvTestSuite 62 63 b *bootloadertest.MockBootloader 64 } 65 66 var _ = Suite(&bootenvTestSuite{}) 67 68 func (s *bootenvTestSuite) SetUpTest(c *C) { 69 s.baseBootenvTestSuite.SetUpTest(c) 70 71 s.b = bootloadertest.Mock("mocky", c.MkDir()) 72 } 73 74 func (s *bootenvTestSuite) TestForceBootloader(c *C) { 75 bootloader.Force(s.b) 76 defer bootloader.Force(nil) 77 78 got, err := bootloader.Find("", nil) 79 c.Assert(err, IsNil) 80 c.Check(got, Equals, s.b) 81 } 82 83 func (s *bootenvTestSuite) TestForceBootloaderError(c *C) { 84 myErr := errors.New("zap") 85 bootloader.ForceError(myErr) 86 defer bootloader.ForceError(nil) 87 88 got, err := bootloader.Find("", nil) 89 c.Assert(err, Equals, myErr) 90 c.Check(got, IsNil) 91 } 92 93 func (s *bootenvTestSuite) TestInstallBootloaderConfigNoConfig(c *C) { 94 err := bootloader.InstallBootConfig(c.MkDir(), s.rootdir, nil) 95 c.Assert(err, ErrorMatches, `cannot find boot config in.*`) 96 } 97 98 func (s *bootenvTestSuite) TestInstallBootloaderConfigFromGadget(c *C) { 99 for _, t := range []struct { 100 name string 101 gadgetFile, sysFile string 102 gadgetFileContent []byte 103 opts *bootloader.Options 104 }{ 105 {name: "grub", gadgetFile: "grub.conf", sysFile: "/boot/grub/grub.cfg"}, 106 // traditional uboot.env - the uboot.env file needs to be non-empty 107 {name: "uboot.env", gadgetFile: "uboot.conf", sysFile: "/boot/uboot/uboot.env", gadgetFileContent: []byte{1}}, 108 // boot.scr in place of uboot.env means we create the boot.sel file 109 { 110 name: "uboot boot.scr", 111 gadgetFile: "uboot.conf", 112 sysFile: "/uboot/ubuntu/boot.sel", 113 opts: &bootloader.Options{Role: bootloader.RoleRecovery}, 114 }, 115 {name: "androidboot", gadgetFile: "androidboot.conf", sysFile: "/boot/androidboot/androidboot.env"}, 116 {name: "lk", gadgetFile: "lk.conf", sysFile: "/boot/lk/snapbootsel.bin"}, 117 } { 118 mockGadgetDir := c.MkDir() 119 rootDir := c.MkDir() 120 err := ioutil.WriteFile(filepath.Join(mockGadgetDir, t.gadgetFile), t.gadgetFileContent, 0644) 121 c.Assert(err, IsNil) 122 err = bootloader.InstallBootConfig(mockGadgetDir, rootDir, t.opts) 123 c.Assert(err, IsNil, Commentf("installing boot config for %s", t.name)) 124 fn := filepath.Join(rootDir, t.sysFile) 125 c.Assert(fn, testutil.FilePresent, Commentf("boot config missing for %s at %s", t.name, t.sysFile)) 126 } 127 } 128 129 func (s *bootenvTestSuite) TestInstallBootloaderConfigFromAssets(c *C) { 130 recoveryOpts := &bootloader.Options{ 131 Role: bootloader.RoleRecovery, 132 } 133 systemBootOpts := &bootloader.Options{ 134 Role: bootloader.RoleRunMode, 135 } 136 defaultRecoveryGrubAsset := assets.Internal("grub-recovery.cfg") 137 c.Assert(defaultRecoveryGrubAsset, NotNil) 138 defaultGrubAsset := assets.Internal("grub.cfg") 139 c.Assert(defaultGrubAsset, NotNil) 140 141 for _, t := range []struct { 142 name string 143 gadgetFile, sysFile string 144 gadgetFileContent []byte 145 sysFileContent []byte 146 assetContent []byte 147 assetName string 148 err string 149 opts *bootloader.Options 150 }{ 151 { 152 name: "recovery grub", 153 opts: recoveryOpts, 154 gadgetFile: "grub.conf", 155 // empty file in the gadget 156 gadgetFileContent: nil, 157 sysFile: "/EFI/ubuntu/grub.cfg", 158 assetName: "grub-recovery.cfg", 159 assetContent: []byte("hello assets"), 160 // boot config from assets 161 sysFileContent: []byte("hello assets"), 162 }, { 163 name: "recovery grub with non empty gadget file", 164 opts: recoveryOpts, 165 gadgetFile: "grub.conf", 166 gadgetFileContent: []byte("not so empty"), 167 sysFile: "/EFI/ubuntu/grub.cfg", 168 assetName: "grub-recovery.cfg", 169 assetContent: []byte("hello assets"), 170 // boot config from assets 171 sysFileContent: []byte("hello assets"), 172 }, { 173 name: "recovery grub with default asset", 174 opts: recoveryOpts, 175 gadgetFile: "grub.conf", 176 // empty file in the gadget 177 gadgetFileContent: nil, 178 sysFile: "/EFI/ubuntu/grub.cfg", 179 sysFileContent: defaultRecoveryGrubAsset, 180 }, { 181 name: "recovery grub missing asset", 182 opts: recoveryOpts, 183 gadgetFile: "grub.conf", 184 // empty file in the gadget 185 gadgetFileContent: nil, 186 sysFile: "/EFI/ubuntu/grub.cfg", 187 assetName: "grub-recovery.cfg", 188 // no asset content 189 err: `internal error: no boot asset for "grub-recovery.cfg"`, 190 }, { 191 name: "system-boot grub", 192 opts: systemBootOpts, 193 gadgetFile: "grub.conf", 194 // empty file in the gadget 195 gadgetFileContent: nil, 196 sysFile: "/EFI/ubuntu/grub.cfg", 197 assetName: "grub.cfg", 198 assetContent: []byte("hello assets"), 199 sysFileContent: []byte("hello assets"), 200 }, { 201 name: "system-boot grub with default asset", 202 opts: systemBootOpts, 203 gadgetFile: "grub.conf", 204 // empty file in the gadget 205 gadgetFileContent: nil, 206 sysFile: "/EFI/ubuntu/grub.cfg", 207 sysFileContent: defaultGrubAsset, 208 }, 209 } { 210 mockGadgetDir := c.MkDir() 211 rootDir := c.MkDir() 212 fn := filepath.Join(rootDir, t.sysFile) 213 err := ioutil.WriteFile(filepath.Join(mockGadgetDir, t.gadgetFile), t.gadgetFileContent, 0644) 214 c.Assert(err, IsNil) 215 var restoreAsset func() 216 if t.assetName != "" { 217 restoreAsset = assets.MockInternal(t.assetName, t.assetContent) 218 } 219 err = bootloader.InstallBootConfig(mockGadgetDir, rootDir, t.opts) 220 if t.err == "" { 221 c.Assert(err, IsNil, Commentf("installing boot config for %s", t.name)) 222 // mocked asset content 223 c.Assert(fn, testutil.FileEquals, string(t.sysFileContent)) 224 } else { 225 c.Assert(err, ErrorMatches, t.err) 226 c.Assert(fn, testutil.FileAbsent) 227 } 228 if restoreAsset != nil { 229 restoreAsset() 230 } 231 } 232 } 233 234 func (s *bootenvTestSuite) TestBootloaderFind(c *C) { 235 for _, tc := range []struct { 236 name string 237 sysFile string 238 opts *bootloader.Options 239 expName string 240 }{ 241 {name: "grub", sysFile: "/boot/grub/grub.cfg", expName: "grub"}, 242 { 243 // native run partition layout 244 name: "grub", sysFile: "/EFI/ubuntu/grub.cfg", 245 opts: &bootloader.Options{Role: bootloader.RoleRunMode, NoSlashBoot: true}, 246 expName: "grub", 247 }, 248 { 249 // recovery layout 250 name: "grub", sysFile: "/EFI/ubuntu/grub.cfg", 251 opts: &bootloader.Options{Role: bootloader.RoleRecovery}, 252 expName: "grub", 253 }, 254 255 // traditional uboot.env - the uboot.env file needs to be non-empty 256 {name: "uboot.env", sysFile: "/boot/uboot/uboot.env", expName: "uboot"}, 257 // boot.sel variant 258 { 259 name: "uboot boot.scr", 260 sysFile: "/uboot/ubuntu/boot.sel", 261 opts: &bootloader.Options{Role: bootloader.RoleRunMode, NoSlashBoot: true}, 262 expName: "uboot", 263 }, 264 {name: "androidboot", sysFile: "/boot/androidboot/androidboot.env", expName: "androidboot"}, 265 // lk is detected differently based on runtime/prepare-image 266 {name: "lk", sysFile: "/dev/disk/by-partlabel/snapbootsel", expName: "lk"}, 267 { 268 name: "lk", sysFile: "/boot/lk/snapbootsel.bin", 269 expName: "lk", opts: &bootloader.Options{PrepareImageTime: true}, 270 }, 271 } { 272 c.Logf("tc: %v", tc.name) 273 rootDir := c.MkDir() 274 err := os.MkdirAll(filepath.Join(rootDir, filepath.Dir(tc.sysFile)), 0755) 275 c.Assert(err, IsNil) 276 err = ioutil.WriteFile(filepath.Join(rootDir, tc.sysFile), nil, 0644) 277 c.Assert(err, IsNil) 278 bl, err := bootloader.Find(rootDir, tc.opts) 279 c.Assert(err, IsNil) 280 c.Assert(bl, NotNil) 281 c.Check(bl.Name(), Equals, tc.expName) 282 } 283 } 284 285 func (s *bootenvTestSuite) TestBootloaderForGadget(c *C) { 286 for _, tc := range []struct { 287 name string 288 gadgetFile string 289 opts *bootloader.Options 290 expName string 291 }{ 292 {name: "grub", gadgetFile: "grub.conf", expName: "grub"}, 293 {name: "grub", gadgetFile: "grub.conf", opts: &bootloader.Options{Role: bootloader.RoleRunMode, NoSlashBoot: true}, expName: "grub"}, 294 {name: "grub", gadgetFile: "grub.conf", opts: &bootloader.Options{Role: bootloader.RoleRecovery}, expName: "grub"}, 295 {name: "uboot", gadgetFile: "uboot.conf", expName: "uboot"}, 296 {name: "androidboot", gadgetFile: "androidboot.conf", expName: "androidboot"}, 297 {name: "lk", gadgetFile: "lk.conf", expName: "lk"}, 298 } { 299 c.Logf("tc: %v", tc.name) 300 gadgetDir := c.MkDir() 301 rootDir := c.MkDir() 302 err := os.MkdirAll(filepath.Join(rootDir, filepath.Dir(tc.gadgetFile)), 0755) 303 c.Assert(err, IsNil) 304 err = ioutil.WriteFile(filepath.Join(gadgetDir, tc.gadgetFile), nil, 0644) 305 c.Assert(err, IsNil) 306 bl, err := bootloader.ForGadget(gadgetDir, rootDir, tc.opts) 307 c.Assert(err, IsNil) 308 c.Assert(bl, NotNil) 309 c.Check(bl.Name(), Equals, tc.expName) 310 } 311 } 312 313 func (s *bootenvTestSuite) TestBootFileWithPath(c *C) { 314 a := bootloader.NewBootFile("", "some/path", bootloader.RoleRunMode) 315 b := a.WithPath("other/path") 316 c.Assert(a.Path, Equals, "some/path") 317 c.Assert(b.Path, Equals, "other/path") 318 }