gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/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/asserts" 32 "github.com/snapcore/snapd/boot" 33 "github.com/snapcore/snapd/boot/boottest" 34 "github.com/snapcore/snapd/bootloader" 35 "github.com/snapcore/snapd/bootloader/bootloadertest" 36 "github.com/snapcore/snapd/dirs" 37 "github.com/snapcore/snapd/overlord/auth" 38 "github.com/snapcore/snapd/overlord/configstate/config" 39 "github.com/snapcore/snapd/overlord/servicestate" 40 "github.com/snapcore/snapd/overlord/snapstate" 41 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 42 "github.com/snapcore/snapd/overlord/state" 43 "github.com/snapcore/snapd/release" 44 "github.com/snapcore/snapd/snap" 45 "github.com/snapcore/snapd/snap/snaptest" 46 "github.com/snapcore/snapd/testutil" 47 ) 48 49 type linkSnapSuite struct { 50 baseHandlerSuite 51 52 stateBackend *witnessRestartReqStateBackend 53 } 54 55 var _ = Suite(&linkSnapSuite{}) 56 57 type witnessRestartReqStateBackend struct { 58 restartRequested []state.RestartType 59 } 60 61 func (b *witnessRestartReqStateBackend) Checkpoint([]byte) error { 62 return nil 63 } 64 65 func (b *witnessRestartReqStateBackend) RequestRestart(t state.RestartType) { 66 b.restartRequested = append(b.restartRequested, t) 67 } 68 69 func (b *witnessRestartReqStateBackend) EnsureBefore(time.Duration) {} 70 71 func (s *linkSnapSuite) SetUpTest(c *C) { 72 s.stateBackend = &witnessRestartReqStateBackend{} 73 74 s.setup(c, s.stateBackend) 75 76 s.AddCleanup(snapstatetest.MockDeviceModel(DefaultModel())) 77 78 oldSnapServiceOptions := snapstate.SnapServiceOptions 79 snapstate.SnapServiceOptions = servicestate.SnapServiceOptions 80 s.AddCleanup(func() { 81 snapstate.SnapServiceOptions = oldSnapServiceOptions 82 }) 83 } 84 85 func checkHasCookieForSnap(c *C, st *state.State, instanceName string) { 86 var contexts map[string]interface{} 87 err := st.Get("snap-cookies", &contexts) 88 c.Assert(err, IsNil) 89 c.Check(contexts, HasLen, 1) 90 91 for _, snap := range contexts { 92 if instanceName == snap { 93 return 94 } 95 } 96 panic(fmt.Sprintf("Cookie missing for snap %q", instanceName)) 97 } 98 99 func (s *linkSnapSuite) TestDoLinkSnapSuccess(c *C) { 100 // we start without the auxiliary store info 101 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 102 103 lp := &testLinkParticipant{} 104 restore := snapstate.MockLinkSnapParticipants([]snapstate.LinkSnapParticipant{lp}) 105 defer restore() 106 107 s.state.Lock() 108 t := s.state.NewTask("link-snap", "test") 109 t.Set("snap-setup", &snapstate.SnapSetup{ 110 SideInfo: &snap.SideInfo{ 111 RealName: "foo", 112 Revision: snap.R(33), 113 SnapID: "foo-id", 114 }, 115 Channel: "beta", 116 UserID: 2, 117 }) 118 s.state.NewChange("dummy", "...").AddTask(t) 119 120 s.state.Unlock() 121 122 s.se.Ensure() 123 s.se.Wait() 124 125 s.state.Lock() 126 defer s.state.Unlock() 127 var snapst snapstate.SnapState 128 err := snapstate.Get(s.state, "foo", &snapst) 129 c.Assert(err, IsNil) 130 131 checkHasCookieForSnap(c, s.state, "foo") 132 133 typ, err := snapst.Type() 134 c.Check(err, IsNil) 135 c.Check(typ, Equals, snap.TypeApp) 136 137 c.Check(snapst.Active, Equals, true) 138 c.Check(snapst.Sequence, HasLen, 1) 139 c.Check(snapst.Current, Equals, snap.R(33)) 140 c.Check(snapst.TrackingChannel, Equals, "latest/beta") 141 c.Check(snapst.UserID, Equals, 2) 142 c.Check(snapst.CohortKey, Equals, "") 143 c.Check(t.Status(), Equals, state.DoneStatus) 144 c.Check(s.stateBackend.restartRequested, HasLen, 0) 145 146 // we end with the auxiliary store info 147 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FilePresent) 148 149 // link snap participant was invoked 150 c.Check(lp.instanceNames, DeepEquals, []string{"foo"}) 151 } 152 153 func (s *linkSnapSuite) TestDoLinkSnapSuccessWithCohort(c *C) { 154 // we start without the auxiliary store info 155 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 156 157 s.state.Lock() 158 t := s.state.NewTask("link-snap", "test") 159 t.Set("snap-setup", &snapstate.SnapSetup{ 160 SideInfo: &snap.SideInfo{ 161 RealName: "foo", 162 Revision: snap.R(33), 163 SnapID: "foo-id", 164 }, 165 Channel: "beta", 166 UserID: 2, 167 CohortKey: "wobbling", 168 }) 169 s.state.NewChange("dummy", "...").AddTask(t) 170 171 s.state.Unlock() 172 173 s.se.Ensure() 174 s.se.Wait() 175 176 s.state.Lock() 177 defer s.state.Unlock() 178 var snapst snapstate.SnapState 179 err := snapstate.Get(s.state, "foo", &snapst) 180 c.Assert(err, IsNil) 181 182 checkHasCookieForSnap(c, s.state, "foo") 183 184 typ, err := snapst.Type() 185 c.Check(err, IsNil) 186 c.Check(typ, Equals, snap.TypeApp) 187 188 c.Check(snapst.Active, Equals, true) 189 c.Check(snapst.Sequence, HasLen, 1) 190 c.Check(snapst.Current, Equals, snap.R(33)) 191 c.Check(snapst.TrackingChannel, Equals, "latest/beta") 192 c.Check(snapst.UserID, Equals, 2) 193 c.Check(snapst.CohortKey, Equals, "wobbling") 194 c.Check(t.Status(), Equals, state.DoneStatus) 195 c.Check(s.stateBackend.restartRequested, HasLen, 0) 196 197 // we end with the auxiliary store info 198 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FilePresent) 199 } 200 201 func (s *linkSnapSuite) TestDoLinkSnapSuccessNoUserID(c *C) { 202 s.state.Lock() 203 t := s.state.NewTask("link-snap", "test") 204 t.Set("snap-setup", &snapstate.SnapSetup{ 205 SideInfo: &snap.SideInfo{ 206 RealName: "foo", 207 Revision: snap.R(33), 208 }, 209 Channel: "beta", 210 }) 211 s.state.NewChange("dummy", "...").AddTask(t) 212 213 s.state.Unlock() 214 s.se.Ensure() 215 s.se.Wait() 216 s.state.Lock() 217 defer s.state.Unlock() 218 219 // check that snapst.UserID does not get set 220 var snapst snapstate.SnapState 221 err := snapstate.Get(s.state, "foo", &snapst) 222 c.Assert(err, IsNil) 223 c.Check(snapst.UserID, Equals, 0) 224 225 var snaps map[string]*json.RawMessage 226 err = s.state.Get("snaps", &snaps) 227 c.Assert(err, IsNil) 228 raw := []byte(*snaps["foo"]) 229 c.Check(string(raw), Not(testutil.Contains), "user-id") 230 } 231 232 func (s *linkSnapSuite) TestDoLinkSnapSuccessUserIDAlreadySet(c *C) { 233 s.state.Lock() 234 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 235 Sequence: []*snap.SideInfo{ 236 {RealName: "foo", Revision: snap.R(1)}, 237 }, 238 Current: snap.R(1), 239 UserID: 1, 240 }) 241 // the user 242 user, err := auth.NewUser(s.state, "username", "email@test.com", "macaroon", []string{"discharge"}) 243 c.Assert(err, IsNil) 244 c.Assert(user.ID, Equals, 1) 245 246 t := s.state.NewTask("link-snap", "test") 247 t.Set("snap-setup", &snapstate.SnapSetup{ 248 SideInfo: &snap.SideInfo{ 249 RealName: "foo", 250 Revision: snap.R(33), 251 }, 252 Channel: "beta", 253 UserID: 2, 254 }) 255 s.state.NewChange("dummy", "...").AddTask(t) 256 257 s.state.Unlock() 258 s.se.Ensure() 259 s.se.Wait() 260 s.state.Lock() 261 defer s.state.Unlock() 262 263 // check that snapst.UserID was not "transferred" 264 var snapst snapstate.SnapState 265 err = snapstate.Get(s.state, "foo", &snapst) 266 c.Assert(err, IsNil) 267 c.Check(snapst.UserID, Equals, 1) 268 } 269 270 func (s *linkSnapSuite) TestDoLinkSnapSuccessUserLoggedOut(c *C) { 271 s.state.Lock() 272 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 273 Sequence: []*snap.SideInfo{ 274 {RealName: "foo", Revision: snap.R(1)}, 275 }, 276 Current: snap.R(1), 277 UserID: 1, 278 }) 279 280 t := s.state.NewTask("link-snap", "test") 281 t.Set("snap-setup", &snapstate.SnapSetup{ 282 SideInfo: &snap.SideInfo{ 283 RealName: "foo", 284 Revision: snap.R(33), 285 }, 286 Channel: "beta", 287 UserID: 2, 288 }) 289 s.state.NewChange("dummy", "...").AddTask(t) 290 291 s.state.Unlock() 292 s.se.Ensure() 293 s.se.Wait() 294 s.state.Lock() 295 defer s.state.Unlock() 296 297 // check that snapst.UserID was transferred 298 // given that user 1 doesn't exist anymore 299 var snapst snapstate.SnapState 300 err := snapstate.Get(s.state, "foo", &snapst) 301 c.Assert(err, IsNil) 302 c.Check(snapst.UserID, Equals, 2) 303 } 304 305 func (s *linkSnapSuite) TestDoLinkSnapSeqFile(c *C) { 306 s.state.Lock() 307 // pretend we have an installed snap 308 si11 := &snap.SideInfo{ 309 RealName: "foo", 310 Revision: snap.R(11), 311 } 312 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 313 Sequence: []*snap.SideInfo{si11}, 314 Current: si11.Revision, 315 }) 316 // add a new one 317 t := s.state.NewTask("link-snap", "test") 318 t.Set("snap-setup", &snapstate.SnapSetup{ 319 SideInfo: &snap.SideInfo{ 320 RealName: "foo", 321 Revision: snap.R(33), 322 }, 323 Channel: "beta", 324 }) 325 s.state.NewChange("dummy", "...").AddTask(t) 326 s.state.Unlock() 327 328 s.se.Ensure() 329 s.se.Wait() 330 331 s.state.Lock() 332 defer s.state.Unlock() 333 var snapst snapstate.SnapState 334 err := snapstate.Get(s.state, "foo", &snapst) 335 c.Assert(err, IsNil) 336 337 // and check that the sequence file got updated 338 seqContent, err := ioutil.ReadFile(filepath.Join(dirs.SnapSeqDir, "foo.json")) 339 c.Assert(err, IsNil) 340 c.Check(string(seqContent), Equals, `{"sequence":[{"name":"foo","snap-id":"","revision":"11"},{"name":"foo","snap-id":"","revision":"33"}],"current":"33"}`) 341 } 342 343 func (s *linkSnapSuite) TestDoUndoLinkSnap(c *C) { 344 s.state.Lock() 345 defer s.state.Unlock() 346 347 linkChangeCount := 0 348 lp := &testLinkParticipant{ 349 linkageChanged: func(st *state.State, instanceName string) error { 350 var snapst snapstate.SnapState 351 err := snapstate.Get(st, instanceName, &snapst) 352 linkChangeCount++ 353 switch linkChangeCount { 354 case 1: 355 // Initially the snap gets linked. 356 c.Check(err, IsNil) 357 c.Check(snapst.Active, Equals, true) 358 case 2: 359 // Then link-snap is undone and the snap gets unlinked. 360 c.Check(err, Equals, state.ErrNoState) 361 } 362 return nil 363 }, 364 } 365 restore := snapstate.MockLinkSnapParticipants([]snapstate.LinkSnapParticipant{lp}) 366 defer restore() 367 368 // a hook might have set some config 369 cfg := json.RawMessage(`{"c":true}`) 370 err := config.SetSnapConfig(s.state, "foo", &cfg) 371 c.Assert(err, IsNil) 372 373 si := &snap.SideInfo{ 374 RealName: "foo", 375 Revision: snap.R(33), 376 } 377 t := s.state.NewTask("link-snap", "test") 378 t.Set("snap-setup", &snapstate.SnapSetup{ 379 SideInfo: si, 380 Channel: "beta", 381 }) 382 chg := s.state.NewChange("dummy", "...") 383 chg.AddTask(t) 384 385 terr := s.state.NewTask("error-trigger", "provoking total undo") 386 terr.WaitFor(t) 387 chg.AddTask(terr) 388 389 s.state.Unlock() 390 391 for i := 0; i < 6; i++ { 392 s.se.Ensure() 393 s.se.Wait() 394 } 395 396 s.state.Lock() 397 var snapst snapstate.SnapState 398 err = snapstate.Get(s.state, "foo", &snapst) 399 c.Assert(err, Equals, state.ErrNoState) 400 c.Check(t.Status(), Equals, state.UndoneStatus) 401 402 // and check that the sequence file got updated 403 seqContent, err := ioutil.ReadFile(filepath.Join(dirs.SnapSeqDir, "foo.json")) 404 c.Assert(err, IsNil) 405 c.Check(string(seqContent), Equals, `{"sequence":[],"current":"unset"}`) 406 407 // nothing in config 408 var config map[string]*json.RawMessage 409 err = s.state.Get("config", &config) 410 c.Assert(err, IsNil) 411 c.Check(config, HasLen, 1) 412 _, ok := config["core"] 413 c.Check(ok, Equals, true) 414 415 // link snap participant was invoked, once for do, once for undo. 416 c.Check(lp.instanceNames, DeepEquals, []string{"foo", "foo"}) 417 } 418 419 func (s *linkSnapSuite) TestDoUnlinkCurrentSnapWithIgnoreRunning(c *C) { 420 s.state.Lock() 421 defer s.state.Unlock() 422 423 // With refresh-app-awareness enabled 424 tr := config.NewTransaction(s.state) 425 tr.Set("core", "experimental.refresh-app-awareness", true) 426 tr.Commit() 427 428 // With a snap "pkg" at revision 42 429 si := &snap.SideInfo{RealName: "pkg", Revision: snap.R(42)} 430 snapstate.Set(s.state, "pkg", &snapstate.SnapState{ 431 Sequence: []*snap.SideInfo{si}, 432 Current: si.Revision, 433 Active: true, 434 }) 435 436 // With an app belonging to the snap that is apparently running. 437 snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) { 438 c.Assert(name, Equals, "pkg") 439 info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp} 440 info.Apps = map[string]*snap.AppInfo{ 441 "app": {Snap: info, Name: "app"}, 442 } 443 return info, nil 444 }) 445 restore := snapstate.MockPidsOfSnap(func(instanceName string) (map[string][]int, error) { 446 c.Assert(instanceName, Equals, "pkg") 447 return map[string][]int{"snap.pkg.app": {1234}}, nil 448 }) 449 defer restore() 450 451 // We can unlink the current revision of that snap, by setting IgnoreRunning flag. 452 task := s.state.NewTask("unlink-current-snap", "") 453 task.Set("snap-setup", &snapstate.SnapSetup{ 454 SideInfo: si, 455 Flags: snapstate.Flags{IgnoreRunning: true}, 456 }) 457 chg := s.state.NewChange("dummy", "...") 458 chg.AddTask(task) 459 460 // Run the task we created 461 s.state.Unlock() 462 s.se.Ensure() 463 s.se.Wait() 464 s.state.Lock() 465 466 // And observe the results. 467 var snapst snapstate.SnapState 468 err := snapstate.Get(s.state, "pkg", &snapst) 469 c.Assert(err, IsNil) 470 c.Check(snapst.Active, Equals, false) 471 c.Check(snapst.Sequence, HasLen, 1) 472 c.Check(snapst.Current, Equals, snap.R(42)) 473 c.Check(task.Status(), Equals, state.DoneStatus) 474 expected := fakeOps{{ 475 op: "unlink-snap", 476 path: filepath.Join(dirs.SnapMountDir, "pkg/42"), 477 }} 478 c.Check(s.fakeBackend.ops, DeepEquals, expected) 479 } 480 481 func (s *linkSnapSuite) TestDoUndoUnlinkCurrentSnapWithVitalityScore(c *C) { 482 s.state.Lock() 483 defer s.state.Unlock() 484 // foo has a vitality-hint 485 cfg := json.RawMessage(`{"resilience":{"vitality-hint":"bar,foo,baz"}}`) 486 err := config.SetSnapConfig(s.state, "core", &cfg) 487 c.Assert(err, IsNil) 488 489 si1 := &snap.SideInfo{ 490 RealName: "foo", 491 Revision: snap.R(11), 492 } 493 si2 := &snap.SideInfo{ 494 RealName: "foo", 495 Revision: snap.R(33), 496 } 497 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 498 Sequence: []*snap.SideInfo{si1}, 499 Current: si1.Revision, 500 Active: true, 501 }) 502 t := s.state.NewTask("unlink-current-snap", "test") 503 t.Set("snap-setup", &snapstate.SnapSetup{ 504 SideInfo: si2, 505 }) 506 chg := s.state.NewChange("dummy", "...") 507 chg.AddTask(t) 508 509 terr := s.state.NewTask("error-trigger", "provoking total undo") 510 terr.WaitFor(t) 511 chg.AddTask(terr) 512 513 s.state.Unlock() 514 515 for i := 0; i < 3; i++ { 516 s.se.Ensure() 517 s.se.Wait() 518 } 519 520 s.state.Lock() 521 var snapst snapstate.SnapState 522 err = snapstate.Get(s.state, "foo", &snapst) 523 c.Assert(err, IsNil) 524 c.Check(snapst.Active, Equals, true) 525 c.Check(snapst.Sequence, HasLen, 1) 526 c.Check(snapst.Current, Equals, snap.R(11)) 527 c.Check(t.Status(), Equals, state.UndoneStatus) 528 529 expected := fakeOps{ 530 { 531 op: "unlink-snap", 532 path: filepath.Join(dirs.SnapMountDir, "foo/11"), 533 }, 534 { 535 op: "link-snap", 536 path: filepath.Join(dirs.SnapMountDir, "foo/11"), 537 vitalityRank: 2, 538 }, 539 } 540 c.Check(s.fakeBackend.ops, DeepEquals, expected) 541 } 542 543 func (s *linkSnapSuite) TestDoUnlinkCurrentSnapSnapdNop(c *C) { 544 s.state.Lock() 545 defer s.state.Unlock() 546 547 si := &snap.SideInfo{ 548 RealName: "snapd", 549 SnapID: "snapd-snap-id", 550 Revision: snap.R(22), 551 } 552 siOld := *si 553 siOld.Revision = snap.R(20) 554 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 555 Sequence: []*snap.SideInfo{&siOld}, 556 Current: siOld.Revision, 557 Active: true, 558 SnapType: "snapd", 559 }) 560 561 task := s.state.NewTask("unlink-current-snap", "") 562 task.Set("snap-setup", &snapstate.SnapSetup{ 563 SideInfo: si, 564 Channel: "beta", 565 }) 566 chg := s.state.NewChange("dummy", "...") 567 chg.AddTask(task) 568 569 // Run the task we created 570 s.state.Unlock() 571 s.se.Ensure() 572 s.se.Wait() 573 s.state.Lock() 574 575 // And observe the results. 576 var snapst snapstate.SnapState 577 err := snapstate.Get(s.state, "snapd", &snapst) 578 c.Assert(err, IsNil) 579 c.Check(snapst.Active, Equals, false) 580 c.Check(snapst.Sequence, HasLen, 1) 581 c.Check(snapst.Current, Equals, snap.R(20)) 582 c.Check(task.Status(), Equals, state.DoneStatus) 583 // backend was not called to unlink the snap 584 c.Check(s.fakeBackend.ops, HasLen, 0) 585 } 586 587 func (s *linkSnapSuite) TestDoUnlinkSnapdUnlinks(c *C) { 588 s.state.Lock() 589 defer s.state.Unlock() 590 591 si := &snap.SideInfo{ 592 RealName: "snapd", 593 Revision: snap.R(20), 594 } 595 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 596 Sequence: []*snap.SideInfo{si}, 597 Current: si.Revision, 598 Active: true, 599 }) 600 601 task := s.state.NewTask("unlink-snap", "") 602 task.Set("snap-setup", &snapstate.SnapSetup{ 603 SideInfo: si, 604 Channel: "beta", 605 }) 606 chg := s.state.NewChange("dummy", "...") 607 chg.AddTask(task) 608 609 // Run the task we created 610 s.state.Unlock() 611 s.se.Ensure() 612 s.se.Wait() 613 s.state.Lock() 614 615 // And observe the results. 616 var snapst snapstate.SnapState 617 err := snapstate.Get(s.state, "snapd", &snapst) 618 c.Assert(err, IsNil) 619 c.Check(snapst.Active, Equals, false) 620 c.Check(snapst.Sequence, HasLen, 1) 621 c.Check(snapst.Current, Equals, snap.R(20)) 622 c.Check(task.Status(), Equals, state.DoneStatus) 623 // backend was called to unlink the snap 624 expected := fakeOps{{ 625 op: "unlink-snap", 626 path: filepath.Join(dirs.SnapMountDir, "snapd/20"), 627 }} 628 c.Check(s.fakeBackend.ops, DeepEquals, expected) 629 } 630 631 func (s *linkSnapSuite) TestDoLinkSnapWithVitalityScore(c *C) { 632 s.state.Lock() 633 defer s.state.Unlock() 634 // a hook might have set some config 635 cfg := json.RawMessage(`{"resilience":{"vitality-hint":"bar,foo,baz"}}`) 636 err := config.SetSnapConfig(s.state, "core", &cfg) 637 c.Assert(err, IsNil) 638 639 si := &snap.SideInfo{ 640 RealName: "foo", 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 chg := s.state.NewChange("dummy", "...") 648 chg.AddTask(t) 649 650 s.state.Unlock() 651 652 for i := 0; i < 6; i++ { 653 s.se.Ensure() 654 s.se.Wait() 655 } 656 657 s.state.Lock() 658 expected := fakeOps{ 659 { 660 op: "candidate", 661 sinfo: *si, 662 }, 663 { 664 op: "link-snap", 665 path: filepath.Join(dirs.SnapMountDir, "foo/33"), 666 vitalityRank: 2, 667 }, 668 } 669 c.Check(s.fakeBackend.ops, DeepEquals, expected) 670 } 671 672 func (s *linkSnapSuite) TestDoLinkSnapTryToCleanupOnError(c *C) { 673 s.state.Lock() 674 defer s.state.Unlock() 675 676 lp := &testLinkParticipant{} 677 restore := snapstate.MockLinkSnapParticipants([]snapstate.LinkSnapParticipant{lp}) 678 defer restore() 679 680 si := &snap.SideInfo{ 681 RealName: "foo", 682 Revision: snap.R(35), 683 } 684 t := s.state.NewTask("link-snap", "test") 685 t.Set("snap-setup", &snapstate.SnapSetup{ 686 SideInfo: si, 687 Channel: "beta", 688 }) 689 690 s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "foo/35") 691 s.state.NewChange("dummy", "...").AddTask(t) 692 s.state.Unlock() 693 694 s.se.Ensure() 695 s.se.Wait() 696 697 s.state.Lock() 698 699 // state as expected 700 var snapst snapstate.SnapState 701 err := snapstate.Get(s.state, "foo", &snapst) 702 c.Assert(err, Equals, state.ErrNoState) 703 704 // tried to cleanup 705 expected := fakeOps{ 706 { 707 op: "candidate", 708 sinfo: *si, 709 }, 710 { 711 op: "link-snap.failed", 712 path: filepath.Join(dirs.SnapMountDir, "foo/35"), 713 }, 714 { 715 op: "unlink-snap", 716 path: filepath.Join(dirs.SnapMountDir, "foo/35"), 717 718 unlinkFirstInstallUndo: true, 719 }, 720 } 721 722 // start with an easier-to-read error if this fails: 723 c.Check(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 724 c.Check(s.fakeBackend.ops, DeepEquals, expected) 725 726 // link snap participant was invoked 727 c.Check(lp.instanceNames, DeepEquals, []string{"foo"}) 728 } 729 730 func (s *linkSnapSuite) TestDoLinkSnapSuccessCoreRestarts(c *C) { 731 restore := release.MockOnClassic(true) 732 defer restore() 733 734 s.state.Lock() 735 si := &snap.SideInfo{ 736 RealName: "core", 737 Revision: snap.R(33), 738 } 739 t := s.state.NewTask("link-snap", "test") 740 t.Set("snap-setup", &snapstate.SnapSetup{ 741 SideInfo: si, 742 }) 743 s.state.NewChange("dummy", "...").AddTask(t) 744 745 s.state.Unlock() 746 747 s.se.Ensure() 748 s.se.Wait() 749 750 s.state.Lock() 751 defer s.state.Unlock() 752 753 var snapst snapstate.SnapState 754 err := snapstate.Get(s.state, "core", &snapst) 755 c.Assert(err, IsNil) 756 757 typ, err := snapst.Type() 758 c.Check(err, IsNil) 759 c.Check(typ, Equals, snap.TypeOS) 760 761 c.Check(t.Status(), Equals, state.DoneStatus) 762 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 763 c.Check(t.Log(), HasLen, 1) 764 c.Check(t.Log()[0], Matches, `.*INFO Requested daemon restart\.`) 765 } 766 767 func (s *linkSnapSuite) TestDoLinkSnapSuccessSnapdRestartsOnCoreWithBase(c *C) { 768 restore := release.MockOnClassic(false) 769 defer restore() 770 771 r := snapstatetest.MockDeviceModel(ModelWithBase("core18")) 772 defer r() 773 774 s.state.Lock() 775 si := &snap.SideInfo{ 776 RealName: "snapd", 777 SnapID: "snapd-snap-id", 778 Revision: snap.R(22), 779 } 780 t := s.state.NewTask("link-snap", "test") 781 t.Set("snap-setup", &snapstate.SnapSetup{ 782 SideInfo: si, 783 Type: snap.TypeSnapd, 784 }) 785 s.state.NewChange("dummy", "...").AddTask(t) 786 787 s.state.Unlock() 788 789 s.se.Ensure() 790 s.se.Wait() 791 792 s.state.Lock() 793 defer s.state.Unlock() 794 795 var snapst snapstate.SnapState 796 err := snapstate.Get(s.state, "snapd", &snapst) 797 c.Assert(err, IsNil) 798 799 typ, err := snapst.Type() 800 c.Check(err, IsNil) 801 c.Check(typ, Equals, snap.TypeSnapd) 802 803 c.Check(t.Status(), Equals, state.DoneStatus) 804 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 805 c.Check(t.Log(), HasLen, 1) 806 c.Check(t.Log()[0], Matches, `.*INFO Requested daemon restart \(snapd snap\)\.`) 807 } 808 809 func (s *linkSnapSuite) TestDoLinkSnapSuccessRebootForCoreBase(c *C) { 810 restore := release.MockOnClassic(false) 811 defer restore() 812 813 r := snapstatetest.MockDeviceModel(ModelWithBase("core18")) 814 defer r() 815 816 s.fakeBackend.linkSnapMaybeReboot = true 817 818 s.state.Lock() 819 defer s.state.Unlock() 820 821 // we need to init the boot-id 822 err := s.state.VerifyReboot("some-boot-id") 823 c.Assert(err, IsNil) 824 825 si := &snap.SideInfo{ 826 RealName: "core18", 827 SnapID: "core18-id", 828 Revision: snap.R(22), 829 } 830 t := s.state.NewTask("link-snap", "test") 831 t.Set("snap-setup", &snapstate.SnapSetup{ 832 SideInfo: si, 833 }) 834 s.state.NewChange("dummy", "...").AddTask(t) 835 836 s.state.Unlock() 837 s.se.Ensure() 838 s.se.Wait() 839 s.state.Lock() 840 841 c.Check(t.Status(), Equals, state.DoneStatus) 842 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartSystem}) 843 c.Assert(t.Log(), HasLen, 1) 844 c.Check(t.Log()[0], Matches, `.*INFO Requested system restart.*`) 845 } 846 847 func (s *linkSnapSuite) TestDoLinkSnapSuccessSnapdRestartsOnClassic(c *C) { 848 restore := release.MockOnClassic(true) 849 defer restore() 850 851 s.state.Lock() 852 si := &snap.SideInfo{ 853 RealName: "snapd", 854 SnapID: "snapd-snap-id", 855 Revision: snap.R(22), 856 } 857 t := s.state.NewTask("link-snap", "test") 858 t.Set("snap-setup", &snapstate.SnapSetup{ 859 SideInfo: si, 860 Type: snap.TypeSnapd, 861 }) 862 s.state.NewChange("dummy", "...").AddTask(t) 863 864 s.state.Unlock() 865 866 s.se.Ensure() 867 s.se.Wait() 868 869 s.state.Lock() 870 defer s.state.Unlock() 871 872 var snapst snapstate.SnapState 873 err := snapstate.Get(s.state, "snapd", &snapst) 874 c.Assert(err, IsNil) 875 876 typ, err := snapst.Type() 877 c.Check(err, IsNil) 878 c.Check(typ, Equals, snap.TypeSnapd) 879 880 c.Check(t.Status(), Equals, state.DoneStatus) 881 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 882 c.Check(t.Log(), HasLen, 1) 883 } 884 885 func (s *linkSnapSuite) TestDoLinkSnapSuccessCoreAndSnapdNoCoreRestart(c *C) { 886 restore := release.MockOnClassic(true) 887 defer restore() 888 889 s.state.Lock() 890 siSnapd := &snap.SideInfo{ 891 RealName: "snapd", 892 Revision: snap.R(64), 893 } 894 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 895 Sequence: []*snap.SideInfo{siSnapd}, 896 Current: siSnapd.Revision, 897 Active: true, 898 SnapType: "snapd", 899 }) 900 901 si := &snap.SideInfo{ 902 RealName: "core", 903 Revision: snap.R(33), 904 } 905 t := s.state.NewTask("link-snap", "test") 906 t.Set("snap-setup", &snapstate.SnapSetup{ 907 SideInfo: si, 908 }) 909 s.state.NewChange("dummy", "...").AddTask(t) 910 911 s.state.Unlock() 912 913 s.se.Ensure() 914 s.se.Wait() 915 916 s.state.Lock() 917 defer s.state.Unlock() 918 919 var snapst snapstate.SnapState 920 err := snapstate.Get(s.state, "core", &snapst) 921 c.Assert(err, IsNil) 922 923 typ, err := snapst.Type() 924 c.Check(err, IsNil) 925 c.Check(typ, Equals, snap.TypeOS) 926 927 c.Check(t.Status(), Equals, state.DoneStatus) 928 c.Check(s.stateBackend.restartRequested, IsNil) 929 c.Check(t.Log(), HasLen, 0) 930 } 931 932 func (s *linkSnapSuite) TestDoLinkSnapdSnapCleanupOnErrorFirstInstall(c *C) { 933 s.state.Lock() 934 defer s.state.Unlock() 935 936 lp := &testLinkParticipant{} 937 restore := snapstate.MockLinkSnapParticipants([]snapstate.LinkSnapParticipant{lp}) 938 defer restore() 939 940 si := &snap.SideInfo{ 941 RealName: "snapd", 942 SnapID: "snapd-snap-id", 943 Revision: snap.R(22), 944 } 945 t := s.state.NewTask("link-snap", "test") 946 t.Set("snap-setup", &snapstate.SnapSetup{ 947 SideInfo: si, 948 Channel: "beta", 949 }) 950 951 s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "snapd/22") 952 s.state.NewChange("dummy", "...").AddTask(t) 953 s.state.Unlock() 954 955 s.se.Ensure() 956 s.se.Wait() 957 958 s.state.Lock() 959 960 // state as expected 961 var snapst snapstate.SnapState 962 err := snapstate.Get(s.state, "foo", &snapst) 963 c.Assert(err, Equals, state.ErrNoState) 964 965 // tried to cleanup 966 expected := fakeOps{ 967 { 968 op: "candidate", 969 sinfo: *si, 970 }, 971 { 972 op: "link-snap.failed", 973 path: filepath.Join(dirs.SnapMountDir, "snapd/22"), 974 }, 975 { 976 op: "unlink-snap", 977 path: filepath.Join(dirs.SnapMountDir, "snapd/22"), 978 979 unlinkFirstInstallUndo: true, 980 }, 981 } 982 983 // start with an easier-to-read error if this fails: 984 c.Check(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 985 c.Check(s.fakeBackend.ops, DeepEquals, expected) 986 987 // link snap participant was invoked 988 c.Check(lp.instanceNames, DeepEquals, []string{"snapd"}) 989 } 990 991 func (s *linkSnapSuite) TestDoLinkSnapdSnapCleanupOnErrorNthInstall(c *C) { 992 s.state.Lock() 993 defer s.state.Unlock() 994 995 lp := &testLinkParticipant{} 996 restore := snapstate.MockLinkSnapParticipants([]snapstate.LinkSnapParticipant{lp}) 997 defer restore() 998 999 si := &snap.SideInfo{ 1000 RealName: "snapd", 1001 SnapID: "snapd-snap-id", 1002 Revision: snap.R(22), 1003 } 1004 siOld := *si 1005 siOld.Revision = snap.R(20) 1006 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 1007 Sequence: []*snap.SideInfo{&siOld}, 1008 Current: siOld.Revision, 1009 Active: true, 1010 SnapType: "snapd", 1011 }) 1012 t := s.state.NewTask("link-snap", "test") 1013 t.Set("snap-setup", &snapstate.SnapSetup{ 1014 SideInfo: si, 1015 Channel: "beta", 1016 }) 1017 1018 s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "snapd/22") 1019 s.state.NewChange("dummy", "...").AddTask(t) 1020 s.state.Unlock() 1021 1022 s.se.Ensure() 1023 s.se.Wait() 1024 1025 s.state.Lock() 1026 1027 // state as expected 1028 var snapst snapstate.SnapState 1029 err := snapstate.Get(s.state, "foo", &snapst) 1030 c.Assert(err, Equals, state.ErrNoState) 1031 1032 // tried to cleanup 1033 expected := fakeOps{ 1034 { 1035 op: "candidate", 1036 sinfo: *si, 1037 }, 1038 { 1039 op: "link-snap.failed", 1040 path: filepath.Join(dirs.SnapMountDir, "snapd/22"), 1041 }, 1042 { 1043 // we link the old revision 1044 op: "link-snap", 1045 path: filepath.Join(dirs.SnapMountDir, "snapd/20"), 1046 1047 unlinkFirstInstallUndo: false, 1048 }, 1049 } 1050 1051 // start with an easier-to-read error if this fails: 1052 c.Check(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1053 c.Check(s.fakeBackend.ops, DeepEquals, expected) 1054 1055 // link snap participant was invoked 1056 c.Check(lp.instanceNames, DeepEquals, []string{"snapd"}) 1057 } 1058 1059 func (s *linkSnapSuite) TestDoUndoLinkSnapSequenceDidNotHaveCandidate(c *C) { 1060 s.state.Lock() 1061 defer s.state.Unlock() 1062 si1 := &snap.SideInfo{ 1063 RealName: "foo", 1064 Revision: snap.R(1), 1065 } 1066 si2 := &snap.SideInfo{ 1067 RealName: "foo", 1068 Revision: snap.R(2), 1069 } 1070 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 1071 Sequence: []*snap.SideInfo{si1}, 1072 Current: si1.Revision, 1073 }) 1074 t := s.state.NewTask("link-snap", "test") 1075 t.Set("snap-setup", &snapstate.SnapSetup{ 1076 SideInfo: si2, 1077 Channel: "beta", 1078 }) 1079 chg := s.state.NewChange("dummy", "...") 1080 chg.AddTask(t) 1081 1082 terr := s.state.NewTask("error-trigger", "provoking total undo") 1083 terr.WaitFor(t) 1084 chg.AddTask(terr) 1085 1086 s.state.Unlock() 1087 1088 for i := 0; i < 6; i++ { 1089 s.se.Ensure() 1090 s.se.Wait() 1091 } 1092 1093 s.state.Lock() 1094 var snapst snapstate.SnapState 1095 err := snapstate.Get(s.state, "foo", &snapst) 1096 c.Assert(err, IsNil) 1097 c.Check(snapst.Active, Equals, false) 1098 c.Check(snapst.Sequence, HasLen, 1) 1099 c.Check(snapst.Current, Equals, snap.R(1)) 1100 c.Check(t.Status(), Equals, state.UndoneStatus) 1101 } 1102 1103 func (s *linkSnapSuite) TestDoUndoLinkSnapSequenceHadCandidate(c *C) { 1104 s.state.Lock() 1105 defer s.state.Unlock() 1106 si1 := &snap.SideInfo{ 1107 RealName: "foo", 1108 Revision: snap.R(1), 1109 } 1110 si2 := &snap.SideInfo{ 1111 RealName: "foo", 1112 Revision: snap.R(2), 1113 } 1114 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 1115 Sequence: []*snap.SideInfo{si1, si2}, 1116 Current: si2.Revision, 1117 }) 1118 t := s.state.NewTask("link-snap", "test") 1119 t.Set("snap-setup", &snapstate.SnapSetup{ 1120 SideInfo: si1, 1121 Channel: "beta", 1122 }) 1123 chg := s.state.NewChange("dummy", "...") 1124 chg.AddTask(t) 1125 1126 terr := s.state.NewTask("error-trigger", "provoking total undo") 1127 terr.WaitFor(t) 1128 chg.AddTask(terr) 1129 1130 s.state.Unlock() 1131 1132 for i := 0; i < 6; i++ { 1133 s.se.Ensure() 1134 s.se.Wait() 1135 } 1136 1137 s.state.Lock() 1138 var snapst snapstate.SnapState 1139 err := snapstate.Get(s.state, "foo", &snapst) 1140 c.Assert(err, IsNil) 1141 c.Check(snapst.Active, Equals, false) 1142 c.Check(snapst.Sequence, HasLen, 2) 1143 c.Check(snapst.Current, Equals, snap.R(2)) 1144 c.Check(t.Status(), Equals, state.UndoneStatus) 1145 } 1146 1147 func (s *linkSnapSuite) TestDoUndoUnlinkCurrentSnapCore(c *C) { 1148 restore := release.MockOnClassic(true) 1149 defer restore() 1150 1151 linkChangeCount := 0 1152 lp := &testLinkParticipant{ 1153 linkageChanged: func(st *state.State, instanceName string) error { 1154 var snapst snapstate.SnapState 1155 err := snapstate.Get(st, instanceName, &snapst) 1156 linkChangeCount++ 1157 switch linkChangeCount { 1158 case 1: 1159 // Initially the snap gets unlinked. 1160 c.Check(err, IsNil) 1161 c.Check(snapst.Active, Equals, false) 1162 case 2: 1163 // Then the undo handler re-links it. 1164 c.Check(err, IsNil) 1165 c.Check(snapst.Active, Equals, true) 1166 } 1167 return nil 1168 }, 1169 } 1170 restore = snapstate.MockLinkSnapParticipants([]snapstate.LinkSnapParticipant{lp}) 1171 defer restore() 1172 1173 s.state.Lock() 1174 defer s.state.Unlock() 1175 si1 := &snap.SideInfo{ 1176 RealName: "core", 1177 Revision: snap.R(1), 1178 } 1179 si2 := &snap.SideInfo{ 1180 RealName: "core", 1181 Revision: snap.R(2), 1182 } 1183 snapstate.Set(s.state, "core", &snapstate.SnapState{ 1184 Sequence: []*snap.SideInfo{si1}, 1185 Current: si1.Revision, 1186 Active: true, 1187 SnapType: "os", 1188 }) 1189 t := s.state.NewTask("unlink-current-snap", "test") 1190 t.Set("snap-setup", &snapstate.SnapSetup{ 1191 SideInfo: si2, 1192 }) 1193 chg := s.state.NewChange("dummy", "...") 1194 chg.AddTask(t) 1195 1196 terr := s.state.NewTask("error-trigger", "provoking total undo") 1197 terr.WaitFor(t) 1198 chg.AddTask(terr) 1199 1200 s.state.Unlock() 1201 1202 for i := 0; i < 3; i++ { 1203 s.se.Ensure() 1204 s.se.Wait() 1205 } 1206 1207 s.state.Lock() 1208 var snapst snapstate.SnapState 1209 err := snapstate.Get(s.state, "core", &snapst) 1210 c.Assert(err, IsNil) 1211 c.Check(snapst.Active, Equals, true) 1212 c.Check(snapst.Sequence, HasLen, 1) 1213 c.Check(snapst.Current, Equals, snap.R(1)) 1214 c.Check(t.Status(), Equals, state.UndoneStatus) 1215 1216 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 1217 c.Check(lp.instanceNames, DeepEquals, []string{"core", "core"}) 1218 } 1219 1220 func (s *linkSnapSuite) TestDoUndoUnlinkCurrentSnapCoreBase(c *C) { 1221 restore := release.MockOnClassic(false) 1222 defer restore() 1223 1224 r := snapstatetest.MockDeviceModel(ModelWithBase("core18")) 1225 defer r() 1226 1227 s.fakeBackend.linkSnapMaybeReboot = true 1228 1229 s.state.Lock() 1230 defer s.state.Unlock() 1231 // we need to init the boot-id 1232 err := s.state.VerifyReboot("some-boot-id") 1233 c.Assert(err, IsNil) 1234 1235 si1 := &snap.SideInfo{ 1236 RealName: "core18", 1237 Revision: snap.R(1), 1238 } 1239 si2 := &snap.SideInfo{ 1240 RealName: "core18", 1241 Revision: snap.R(2), 1242 } 1243 snapstate.Set(s.state, "core18", &snapstate.SnapState{ 1244 Sequence: []*snap.SideInfo{si1}, 1245 Current: si1.Revision, 1246 Active: true, 1247 SnapType: "base", 1248 }) 1249 t := s.state.NewTask("unlink-current-snap", "test") 1250 t.Set("snap-setup", &snapstate.SnapSetup{ 1251 SideInfo: si2, 1252 }) 1253 chg := s.state.NewChange("dummy", "...") 1254 chg.AddTask(t) 1255 1256 terr := s.state.NewTask("error-trigger", "provoking total undo") 1257 terr.WaitFor(t) 1258 chg.AddTask(terr) 1259 1260 s.state.Unlock() 1261 for i := 0; i < 3; i++ { 1262 s.se.Ensure() 1263 s.se.Wait() 1264 } 1265 s.state.Lock() 1266 1267 var snapst snapstate.SnapState 1268 err = snapstate.Get(s.state, "core18", &snapst) 1269 c.Assert(err, IsNil) 1270 c.Check(snapst.Active, Equals, true) 1271 c.Check(snapst.Sequence, HasLen, 1) 1272 c.Check(snapst.Current, Equals, snap.R(1)) 1273 c.Check(t.Status(), Equals, state.UndoneStatus) 1274 1275 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartSystem}) 1276 } 1277 1278 func (s *linkSnapSuite) TestDoUndoLinkSnapCoreClassic(c *C) { 1279 restore := release.MockOnClassic(true) 1280 defer restore() 1281 1282 s.state.Lock() 1283 defer s.state.Unlock() 1284 1285 // no previous core snap and an error on link, in this 1286 // case we need to restart on classic back into the distro 1287 // package version 1288 si1 := &snap.SideInfo{ 1289 RealName: "core", 1290 Revision: snap.R(1), 1291 } 1292 t := s.state.NewTask("link-snap", "test") 1293 t.Set("snap-setup", &snapstate.SnapSetup{ 1294 SideInfo: si1, 1295 }) 1296 chg := s.state.NewChange("dummy", "...") 1297 chg.AddTask(t) 1298 1299 terr := s.state.NewTask("error-trigger", "provoking total undo") 1300 terr.WaitFor(t) 1301 chg.AddTask(terr) 1302 1303 s.state.Unlock() 1304 1305 for i := 0; i < 3; i++ { 1306 s.se.Ensure() 1307 s.se.Wait() 1308 } 1309 1310 s.state.Lock() 1311 var snapst snapstate.SnapState 1312 err := snapstate.Get(s.state, "core", &snapst) 1313 c.Assert(err, Equals, state.ErrNoState) 1314 c.Check(t.Status(), Equals, state.UndoneStatus) 1315 1316 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon, state.RestartDaemon}) 1317 1318 } 1319 1320 func (s *linkSnapSuite) TestLinkSnapInjectsAutoConnectIfMissing(c *C) { 1321 si1 := &snap.SideInfo{ 1322 RealName: "snap1", 1323 Revision: snap.R(1), 1324 } 1325 sup1 := &snapstate.SnapSetup{SideInfo: si1} 1326 si2 := &snap.SideInfo{ 1327 RealName: "snap2", 1328 Revision: snap.R(1), 1329 } 1330 sup2 := &snapstate.SnapSetup{SideInfo: si2} 1331 1332 s.state.Lock() 1333 defer s.state.Unlock() 1334 1335 task0 := s.state.NewTask("setup-profiles", "") 1336 task1 := s.state.NewTask("link-snap", "") 1337 task1.WaitFor(task0) 1338 task0.Set("snap-setup", sup1) 1339 task1.Set("snap-setup", sup1) 1340 1341 task2 := s.state.NewTask("setup-profiles", "") 1342 task3 := s.state.NewTask("link-snap", "") 1343 task2.WaitFor(task1) 1344 task3.WaitFor(task2) 1345 task2.Set("snap-setup", sup2) 1346 task3.Set("snap-setup", sup2) 1347 1348 chg := s.state.NewChange("test", "") 1349 chg.AddTask(task0) 1350 chg.AddTask(task1) 1351 chg.AddTask(task2) 1352 chg.AddTask(task3) 1353 1354 s.state.Unlock() 1355 1356 for i := 0; i < 10; i++ { 1357 s.se.Ensure() 1358 s.se.Wait() 1359 } 1360 1361 s.state.Lock() 1362 1363 // ensure all our tasks ran 1364 c.Assert(chg.Err(), IsNil) 1365 c.Assert(chg.Tasks(), HasLen, 6) 1366 1367 // sanity checks 1368 t := chg.Tasks()[1] 1369 c.Assert(t.Kind(), Equals, "link-snap") 1370 t = chg.Tasks()[3] 1371 c.Assert(t.Kind(), Equals, "link-snap") 1372 1373 // check that auto-connect tasks were added and have snap-setup 1374 var autoconnectSup snapstate.SnapSetup 1375 t = chg.Tasks()[4] 1376 c.Assert(t.Kind(), Equals, "auto-connect") 1377 c.Assert(t.Get("snap-setup", &autoconnectSup), IsNil) 1378 c.Assert(autoconnectSup.InstanceName(), Equals, "snap1") 1379 1380 t = chg.Tasks()[5] 1381 c.Assert(t.Kind(), Equals, "auto-connect") 1382 c.Assert(t.Get("snap-setup", &autoconnectSup), IsNil) 1383 c.Assert(autoconnectSup.InstanceName(), Equals, "snap2") 1384 } 1385 1386 func (s *linkSnapSuite) TestDoLinkSnapFailureCleansUpAux(c *C) { 1387 // this is very chummy with the order of LinkSnap 1388 c.Assert(ioutil.WriteFile(dirs.SnapSeqDir, nil, 0644), IsNil) 1389 1390 // we start without the auxiliary store info 1391 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 1392 1393 s.state.Lock() 1394 t := s.state.NewTask("link-snap", "test") 1395 t.Set("snap-setup", &snapstate.SnapSetup{ 1396 SideInfo: &snap.SideInfo{ 1397 RealName: "foo", 1398 Revision: snap.R(33), 1399 SnapID: "foo-id", 1400 }, 1401 Channel: "beta", 1402 UserID: 2, 1403 }) 1404 s.state.NewChange("dummy", "...").AddTask(t) 1405 1406 s.state.Unlock() 1407 1408 s.se.Ensure() 1409 s.se.Wait() 1410 1411 s.state.Lock() 1412 defer s.state.Unlock() 1413 1414 c.Check(t.Status(), Equals, state.ErrorStatus) 1415 c.Check(s.stateBackend.restartRequested, HasLen, 0) 1416 1417 // we end without the auxiliary store info 1418 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 1419 } 1420 1421 func (s *linkSnapSuite) TestLinkSnapResetsRefreshInhibitedTime(c *C) { 1422 // When a snap is linked the refresh-inhibited-time is reset to zero 1423 // to indicate a successful refresh. The old value is stored in task 1424 // state for task undo logic. 1425 s.state.Lock() 1426 defer s.state.Unlock() 1427 1428 instant := time.Now() 1429 1430 si := &snap.SideInfo{RealName: "snap", Revision: snap.R(1)} 1431 sup := &snapstate.SnapSetup{SideInfo: si} 1432 snapstate.Set(s.state, "snap", &snapstate.SnapState{ 1433 Sequence: []*snap.SideInfo{si}, 1434 Current: si.Revision, 1435 RefreshInhibitedTime: &instant, 1436 }) 1437 1438 task := s.state.NewTask("link-snap", "") 1439 task.Set("snap-setup", sup) 1440 chg := s.state.NewChange("test", "") 1441 chg.AddTask(task) 1442 1443 s.state.Unlock() 1444 1445 for i := 0; i < 10; i++ { 1446 s.se.Ensure() 1447 s.se.Wait() 1448 } 1449 1450 s.state.Lock() 1451 1452 c.Assert(chg.Err(), IsNil) 1453 c.Assert(chg.Tasks(), HasLen, 1) 1454 1455 var snapst snapstate.SnapState 1456 err := snapstate.Get(s.state, "snap", &snapst) 1457 c.Assert(err, IsNil) 1458 c.Check(snapst.RefreshInhibitedTime, IsNil) 1459 1460 var oldTime time.Time 1461 c.Assert(task.Get("old-refresh-inhibited-time", &oldTime), IsNil) 1462 c.Check(oldTime.Equal(instant), Equals, true) 1463 } 1464 1465 func (s *linkSnapSuite) TestDoUndoLinkSnapRestoresRefreshInhibitedTime(c *C) { 1466 s.state.Lock() 1467 defer s.state.Unlock() 1468 1469 instant := time.Now() 1470 1471 si := &snap.SideInfo{RealName: "snap", Revision: snap.R(1)} 1472 sup := &snapstate.SnapSetup{SideInfo: si} 1473 snapstate.Set(s.state, "snap", &snapstate.SnapState{ 1474 Sequence: []*snap.SideInfo{si}, 1475 Current: si.Revision, 1476 RefreshInhibitedTime: &instant, 1477 }) 1478 1479 task := s.state.NewTask("link-snap", "") 1480 task.Set("snap-setup", sup) 1481 chg := s.state.NewChange("test", "") 1482 chg.AddTask(task) 1483 1484 terr := s.state.NewTask("error-trigger", "provoking total undo") 1485 terr.WaitFor(task) 1486 chg.AddTask(terr) 1487 1488 s.state.Unlock() 1489 1490 for i := 0; i < 6; i++ { 1491 s.se.Ensure() 1492 s.se.Wait() 1493 } 1494 1495 s.state.Lock() 1496 1497 c.Assert(chg.Err(), NotNil) 1498 c.Assert(chg.Tasks(), HasLen, 2) 1499 c.Check(task.Status(), Equals, state.UndoneStatus) 1500 1501 var snapst snapstate.SnapState 1502 err := snapstate.Get(s.state, "snap", &snapst) 1503 c.Assert(err, IsNil) 1504 c.Check(snapst.RefreshInhibitedTime.Equal(instant), Equals, true) 1505 } 1506 1507 func (s *linkSnapSuite) TestLinkSnapSetsLastRefreshTime(c *C) { 1508 s.state.Lock() 1509 defer s.state.Unlock() 1510 1511 now, err := time.Parse(time.RFC3339, "2021-07-20T10:00:00Z") 1512 c.Assert(err, IsNil) 1513 restoreTimeNow := snapstate.MockTimeNow(func() time.Time { 1514 return now 1515 }) 1516 defer restoreTimeNow() 1517 1518 si := &snap.SideInfo{RealName: "snap", Revision: snap.R(1)} 1519 sup := &snapstate.SnapSetup{SideInfo: si} 1520 snapstate.Set(s.state, "snap", &snapstate.SnapState{ 1521 Sequence: []*snap.SideInfo{si}, 1522 Current: si.Revision, 1523 // LastRefreshTime not set initially (first install) 1524 }) 1525 1526 task := s.state.NewTask("link-snap", "") 1527 task.Set("snap-setup", sup) 1528 chg := s.state.NewChange("test", "") 1529 chg.AddTask(task) 1530 1531 s.state.Unlock() 1532 1533 for i := 0; i < 10; i++ { 1534 s.se.Ensure() 1535 s.se.Wait() 1536 } 1537 1538 s.state.Lock() 1539 1540 c.Assert(chg.Err(), IsNil) 1541 c.Assert(chg.Tasks(), HasLen, 1) 1542 1543 var snapst snapstate.SnapState 1544 c.Assert(snapstate.Get(s.state, "snap", &snapst), IsNil) 1545 c.Assert(snapst.LastRefreshTime, NotNil) 1546 c.Check(snapst.LastRefreshTime.Equal(now), Equals, true) 1547 1548 var oldTime *time.Time 1549 c.Assert(task.Get("old-last-refresh-time", &oldTime), IsNil) 1550 c.Check(oldTime, IsNil) 1551 } 1552 1553 func (s *linkSnapSuite) TestLinkSnapUpdatesLastRefreshTime(c *C) { 1554 s.state.Lock() 1555 defer s.state.Unlock() 1556 1557 now, err := time.Parse(time.RFC3339, "2021-07-20T10:00:00Z") 1558 c.Assert(err, IsNil) 1559 restoreTimeNow := snapstate.MockTimeNow(func() time.Time { 1560 return now 1561 }) 1562 defer restoreTimeNow() 1563 1564 lastRefresh, err := time.Parse(time.RFC3339, "2021-02-20T10:00:00Z") 1565 c.Assert(err, IsNil) 1566 si := &snap.SideInfo{RealName: "snap", Revision: snap.R(1)} 1567 sup := &snapstate.SnapSetup{SideInfo: si} 1568 snapstate.Set(s.state, "snap", &snapstate.SnapState{ 1569 Sequence: []*snap.SideInfo{si}, 1570 Current: si.Revision, 1571 LastRefreshTime: &lastRefresh, 1572 }) 1573 1574 task := s.state.NewTask("link-snap", "") 1575 task.Set("snap-setup", sup) 1576 chg := s.state.NewChange("test", "") 1577 chg.AddTask(task) 1578 1579 s.state.Unlock() 1580 1581 for i := 0; i < 10; i++ { 1582 s.se.Ensure() 1583 s.se.Wait() 1584 } 1585 1586 s.state.Lock() 1587 1588 c.Assert(chg.Err(), IsNil) 1589 1590 var snapst snapstate.SnapState 1591 c.Assert(snapstate.Get(s.state, "snap", &snapst), IsNil) 1592 c.Assert(snapst.LastRefreshTime, NotNil) 1593 c.Check(snapst.LastRefreshTime.Equal(now), Equals, true) 1594 1595 var oldTime *time.Time 1596 c.Assert(task.Get("old-last-refresh-time", &oldTime), IsNil) 1597 c.Check(oldTime.Equal(lastRefresh), Equals, true) 1598 } 1599 1600 func (s *linkSnapSuite) TestDoUndoLinkSnapRestoresLastRefreshTime(c *C) { 1601 s.state.Lock() 1602 defer s.state.Unlock() 1603 1604 lastRefresh, err := time.Parse(time.RFC3339, "2021-06-10T10:00:00Z") 1605 c.Assert(err, IsNil) 1606 1607 var called int 1608 restoreTimeNow := snapstate.MockTimeNow(func() time.Time { 1609 now, err := time.Parse(time.RFC3339, "2021-07-20T10:00:00Z") 1610 c.Assert(err, IsNil) 1611 called++ 1612 return now 1613 }) 1614 defer restoreTimeNow() 1615 1616 si := &snap.SideInfo{RealName: "snap", Revision: snap.R(1)} 1617 sup := &snapstate.SnapSetup{SideInfo: si} 1618 snapstate.Set(s.state, "snap", &snapstate.SnapState{ 1619 Sequence: []*snap.SideInfo{si}, 1620 Current: si.Revision, 1621 LastRefreshTime: &lastRefresh, 1622 }) 1623 1624 task := s.state.NewTask("link-snap", "") 1625 task.Set("snap-setup", sup) 1626 chg := s.state.NewChange("test", "") 1627 chg.AddTask(task) 1628 1629 terr := s.state.NewTask("error-trigger", "provoking total undo") 1630 terr.WaitFor(task) 1631 chg.AddTask(terr) 1632 1633 s.state.Unlock() 1634 for i := 0; i < 6; i++ { 1635 s.se.Ensure() 1636 s.se.Wait() 1637 } 1638 s.state.Lock() 1639 1640 c.Assert(chg.Err(), NotNil) 1641 c.Check(task.Status(), Equals, state.UndoneStatus) 1642 1643 var snapst snapstate.SnapState 1644 c.Assert(snapstate.Get(s.state, "snap", &snapst), IsNil) 1645 // the original last-refresh-time has been restored. 1646 c.Check(snapst.LastRefreshTime.Equal(lastRefresh), Equals, true) 1647 c.Check(called, Equals, 1) 1648 } 1649 1650 func (s *linkSnapSuite) TestUndoLinkSnapdFirstInstall(c *C) { 1651 restore := release.MockOnClassic(false) 1652 defer restore() 1653 1654 s.state.Lock() 1655 si := &snap.SideInfo{ 1656 RealName: "snapd", 1657 SnapID: "snapd-snap-id", 1658 Revision: snap.R(22), 1659 } 1660 chg := s.state.NewChange("dummy", "...") 1661 t := s.state.NewTask("link-snap", "test") 1662 t.Set("snap-setup", &snapstate.SnapSetup{ 1663 SideInfo: si, 1664 Type: snap.TypeSnapd, 1665 }) 1666 chg.AddTask(t) 1667 terr := s.state.NewTask("error-trigger", "provoking total undo") 1668 terr.WaitFor(t) 1669 chg.AddTask(terr) 1670 1671 s.state.Unlock() 1672 1673 for i := 0; i < 6; i++ { 1674 s.se.Ensure() 1675 s.se.Wait() 1676 } 1677 1678 s.state.Lock() 1679 defer s.state.Unlock() 1680 1681 var snapst snapstate.SnapState 1682 err := snapstate.Get(s.state, "snapd", &snapst) 1683 c.Assert(err, Equals, state.ErrNoState) 1684 c.Check(t.Status(), Equals, state.UndoneStatus) 1685 1686 expected := fakeOps{ 1687 { 1688 op: "candidate", 1689 sinfo: *si, 1690 }, 1691 { 1692 op: "link-snap", 1693 path: filepath.Join(dirs.SnapMountDir, "snapd/22"), 1694 }, 1695 { 1696 op: "discard-namespace", 1697 name: "snapd", 1698 }, 1699 { 1700 op: "unlink-snap", 1701 path: filepath.Join(dirs.SnapMountDir, "snapd/22"), 1702 1703 unlinkFirstInstallUndo: true, 1704 }, 1705 } 1706 1707 // start with an easier-to-read error if this fails: 1708 c.Check(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1709 c.Check(s.fakeBackend.ops, DeepEquals, expected) 1710 1711 // 2 restarts, one from link snap, another one from undo 1712 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon, state.RestartDaemon}) 1713 c.Check(t.Log(), HasLen, 3) 1714 c.Check(t.Log()[0], Matches, `.*INFO Requested daemon restart \(snapd snap\)\.`) 1715 c.Check(t.Log()[2], Matches, `.*INFO Requested daemon restart \(snapd snap\)\.`) 1716 1717 } 1718 1719 func (s *linkSnapSuite) TestUndoLinkSnapdNthInstall(c *C) { 1720 restore := release.MockOnClassic(false) 1721 defer restore() 1722 1723 s.state.Lock() 1724 si := &snap.SideInfo{ 1725 RealName: "snapd", 1726 SnapID: "snapd-snap-id", 1727 Revision: snap.R(22), 1728 } 1729 siOld := *si 1730 siOld.Revision = snap.R(20) 1731 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 1732 Sequence: []*snap.SideInfo{&siOld}, 1733 Current: siOld.Revision, 1734 Active: true, 1735 SnapType: "snapd", 1736 }) 1737 chg := s.state.NewChange("dummy", "...") 1738 t := s.state.NewTask("link-snap", "test") 1739 t.Set("snap-setup", &snapstate.SnapSetup{ 1740 SideInfo: si, 1741 Type: snap.TypeSnapd, 1742 }) 1743 chg.AddTask(t) 1744 terr := s.state.NewTask("error-trigger", "provoking total undo") 1745 terr.WaitFor(t) 1746 chg.AddTask(terr) 1747 1748 s.state.Unlock() 1749 1750 for i := 0; i < 6; i++ { 1751 s.se.Ensure() 1752 s.se.Wait() 1753 } 1754 1755 s.state.Lock() 1756 defer s.state.Unlock() 1757 1758 var snapst snapstate.SnapState 1759 err := snapstate.Get(s.state, "snapd", &snapst) 1760 c.Assert(err, IsNil) 1761 snapst.Current = siOld.Revision 1762 c.Check(t.Status(), Equals, state.UndoneStatus) 1763 1764 expected := fakeOps{ 1765 { 1766 op: "candidate", 1767 sinfo: *si, 1768 }, 1769 { 1770 op: "link-snap", 1771 path: filepath.Join(dirs.SnapMountDir, "snapd/22"), 1772 }, 1773 { 1774 op: "link-snap", 1775 path: filepath.Join(dirs.SnapMountDir, "snapd/20"), 1776 }, 1777 } 1778 1779 // start with an easier-to-read error if this fails: 1780 c.Check(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 1781 c.Check(s.fakeBackend.ops, DeepEquals, expected) 1782 1783 // 1 restart from link snap, the other restart happens 1784 // in undoUnlinkCurrentSnap (not tested here) 1785 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 1786 c.Assert(t.Log(), HasLen, 1) 1787 c.Check(t.Log()[0], Matches, `.*INFO Requested daemon restart \(snapd snap\)\.`) 1788 } 1789 1790 func (s *linkSnapSuite) TestDoUnlinkSnapRefreshAwarenessHardCheckOn(c *C) { 1791 s.state.Lock() 1792 defer s.state.Unlock() 1793 1794 tr := config.NewTransaction(s.state) 1795 tr.Set("core", "experimental.refresh-app-awareness", true) 1796 tr.Commit() 1797 1798 chg := s.testDoUnlinkSnapRefreshAwareness(c) 1799 1800 c.Check(chg.Err(), ErrorMatches, `(?ms).*^- some-change-descr \(snap "some-snap" has running apps \(some-app\)\).*`) 1801 } 1802 1803 func (s *linkSnapSuite) TestDoUnlinkSnapRefreshHardCheckOff(c *C) { 1804 s.state.Lock() 1805 defer s.state.Unlock() 1806 1807 tr := config.NewTransaction(s.state) 1808 tr.Set("core", "experimental.refresh-app-awareness", false) 1809 tr.Commit() 1810 1811 chg := s.testDoUnlinkSnapRefreshAwareness(c) 1812 1813 c.Check(chg.Err(), IsNil) 1814 } 1815 1816 func (s *linkSnapSuite) testDoUnlinkSnapRefreshAwareness(c *C) *state.Change { 1817 restore := release.MockOnClassic(true) 1818 defer restore() 1819 1820 dirs.SetRootDir(c.MkDir()) 1821 defer dirs.SetRootDir("/") 1822 1823 snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) { 1824 info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp} 1825 info.Apps = map[string]*snap.AppInfo{ 1826 "some-app": {Snap: info, Name: "some-app"}, 1827 } 1828 return info, nil 1829 }) 1830 // mock that "some-snap" has an app and that this app has pids running 1831 restore = snapstate.MockPidsOfSnap(func(instanceName string) (map[string][]int, error) { 1832 c.Assert(instanceName, Equals, "some-snap") 1833 return map[string][]int{ 1834 "snap.some-snap.some-app": {1234}, 1835 }, nil 1836 }) 1837 defer restore() 1838 1839 si1 := &snap.SideInfo{ 1840 RealName: "some-snap", 1841 Revision: snap.R(1), 1842 } 1843 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 1844 Sequence: []*snap.SideInfo{si1}, 1845 Current: si1.Revision, 1846 Active: true, 1847 }) 1848 t := s.state.NewTask("unlink-current-snap", "some-change-descr") 1849 t.Set("snap-setup", &snapstate.SnapSetup{ 1850 SideInfo: si1, 1851 }) 1852 chg := s.state.NewChange("dummy", "...") 1853 chg.AddTask(t) 1854 1855 s.state.Unlock() 1856 defer s.state.Lock() 1857 1858 for i := 0; i < 3; i++ { 1859 s.se.Ensure() 1860 s.se.Wait() 1861 } 1862 1863 return chg 1864 } 1865 1866 func (s *linkSnapSuite) setMockKernelRemodelCtx(c *C, oldKernel, newKernel string) { 1867 newModel := MakeModel(map[string]interface{}{"kernel": newKernel}) 1868 oldModel := MakeModel(map[string]interface{}{"kernel": oldKernel}) 1869 mockRemodelCtx := &snapstatetest.TrivialDeviceContext{ 1870 DeviceModel: newModel, 1871 OldDeviceModel: oldModel, 1872 Remodeling: true, 1873 } 1874 restore := snapstatetest.MockDeviceContext(mockRemodelCtx) 1875 s.AddCleanup(restore) 1876 } 1877 1878 func (s *linkSnapSuite) TestMaybeUndoRemodelBootChangesUnrelatedAppDoesNothing(c *C) { 1879 s.state.Lock() 1880 defer s.state.Unlock() 1881 1882 s.setMockKernelRemodelCtx(c, "kernel", "new-kernel") 1883 t := s.state.NewTask("link-snap", "...") 1884 t.Set("snap-setup", &snapstate.SnapSetup{ 1885 SideInfo: &snap.SideInfo{ 1886 RealName: "some-app", 1887 Revision: snap.R(1), 1888 }, 1889 }) 1890 1891 err := s.snapmgr.MaybeUndoRemodelBootChanges(t) 1892 c.Assert(err, IsNil) 1893 c.Check(s.stateBackend.restartRequested, HasLen, 0) 1894 } 1895 1896 func (s *linkSnapSuite) TestMaybeUndoRemodelBootChangesSameKernel(c *C) { 1897 s.state.Lock() 1898 defer s.state.Unlock() 1899 1900 s.setMockKernelRemodelCtx(c, "kernel", "kernel") 1901 t := s.state.NewTask("link-snap", "...") 1902 t.Set("snap-setup", &snapstate.SnapSetup{ 1903 SideInfo: &snap.SideInfo{ 1904 RealName: "kernel", 1905 Revision: snap.R(1), 1906 }, 1907 Type: "kernel", 1908 }) 1909 1910 err := s.snapmgr.MaybeUndoRemodelBootChanges(t) 1911 c.Assert(err, IsNil) 1912 c.Check(s.stateBackend.restartRequested, HasLen, 0) 1913 } 1914 1915 func (s *linkSnapSuite) TestMaybeUndoRemodelBootChangesNeedsUndo(c *C) { 1916 s.state.Lock() 1917 defer s.state.Unlock() 1918 1919 // undoing remodel bootenv changes is only relevant on !classic 1920 restore := release.MockOnClassic(false) 1921 defer restore() 1922 1923 // using "snaptest.MockSnap()" is more convenient here so we avoid 1924 // the (default) mocking of snapReadInfo() 1925 restore = snapstate.MockSnapReadInfo(snap.ReadInfo) 1926 defer restore() 1927 1928 // we need to init the boot-id 1929 err := s.state.VerifyReboot("some-boot-id") 1930 c.Assert(err, IsNil) 1931 1932 // we pretend we do a remodel from kernel -> new-kernel 1933 s.setMockKernelRemodelCtx(c, "kernel", "new-kernel") 1934 1935 // and we pretend that we booted into the "new-kernel" already 1936 // and now that needs to be undone 1937 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 1938 bootloader.Force(bloader) 1939 bloader.SetBootKernel("new-kernel_1.snap") 1940 1941 // both kernel and new-kernel are instaleld at this point 1942 si := &snap.SideInfo{RealName: "kernel", Revision: snap.R(1)} 1943 snapstate.Set(s.state, "kernel", &snapstate.SnapState{ 1944 Sequence: []*snap.SideInfo{si}, 1945 SnapType: "kernel", 1946 Current: snap.R(1), 1947 }) 1948 snaptest.MockSnap(c, "name: kernel\ntype: kernel\nversion: 1.0", si) 1949 si2 := &snap.SideInfo{RealName: "new-kernel", Revision: snap.R(1)} 1950 snapstate.Set(s.state, "new-kernel", &snapstate.SnapState{ 1951 Sequence: []*snap.SideInfo{si2}, 1952 SnapType: "kernel", 1953 Current: snap.R(1), 1954 }) 1955 snaptest.MockSnap(c, "name: new-kernel\ntype: kernel\nversion: 1.0", si) 1956 1957 t := s.state.NewTask("link-snap", "...") 1958 t.Set("snap-setup", &snapstate.SnapSetup{ 1959 SideInfo: &snap.SideInfo{ 1960 RealName: "new-kernel", 1961 Revision: snap.R(1), 1962 SnapID: "new-kernel-id", 1963 }, 1964 Type: "kernel", 1965 }) 1966 1967 // now we simulate that the new kernel is getting undone 1968 err = s.snapmgr.MaybeUndoRemodelBootChanges(t) 1969 c.Assert(err, IsNil) 1970 1971 // that will schedule a boot into the previous kernel 1972 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 1973 "snap_mode": boot.TryStatus, 1974 "snap_kernel": "new-kernel_1.snap", 1975 "snap_try_kernel": "kernel_1.snap", 1976 }) 1977 c.Check(s.stateBackend.restartRequested, HasLen, 1) 1978 c.Check(s.stateBackend.restartRequested[0], Equals, state.RestartSystem) 1979 } 1980 1981 func (s *linkSnapSuite) testDoLinkSnapWithToolingDependency(c *C, classicOrBase string) { 1982 var model *asserts.Model 1983 var needsTooling bool 1984 switch classicOrBase { 1985 case "classic-system": 1986 model = ClassicModel() 1987 case "": 1988 model = DefaultModel() 1989 default: 1990 // the tooling mount is needed on UC18+ 1991 needsTooling = true 1992 model = ModelWithBase(classicOrBase) 1993 } 1994 r := snapstatetest.MockDeviceModel(model) 1995 defer r() 1996 1997 s.state.Lock() 1998 si := &snap.SideInfo{ 1999 RealName: "services-snap", 2000 SnapID: "services-snap-id", 2001 Revision: snap.R(11), 2002 } 2003 t := s.state.NewTask("link-snap", "test") 2004 t.Set("snap-setup", &snapstate.SnapSetup{ 2005 SideInfo: si, 2006 Type: snap.TypeApp, 2007 }) 2008 s.state.NewChange("dummy", "...").AddTask(t) 2009 2010 s.state.Unlock() 2011 2012 s.se.Ensure() 2013 s.se.Wait() 2014 2015 s.state.Lock() 2016 defer s.state.Unlock() 2017 2018 expected := fakeOps{ 2019 { 2020 op: "candidate", 2021 sinfo: *si, 2022 }, 2023 { 2024 op: "link-snap", 2025 path: filepath.Join(dirs.SnapMountDir, "services-snap/11"), 2026 requireSnapdTooling: needsTooling, 2027 }, 2028 } 2029 2030 // start with an easier-to-read error if this fails: 2031 c.Check(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 2032 c.Check(s.fakeBackend.ops, DeepEquals, expected) 2033 } 2034 2035 func (s *linkSnapSuite) TestDoLinkSnapWithToolingClassic(c *C) { 2036 s.testDoLinkSnapWithToolingDependency(c, "classic-system") 2037 } 2038 2039 func (s *linkSnapSuite) TestDoLinkSnapWithToolingCore(c *C) { 2040 s.testDoLinkSnapWithToolingDependency(c, "") 2041 } 2042 2043 func (s *linkSnapSuite) TestDoLinkSnapWithToolingCore18(c *C) { 2044 s.testDoLinkSnapWithToolingDependency(c, "core18") 2045 } 2046 2047 func (s *linkSnapSuite) TestDoLinkSnapWithToolingCore20(c *C) { 2048 s.testDoLinkSnapWithToolingDependency(c, "core20") 2049 }