github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/overlord/snapstate/handlers_link_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 import ( 23 "encoding/json" 24 "fmt" 25 "io/ioutil" 26 "path/filepath" 27 "time" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/boot" 32 "github.com/snapcore/snapd/boot/boottest" 33 "github.com/snapcore/snapd/bootloader" 34 "github.com/snapcore/snapd/bootloader/bootloadertest" 35 "github.com/snapcore/snapd/dirs" 36 "github.com/snapcore/snapd/overlord/auth" 37 "github.com/snapcore/snapd/overlord/configstate/config" 38 "github.com/snapcore/snapd/overlord/snapstate" 39 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 40 "github.com/snapcore/snapd/overlord/state" 41 "github.com/snapcore/snapd/release" 42 "github.com/snapcore/snapd/snap" 43 "github.com/snapcore/snapd/snap/snaptest" 44 "github.com/snapcore/snapd/testutil" 45 ) 46 47 type linkSnapSuite struct { 48 baseHandlerSuite 49 50 stateBackend *witnessRestartReqStateBackend 51 } 52 53 var _ = Suite(&linkSnapSuite{}) 54 55 type witnessRestartReqStateBackend struct { 56 restartRequested []state.RestartType 57 } 58 59 func (b *witnessRestartReqStateBackend) Checkpoint([]byte) error { 60 return nil 61 } 62 63 func (b *witnessRestartReqStateBackend) RequestRestart(t state.RestartType) { 64 b.restartRequested = append(b.restartRequested, t) 65 } 66 67 func (b *witnessRestartReqStateBackend) EnsureBefore(time.Duration) {} 68 69 func (s *linkSnapSuite) SetUpTest(c *C) { 70 s.stateBackend = &witnessRestartReqStateBackend{} 71 72 s.setup(c, s.stateBackend) 73 74 s.AddCleanup(snapstatetest.MockDeviceModel(DefaultModel())) 75 } 76 77 func checkHasCookieForSnap(c *C, st *state.State, instanceName string) { 78 var contexts map[string]interface{} 79 err := st.Get("snap-cookies", &contexts) 80 c.Assert(err, IsNil) 81 c.Check(contexts, HasLen, 1) 82 83 for _, snap := range contexts { 84 if instanceName == snap { 85 return 86 } 87 } 88 panic(fmt.Sprintf("Cookie missing for snap %q", instanceName)) 89 } 90 91 func (s *linkSnapSuite) TestDoLinkSnapSuccess(c *C) { 92 // we start without the auxiliary store info 93 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 94 95 s.state.Lock() 96 t := s.state.NewTask("link-snap", "test") 97 t.Set("snap-setup", &snapstate.SnapSetup{ 98 SideInfo: &snap.SideInfo{ 99 RealName: "foo", 100 Revision: snap.R(33), 101 SnapID: "foo-id", 102 }, 103 Channel: "beta", 104 UserID: 2, 105 }) 106 s.state.NewChange("dummy", "...").AddTask(t) 107 108 s.state.Unlock() 109 110 s.se.Ensure() 111 s.se.Wait() 112 113 s.state.Lock() 114 defer s.state.Unlock() 115 var snapst snapstate.SnapState 116 err := snapstate.Get(s.state, "foo", &snapst) 117 c.Assert(err, IsNil) 118 119 checkHasCookieForSnap(c, s.state, "foo") 120 121 typ, err := snapst.Type() 122 c.Check(err, IsNil) 123 c.Check(typ, Equals, snap.TypeApp) 124 125 c.Check(snapst.Active, Equals, true) 126 c.Check(snapst.Sequence, HasLen, 1) 127 c.Check(snapst.Current, Equals, snap.R(33)) 128 c.Check(snapst.TrackingChannel, Equals, "latest/beta") 129 c.Check(snapst.UserID, Equals, 2) 130 c.Check(snapst.CohortKey, Equals, "") 131 c.Check(t.Status(), Equals, state.DoneStatus) 132 c.Check(s.stateBackend.restartRequested, HasLen, 0) 133 134 // we end with the auxiliary store info 135 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FilePresent) 136 } 137 138 func (s *linkSnapSuite) TestDoLinkSnapSuccessWithCohort(c *C) { 139 // we start without the auxiliary store info 140 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 141 142 s.state.Lock() 143 t := s.state.NewTask("link-snap", "test") 144 t.Set("snap-setup", &snapstate.SnapSetup{ 145 SideInfo: &snap.SideInfo{ 146 RealName: "foo", 147 Revision: snap.R(33), 148 SnapID: "foo-id", 149 }, 150 Channel: "beta", 151 UserID: 2, 152 CohortKey: "wobbling", 153 }) 154 s.state.NewChange("dummy", "...").AddTask(t) 155 156 s.state.Unlock() 157 158 s.se.Ensure() 159 s.se.Wait() 160 161 s.state.Lock() 162 defer s.state.Unlock() 163 var snapst snapstate.SnapState 164 err := snapstate.Get(s.state, "foo", &snapst) 165 c.Assert(err, IsNil) 166 167 checkHasCookieForSnap(c, s.state, "foo") 168 169 typ, err := snapst.Type() 170 c.Check(err, IsNil) 171 c.Check(typ, Equals, snap.TypeApp) 172 173 c.Check(snapst.Active, Equals, true) 174 c.Check(snapst.Sequence, HasLen, 1) 175 c.Check(snapst.Current, Equals, snap.R(33)) 176 c.Check(snapst.TrackingChannel, Equals, "latest/beta") 177 c.Check(snapst.UserID, Equals, 2) 178 c.Check(snapst.CohortKey, Equals, "wobbling") 179 c.Check(t.Status(), Equals, state.DoneStatus) 180 c.Check(s.stateBackend.restartRequested, HasLen, 0) 181 182 // we end with the auxiliary store info 183 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FilePresent) 184 } 185 186 func (s *linkSnapSuite) TestDoLinkSnapSuccessNoUserID(c *C) { 187 s.state.Lock() 188 t := s.state.NewTask("link-snap", "test") 189 t.Set("snap-setup", &snapstate.SnapSetup{ 190 SideInfo: &snap.SideInfo{ 191 RealName: "foo", 192 Revision: snap.R(33), 193 }, 194 Channel: "beta", 195 }) 196 s.state.NewChange("dummy", "...").AddTask(t) 197 198 s.state.Unlock() 199 s.se.Ensure() 200 s.se.Wait() 201 s.state.Lock() 202 defer s.state.Unlock() 203 204 // check that snapst.UserID does not get set 205 var snapst snapstate.SnapState 206 err := snapstate.Get(s.state, "foo", &snapst) 207 c.Assert(err, IsNil) 208 c.Check(snapst.UserID, Equals, 0) 209 210 var snaps map[string]*json.RawMessage 211 err = s.state.Get("snaps", &snaps) 212 c.Assert(err, IsNil) 213 raw := []byte(*snaps["foo"]) 214 c.Check(string(raw), Not(testutil.Contains), "user-id") 215 } 216 217 func (s *linkSnapSuite) TestDoLinkSnapSuccessUserIDAlreadySet(c *C) { 218 s.state.Lock() 219 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 220 Sequence: []*snap.SideInfo{ 221 {RealName: "foo", Revision: snap.R(1)}, 222 }, 223 Current: snap.R(1), 224 UserID: 1, 225 }) 226 // the user 227 user, err := auth.NewUser(s.state, "username", "email@test.com", "macaroon", []string{"discharge"}) 228 c.Assert(err, IsNil) 229 c.Assert(user.ID, Equals, 1) 230 231 t := s.state.NewTask("link-snap", "test") 232 t.Set("snap-setup", &snapstate.SnapSetup{ 233 SideInfo: &snap.SideInfo{ 234 RealName: "foo", 235 Revision: snap.R(33), 236 }, 237 Channel: "beta", 238 UserID: 2, 239 }) 240 s.state.NewChange("dummy", "...").AddTask(t) 241 242 s.state.Unlock() 243 s.se.Ensure() 244 s.se.Wait() 245 s.state.Lock() 246 defer s.state.Unlock() 247 248 // check that snapst.UserID was not "transferred" 249 var snapst snapstate.SnapState 250 err = snapstate.Get(s.state, "foo", &snapst) 251 c.Assert(err, IsNil) 252 c.Check(snapst.UserID, Equals, 1) 253 } 254 255 func (s *linkSnapSuite) TestDoLinkSnapSuccessUserLoggedOut(c *C) { 256 s.state.Lock() 257 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 258 Sequence: []*snap.SideInfo{ 259 {RealName: "foo", Revision: snap.R(1)}, 260 }, 261 Current: snap.R(1), 262 UserID: 1, 263 }) 264 265 t := s.state.NewTask("link-snap", "test") 266 t.Set("snap-setup", &snapstate.SnapSetup{ 267 SideInfo: &snap.SideInfo{ 268 RealName: "foo", 269 Revision: snap.R(33), 270 }, 271 Channel: "beta", 272 UserID: 2, 273 }) 274 s.state.NewChange("dummy", "...").AddTask(t) 275 276 s.state.Unlock() 277 s.se.Ensure() 278 s.se.Wait() 279 s.state.Lock() 280 defer s.state.Unlock() 281 282 // check that snapst.UserID was transferred 283 // given that user 1 doesn't exist anymore 284 var snapst snapstate.SnapState 285 err := snapstate.Get(s.state, "foo", &snapst) 286 c.Assert(err, IsNil) 287 c.Check(snapst.UserID, Equals, 2) 288 } 289 290 func (s *linkSnapSuite) TestDoLinkSnapSeqFile(c *C) { 291 s.state.Lock() 292 // pretend we have an installed snap 293 si11 := &snap.SideInfo{ 294 RealName: "foo", 295 Revision: snap.R(11), 296 } 297 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 298 Sequence: []*snap.SideInfo{si11}, 299 Current: si11.Revision, 300 }) 301 // add a new one 302 t := s.state.NewTask("link-snap", "test") 303 t.Set("snap-setup", &snapstate.SnapSetup{ 304 SideInfo: &snap.SideInfo{ 305 RealName: "foo", 306 Revision: snap.R(33), 307 }, 308 Channel: "beta", 309 }) 310 s.state.NewChange("dummy", "...").AddTask(t) 311 s.state.Unlock() 312 313 s.se.Ensure() 314 s.se.Wait() 315 316 s.state.Lock() 317 defer s.state.Unlock() 318 var snapst snapstate.SnapState 319 err := snapstate.Get(s.state, "foo", &snapst) 320 c.Assert(err, IsNil) 321 322 // and check that the sequence file got updated 323 seqContent, err := ioutil.ReadFile(filepath.Join(dirs.SnapSeqDir, "foo.json")) 324 c.Assert(err, IsNil) 325 c.Check(string(seqContent), Equals, `{"sequence":[{"name":"foo","snap-id":"","revision":"11"},{"name":"foo","snap-id":"","revision":"33"}],"current":"33"}`) 326 } 327 328 func (s *linkSnapSuite) TestDoUndoLinkSnap(c *C) { 329 s.state.Lock() 330 defer s.state.Unlock() 331 // a hook might have set some config 332 cfg := json.RawMessage(`{"c":true}`) 333 err := config.SetSnapConfig(s.state, "foo", &cfg) 334 c.Assert(err, IsNil) 335 336 si := &snap.SideInfo{ 337 RealName: "foo", 338 Revision: snap.R(33), 339 } 340 t := s.state.NewTask("link-snap", "test") 341 t.Set("snap-setup", &snapstate.SnapSetup{ 342 SideInfo: si, 343 Channel: "beta", 344 }) 345 chg := s.state.NewChange("dummy", "...") 346 chg.AddTask(t) 347 348 terr := s.state.NewTask("error-trigger", "provoking total undo") 349 terr.WaitFor(t) 350 chg.AddTask(terr) 351 352 s.state.Unlock() 353 354 for i := 0; i < 6; i++ { 355 s.se.Ensure() 356 s.se.Wait() 357 } 358 359 s.state.Lock() 360 var snapst snapstate.SnapState 361 err = snapstate.Get(s.state, "foo", &snapst) 362 c.Assert(err, Equals, state.ErrNoState) 363 c.Check(t.Status(), Equals, state.UndoneStatus) 364 365 // and check that the sequence file got updated 366 seqContent, err := ioutil.ReadFile(filepath.Join(dirs.SnapSeqDir, "foo.json")) 367 c.Assert(err, IsNil) 368 c.Check(string(seqContent), Equals, `{"sequence":[],"current":"unset"}`) 369 370 // nothing in config 371 var config map[string]*json.RawMessage 372 err = s.state.Get("config", &config) 373 c.Assert(err, IsNil) 374 c.Check(config, HasLen, 1) 375 _, ok := config["core"] 376 c.Check(ok, Equals, true) 377 } 378 379 func (s *linkSnapSuite) TestDoUndoUnlinkCurrentSnapWithVitalityScore(c *C) { 380 s.state.Lock() 381 defer s.state.Unlock() 382 // foo has a vitality-hint 383 cfg := json.RawMessage(`{"resilience":{"vitality-hint":"bar,foo,baz"}}`) 384 err := config.SetSnapConfig(s.state, "core", &cfg) 385 c.Assert(err, IsNil) 386 387 si1 := &snap.SideInfo{ 388 RealName: "foo", 389 Revision: snap.R(11), 390 } 391 si2 := &snap.SideInfo{ 392 RealName: "foo", 393 Revision: snap.R(33), 394 } 395 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 396 Sequence: []*snap.SideInfo{si1}, 397 Current: si1.Revision, 398 Active: true, 399 }) 400 t := s.state.NewTask("unlink-current-snap", "test") 401 t.Set("snap-setup", &snapstate.SnapSetup{ 402 SideInfo: si2, 403 }) 404 chg := s.state.NewChange("dummy", "...") 405 chg.AddTask(t) 406 407 terr := s.state.NewTask("error-trigger", "provoking total undo") 408 terr.WaitFor(t) 409 chg.AddTask(terr) 410 411 s.state.Unlock() 412 413 for i := 0; i < 3; i++ { 414 s.se.Ensure() 415 s.se.Wait() 416 } 417 418 s.state.Lock() 419 var snapst snapstate.SnapState 420 err = snapstate.Get(s.state, "foo", &snapst) 421 c.Assert(err, IsNil) 422 c.Check(snapst.Active, Equals, true) 423 c.Check(snapst.Sequence, HasLen, 1) 424 c.Check(snapst.Current, Equals, snap.R(11)) 425 c.Check(t.Status(), Equals, state.UndoneStatus) 426 427 expected := fakeOps{ 428 { 429 op: "unlink-snap", 430 path: filepath.Join(dirs.SnapMountDir, "foo/11"), 431 }, 432 { 433 op: "link-snap", 434 path: filepath.Join(dirs.SnapMountDir, "foo/11"), 435 vitalityRank: 2, 436 }, 437 } 438 c.Check(s.fakeBackend.ops, DeepEquals, expected) 439 } 440 441 func (s *linkSnapSuite) TestDoLinkSnapWithVitalityScore(c *C) { 442 s.state.Lock() 443 defer s.state.Unlock() 444 // a hook might have set some config 445 cfg := json.RawMessage(`{"resilience":{"vitality-hint":"bar,foo,baz"}}`) 446 err := config.SetSnapConfig(s.state, "core", &cfg) 447 c.Assert(err, IsNil) 448 449 si := &snap.SideInfo{ 450 RealName: "foo", 451 Revision: snap.R(33), 452 } 453 t := s.state.NewTask("link-snap", "test") 454 t.Set("snap-setup", &snapstate.SnapSetup{ 455 SideInfo: si, 456 }) 457 chg := s.state.NewChange("dummy", "...") 458 chg.AddTask(t) 459 460 s.state.Unlock() 461 462 for i := 0; i < 6; i++ { 463 s.se.Ensure() 464 s.se.Wait() 465 } 466 467 s.state.Lock() 468 expected := fakeOps{ 469 { 470 op: "candidate", 471 sinfo: *si, 472 }, 473 { 474 op: "link-snap", 475 path: filepath.Join(dirs.SnapMountDir, "foo/33"), 476 vitalityRank: 2, 477 }, 478 } 479 c.Check(s.fakeBackend.ops, DeepEquals, expected) 480 } 481 482 func (s *linkSnapSuite) TestDoLinkSnapTryToCleanupOnError(c *C) { 483 s.state.Lock() 484 defer s.state.Unlock() 485 si := &snap.SideInfo{ 486 RealName: "foo", 487 Revision: snap.R(35), 488 } 489 t := s.state.NewTask("link-snap", "test") 490 t.Set("snap-setup", &snapstate.SnapSetup{ 491 SideInfo: si, 492 Channel: "beta", 493 }) 494 495 s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "foo/35") 496 s.state.NewChange("dummy", "...").AddTask(t) 497 s.state.Unlock() 498 499 s.se.Ensure() 500 s.se.Wait() 501 502 s.state.Lock() 503 504 // state as expected 505 var snapst snapstate.SnapState 506 err := snapstate.Get(s.state, "foo", &snapst) 507 c.Assert(err, Equals, state.ErrNoState) 508 509 // tried to cleanup 510 expected := fakeOps{ 511 { 512 op: "candidate", 513 sinfo: *si, 514 }, 515 { 516 op: "link-snap.failed", 517 path: filepath.Join(dirs.SnapMountDir, "foo/35"), 518 }, 519 { 520 op: "unlink-snap", 521 path: filepath.Join(dirs.SnapMountDir, "foo/35"), 522 523 unlinkFirstInstallUndo: true, 524 }, 525 } 526 527 // start with an easier-to-read error if this fails: 528 c.Check(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 529 c.Check(s.fakeBackend.ops, DeepEquals, expected) 530 } 531 532 func (s *linkSnapSuite) TestDoLinkSnapSuccessCoreRestarts(c *C) { 533 restore := release.MockOnClassic(true) 534 defer restore() 535 536 s.state.Lock() 537 si := &snap.SideInfo{ 538 RealName: "core", 539 Revision: snap.R(33), 540 } 541 t := s.state.NewTask("link-snap", "test") 542 t.Set("snap-setup", &snapstate.SnapSetup{ 543 SideInfo: si, 544 }) 545 s.state.NewChange("dummy", "...").AddTask(t) 546 547 s.state.Unlock() 548 549 s.se.Ensure() 550 s.se.Wait() 551 552 s.state.Lock() 553 defer s.state.Unlock() 554 555 var snapst snapstate.SnapState 556 err := snapstate.Get(s.state, "core", &snapst) 557 c.Assert(err, IsNil) 558 559 typ, err := snapst.Type() 560 c.Check(err, IsNil) 561 c.Check(typ, Equals, snap.TypeOS) 562 563 c.Check(t.Status(), Equals, state.DoneStatus) 564 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 565 c.Check(t.Log(), HasLen, 1) 566 c.Check(t.Log()[0], Matches, `.*INFO Requested daemon restart\.`) 567 } 568 569 func (s *linkSnapSuite) TestDoLinkSnapSuccessSnapdRestartsOnCoreWithBase(c *C) { 570 restore := release.MockOnClassic(false) 571 defer restore() 572 573 r := snapstatetest.MockDeviceModel(ModelWithBase("core18")) 574 defer r() 575 576 s.state.Lock() 577 si := &snap.SideInfo{ 578 RealName: "snapd", 579 SnapID: "snapd-snap-id", 580 Revision: snap.R(22), 581 } 582 t := s.state.NewTask("link-snap", "test") 583 t.Set("snap-setup", &snapstate.SnapSetup{ 584 SideInfo: si, 585 Type: snap.TypeSnapd, 586 }) 587 s.state.NewChange("dummy", "...").AddTask(t) 588 589 s.state.Unlock() 590 591 s.se.Ensure() 592 s.se.Wait() 593 594 s.state.Lock() 595 defer s.state.Unlock() 596 597 var snapst snapstate.SnapState 598 err := snapstate.Get(s.state, "snapd", &snapst) 599 c.Assert(err, IsNil) 600 601 typ, err := snapst.Type() 602 c.Check(err, IsNil) 603 c.Check(typ, Equals, snap.TypeSnapd) 604 605 c.Check(t.Status(), Equals, state.DoneStatus) 606 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 607 c.Check(t.Log(), HasLen, 1) 608 c.Check(t.Log()[0], Matches, `.*INFO Requested daemon restart \(snapd snap\)\.`) 609 } 610 611 func (s *linkSnapSuite) TestDoLinkSnapSuccessRebootForCoreBase(c *C) { 612 restore := release.MockOnClassic(false) 613 defer restore() 614 615 r := snapstatetest.MockDeviceModel(ModelWithBase("core18")) 616 defer r() 617 618 s.fakeBackend.linkSnapMaybeReboot = true 619 620 s.state.Lock() 621 defer s.state.Unlock() 622 623 // we need to init the boot-id 624 err := s.state.VerifyReboot("some-boot-id") 625 c.Assert(err, IsNil) 626 627 si := &snap.SideInfo{ 628 RealName: "core18", 629 SnapID: "core18-id", 630 Revision: snap.R(22), 631 } 632 t := s.state.NewTask("link-snap", "test") 633 t.Set("snap-setup", &snapstate.SnapSetup{ 634 SideInfo: si, 635 }) 636 s.state.NewChange("dummy", "...").AddTask(t) 637 638 s.state.Unlock() 639 s.se.Ensure() 640 s.se.Wait() 641 s.state.Lock() 642 643 c.Check(t.Status(), Equals, state.DoneStatus) 644 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartSystem}) 645 c.Assert(t.Log(), HasLen, 1) 646 c.Check(t.Log()[0], Matches, `.*INFO Requested system restart.*`) 647 } 648 649 func (s *linkSnapSuite) TestDoLinkSnapSuccessSnapdRestartsOnClassic(c *C) { 650 restore := release.MockOnClassic(true) 651 defer restore() 652 653 s.state.Lock() 654 si := &snap.SideInfo{ 655 RealName: "snapd", 656 SnapID: "snapd-snap-id", 657 Revision: snap.R(22), 658 } 659 t := s.state.NewTask("link-snap", "test") 660 t.Set("snap-setup", &snapstate.SnapSetup{ 661 SideInfo: si, 662 Type: snap.TypeSnapd, 663 }) 664 s.state.NewChange("dummy", "...").AddTask(t) 665 666 s.state.Unlock() 667 668 s.se.Ensure() 669 s.se.Wait() 670 671 s.state.Lock() 672 defer s.state.Unlock() 673 674 var snapst snapstate.SnapState 675 err := snapstate.Get(s.state, "snapd", &snapst) 676 c.Assert(err, IsNil) 677 678 typ, err := snapst.Type() 679 c.Check(err, IsNil) 680 c.Check(typ, Equals, snap.TypeSnapd) 681 682 c.Check(t.Status(), Equals, state.DoneStatus) 683 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 684 c.Check(t.Log(), HasLen, 1) 685 } 686 687 func (s *linkSnapSuite) TestDoLinkSnapSuccessCoreAndSnapdNoCoreRestart(c *C) { 688 restore := release.MockOnClassic(true) 689 defer restore() 690 691 s.state.Lock() 692 siSnapd := &snap.SideInfo{ 693 RealName: "snapd", 694 Revision: snap.R(64), 695 } 696 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 697 Sequence: []*snap.SideInfo{siSnapd}, 698 Current: siSnapd.Revision, 699 Active: true, 700 SnapType: "snapd", 701 }) 702 703 si := &snap.SideInfo{ 704 RealName: "core", 705 Revision: snap.R(33), 706 } 707 t := s.state.NewTask("link-snap", "test") 708 t.Set("snap-setup", &snapstate.SnapSetup{ 709 SideInfo: si, 710 }) 711 s.state.NewChange("dummy", "...").AddTask(t) 712 713 s.state.Unlock() 714 715 s.se.Ensure() 716 s.se.Wait() 717 718 s.state.Lock() 719 defer s.state.Unlock() 720 721 var snapst snapstate.SnapState 722 err := snapstate.Get(s.state, "core", &snapst) 723 c.Assert(err, IsNil) 724 725 typ, err := snapst.Type() 726 c.Check(err, IsNil) 727 c.Check(typ, Equals, snap.TypeOS) 728 729 c.Check(t.Status(), Equals, state.DoneStatus) 730 c.Check(s.stateBackend.restartRequested, IsNil) 731 c.Check(t.Log(), HasLen, 0) 732 } 733 734 func (s *linkSnapSuite) TestDoUndoLinkSnapSequenceDidNotHaveCandidate(c *C) { 735 s.state.Lock() 736 defer s.state.Unlock() 737 si1 := &snap.SideInfo{ 738 RealName: "foo", 739 Revision: snap.R(1), 740 } 741 si2 := &snap.SideInfo{ 742 RealName: "foo", 743 Revision: snap.R(2), 744 } 745 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 746 Sequence: []*snap.SideInfo{si1}, 747 Current: si1.Revision, 748 }) 749 t := s.state.NewTask("link-snap", "test") 750 t.Set("snap-setup", &snapstate.SnapSetup{ 751 SideInfo: si2, 752 Channel: "beta", 753 }) 754 chg := s.state.NewChange("dummy", "...") 755 chg.AddTask(t) 756 757 terr := s.state.NewTask("error-trigger", "provoking total undo") 758 terr.WaitFor(t) 759 chg.AddTask(terr) 760 761 s.state.Unlock() 762 763 for i := 0; i < 6; i++ { 764 s.se.Ensure() 765 s.se.Wait() 766 } 767 768 s.state.Lock() 769 var snapst snapstate.SnapState 770 err := snapstate.Get(s.state, "foo", &snapst) 771 c.Assert(err, IsNil) 772 c.Check(snapst.Active, Equals, false) 773 c.Check(snapst.Sequence, HasLen, 1) 774 c.Check(snapst.Current, Equals, snap.R(1)) 775 c.Check(t.Status(), Equals, state.UndoneStatus) 776 } 777 778 func (s *linkSnapSuite) TestDoUndoLinkSnapSequenceHadCandidate(c *C) { 779 s.state.Lock() 780 defer s.state.Unlock() 781 si1 := &snap.SideInfo{ 782 RealName: "foo", 783 Revision: snap.R(1), 784 } 785 si2 := &snap.SideInfo{ 786 RealName: "foo", 787 Revision: snap.R(2), 788 } 789 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 790 Sequence: []*snap.SideInfo{si1, si2}, 791 Current: si2.Revision, 792 }) 793 t := s.state.NewTask("link-snap", "test") 794 t.Set("snap-setup", &snapstate.SnapSetup{ 795 SideInfo: si1, 796 Channel: "beta", 797 }) 798 chg := s.state.NewChange("dummy", "...") 799 chg.AddTask(t) 800 801 terr := s.state.NewTask("error-trigger", "provoking total undo") 802 terr.WaitFor(t) 803 chg.AddTask(terr) 804 805 s.state.Unlock() 806 807 for i := 0; i < 6; i++ { 808 s.se.Ensure() 809 s.se.Wait() 810 } 811 812 s.state.Lock() 813 var snapst snapstate.SnapState 814 err := snapstate.Get(s.state, "foo", &snapst) 815 c.Assert(err, IsNil) 816 c.Check(snapst.Active, Equals, false) 817 c.Check(snapst.Sequence, HasLen, 2) 818 c.Check(snapst.Current, Equals, snap.R(2)) 819 c.Check(t.Status(), Equals, state.UndoneStatus) 820 } 821 822 func (s *linkSnapSuite) TestDoUndoUnlinkCurrentSnapCore(c *C) { 823 restore := release.MockOnClassic(true) 824 defer restore() 825 826 s.state.Lock() 827 defer s.state.Unlock() 828 si1 := &snap.SideInfo{ 829 RealName: "core", 830 Revision: snap.R(1), 831 } 832 si2 := &snap.SideInfo{ 833 RealName: "core", 834 Revision: snap.R(2), 835 } 836 snapstate.Set(s.state, "core", &snapstate.SnapState{ 837 Sequence: []*snap.SideInfo{si1}, 838 Current: si1.Revision, 839 Active: true, 840 SnapType: "os", 841 }) 842 t := s.state.NewTask("unlink-current-snap", "test") 843 t.Set("snap-setup", &snapstate.SnapSetup{ 844 SideInfo: si2, 845 }) 846 chg := s.state.NewChange("dummy", "...") 847 chg.AddTask(t) 848 849 terr := s.state.NewTask("error-trigger", "provoking total undo") 850 terr.WaitFor(t) 851 chg.AddTask(terr) 852 853 s.state.Unlock() 854 855 for i := 0; i < 3; i++ { 856 s.se.Ensure() 857 s.se.Wait() 858 } 859 860 s.state.Lock() 861 var snapst snapstate.SnapState 862 err := snapstate.Get(s.state, "core", &snapst) 863 c.Assert(err, IsNil) 864 c.Check(snapst.Active, Equals, true) 865 c.Check(snapst.Sequence, HasLen, 1) 866 c.Check(snapst.Current, Equals, snap.R(1)) 867 c.Check(t.Status(), Equals, state.UndoneStatus) 868 869 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 870 } 871 872 func (s *linkSnapSuite) TestDoUndoUnlinkCurrentSnapCoreBase(c *C) { 873 restore := release.MockOnClassic(false) 874 defer restore() 875 876 r := snapstatetest.MockDeviceModel(ModelWithBase("core18")) 877 defer r() 878 879 s.fakeBackend.linkSnapMaybeReboot = true 880 881 s.state.Lock() 882 defer s.state.Unlock() 883 // we need to init the boot-id 884 err := s.state.VerifyReboot("some-boot-id") 885 c.Assert(err, IsNil) 886 887 si1 := &snap.SideInfo{ 888 RealName: "core18", 889 Revision: snap.R(1), 890 } 891 si2 := &snap.SideInfo{ 892 RealName: "core18", 893 Revision: snap.R(2), 894 } 895 snapstate.Set(s.state, "core18", &snapstate.SnapState{ 896 Sequence: []*snap.SideInfo{si1}, 897 Current: si1.Revision, 898 Active: true, 899 SnapType: "base", 900 }) 901 t := s.state.NewTask("unlink-current-snap", "test") 902 t.Set("snap-setup", &snapstate.SnapSetup{ 903 SideInfo: si2, 904 }) 905 chg := s.state.NewChange("dummy", "...") 906 chg.AddTask(t) 907 908 terr := s.state.NewTask("error-trigger", "provoking total undo") 909 terr.WaitFor(t) 910 chg.AddTask(terr) 911 912 s.state.Unlock() 913 for i := 0; i < 3; i++ { 914 s.se.Ensure() 915 s.se.Wait() 916 } 917 s.state.Lock() 918 919 var snapst snapstate.SnapState 920 err = snapstate.Get(s.state, "core18", &snapst) 921 c.Assert(err, IsNil) 922 c.Check(snapst.Active, Equals, true) 923 c.Check(snapst.Sequence, HasLen, 1) 924 c.Check(snapst.Current, Equals, snap.R(1)) 925 c.Check(t.Status(), Equals, state.UndoneStatus) 926 927 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartSystem}) 928 } 929 930 func (s *linkSnapSuite) TestDoUndoLinkSnapCoreClassic(c *C) { 931 restore := release.MockOnClassic(true) 932 defer restore() 933 934 s.state.Lock() 935 defer s.state.Unlock() 936 937 // no previous core snap and an error on link, in this 938 // case we need to restart on classic back into the distro 939 // package version 940 si1 := &snap.SideInfo{ 941 RealName: "core", 942 Revision: snap.R(1), 943 } 944 t := s.state.NewTask("link-snap", "test") 945 t.Set("snap-setup", &snapstate.SnapSetup{ 946 SideInfo: si1, 947 }) 948 chg := s.state.NewChange("dummy", "...") 949 chg.AddTask(t) 950 951 terr := s.state.NewTask("error-trigger", "provoking total undo") 952 terr.WaitFor(t) 953 chg.AddTask(terr) 954 955 s.state.Unlock() 956 957 for i := 0; i < 3; i++ { 958 s.se.Ensure() 959 s.se.Wait() 960 } 961 962 s.state.Lock() 963 var snapst snapstate.SnapState 964 err := snapstate.Get(s.state, "core", &snapst) 965 c.Assert(err, Equals, state.ErrNoState) 966 c.Check(t.Status(), Equals, state.UndoneStatus) 967 968 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon, state.RestartDaemon}) 969 970 } 971 972 func (s *linkSnapSuite) TestLinkSnapInjectsAutoConnectIfMissing(c *C) { 973 si1 := &snap.SideInfo{ 974 RealName: "snap1", 975 Revision: snap.R(1), 976 } 977 sup1 := &snapstate.SnapSetup{SideInfo: si1} 978 si2 := &snap.SideInfo{ 979 RealName: "snap2", 980 Revision: snap.R(1), 981 } 982 sup2 := &snapstate.SnapSetup{SideInfo: si2} 983 984 s.state.Lock() 985 defer s.state.Unlock() 986 987 task0 := s.state.NewTask("setup-profiles", "") 988 task1 := s.state.NewTask("link-snap", "") 989 task1.WaitFor(task0) 990 task0.Set("snap-setup", sup1) 991 task1.Set("snap-setup", sup1) 992 993 task2 := s.state.NewTask("setup-profiles", "") 994 task3 := s.state.NewTask("link-snap", "") 995 task2.WaitFor(task1) 996 task3.WaitFor(task2) 997 task2.Set("snap-setup", sup2) 998 task3.Set("snap-setup", sup2) 999 1000 chg := s.state.NewChange("test", "") 1001 chg.AddTask(task0) 1002 chg.AddTask(task1) 1003 chg.AddTask(task2) 1004 chg.AddTask(task3) 1005 1006 s.state.Unlock() 1007 1008 for i := 0; i < 10; i++ { 1009 s.se.Ensure() 1010 s.se.Wait() 1011 } 1012 1013 s.state.Lock() 1014 1015 // ensure all our tasks ran 1016 c.Assert(chg.Err(), IsNil) 1017 c.Assert(chg.Tasks(), HasLen, 6) 1018 1019 // sanity checks 1020 t := chg.Tasks()[1] 1021 c.Assert(t.Kind(), Equals, "link-snap") 1022 t = chg.Tasks()[3] 1023 c.Assert(t.Kind(), Equals, "link-snap") 1024 1025 // check that auto-connect tasks were added and have snap-setup 1026 var autoconnectSup snapstate.SnapSetup 1027 t = chg.Tasks()[4] 1028 c.Assert(t.Kind(), Equals, "auto-connect") 1029 c.Assert(t.Get("snap-setup", &autoconnectSup), IsNil) 1030 c.Assert(autoconnectSup.InstanceName(), Equals, "snap1") 1031 1032 t = chg.Tasks()[5] 1033 c.Assert(t.Kind(), Equals, "auto-connect") 1034 c.Assert(t.Get("snap-setup", &autoconnectSup), IsNil) 1035 c.Assert(autoconnectSup.InstanceName(), Equals, "snap2") 1036 } 1037 1038 func (s *linkSnapSuite) TestDoLinkSnapFailureCleansUpAux(c *C) { 1039 // this is very chummy with the order of LinkSnap 1040 c.Assert(ioutil.WriteFile(dirs.SnapSeqDir, nil, 0644), IsNil) 1041 1042 // we start without the auxiliary store info 1043 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 1044 1045 s.state.Lock() 1046 t := s.state.NewTask("link-snap", "test") 1047 t.Set("snap-setup", &snapstate.SnapSetup{ 1048 SideInfo: &snap.SideInfo{ 1049 RealName: "foo", 1050 Revision: snap.R(33), 1051 SnapID: "foo-id", 1052 }, 1053 Channel: "beta", 1054 UserID: 2, 1055 }) 1056 s.state.NewChange("dummy", "...").AddTask(t) 1057 1058 s.state.Unlock() 1059 1060 s.se.Ensure() 1061 s.se.Wait() 1062 1063 s.state.Lock() 1064 defer s.state.Unlock() 1065 1066 c.Check(t.Status(), Equals, state.ErrorStatus) 1067 c.Check(s.stateBackend.restartRequested, HasLen, 0) 1068 1069 // we end without the auxiliary store info 1070 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 1071 } 1072 1073 func (s *linkSnapSuite) TestLinkSnapResetsRefreshInhibitedTime(c *C) { 1074 // When a snap is linked the refresh-inhibited-time is reset to zero 1075 // to indicate a successful refresh. The old value is stored in task 1076 // state for task undo logic. 1077 s.state.Lock() 1078 defer s.state.Unlock() 1079 1080 instant := time.Now() 1081 1082 si := &snap.SideInfo{RealName: "snap", Revision: snap.R(1)} 1083 sup := &snapstate.SnapSetup{SideInfo: si} 1084 snapstate.Set(s.state, "snap", &snapstate.SnapState{ 1085 Sequence: []*snap.SideInfo{si}, 1086 Current: si.Revision, 1087 RefreshInhibitedTime: &instant, 1088 }) 1089 1090 task := s.state.NewTask("link-snap", "") 1091 task.Set("snap-setup", sup) 1092 chg := s.state.NewChange("test", "") 1093 chg.AddTask(task) 1094 1095 s.state.Unlock() 1096 1097 for i := 0; i < 10; i++ { 1098 s.se.Ensure() 1099 s.se.Wait() 1100 } 1101 1102 s.state.Lock() 1103 1104 c.Assert(chg.Err(), IsNil) 1105 c.Assert(chg.Tasks(), HasLen, 1) 1106 1107 var snapst snapstate.SnapState 1108 err := snapstate.Get(s.state, "snap", &snapst) 1109 c.Assert(err, IsNil) 1110 c.Check(snapst.RefreshInhibitedTime, IsNil) 1111 1112 var oldTime time.Time 1113 c.Assert(task.Get("old-refresh-inhibited-time", &oldTime), IsNil) 1114 c.Check(oldTime.Equal(instant), Equals, true) 1115 } 1116 1117 func (s *linkSnapSuite) TestDoUndoLinkSnapRestoresRefreshInhibitedTime(c *C) { 1118 s.state.Lock() 1119 defer s.state.Unlock() 1120 1121 instant := time.Now() 1122 1123 si := &snap.SideInfo{RealName: "snap", Revision: snap.R(1)} 1124 sup := &snapstate.SnapSetup{SideInfo: si} 1125 snapstate.Set(s.state, "snap", &snapstate.SnapState{ 1126 Sequence: []*snap.SideInfo{si}, 1127 Current: si.Revision, 1128 RefreshInhibitedTime: &instant, 1129 }) 1130 1131 task := s.state.NewTask("link-snap", "") 1132 task.Set("snap-setup", sup) 1133 chg := s.state.NewChange("test", "") 1134 chg.AddTask(task) 1135 1136 terr := s.state.NewTask("error-trigger", "provoking total undo") 1137 terr.WaitFor(task) 1138 chg.AddTask(terr) 1139 1140 s.state.Unlock() 1141 1142 for i := 0; i < 6; i++ { 1143 s.se.Ensure() 1144 s.se.Wait() 1145 } 1146 1147 s.state.Lock() 1148 1149 c.Assert(chg.Err(), NotNil) 1150 c.Assert(chg.Tasks(), HasLen, 2) 1151 c.Check(task.Status(), Equals, state.UndoneStatus) 1152 1153 var snapst snapstate.SnapState 1154 err := snapstate.Get(s.state, "snap", &snapst) 1155 c.Assert(err, IsNil) 1156 c.Check(snapst.RefreshInhibitedTime.Equal(instant), Equals, true) 1157 } 1158 1159 func (s *linkSnapSuite) TestUndoLinkSnapdFirstInstall(c *C) { 1160 restore := release.MockOnClassic(false) 1161 defer restore() 1162 1163 s.state.Lock() 1164 si := &snap.SideInfo{ 1165 RealName: "snapd", 1166 SnapID: "snapd-snap-id", 1167 Revision: snap.R(22), 1168 } 1169 chg := s.state.NewChange("dummy", "...") 1170 t := s.state.NewTask("link-snap", "test") 1171 t.Set("snap-setup", &snapstate.SnapSetup{ 1172 SideInfo: si, 1173 Type: snap.TypeSnapd, 1174 }) 1175 chg.AddTask(t) 1176 terr := s.state.NewTask("error-trigger", "provoking total undo") 1177 terr.WaitFor(t) 1178 chg.AddTask(terr) 1179 1180 s.state.Unlock() 1181 1182 for i := 0; i < 6; i++ { 1183 s.se.Ensure() 1184 s.se.Wait() 1185 } 1186 1187 s.state.Lock() 1188 defer s.state.Unlock() 1189 1190 var snapst snapstate.SnapState 1191 err := snapstate.Get(s.state, "snapd", &snapst) 1192 c.Assert(err, Equals, state.ErrNoState) 1193 c.Check(t.Status(), Equals, state.UndoneStatus) 1194 1195 expected := fakeOps{ 1196 { 1197 op: "candidate", 1198 sinfo: *si, 1199 }, 1200 { 1201 op: "link-snap", 1202 path: filepath.Join(dirs.SnapMountDir, "snapd/22"), 1203 }, 1204 { 1205 op: "discard-namespace", 1206 name: "snapd", 1207 }, 1208 { 1209 op: "unlink-snap", 1210 path: filepath.Join(dirs.SnapMountDir, "snapd/22"), 1211 1212 unlinkFirstInstallUndo: true, 1213 }, 1214 } 1215 1216 // start with an easier-to-read error if this fails: 1217 c.Check(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1218 c.Check(s.fakeBackend.ops, DeepEquals, expected) 1219 1220 // 2 restarts, one from link snap, another one from undo 1221 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon, state.RestartDaemon}) 1222 c.Check(t.Log(), HasLen, 3) 1223 c.Check(t.Log()[0], Matches, `.*INFO Requested daemon restart \(snapd snap\)\.`) 1224 c.Check(t.Log()[2], Matches, `.*INFO Requested daemon restart \(snapd snap\)\.`) 1225 1226 } 1227 1228 func (s *linkSnapSuite) TestUndoLinkSnapdNthInstall(c *C) { 1229 restore := release.MockOnClassic(false) 1230 defer restore() 1231 1232 s.state.Lock() 1233 si := &snap.SideInfo{ 1234 RealName: "snapd", 1235 SnapID: "snapd-snap-id", 1236 Revision: snap.R(22), 1237 } 1238 siOld := *si 1239 siOld.Revision = snap.R(20) 1240 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 1241 Sequence: []*snap.SideInfo{&siOld}, 1242 Current: siOld.Revision, 1243 Active: true, 1244 SnapType: "snapd", 1245 }) 1246 chg := s.state.NewChange("dummy", "...") 1247 t := s.state.NewTask("link-snap", "test") 1248 t.Set("snap-setup", &snapstate.SnapSetup{ 1249 SideInfo: si, 1250 Type: snap.TypeSnapd, 1251 }) 1252 chg.AddTask(t) 1253 terr := s.state.NewTask("error-trigger", "provoking total undo") 1254 terr.WaitFor(t) 1255 chg.AddTask(terr) 1256 1257 s.state.Unlock() 1258 1259 for i := 0; i < 6; i++ { 1260 s.se.Ensure() 1261 s.se.Wait() 1262 } 1263 1264 s.state.Lock() 1265 defer s.state.Unlock() 1266 1267 var snapst snapstate.SnapState 1268 err := snapstate.Get(s.state, "snapd", &snapst) 1269 c.Assert(err, IsNil) 1270 snapst.Current = siOld.Revision 1271 c.Check(t.Status(), Equals, state.UndoneStatus) 1272 1273 expected := fakeOps{ 1274 { 1275 op: "candidate", 1276 sinfo: *si, 1277 }, 1278 { 1279 op: "link-snap", 1280 path: filepath.Join(dirs.SnapMountDir, "snapd/22"), 1281 }, 1282 { 1283 op: "current-snap-service-states", 1284 }, 1285 { 1286 op: "unlink-snap", 1287 path: filepath.Join(dirs.SnapMountDir, "snapd/22"), 1288 }, 1289 } 1290 1291 // start with an easier-to-read error if this fails: 1292 c.Check(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1293 c.Check(s.fakeBackend.ops, DeepEquals, expected) 1294 1295 // 1 restart from link snap, the other restart happens 1296 // in undoUnlinkCurrentSnap (not tested here) 1297 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 1298 c.Check(t.Log(), HasLen, 2) 1299 c.Check(t.Log()[0], Matches, `.*INFO Requested daemon restart \(snapd snap\)\.`) 1300 c.Check(t.Log()[1], Matches, `.*INFO unlink`) 1301 } 1302 1303 func (s *linkSnapSuite) TestDoUnlinkSnapRefreshAwarenessHardCheck(c *C) { 1304 s.state.Lock() 1305 defer s.state.Unlock() 1306 1307 tr := config.NewTransaction(s.state) 1308 tr.Set("core", "experimental.refresh-app-awareness", true) 1309 tr.Commit() 1310 1311 chg := s.testDoUnlinkSnapRefreshAwareness(c) 1312 1313 c.Check(chg.Err(), ErrorMatches, `(?ms).*^- some-change-descr \(snap "some-snap" has running apps \(some-app\)\).*`) 1314 } 1315 1316 func (s *linkSnapSuite) TestDoUnlinkSnapRefreshHardCheckOff(c *C) { 1317 s.state.Lock() 1318 defer s.state.Unlock() 1319 1320 chg := s.testDoUnlinkSnapRefreshAwareness(c) 1321 1322 c.Check(chg.Err(), IsNil) 1323 } 1324 1325 func (s *linkSnapSuite) testDoUnlinkSnapRefreshAwareness(c *C) *state.Change { 1326 restore := release.MockOnClassic(true) 1327 defer restore() 1328 1329 dirs.SetRootDir(c.MkDir()) 1330 defer dirs.SetRootDir("/") 1331 1332 snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) { 1333 info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp} 1334 info.Apps = map[string]*snap.AppInfo{ 1335 "some-app": {Snap: info, Name: "some-app"}, 1336 } 1337 return info, nil 1338 }) 1339 // mock that "some-snap" has an app and that this app has pids running 1340 restore = snapstate.MockPidsOfSnap(func(instanceName string) (map[string][]int, error) { 1341 c.Assert(instanceName, Equals, "some-snap") 1342 return map[string][]int{ 1343 "snap.some-snap.some-app": {1234}, 1344 }, nil 1345 }) 1346 defer restore() 1347 1348 si1 := &snap.SideInfo{ 1349 RealName: "some-snap", 1350 Revision: snap.R(1), 1351 } 1352 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1353 Sequence: []*snap.SideInfo{si1}, 1354 Current: si1.Revision, 1355 Active: true, 1356 }) 1357 t := s.state.NewTask("unlink-current-snap", "some-change-descr") 1358 t.Set("snap-setup", &snapstate.SnapSetup{ 1359 SideInfo: si1, 1360 }) 1361 chg := s.state.NewChange("dummy", "...") 1362 chg.AddTask(t) 1363 1364 s.state.Unlock() 1365 defer s.state.Lock() 1366 1367 for i := 0; i < 3; i++ { 1368 s.se.Ensure() 1369 s.se.Wait() 1370 } 1371 1372 return chg 1373 } 1374 1375 func (s *linkSnapSuite) setMockKernelRemodelCtx(c *C, oldKernel, newKernel string) { 1376 newModel := MakeModel(map[string]interface{}{"kernel": newKernel}) 1377 oldModel := MakeModel(map[string]interface{}{"kernel": oldKernel}) 1378 mockRemodelCtx := &snapstatetest.TrivialDeviceContext{ 1379 DeviceModel: newModel, 1380 OldDeviceModel: oldModel, 1381 Remodeling: true, 1382 } 1383 restore := snapstatetest.MockDeviceContext(mockRemodelCtx) 1384 s.AddCleanup(restore) 1385 } 1386 1387 func (s *linkSnapSuite) TestMaybeUndoRemodelBootChangesUnrelatedAppDoesNothing(c *C) { 1388 s.state.Lock() 1389 defer s.state.Unlock() 1390 1391 s.setMockKernelRemodelCtx(c, "kernel", "new-kernel") 1392 t := s.state.NewTask("link-snap", "...") 1393 t.Set("snap-setup", &snapstate.SnapSetup{ 1394 SideInfo: &snap.SideInfo{ 1395 RealName: "some-app", 1396 Revision: snap.R(1), 1397 }, 1398 }) 1399 1400 err := s.snapmgr.MaybeUndoRemodelBootChanges(t) 1401 c.Assert(err, IsNil) 1402 c.Check(s.stateBackend.restartRequested, HasLen, 0) 1403 } 1404 1405 func (s *linkSnapSuite) TestMaybeUndoRemodelBootChangesSameKernel(c *C) { 1406 s.state.Lock() 1407 defer s.state.Unlock() 1408 1409 s.setMockKernelRemodelCtx(c, "kernel", "kernel") 1410 t := s.state.NewTask("link-snap", "...") 1411 t.Set("snap-setup", &snapstate.SnapSetup{ 1412 SideInfo: &snap.SideInfo{ 1413 RealName: "kernel", 1414 Revision: snap.R(1), 1415 }, 1416 Type: "kernel", 1417 }) 1418 1419 err := s.snapmgr.MaybeUndoRemodelBootChanges(t) 1420 c.Assert(err, IsNil) 1421 c.Check(s.stateBackend.restartRequested, HasLen, 0) 1422 } 1423 1424 func (s *linkSnapSuite) TestMaybeUndoRemodelBootChangesNeedsUndo(c *C) { 1425 s.state.Lock() 1426 defer s.state.Unlock() 1427 1428 // undoing remodel bootenv changes is only relevant on !classic 1429 restore := release.MockOnClassic(false) 1430 defer restore() 1431 1432 // using "snaptest.MockSnap()" is more convenient here so we avoid 1433 // the (default) mocking of snapReadInfo() 1434 restore = snapstate.MockSnapReadInfo(snap.ReadInfo) 1435 defer restore() 1436 1437 // we need to init the boot-id 1438 err := s.state.VerifyReboot("some-boot-id") 1439 c.Assert(err, IsNil) 1440 1441 // we pretend we do a remodel from kernel -> new-kernel 1442 s.setMockKernelRemodelCtx(c, "kernel", "new-kernel") 1443 1444 // and we pretend that we booted into the "new-kernel" already 1445 // and now that needs to be undone 1446 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 1447 bootloader.Force(bloader) 1448 bloader.SetBootKernel("new-kernel_1.snap") 1449 1450 // both kernel and new-kernel are instaleld at this point 1451 si := &snap.SideInfo{RealName: "kernel", Revision: snap.R(1)} 1452 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 1453 Sequence: []*snap.SideInfo{si}, 1454 SnapType: "kernel", 1455 Current: snap.R(1), 1456 }) 1457 snaptest.MockSnap(c, "name: kernel\ntype: kernel\nversion: 1.0", si) 1458 si2 := &snap.SideInfo{RealName: "new-kernel", Revision: snap.R(1)} 1459 snapstate.Set(s.state, "new-kernel", &snapstate.SnapState{ 1460 Sequence: []*snap.SideInfo{si2}, 1461 SnapType: "kernel", 1462 Current: snap.R(1), 1463 }) 1464 snaptest.MockSnap(c, "name: new-kernel\ntype: kernel\nversion: 1.0", si) 1465 1466 t := s.state.NewTask("link-snap", "...") 1467 t.Set("snap-setup", &snapstate.SnapSetup{ 1468 SideInfo: &snap.SideInfo{ 1469 RealName: "new-kernel", 1470 Revision: snap.R(1), 1471 SnapID: "new-kernel-id", 1472 }, 1473 Type: "kernel", 1474 }) 1475 1476 // now we simulate that the new kernel is getting undone 1477 err = s.snapmgr.MaybeUndoRemodelBootChanges(t) 1478 c.Assert(err, IsNil) 1479 1480 // that will schedule a boot into the previous kernel 1481 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 1482 "snap_mode": boot.TryStatus, 1483 "snap_kernel": "new-kernel_1.snap", 1484 "snap_try_kernel": "kernel_1.snap", 1485 }) 1486 c.Check(s.stateBackend.restartRequested, HasLen, 1) 1487 c.Check(s.stateBackend.restartRequested[0], Equals, state.RestartSystem) 1488 }