github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/snapstate/booted_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016 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 snapstate_test 21 22 // test the boot related code 23 24 import ( 25 "os" 26 "path/filepath" 27 "time" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/bootloader" 32 "github.com/snapcore/snapd/bootloader/bootloadertest" 33 "github.com/snapcore/snapd/dirs" 34 "github.com/snapcore/snapd/overlord" 35 "github.com/snapcore/snapd/overlord/snapstate" 36 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 37 "github.com/snapcore/snapd/overlord/state" 38 "github.com/snapcore/snapd/release" 39 "github.com/snapcore/snapd/snap" 40 "github.com/snapcore/snapd/snap/snaptest" 41 "github.com/snapcore/snapd/testutil" 42 ) 43 44 type bootedSuite struct { 45 testutil.BaseTest 46 bootloader *bootloadertest.MockBootloader 47 48 o *overlord.Overlord 49 state *state.State 50 snapmgr *snapstate.SnapManager 51 fakeBackend *fakeSnappyBackend 52 restore func() 53 } 54 55 var _ = Suite(&bootedSuite{}) 56 57 func (bs *bootedSuite) SetUpTest(c *C) { 58 bs.BaseTest.SetUpTest(c) 59 60 dirs.SetRootDir(c.MkDir()) 61 err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755) 62 c.Assert(err, IsNil) 63 64 bs.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 65 66 // booted is not running on classic 67 release.MockOnClassic(false) 68 69 bs.bootloader = bootloadertest.Mock("mock", c.MkDir()) 70 bs.bootloader.SetBootKernel("canonical-pc-linux_2.snap") 71 bs.bootloader.SetBootBase("core_2.snap") 72 bootloader.Force(bs.bootloader) 73 74 bs.fakeBackend = &fakeSnappyBackend{} 75 bs.o = overlord.Mock() 76 bs.state = bs.o.State() 77 bs.snapmgr, err = snapstate.Manager(bs.state, bs.o.TaskRunner()) 78 c.Assert(err, IsNil) 79 80 AddForeignTaskHandlers(bs.o.TaskRunner(), bs.fakeBackend) 81 82 bs.o.AddManager(bs.snapmgr) 83 bs.o.AddManager(bs.o.TaskRunner()) 84 85 c.Assert(bs.o.StartUp(), IsNil) 86 87 snapstate.SetSnapManagerBackend(bs.snapmgr, bs.fakeBackend) 88 snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { 89 return nil, nil 90 } 91 bs.restore = snapstatetest.MockDeviceModel(DefaultModel()) 92 } 93 94 func (bs *bootedSuite) TearDownTest(c *C) { 95 bs.BaseTest.TearDownTest(c) 96 snapstate.AutoAliases = nil 97 bs.restore() 98 release.MockOnClassic(true) 99 dirs.SetRootDir("") 100 bootloader.Force(nil) 101 } 102 103 var osSI1 = &snap.SideInfo{RealName: "core", Revision: snap.R(1)} 104 var osSI2 = &snap.SideInfo{RealName: "core", Revision: snap.R(2)} 105 var kernelSI1 = &snap.SideInfo{RealName: "canonical-pc-linux", Revision: snap.R(1)} 106 var kernelSI2 = &snap.SideInfo{RealName: "canonical-pc-linux", Revision: snap.R(2)} 107 108 func (bs *bootedSuite) settle() { 109 bs.o.Settle(5 * time.Second) 110 } 111 112 func (bs *bootedSuite) makeInstalledKernelOS(c *C, st *state.State) { 113 snaptest.MockSnap(c, "name: core\ntype: os\nversion: 1", osSI1) 114 snaptest.MockSnap(c, "name: core\ntype: os\nversion: 2", osSI2) 115 snapstate.Set(st, "core", &snapstate.SnapState{ 116 SnapType: "os", 117 Active: true, 118 Sequence: []*snap.SideInfo{osSI1, osSI2}, 119 Current: snap.R(2), 120 }) 121 122 snaptest.MockSnap(c, "name: canonical-pc-linux\ntype: os\nversion: 1", kernelSI1) 123 snaptest.MockSnap(c, "name: canonical-pc-linux\ntype: os\nversion: 2", kernelSI2) 124 snapstate.Set(st, "canonical-pc-linux", &snapstate.SnapState{ 125 SnapType: "kernel", 126 Active: true, 127 Sequence: []*snap.SideInfo{kernelSI1, kernelSI2}, 128 Current: snap.R(2), 129 }) 130 131 } 132 133 func (bs *bootedSuite) TestUpdateBootRevisionsOSSimple(c *C) { 134 st := bs.state 135 st.Lock() 136 defer st.Unlock() 137 138 bs.makeInstalledKernelOS(c, st) 139 140 bs.bootloader.SetBootBase("core_1.snap") 141 err := snapstate.UpdateBootRevisions(st) 142 c.Assert(err, IsNil) 143 144 st.Unlock() 145 bs.settle() 146 st.Lock() 147 148 c.Assert(st.Changes(), HasLen, 1) 149 chg := st.Changes()[0] 150 c.Assert(chg.Err(), IsNil) 151 c.Assert(chg.Kind(), Equals, "update-revisions") 152 c.Assert(chg.IsReady(), Equals, true) 153 154 // core "current" got reverted but canonical-pc-linux did not 155 var snapst snapstate.SnapState 156 err = snapstate.Get(st, "core", &snapst) 157 c.Assert(err, IsNil) 158 c.Assert(snapst.Current, Equals, snap.R(1)) 159 c.Assert(snapst.Active, Equals, true) 160 161 err = snapstate.Get(st, "canonical-pc-linux", &snapst) 162 c.Assert(err, IsNil) 163 c.Assert(snapst.Current, Equals, snap.R(2)) 164 c.Assert(snapst.Active, Equals, true) 165 } 166 167 func (bs *bootedSuite) TestUpdateBootRevisionsKernelSimple(c *C) { 168 st := bs.state 169 st.Lock() 170 defer st.Unlock() 171 172 bs.makeInstalledKernelOS(c, st) 173 174 bs.bootloader.SetBootKernel("canonical-pc-linux_1.snap") 175 err := snapstate.UpdateBootRevisions(st) 176 c.Assert(err, IsNil) 177 178 st.Unlock() 179 bs.settle() 180 st.Lock() 181 182 c.Assert(st.Changes(), HasLen, 1) 183 chg := st.Changes()[0] 184 c.Assert(chg.Err(), IsNil) 185 c.Assert(chg.Kind(), Equals, "update-revisions") 186 c.Assert(chg.IsReady(), Equals, true) 187 188 // canonical-pc-linux "current" got reverted but core did not 189 var snapst snapstate.SnapState 190 err = snapstate.Get(st, "canonical-pc-linux", &snapst) 191 c.Assert(err, IsNil) 192 c.Assert(snapst.Current, Equals, snap.R(1)) 193 c.Assert(snapst.Active, Equals, true) 194 195 err = snapstate.Get(st, "core", &snapst) 196 c.Assert(err, IsNil) 197 c.Assert(snapst.Current, Equals, snap.R(2)) 198 c.Assert(snapst.Active, Equals, true) 199 } 200 201 func (bs *bootedSuite) TestUpdateBootRevisionsKernelErrorsEarly(c *C) { 202 st := bs.state 203 st.Lock() 204 defer st.Unlock() 205 206 bs.makeInstalledKernelOS(c, st) 207 208 bs.bootloader.SetBootKernel("canonical-pc-linux_99.snap") 209 err := snapstate.UpdateBootRevisions(st) 210 c.Assert(err, ErrorMatches, `cannot find revision 99 for snap "canonical-pc-linux"`) 211 } 212 213 func (bs *bootedSuite) TestUpdateBootRevisionsOSErrorsEarly(c *C) { 214 st := bs.state 215 st.Lock() 216 defer st.Unlock() 217 218 bs.makeInstalledKernelOS(c, st) 219 220 bs.bootloader.SetBootBase("core_99.snap") 221 err := snapstate.UpdateBootRevisions(st) 222 c.Assert(err, ErrorMatches, `cannot find revision 99 for snap "core"`) 223 } 224 225 func (bs *bootedSuite) TestUpdateBootRevisionsOSErrorsLate(c *C) { 226 st := bs.state 227 st.Lock() 228 defer st.Unlock() 229 230 // have a kernel 231 snaptest.MockSnap(c, "name: canonical-pc-linux\ntype: os\nversion: 2", kernelSI2) 232 snapstate.Set(st, "canonical-pc-linux", &snapstate.SnapState{ 233 SnapType: "kernel", 234 Active: true, 235 Sequence: []*snap.SideInfo{kernelSI2}, 236 Current: snap.R(2), 237 }) 238 239 // put core into the state but add no files on disk 240 // will break in the tasks 241 snapstate.Set(st, "core", &snapstate.SnapState{ 242 SnapType: "os", 243 Active: true, 244 Sequence: []*snap.SideInfo{osSI1, osSI2}, 245 Current: snap.R(2), 246 }) 247 bs.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "/core/1") 248 249 bs.bootloader.SetBootBase("core_1.snap") 250 err := snapstate.UpdateBootRevisions(st) 251 c.Assert(err, IsNil) 252 253 st.Unlock() 254 bs.settle() 255 st.Lock() 256 257 c.Assert(st.Changes(), HasLen, 1) 258 chg := st.Changes()[0] 259 c.Assert(chg.Kind(), Equals, "update-revisions") 260 c.Assert(chg.IsReady(), Equals, true) 261 c.Assert(chg.Err(), ErrorMatches, `(?ms).*Make snap "core" \(1\) available to the system \(fail\).*`) 262 } 263 264 func (bs *bootedSuite) TestWaitRestartCore(c *C) { 265 st := bs.state 266 st.Lock() 267 defer st.Unlock() 268 269 task := st.NewTask("auto-connect", "...") 270 271 // not core snap 272 si := &snap.SideInfo{RealName: "some-app"} 273 snaptest.MockSnap(c, "name: some-app\nversion: 1", si) 274 err := snapstate.WaitRestart(task, &snapstate.SnapSetup{SideInfo: si}) 275 c.Check(err, IsNil) 276 277 si = &snap.SideInfo{RealName: "core"} 278 snapsup := &snapstate.SnapSetup{SideInfo: si} 279 280 // core snap, restarting ... wait 281 state.MockRestarting(st, state.RestartSystem) 282 snaptest.MockSnap(c, "name: core\ntype: os\nversion: 1", si) 283 err = snapstate.WaitRestart(task, snapsup) 284 c.Check(err, FitsTypeOf, &state.Retry{}) 285 286 // core snap, restarted, waiting for current core revision 287 state.MockRestarting(st, state.RestartUnset) 288 bs.bootloader.BootVars["snap_mode"] = "trying" 289 err = snapstate.WaitRestart(task, snapsup) 290 c.Check(err, DeepEquals, &state.Retry{After: 5 * time.Second}) 291 292 // core snap updated 293 si.Revision = snap.R(2) 294 snaptest.MockSnap(c, "name: core\ntype: os\nversion: 2", si) 295 296 // core snap, restarted, right core revision, no rollback 297 bs.bootloader.BootVars["snap_mode"] = "" 298 err = snapstate.WaitRestart(task, snapsup) 299 c.Check(err, IsNil) 300 301 // core snap, restarted, wrong core revision, rollback! 302 bs.bootloader.SetBootBase("core_1.snap") 303 err = snapstate.WaitRestart(task, snapsup) 304 c.Check(err, ErrorMatches, `cannot finish core installation, there was a rollback across reboot`) 305 } 306 307 func (bs *bootedSuite) TestWaitRestartBootableBase(c *C) { 308 r := snapstatetest.MockDeviceModel(ModelWithBase("core18")) 309 defer r() 310 311 st := bs.state 312 st.Lock() 313 defer st.Unlock() 314 315 task := st.NewTask("auto-connect", "...") 316 317 // not core snap 318 si := &snap.SideInfo{RealName: "some-app", Revision: snap.R(1)} 319 snaptest.MockSnap(c, "name: some-app\nversion: 1", si) 320 err := snapstate.WaitRestart(task, &snapstate.SnapSetup{SideInfo: si}) 321 c.Check(err, IsNil) 322 323 // core snap but we are on a model with a different base 324 si = &snap.SideInfo{RealName: "core"} 325 snaptest.MockSnap(c, "name: core\ntype: os\nversion: 1", si) 326 err = snapstate.WaitRestart(task, &snapstate.SnapSetup{SideInfo: si}) 327 c.Check(err, IsNil) 328 329 si = &snap.SideInfo{RealName: "core18"} 330 snapsup := &snapstate.SnapSetup{SideInfo: si} 331 snaptest.MockSnap(c, "name: core18\ntype: base\nversion: 1", si) 332 // core snap, restarting ... wait 333 state.MockRestarting(st, state.RestartSystem) 334 err = snapstate.WaitRestart(task, snapsup) 335 c.Check(err, FitsTypeOf, &state.Retry{}) 336 337 // core snap, restarted, waiting for current core revision 338 state.MockRestarting(st, state.RestartUnset) 339 bs.bootloader.BootVars["snap_mode"] = "trying" 340 err = snapstate.WaitRestart(task, snapsup) 341 c.Check(err, DeepEquals, &state.Retry{After: 5 * time.Second}) 342 343 // core18 snap updated 344 si.Revision = snap.R(2) 345 snaptest.MockSnap(c, "name: core18\ntype: base\nversion: 2", si) 346 347 // core snap, restarted, right core revision, no rollback 348 bs.bootloader.BootVars["snap_mode"] = "" 349 bs.bootloader.SetBootBase("core18_2.snap") 350 err = snapstate.WaitRestart(task, snapsup) 351 c.Check(err, IsNil) 352 353 // core snap, restarted, wrong core revision, rollback! 354 bs.bootloader.SetBootBase("core18_1.snap") 355 err = snapstate.WaitRestart(task, snapsup) 356 c.Check(err, ErrorMatches, `cannot finish core18 installation, there was a rollback across reboot`) 357 }