github.com/rigado/snapd@v2.42.5-go-mod+incompatible/overlord/devicestate/handlers_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 "context" 24 "fmt" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/asserts" 29 "github.com/snapcore/snapd/overlord/assertstate" 30 "github.com/snapcore/snapd/overlord/auth" 31 "github.com/snapcore/snapd/overlord/devicestate" 32 "github.com/snapcore/snapd/overlord/devicestate/devicestatetest" 33 "github.com/snapcore/snapd/overlord/snapstate" 34 "github.com/snapcore/snapd/overlord/state" 35 "github.com/snapcore/snapd/overlord/storecontext" 36 "github.com/snapcore/snapd/snap" 37 ) 38 39 // TODO: should we move this into a new handlers suite? 40 func (s *deviceMgrSuite) TestSetModelHandlerNewRevision(c *C) { 41 s.state.Lock() 42 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 43 Brand: "canonical", 44 Model: "pc-model", 45 }) 46 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 47 "architecture": "amd64", 48 "kernel": "pc-kernel", 49 "gadget": "pc", 50 "revision": "1", 51 "required-snaps": []interface{}{"foo", "bar"}, 52 }) 53 // foo and bar 54 fooSI := &snap.SideInfo{ 55 RealName: "foo", 56 Revision: snap.R(1), 57 } 58 barSI := &snap.SideInfo{ 59 RealName: "foo", 60 Revision: snap.R(1), 61 } 62 pcKernelSI := &snap.SideInfo{ 63 RealName: "pc-kernel", 64 Revision: snap.R(1), 65 } 66 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 67 SnapType: "app", 68 Active: true, 69 Sequence: []*snap.SideInfo{fooSI}, 70 Current: fooSI.Revision, 71 Flags: snapstate.Flags{Required: true}, 72 }) 73 snapstate.Set(s.state, "bar", &snapstate.SnapState{ 74 SnapType: "app", 75 Active: true, 76 Sequence: []*snap.SideInfo{barSI}, 77 Current: barSI.Revision, 78 Flags: snapstate.Flags{Required: true}, 79 }) 80 snapstate.Set(s.state, "pc-kernel", &snapstate.SnapState{ 81 SnapType: "kernel", 82 Active: true, 83 Sequence: []*snap.SideInfo{pcKernelSI}, 84 Current: pcKernelSI.Revision, 85 Flags: snapstate.Flags{Required: true}, 86 }) 87 s.state.Unlock() 88 89 newModel := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 90 "architecture": "amd64", 91 "kernel": "other-kernel", 92 "gadget": "pc", 93 "revision": "2", 94 "required-snaps": []interface{}{"foo"}, 95 }) 96 97 s.state.Lock() 98 t := s.state.NewTask("set-model", "set-model test") 99 chg := s.state.NewChange("dummy", "...") 100 chg.Set("new-model", string(asserts.Encode(newModel))) 101 chg.AddTask(t) 102 103 s.state.Unlock() 104 105 s.se.Ensure() 106 s.se.Wait() 107 108 s.state.Lock() 109 defer s.state.Unlock() 110 m, err := s.mgr.Model() 111 c.Assert(err, IsNil) 112 c.Assert(m, DeepEquals, newModel) 113 114 c.Assert(chg.Err(), IsNil) 115 116 // check required 117 var fooState snapstate.SnapState 118 var barState snapstate.SnapState 119 err = snapstate.Get(s.state, "foo", &fooState) 120 c.Assert(err, IsNil) 121 err = snapstate.Get(s.state, "bar", &barState) 122 c.Assert(err, IsNil) 123 c.Check(fooState.Flags.Required, Equals, true) 124 c.Check(barState.Flags.Required, Equals, false) 125 // the kernel is no longer required 126 var kernelState snapstate.SnapState 127 err = snapstate.Get(s.state, "pc-kernel", &kernelState) 128 c.Assert(err, IsNil) 129 c.Check(kernelState.Flags.Required, Equals, false) 130 } 131 132 func (s *deviceMgrSuite) TestSetModelHandlerSameRevisionNoError(c *C) { 133 model := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 134 "architecture": "amd64", 135 "kernel": "pc-kernel", 136 "gadget": "pc", 137 "revision": "1", 138 }) 139 140 s.state.Lock() 141 142 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 143 Brand: "canonical", 144 Model: "pc-model", 145 }) 146 err := assertstate.Add(s.state, model) 147 c.Assert(err, IsNil) 148 149 t := s.state.NewTask("set-model", "set-model test") 150 chg := s.state.NewChange("dummy", "...") 151 chg.Set("new-model", string(asserts.Encode(model))) 152 chg.AddTask(t) 153 154 s.state.Unlock() 155 156 s.se.Ensure() 157 s.se.Wait() 158 159 s.state.Lock() 160 defer s.state.Unlock() 161 c.Assert(chg.Err(), IsNil) 162 } 163 164 func (s *deviceMgrSuite) TestSetModelHandlerStoreSwitch(c *C) { 165 s.state.Lock() 166 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 167 Brand: "canonical", 168 Model: "pc-model", 169 }) 170 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 171 "architecture": "amd64", 172 "kernel": "pc-kernel", 173 "gadget": "pc", 174 "revision": "1", 175 }) 176 s.state.Unlock() 177 178 newModel := s.brands.Model("canonical", "pc-model", map[string]interface{}{ 179 "architecture": "amd64", 180 "kernel": "pc-kernel", 181 "gadget": "pc", 182 "store": "switched-store", 183 "revision": "2", 184 }) 185 186 s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService { 187 mod, err := devBE.Model() 188 c.Check(err, IsNil) 189 if err == nil { 190 c.Check(mod, DeepEquals, newModel) 191 } 192 return &freshSessionStore{} 193 } 194 195 s.state.Lock() 196 t := s.state.NewTask("set-model", "set-model test") 197 chg := s.state.NewChange("dummy", "...") 198 chg.Set("new-model", string(asserts.Encode(newModel))) 199 chg.Set("device", auth.DeviceState{ 200 Brand: "canonical", 201 Model: "pc-model", 202 SessionMacaroon: "switched-store-session", 203 }) 204 chg.AddTask(t) 205 206 s.state.Unlock() 207 208 s.se.Ensure() 209 s.se.Wait() 210 211 s.state.Lock() 212 defer s.state.Unlock() 213 c.Assert(chg.Err(), IsNil) 214 215 m, err := s.mgr.Model() 216 c.Assert(err, IsNil) 217 c.Assert(m, DeepEquals, newModel) 218 219 device, err := devicestatetest.Device(s.state) 220 c.Assert(err, IsNil) 221 c.Check(device, DeepEquals, &auth.DeviceState{ 222 Brand: "canonical", 223 Model: "pc-model", 224 SessionMacaroon: "switched-store-session", 225 }) 226 227 // cleanup 228 _, ok := devicestate.CachedRemodelCtx(chg) 229 c.Check(ok, Equals, true) 230 231 s.state.Unlock() 232 233 s.se.Ensure() 234 s.se.Wait() 235 236 s.state.Lock() 237 238 _, ok = devicestate.CachedRemodelCtx(chg) 239 c.Check(ok, Equals, false) 240 } 241 242 func (s *deviceMgrSuite) TestSetModelHandlerRereg(c *C) { 243 s.state.Lock() 244 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 245 Brand: "canonical", 246 Model: "pc-model", 247 Serial: "orig-serial", 248 }) 249 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 250 "architecture": "amd64", 251 "kernel": "pc-kernel", 252 "gadget": "pc", 253 }) 254 s.makeSerialAssertionInState(c, "canonical", "pc-model", "orig-serial") 255 s.state.Unlock() 256 257 newModel := s.brands.Model("canonical", "rereg-model", map[string]interface{}{ 258 "architecture": "amd64", 259 "kernel": "pc-kernel", 260 "gadget": "pc", 261 }) 262 263 s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService { 264 mod, err := devBE.Model() 265 c.Check(err, IsNil) 266 if err == nil { 267 c.Check(mod, DeepEquals, newModel) 268 } 269 return &freshSessionStore{} 270 } 271 272 s.state.Lock() 273 t := s.state.NewTask("set-model", "set-model test") 274 chg := s.state.NewChange("dummy", "...") 275 chg.Set("new-model", string(asserts.Encode(newModel))) 276 chg.Set("device", auth.DeviceState{ 277 Brand: "canonical", 278 Model: "rereg-model", 279 Serial: "orig-serial", 280 SessionMacaroon: "switched-store-session", 281 }) 282 chg.AddTask(t) 283 284 s.state.Unlock() 285 286 s.se.Ensure() 287 s.se.Wait() 288 289 s.state.Lock() 290 defer s.state.Unlock() 291 c.Assert(chg.Err(), IsNil) 292 293 m, err := s.mgr.Model() 294 c.Assert(err, IsNil) 295 c.Assert(m, DeepEquals, newModel) 296 297 device, err := devicestatetest.Device(s.state) 298 c.Assert(err, IsNil) 299 c.Check(device, DeepEquals, &auth.DeviceState{ 300 Brand: "canonical", 301 Model: "rereg-model", 302 Serial: "orig-serial", 303 SessionMacaroon: "switched-store-session", 304 }) 305 } 306 307 func (s *deviceMgrSuite) TestDoPrepareRemodeling(c *C) { 308 s.state.Lock() 309 defer s.state.Unlock() 310 s.state.Set("seeded", true) 311 s.state.Set("refresh-privacy-key", "some-privacy-key") 312 313 var testStore snapstate.StoreService 314 315 restore := devicestate.MockSnapstateInstallWithDeviceContext(func(ctx context.Context, st *state.State, name string, opts *snapstate.RevisionOptions, userID int, flags snapstate.Flags, deviceCtx snapstate.DeviceContext, fromChange string) (*state.TaskSet, error) { 316 c.Check(flags.Required, Equals, true) 317 c.Check(deviceCtx, NotNil) 318 c.Check(deviceCtx.ForRemodeling(), Equals, true) 319 320 tDownload := s.state.NewTask("fake-download", fmt.Sprintf("Download %s", name)) 321 tValidate := s.state.NewTask("validate-snap", fmt.Sprintf("Validate %s", name)) 322 tValidate.WaitFor(tDownload) 323 tInstall := s.state.NewTask("fake-install", fmt.Sprintf("Install %s", name)) 324 tInstall.WaitFor(tValidate) 325 ts := state.NewTaskSet(tDownload, tValidate, tInstall) 326 ts.MarkEdge(tValidate, snapstate.DownloadAndChecksDoneEdge) 327 return ts, nil 328 }) 329 defer restore() 330 331 // set a model assertion 332 s.makeModelAssertionInState(c, "canonical", "pc-model", map[string]interface{}{ 333 "architecture": "amd64", 334 "kernel": "pc-kernel", 335 "gadget": "pc", 336 "base": "core18", 337 }) 338 s.makeSerialAssertionInState(c, "canonical", "pc-model", "orig-serial") 339 devicestatetest.SetDevice(s.state, &auth.DeviceState{ 340 Brand: "canonical", 341 Model: "pc-model", 342 Serial: "orig-serial", 343 SessionMacaroon: "old-session", 344 }) 345 346 new := s.brands.Model("canonical", "rereg-model", map[string]interface{}{ 347 "architecture": "amd64", 348 "kernel": "pc-kernel", 349 "gadget": "pc", 350 "base": "core18", 351 "required-snaps": []interface{}{"new-required-snap-1", "new-required-snap-2"}, 352 }) 353 354 freshStore := &freshSessionStore{} 355 testStore = freshStore 356 357 s.newFakeStore = func(devBE storecontext.DeviceBackend) snapstate.StoreService { 358 mod, err := devBE.Model() 359 c.Check(err, IsNil) 360 if err == nil { 361 c.Check(mod, DeepEquals, new) 362 } 363 return testStore 364 } 365 366 cur, err := s.mgr.Model() 367 c.Assert(err, IsNil) 368 369 remodCtx, err := devicestate.RemodelCtx(s.state, cur, new) 370 c.Assert(err, IsNil) 371 372 c.Check(remodCtx.Kind(), Equals, devicestate.ReregRemodel) 373 374 chg := s.state.NewChange("remodel", "...") 375 remodCtx.Init(chg) 376 t := s.state.NewTask("prepare-remodeling", "...") 377 chg.AddTask(t) 378 379 // set new serial 380 s.makeSerialAssertionInState(c, "canonical", "rereg-model", "orig-serial") 381 chg.Set("device", auth.DeviceState{ 382 Brand: "canonical", 383 Model: "rereg-model", 384 Serial: "orig-serial", 385 SessionMacaroon: "switched-store-session", 386 }) 387 388 s.state.Unlock() 389 390 s.se.Ensure() 391 s.se.Wait() 392 393 s.state.Lock() 394 c.Assert(chg.Err(), IsNil) 395 396 c.Check(freshStore.ensureDeviceSession, Equals, 1) 397 398 // check that the expected tasks were injected 399 tl := chg.Tasks() 400 // 1 prepare-remodeling 401 // 2 snaps * 3 tasks (from the mock install above) + 402 // 1 "set-model" task at the end 403 c.Assert(tl, HasLen, 1+2*3+1) 404 405 // sanity 406 c.Check(tl[1].Kind(), Equals, "fake-download") 407 c.Check(tl[1+2*3].Kind(), Equals, "set-model") 408 409 // cleanup 410 // fake completion 411 for _, t := range tl[1:] { 412 t.SetStatus(state.DoneStatus) 413 } 414 _, ok := devicestate.CachedRemodelCtx(chg) 415 c.Check(ok, Equals, true) 416 417 s.state.Unlock() 418 419 s.se.Ensure() 420 s.se.Wait() 421 422 s.state.Lock() 423 424 _, ok = devicestate.CachedRemodelCtx(chg) 425 c.Check(ok, Equals, false) 426 }