github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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/dirs" 32 "github.com/snapcore/snapd/overlord/auth" 33 "github.com/snapcore/snapd/overlord/configstate/config" 34 "github.com/snapcore/snapd/overlord/snapstate" 35 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 36 "github.com/snapcore/snapd/overlord/state" 37 "github.com/snapcore/snapd/release" 38 "github.com/snapcore/snapd/snap" 39 "github.com/snapcore/snapd/testutil" 40 ) 41 42 type linkSnapSuite struct { 43 baseHandlerSuite 44 45 stateBackend *witnessRestartReqStateBackend 46 } 47 48 var _ = Suite(&linkSnapSuite{}) 49 50 type witnessRestartReqStateBackend struct { 51 restartRequested []state.RestartType 52 } 53 54 func (b *witnessRestartReqStateBackend) Checkpoint([]byte) error { 55 return nil 56 } 57 58 func (b *witnessRestartReqStateBackend) RequestRestart(t state.RestartType) { 59 b.restartRequested = append(b.restartRequested, t) 60 } 61 62 func (b *witnessRestartReqStateBackend) EnsureBefore(time.Duration) {} 63 64 func (s *linkSnapSuite) SetUpTest(c *C) { 65 s.stateBackend = &witnessRestartReqStateBackend{} 66 67 s.setup(c, s.stateBackend) 68 69 s.AddCleanup(snapstatetest.MockDeviceModel(DefaultModel())) 70 s.AddCleanup(snap.MockSnapdSnapID("snapd-snap-id")) 71 } 72 73 func checkHasCookieForSnap(c *C, st *state.State, instanceName string) { 74 var contexts map[string]interface{} 75 err := st.Get("snap-cookies", &contexts) 76 c.Assert(err, IsNil) 77 c.Check(contexts, HasLen, 1) 78 79 for _, snap := range contexts { 80 if instanceName == snap { 81 return 82 } 83 } 84 panic(fmt.Sprintf("Cookie missing for snap %q", instanceName)) 85 } 86 87 func (s *linkSnapSuite) TestDoLinkSnapSuccess(c *C) { 88 // we start without the auxiliary store info 89 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 90 91 s.state.Lock() 92 t := s.state.NewTask("link-snap", "test") 93 t.Set("snap-setup", &snapstate.SnapSetup{ 94 SideInfo: &snap.SideInfo{ 95 RealName: "foo", 96 Revision: snap.R(33), 97 SnapID: "foo-id", 98 }, 99 Channel: "beta", 100 UserID: 2, 101 }) 102 s.state.NewChange("dummy", "...").AddTask(t) 103 104 s.state.Unlock() 105 106 s.se.Ensure() 107 s.se.Wait() 108 109 s.state.Lock() 110 defer s.state.Unlock() 111 var snapst snapstate.SnapState 112 err := snapstate.Get(s.state, "foo", &snapst) 113 c.Assert(err, IsNil) 114 115 checkHasCookieForSnap(c, s.state, "foo") 116 117 typ, err := snapst.Type() 118 c.Check(err, IsNil) 119 c.Check(typ, Equals, snap.TypeApp) 120 121 c.Check(snapst.Active, Equals, true) 122 c.Check(snapst.Sequence, HasLen, 1) 123 c.Check(snapst.Current, Equals, snap.R(33)) 124 c.Check(snapst.Channel, Equals, "beta") 125 c.Check(snapst.UserID, Equals, 2) 126 c.Check(snapst.CohortKey, Equals, "") 127 c.Check(t.Status(), Equals, state.DoneStatus) 128 c.Check(s.stateBackend.restartRequested, HasLen, 0) 129 130 // we end with the auxiliary store info 131 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FilePresent) 132 } 133 134 func (s *linkSnapSuite) TestDoLinkSnapSuccessWithCohort(c *C) { 135 // we start without the auxiliary store info 136 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 137 138 s.state.Lock() 139 t := s.state.NewTask("link-snap", "test") 140 t.Set("snap-setup", &snapstate.SnapSetup{ 141 SideInfo: &snap.SideInfo{ 142 RealName: "foo", 143 Revision: snap.R(33), 144 SnapID: "foo-id", 145 }, 146 Channel: "beta", 147 UserID: 2, 148 CohortKey: "wobbling", 149 }) 150 s.state.NewChange("dummy", "...").AddTask(t) 151 152 s.state.Unlock() 153 154 s.se.Ensure() 155 s.se.Wait() 156 157 s.state.Lock() 158 defer s.state.Unlock() 159 var snapst snapstate.SnapState 160 err := snapstate.Get(s.state, "foo", &snapst) 161 c.Assert(err, IsNil) 162 163 checkHasCookieForSnap(c, s.state, "foo") 164 165 typ, err := snapst.Type() 166 c.Check(err, IsNil) 167 c.Check(typ, Equals, snap.TypeApp) 168 169 c.Check(snapst.Active, Equals, true) 170 c.Check(snapst.Sequence, HasLen, 1) 171 c.Check(snapst.Current, Equals, snap.R(33)) 172 c.Check(snapst.Channel, Equals, "beta") 173 c.Check(snapst.UserID, Equals, 2) 174 c.Check(snapst.CohortKey, Equals, "wobbling") 175 c.Check(t.Status(), Equals, state.DoneStatus) 176 c.Check(s.stateBackend.restartRequested, HasLen, 0) 177 178 // we end with the auxiliary store info 179 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FilePresent) 180 } 181 182 func (s *linkSnapSuite) TestDoLinkSnapSuccessNoUserID(c *C) { 183 s.state.Lock() 184 t := s.state.NewTask("link-snap", "test") 185 t.Set("snap-setup", &snapstate.SnapSetup{ 186 SideInfo: &snap.SideInfo{ 187 RealName: "foo", 188 Revision: snap.R(33), 189 }, 190 Channel: "beta", 191 }) 192 s.state.NewChange("dummy", "...").AddTask(t) 193 194 s.state.Unlock() 195 s.se.Ensure() 196 s.se.Wait() 197 s.state.Lock() 198 defer s.state.Unlock() 199 200 // check that snapst.UserID does not get set 201 var snapst snapstate.SnapState 202 err := snapstate.Get(s.state, "foo", &snapst) 203 c.Assert(err, IsNil) 204 c.Check(snapst.UserID, Equals, 0) 205 206 var snaps map[string]*json.RawMessage 207 err = s.state.Get("snaps", &snaps) 208 c.Assert(err, IsNil) 209 raw := []byte(*snaps["foo"]) 210 c.Check(string(raw), Not(testutil.Contains), "user-id") 211 } 212 213 func (s *linkSnapSuite) TestDoLinkSnapSuccessUserIDAlreadySet(c *C) { 214 s.state.Lock() 215 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 216 Sequence: []*snap.SideInfo{ 217 {RealName: "foo", Revision: snap.R(1)}, 218 }, 219 Current: snap.R(1), 220 UserID: 1, 221 }) 222 // the user 223 user, err := auth.NewUser(s.state, "username", "email@test.com", "macaroon", []string{"discharge"}) 224 c.Assert(err, IsNil) 225 c.Assert(user.ID, Equals, 1) 226 227 t := s.state.NewTask("link-snap", "test") 228 t.Set("snap-setup", &snapstate.SnapSetup{ 229 SideInfo: &snap.SideInfo{ 230 RealName: "foo", 231 Revision: snap.R(33), 232 }, 233 Channel: "beta", 234 UserID: 2, 235 }) 236 s.state.NewChange("dummy", "...").AddTask(t) 237 238 s.state.Unlock() 239 s.se.Ensure() 240 s.se.Wait() 241 s.state.Lock() 242 defer s.state.Unlock() 243 244 // check that snapst.UserID was not "transferred" 245 var snapst snapstate.SnapState 246 err = snapstate.Get(s.state, "foo", &snapst) 247 c.Assert(err, IsNil) 248 c.Check(snapst.UserID, Equals, 1) 249 } 250 251 func (s *linkSnapSuite) TestDoLinkSnapSuccessUserLoggedOut(c *C) { 252 s.state.Lock() 253 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 254 Sequence: []*snap.SideInfo{ 255 {RealName: "foo", Revision: snap.R(1)}, 256 }, 257 Current: snap.R(1), 258 UserID: 1, 259 }) 260 261 t := s.state.NewTask("link-snap", "test") 262 t.Set("snap-setup", &snapstate.SnapSetup{ 263 SideInfo: &snap.SideInfo{ 264 RealName: "foo", 265 Revision: snap.R(33), 266 }, 267 Channel: "beta", 268 UserID: 2, 269 }) 270 s.state.NewChange("dummy", "...").AddTask(t) 271 272 s.state.Unlock() 273 s.se.Ensure() 274 s.se.Wait() 275 s.state.Lock() 276 defer s.state.Unlock() 277 278 // check that snapst.UserID was transferred 279 // given that user 1 doesn't exist anymore 280 var snapst snapstate.SnapState 281 err := snapstate.Get(s.state, "foo", &snapst) 282 c.Assert(err, IsNil) 283 c.Check(snapst.UserID, Equals, 2) 284 } 285 286 func (s *linkSnapSuite) TestDoLinkSnapSeqFile(c *C) { 287 s.state.Lock() 288 // pretend we have an installed snap 289 si11 := &snap.SideInfo{ 290 RealName: "foo", 291 Revision: snap.R(11), 292 } 293 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 294 Sequence: []*snap.SideInfo{si11}, 295 Current: si11.Revision, 296 }) 297 // add a new one 298 t := s.state.NewTask("link-snap", "test") 299 t.Set("snap-setup", &snapstate.SnapSetup{ 300 SideInfo: &snap.SideInfo{ 301 RealName: "foo", 302 Revision: snap.R(33), 303 }, 304 Channel: "beta", 305 }) 306 s.state.NewChange("dummy", "...").AddTask(t) 307 s.state.Unlock() 308 309 s.se.Ensure() 310 s.se.Wait() 311 312 s.state.Lock() 313 defer s.state.Unlock() 314 var snapst snapstate.SnapState 315 err := snapstate.Get(s.state, "foo", &snapst) 316 c.Assert(err, IsNil) 317 318 // and check that the sequence file got updated 319 seqContent, err := ioutil.ReadFile(filepath.Join(dirs.SnapSeqDir, "foo.json")) 320 c.Assert(err, IsNil) 321 c.Check(string(seqContent), Equals, `{"sequence":[{"name":"foo","snap-id":"","revision":"11"},{"name":"foo","snap-id":"","revision":"33"}],"current":"33"}`) 322 } 323 324 func (s *linkSnapSuite) TestDoUndoLinkSnap(c *C) { 325 s.state.Lock() 326 defer s.state.Unlock() 327 si := &snap.SideInfo{ 328 RealName: "foo", 329 Revision: snap.R(33), 330 } 331 t := s.state.NewTask("link-snap", "test") 332 t.Set("snap-setup", &snapstate.SnapSetup{ 333 SideInfo: si, 334 Channel: "beta", 335 }) 336 chg := s.state.NewChange("dummy", "...") 337 chg.AddTask(t) 338 339 terr := s.state.NewTask("error-trigger", "provoking total undo") 340 terr.WaitFor(t) 341 chg.AddTask(terr) 342 343 s.state.Unlock() 344 345 for i := 0; i < 6; i++ { 346 s.se.Ensure() 347 s.se.Wait() 348 } 349 350 s.state.Lock() 351 var snapst snapstate.SnapState 352 err := snapstate.Get(s.state, "foo", &snapst) 353 c.Assert(err, Equals, state.ErrNoState) 354 c.Check(t.Status(), Equals, state.UndoneStatus) 355 356 // and check that the sequence file got updated 357 seqContent, err := ioutil.ReadFile(filepath.Join(dirs.SnapSeqDir, "foo.json")) 358 c.Assert(err, IsNil) 359 c.Check(string(seqContent), Equals, `{"sequence":[],"current":"unset"}`) 360 } 361 362 func (s *linkSnapSuite) TestDoLinkSnapTryToCleanupOnError(c *C) { 363 s.state.Lock() 364 defer s.state.Unlock() 365 si := &snap.SideInfo{ 366 RealName: "foo", 367 Revision: snap.R(35), 368 } 369 t := s.state.NewTask("link-snap", "test") 370 t.Set("snap-setup", &snapstate.SnapSetup{ 371 SideInfo: si, 372 Channel: "beta", 373 }) 374 375 s.fakeBackend.linkSnapFailTrigger = filepath.Join(dirs.SnapMountDir, "foo/35") 376 s.state.NewChange("dummy", "...").AddTask(t) 377 s.state.Unlock() 378 379 s.se.Ensure() 380 s.se.Wait() 381 382 s.state.Lock() 383 384 // state as expected 385 var snapst snapstate.SnapState 386 err := snapstate.Get(s.state, "foo", &snapst) 387 c.Assert(err, Equals, state.ErrNoState) 388 389 // tried to cleanup 390 expected := fakeOps{ 391 { 392 op: "candidate", 393 sinfo: *si, 394 }, 395 { 396 op: "link-snap.failed", 397 path: filepath.Join(dirs.SnapMountDir, "foo/35"), 398 }, 399 { 400 op: "unlink-snap", 401 path: filepath.Join(dirs.SnapMountDir, "foo/35"), 402 }, 403 } 404 405 // start with an easier-to-read error if this fails: 406 c.Check(s.fakeBackend.ops.Ops(), DeepEquals, expected.Ops()) 407 c.Check(s.fakeBackend.ops, DeepEquals, expected) 408 } 409 410 func (s *linkSnapSuite) TestDoLinkSnapSuccessCoreRestarts(c *C) { 411 restore := release.MockOnClassic(true) 412 defer restore() 413 414 s.state.Lock() 415 si := &snap.SideInfo{ 416 RealName: "core", 417 Revision: snap.R(33), 418 } 419 t := s.state.NewTask("link-snap", "test") 420 t.Set("snap-setup", &snapstate.SnapSetup{ 421 SideInfo: si, 422 }) 423 s.state.NewChange("dummy", "...").AddTask(t) 424 425 s.state.Unlock() 426 427 s.se.Ensure() 428 s.se.Wait() 429 430 s.state.Lock() 431 defer s.state.Unlock() 432 433 var snapst snapstate.SnapState 434 err := snapstate.Get(s.state, "core", &snapst) 435 c.Assert(err, IsNil) 436 437 typ, err := snapst.Type() 438 c.Check(err, IsNil) 439 c.Check(typ, Equals, snap.TypeOS) 440 441 c.Check(t.Status(), Equals, state.DoneStatus) 442 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 443 c.Check(t.Log(), HasLen, 1) 444 c.Check(t.Log()[0], Matches, `.*INFO Requested daemon restart\.`) 445 } 446 447 func (s *linkSnapSuite) TestDoLinkSnapSuccessSnapdRestartsOnCoreWithBase(c *C) { 448 restore := release.MockOnClassic(false) 449 defer restore() 450 451 r := snapstatetest.MockDeviceModel(ModelWithBase("core18")) 452 defer r() 453 454 s.state.Lock() 455 si := &snap.SideInfo{ 456 RealName: "snapd", 457 SnapID: "snapd-snap-id", 458 Revision: snap.R(22), 459 } 460 t := s.state.NewTask("link-snap", "test") 461 t.Set("snap-setup", &snapstate.SnapSetup{ 462 SideInfo: si, 463 }) 464 s.state.NewChange("dummy", "...").AddTask(t) 465 466 s.state.Unlock() 467 468 s.se.Ensure() 469 s.se.Wait() 470 471 s.state.Lock() 472 defer s.state.Unlock() 473 474 var snapst snapstate.SnapState 475 err := snapstate.Get(s.state, "snapd", &snapst) 476 c.Assert(err, IsNil) 477 478 typ, err := snapst.Type() 479 c.Check(err, IsNil) 480 c.Check(typ, Equals, snap.TypeSnapd) 481 482 c.Check(t.Status(), Equals, state.DoneStatus) 483 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 484 c.Check(t.Log(), HasLen, 1) 485 c.Check(t.Log()[0], Matches, `.*INFO Requested daemon restart \(snapd snap\)\.`) 486 } 487 488 func (s *linkSnapSuite) TestDoLinkSnapSuccessSnapdRestartsOnClassic(c *C) { 489 restore := release.MockOnClassic(true) 490 defer restore() 491 492 s.state.Lock() 493 si := &snap.SideInfo{ 494 RealName: "snapd", 495 SnapID: "snapd-snap-id", 496 Revision: snap.R(22), 497 } 498 t := s.state.NewTask("link-snap", "test") 499 t.Set("snap-setup", &snapstate.SnapSetup{ 500 SideInfo: si, 501 }) 502 s.state.NewChange("dummy", "...").AddTask(t) 503 504 s.state.Unlock() 505 506 s.se.Ensure() 507 s.se.Wait() 508 509 s.state.Lock() 510 defer s.state.Unlock() 511 512 var snapst snapstate.SnapState 513 err := snapstate.Get(s.state, "snapd", &snapst) 514 c.Assert(err, IsNil) 515 516 typ, err := snapst.Type() 517 c.Check(err, IsNil) 518 c.Check(typ, Equals, snap.TypeSnapd) 519 520 c.Check(t.Status(), Equals, state.DoneStatus) 521 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 522 c.Check(t.Log(), HasLen, 1) 523 } 524 525 func (s *linkSnapSuite) TestDoLinkSnapSuccessCoreAndSnapdNoCoreRestart(c *C) { 526 restore := release.MockOnClassic(true) 527 defer restore() 528 529 s.state.Lock() 530 siSnapd := &snap.SideInfo{ 531 RealName: "snapd", 532 Revision: snap.R(64), 533 } 534 snapstate.Set(s.state, "snapd", &snapstate.SnapState{ 535 Sequence: []*snap.SideInfo{siSnapd}, 536 Current: siSnapd.Revision, 537 Active: true, 538 }) 539 540 si := &snap.SideInfo{ 541 RealName: "core", 542 Revision: snap.R(33), 543 } 544 t := s.state.NewTask("link-snap", "test") 545 t.Set("snap-setup", &snapstate.SnapSetup{ 546 SideInfo: si, 547 }) 548 s.state.NewChange("dummy", "...").AddTask(t) 549 550 s.state.Unlock() 551 552 s.se.Ensure() 553 s.se.Wait() 554 555 s.state.Lock() 556 defer s.state.Unlock() 557 558 var snapst snapstate.SnapState 559 err := snapstate.Get(s.state, "core", &snapst) 560 c.Assert(err, IsNil) 561 562 typ, err := snapst.Type() 563 c.Check(err, IsNil) 564 c.Check(typ, Equals, snap.TypeOS) 565 566 c.Check(t.Status(), Equals, state.DoneStatus) 567 c.Check(s.stateBackend.restartRequested, IsNil) 568 c.Check(t.Log(), HasLen, 0) 569 } 570 571 func (s *linkSnapSuite) TestDoUndoLinkSnapSequenceDidNotHaveCandidate(c *C) { 572 s.state.Lock() 573 defer s.state.Unlock() 574 si1 := &snap.SideInfo{ 575 RealName: "foo", 576 Revision: snap.R(1), 577 } 578 si2 := &snap.SideInfo{ 579 RealName: "foo", 580 Revision: snap.R(2), 581 } 582 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 583 Sequence: []*snap.SideInfo{si1}, 584 Current: si1.Revision, 585 }) 586 t := s.state.NewTask("link-snap", "test") 587 t.Set("snap-setup", &snapstate.SnapSetup{ 588 SideInfo: si2, 589 Channel: "beta", 590 }) 591 chg := s.state.NewChange("dummy", "...") 592 chg.AddTask(t) 593 594 terr := s.state.NewTask("error-trigger", "provoking total undo") 595 terr.WaitFor(t) 596 chg.AddTask(terr) 597 598 s.state.Unlock() 599 600 for i := 0; i < 6; i++ { 601 s.se.Ensure() 602 s.se.Wait() 603 } 604 605 s.state.Lock() 606 var snapst snapstate.SnapState 607 err := snapstate.Get(s.state, "foo", &snapst) 608 c.Assert(err, IsNil) 609 c.Check(snapst.Active, Equals, false) 610 c.Check(snapst.Sequence, HasLen, 1) 611 c.Check(snapst.Current, Equals, snap.R(1)) 612 c.Check(t.Status(), Equals, state.UndoneStatus) 613 } 614 615 func (s *linkSnapSuite) TestDoUndoLinkSnapSequenceHadCandidate(c *C) { 616 s.state.Lock() 617 defer s.state.Unlock() 618 si1 := &snap.SideInfo{ 619 RealName: "foo", 620 Revision: snap.R(1), 621 } 622 si2 := &snap.SideInfo{ 623 RealName: "foo", 624 Revision: snap.R(2), 625 } 626 snapstate.Set(s.state, "foo", &snapstate.SnapState{ 627 Sequence: []*snap.SideInfo{si1, si2}, 628 Current: si2.Revision, 629 }) 630 t := s.state.NewTask("link-snap", "test") 631 t.Set("snap-setup", &snapstate.SnapSetup{ 632 SideInfo: si1, 633 Channel: "beta", 634 }) 635 chg := s.state.NewChange("dummy", "...") 636 chg.AddTask(t) 637 638 terr := s.state.NewTask("error-trigger", "provoking total undo") 639 terr.WaitFor(t) 640 chg.AddTask(terr) 641 642 s.state.Unlock() 643 644 for i := 0; i < 6; i++ { 645 s.se.Ensure() 646 s.se.Wait() 647 } 648 649 s.state.Lock() 650 var snapst snapstate.SnapState 651 err := snapstate.Get(s.state, "foo", &snapst) 652 c.Assert(err, IsNil) 653 c.Check(snapst.Active, Equals, false) 654 c.Check(snapst.Sequence, HasLen, 2) 655 c.Check(snapst.Current, Equals, snap.R(2)) 656 c.Check(t.Status(), Equals, state.UndoneStatus) 657 } 658 659 func (s *linkSnapSuite) TestDoUndoUnlinkCurrentSnapCore(c *C) { 660 restore := release.MockOnClassic(true) 661 defer restore() 662 663 s.state.Lock() 664 defer s.state.Unlock() 665 si1 := &snap.SideInfo{ 666 RealName: "core", 667 Revision: snap.R(1), 668 } 669 si2 := &snap.SideInfo{ 670 RealName: "core", 671 Revision: snap.R(2), 672 } 673 snapstate.Set(s.state, "core", &snapstate.SnapState{ 674 Sequence: []*snap.SideInfo{si1}, 675 Current: si1.Revision, 676 Active: true, 677 SnapType: "os", 678 }) 679 t := s.state.NewTask("unlink-current-snap", "test") 680 t.Set("snap-setup", &snapstate.SnapSetup{ 681 SideInfo: si2, 682 }) 683 chg := s.state.NewChange("dummy", "...") 684 chg.AddTask(t) 685 686 terr := s.state.NewTask("error-trigger", "provoking total undo") 687 terr.WaitFor(t) 688 chg.AddTask(terr) 689 690 s.state.Unlock() 691 692 for i := 0; i < 3; i++ { 693 s.se.Ensure() 694 s.se.Wait() 695 } 696 697 s.state.Lock() 698 var snapst snapstate.SnapState 699 err := snapstate.Get(s.state, "core", &snapst) 700 c.Assert(err, IsNil) 701 c.Check(snapst.Active, Equals, true) 702 c.Check(snapst.Sequence, HasLen, 1) 703 c.Check(snapst.Current, Equals, snap.R(1)) 704 c.Check(t.Status(), Equals, state.UndoneStatus) 705 706 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon}) 707 } 708 709 func (s *linkSnapSuite) TestDoUndoLinkSnapCoreClassic(c *C) { 710 restore := release.MockOnClassic(true) 711 defer restore() 712 713 s.state.Lock() 714 defer s.state.Unlock() 715 716 // no previous core snap and an error on link, in this 717 // case we need to restart on classic back into the distro 718 // package version 719 si1 := &snap.SideInfo{ 720 RealName: "core", 721 Revision: snap.R(1), 722 } 723 t := s.state.NewTask("link-snap", "test") 724 t.Set("snap-setup", &snapstate.SnapSetup{ 725 SideInfo: si1, 726 }) 727 chg := s.state.NewChange("dummy", "...") 728 chg.AddTask(t) 729 730 terr := s.state.NewTask("error-trigger", "provoking total undo") 731 terr.WaitFor(t) 732 chg.AddTask(terr) 733 734 s.state.Unlock() 735 736 for i := 0; i < 3; i++ { 737 s.se.Ensure() 738 s.se.Wait() 739 } 740 741 s.state.Lock() 742 var snapst snapstate.SnapState 743 err := snapstate.Get(s.state, "core", &snapst) 744 c.Assert(err, Equals, state.ErrNoState) 745 c.Check(t.Status(), Equals, state.UndoneStatus) 746 747 c.Check(s.stateBackend.restartRequested, DeepEquals, []state.RestartType{state.RestartDaemon, state.RestartDaemon}) 748 749 } 750 751 func (s *linkSnapSuite) TestLinkSnapInjectsAutoConnectIfMissing(c *C) { 752 si1 := &snap.SideInfo{ 753 RealName: "snap1", 754 Revision: snap.R(1), 755 } 756 sup1 := &snapstate.SnapSetup{SideInfo: si1} 757 si2 := &snap.SideInfo{ 758 RealName: "snap2", 759 Revision: snap.R(1), 760 } 761 sup2 := &snapstate.SnapSetup{SideInfo: si2} 762 763 s.state.Lock() 764 defer s.state.Unlock() 765 766 task0 := s.state.NewTask("setup-profiles", "") 767 task1 := s.state.NewTask("link-snap", "") 768 task1.WaitFor(task0) 769 task0.Set("snap-setup", sup1) 770 task1.Set("snap-setup", sup1) 771 772 task2 := s.state.NewTask("setup-profiles", "") 773 task3 := s.state.NewTask("link-snap", "") 774 task2.WaitFor(task1) 775 task3.WaitFor(task2) 776 task2.Set("snap-setup", sup2) 777 task3.Set("snap-setup", sup2) 778 779 chg := s.state.NewChange("test", "") 780 chg.AddTask(task0) 781 chg.AddTask(task1) 782 chg.AddTask(task2) 783 chg.AddTask(task3) 784 785 s.state.Unlock() 786 787 for i := 0; i < 10; i++ { 788 s.se.Ensure() 789 s.se.Wait() 790 } 791 792 s.state.Lock() 793 794 // ensure all our tasks ran 795 c.Assert(chg.Err(), IsNil) 796 c.Assert(chg.Tasks(), HasLen, 6) 797 798 // sanity checks 799 t := chg.Tasks()[1] 800 c.Assert(t.Kind(), Equals, "link-snap") 801 t = chg.Tasks()[3] 802 c.Assert(t.Kind(), Equals, "link-snap") 803 804 // check that auto-connect tasks were added and have snap-setup 805 var autoconnectSup snapstate.SnapSetup 806 t = chg.Tasks()[4] 807 c.Assert(t.Kind(), Equals, "auto-connect") 808 c.Assert(t.Get("snap-setup", &autoconnectSup), IsNil) 809 c.Assert(autoconnectSup.InstanceName(), Equals, "snap1") 810 811 t = chg.Tasks()[5] 812 c.Assert(t.Kind(), Equals, "auto-connect") 813 c.Assert(t.Get("snap-setup", &autoconnectSup), IsNil) 814 c.Assert(autoconnectSup.InstanceName(), Equals, "snap2") 815 } 816 817 func (s *linkSnapSuite) TestDoLinkSnapFailureCleansUpAux(c *C) { 818 // this is very chummy with the order of LinkSnap 819 c.Assert(ioutil.WriteFile(dirs.SnapSeqDir, nil, 0644), IsNil) 820 821 // we start without the auxiliary store info 822 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 823 824 s.state.Lock() 825 t := s.state.NewTask("link-snap", "test") 826 t.Set("snap-setup", &snapstate.SnapSetup{ 827 SideInfo: &snap.SideInfo{ 828 RealName: "foo", 829 Revision: snap.R(33), 830 SnapID: "foo-id", 831 }, 832 Channel: "beta", 833 UserID: 2, 834 }) 835 s.state.NewChange("dummy", "...").AddTask(t) 836 837 s.state.Unlock() 838 839 s.se.Ensure() 840 s.se.Wait() 841 842 s.state.Lock() 843 defer s.state.Unlock() 844 845 c.Check(t.Status(), Equals, state.ErrorStatus) 846 c.Check(s.stateBackend.restartRequested, HasLen, 0) 847 848 // we end without the auxiliary store info 849 c.Check(snapstate.AuxStoreInfoFilename("foo-id"), testutil.FileAbsent) 850 } 851 852 func (s *linkSnapSuite) TestLinkSnapResetsRefreshInhibitedTime(c *C) { 853 // When a snap is linked the refresh-inhibited-time is reset to zero 854 // to indicate a successful refresh. The old value is stored in task 855 // state for task undo logic. 856 s.state.Lock() 857 defer s.state.Unlock() 858 859 instant := time.Now() 860 861 si := &snap.SideInfo{RealName: "snap", Revision: snap.R(1)} 862 sup := &snapstate.SnapSetup{SideInfo: si} 863 snapstate.Set(s.state, "snap", &snapstate.SnapState{ 864 Sequence: []*snap.SideInfo{si}, 865 Current: si.Revision, 866 RefreshInhibitedTime: &instant, 867 }) 868 869 task := s.state.NewTask("link-snap", "") 870 task.Set("snap-setup", sup) 871 chg := s.state.NewChange("test", "") 872 chg.AddTask(task) 873 874 s.state.Unlock() 875 876 for i := 0; i < 10; i++ { 877 s.se.Ensure() 878 s.se.Wait() 879 } 880 881 s.state.Lock() 882 883 c.Assert(chg.Err(), IsNil) 884 c.Assert(chg.Tasks(), HasLen, 1) 885 886 var snapst snapstate.SnapState 887 err := snapstate.Get(s.state, "snap", &snapst) 888 c.Assert(err, IsNil) 889 c.Check(snapst.RefreshInhibitedTime, IsNil) 890 891 var oldTime time.Time 892 c.Assert(task.Get("old-refresh-inhibited-time", &oldTime), IsNil) 893 c.Check(oldTime.Equal(instant), Equals, true) 894 } 895 896 func (s *linkSnapSuite) TestDoUndoLinkSnapRestoresRefreshInhibitedTime(c *C) { 897 s.state.Lock() 898 defer s.state.Unlock() 899 900 instant := time.Now() 901 902 si := &snap.SideInfo{RealName: "snap", Revision: snap.R(1)} 903 sup := &snapstate.SnapSetup{SideInfo: si} 904 snapstate.Set(s.state, "snap", &snapstate.SnapState{ 905 Sequence: []*snap.SideInfo{si}, 906 Current: si.Revision, 907 RefreshInhibitedTime: &instant, 908 }) 909 910 task := s.state.NewTask("link-snap", "") 911 task.Set("snap-setup", sup) 912 chg := s.state.NewChange("test", "") 913 chg.AddTask(task) 914 915 terr := s.state.NewTask("error-trigger", "provoking total undo") 916 terr.WaitFor(task) 917 chg.AddTask(terr) 918 919 s.state.Unlock() 920 921 for i := 0; i < 6; i++ { 922 s.se.Ensure() 923 s.se.Wait() 924 } 925 926 s.state.Lock() 927 928 c.Assert(chg.Err(), NotNil) 929 c.Assert(chg.Tasks(), HasLen, 2) 930 c.Check(task.Status(), Equals, state.UndoneStatus) 931 932 var snapst snapstate.SnapState 933 err := snapstate.Get(s.state, "snap", &snapst) 934 c.Assert(err, IsNil) 935 c.Check(snapst.RefreshInhibitedTime.Equal(instant), Equals, true) 936 } 937 938 func (s *linkSnapSuite) TestDoUnlinkSnapRefreshAwarenessHardCheck(c *C) { 939 s.state.Lock() 940 defer s.state.Unlock() 941 942 tr := config.NewTransaction(s.state) 943 tr.Set("core", "experimental.refresh-app-awareness", true) 944 tr.Commit() 945 946 chg := s.testDoUnlinkSnapRefreshAwareness(c) 947 948 c.Check(chg.Err(), ErrorMatches, `(?ms).*^- some-change-descr \(snap "some-snap" has running apps \(some-app\)\).*`) 949 } 950 951 func (s *linkSnapSuite) TestDoUnlinkSnapRefreshHardCheckOff(c *C) { 952 s.state.Lock() 953 defer s.state.Unlock() 954 955 chg := s.testDoUnlinkSnapRefreshAwareness(c) 956 957 c.Check(chg.Err(), IsNil) 958 } 959 960 func (s *linkSnapSuite) testDoUnlinkSnapRefreshAwareness(c *C) *state.Change { 961 restore := release.MockOnClassic(true) 962 defer restore() 963 964 // mock that "some-snap" has an app and that this app has pids running 965 writePids(c, filepath.Join(dirs.PidsCgroupDir, "snap.some-snap.some-app"), []int{1234}) 966 snapstate.MockSnapReadInfo(func(name string, si *snap.SideInfo) (*snap.Info, error) { 967 info := &snap.Info{SuggestedName: name, SideInfo: *si, SnapType: snap.TypeApp} 968 info.Apps = map[string]*snap.AppInfo{ 969 "some-app": {Snap: info, Name: "some-app"}, 970 } 971 return info, nil 972 }) 973 974 si1 := &snap.SideInfo{ 975 RealName: "some-snap", 976 Revision: snap.R(1), 977 } 978 snapstate.Set(s.state, "some-snap", &snapstate.SnapState{ 979 Sequence: []*snap.SideInfo{si1}, 980 Current: si1.Revision, 981 Active: true, 982 }) 983 t := s.state.NewTask("unlink-current-snap", "some-change-descr") 984 t.Set("snap-setup", &snapstate.SnapSetup{ 985 SideInfo: si1, 986 }) 987 chg := s.state.NewChange("dummy", "...") 988 chg.AddTask(t) 989 990 s.state.Unlock() 991 defer s.state.Lock() 992 993 for i := 0; i < 3; i++ { 994 s.se.Ensure() 995 s.se.Wait() 996 } 997 998 return chg 999 }