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